(function($){
  '$:nomunge'; // Used by YUI compressor.

  var cache = {},

    // Reused internal string.
    doTimeout = 'doTimeout',

    // A convenient shortcut.
    aps = Array.prototype.slice;


  $[doTimeout] = function() {
    return p_doTimeout.apply( window, [ 0 ].concat( aps.call( arguments ) ) );
  };


  $.fn[doTimeout] = function() {
    var args = aps.call( arguments ),
      result = p_doTimeout.apply( this, [ doTimeout + args[0] ].concat( args ) );

    return typeof args[0] === 'number' || typeof args[1] === 'number'
      ? this
      : result;
  };

  function p_doTimeout( jquery_data_key ) {
    var that = this,
      elem,
      data = {},

      // Allows the plugin to call a string callback method.
      method_base = jquery_data_key ? $.fn : $,

      // Any additional arguments will be passed to the callback.
      args = arguments,
      slice_args = 4,

      id        = args[1],
      delay     = args[2],
      callback  = args[3];

    if ( typeof id !== 'string' ) {
      slice_args--;

      id        = jquery_data_key = 0;
      delay     = args[1];
      callback  = args[2];
    }

    if ( jquery_data_key ) { // Note: key is 'doTimeout' + id

      // Get id-object from the first element's data, otherwise initialize it to {}.
      elem = that.eq(0);
      elem.data( jquery_data_key, data = elem.data( jquery_data_key ) || {} );

    } else if ( id ) {
      // Get id-object from the cache, otherwise initialize it to {}.
      data = cache[ id ] || ( cache[ id ] = {} );
    }

    data.id && clearTimeout( data.id );
    delete data.id;

    function cleanup() {
      if ( jquery_data_key ) {
        elem.removeData( jquery_data_key );
      } else if ( id ) {
        delete cache[ id ];
      }
    };

    function actually_setTimeout() {
      data.id = setTimeout( function(){ data.fn(); }, delay );
    };

    if ( callback ) {
      // A callback (and delay) were specified. Store the callback reference for
      // possible later use, and then setTimeout.
      data.fn = function( no_polling_loop ) {

        // If the callback value is a string, it is assumed to be the name of a
        // method on $ or $.fn depending on where doTimeout was executed.
        if ( typeof callback === 'string' ) {
          callback = method_base[ callback ];
        }

        callback.apply( that, aps.call( args, slice_args ) ) === true && !no_polling_loop

          // Since the callback returned true, and we're not specifically
          // canceling a polling loop, do it again!
          ? actually_setTimeout()

          // Otherwise, clean up and quit.
          : cleanup();
      };

      // Set that timeout!
      actually_setTimeout();

    } else if ( data.fn ) {
      delay === undefined ? cleanup() : data.fn( delay === false );
      return true;

    } else {
      cleanup();
    }

  };

})(jQuery);
