"./var/rnotwhite"
], function( jQuery, rnotwhite ) {
-// String to Object options format cache
-var optionsCache = {};
-
-// Convert String-formatted options into Object-formatted ones and store in cache
+// Convert String-formatted options into Object-formatted ones
function createOptions( options ) {
- var object = optionsCache[ options ] = {};
+ var object = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true;
});
// Convert options from String-formatted to Object-formatted if needed
// (we check in cache first)
options = typeof options === "string" ?
- ( optionsCache[ options ] || createOptions( options ) ) :
+ createOptions( options ) :
jQuery.extend( {}, options );
var // Flag to know if list is currently firing
firing,
- // Last fire value (for non-forgettable lists)
+ // Last fire value for non-forgettable lists
memory,
// Flag to know if list was already fired
fired,
- // Flag to prevent .fire/.fireWith
+ // Flag to prevent firing
locked,
- // End of the loop when firing
- firingLength,
- // Index of currently firing callback (modified by remove if needed)
- firingIndex,
- // First callback to fire (used internally by add and fireWith)
- firingStart,
// Actual callback list
list = [],
- // Stack of fire calls for repeatable lists
- stack = !options.once && [],
+ // Queue of execution data for repeatable lists
+ queue = [],
+ // Index of currently firing callback (modified by add/remove as needed)
+ firingIndex = -1,
// Fire callbacks
- fire = function( data ) {
+ fire = function() {
+
+ // Enforce single-firing
locked = options.once;
- memory = options.memory && data;
- fired = true;
- firingIndex = firingStart || 0;
- firingStart = 0;
- firingLength = list.length;
- firing = true;
- for ( ; list && firingIndex < firingLength; firingIndex++ ) {
- if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false &&
- options.stopOnFalse ) {
-
- memory = false; // To prevent further calls using add
- break;
+
+ // Execute callbacks for all pending executions,
+ // respecting firingIndex overrides and runtime changes
+ fired = firing = true;
+ for ( ; queue.length; firingIndex = -1 ) {
+ memory = queue.shift();
+ while ( ++firingIndex < list.length ) {
+
+ // Run callback and check for early termination
+ if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
+ options.stopOnFalse ) {
+
+ // Jump to end and forget the data so .add doesn't re-fire
+ firingIndex = list.length;
+ memory = false;
+ }
}
}
- firing = false;
- // If not disabled,
- if ( list ) {
+ // Forget the data if we're done with it
+ if ( !options.memory ) {
+ memory = false;
+ }
- // If repeatable, check for pending execution
- if ( stack ) {
- if ( stack.length ) {
- fire( stack.shift() );
- }
+ firing = false;
+
+ // Clean up if we're done firing for good
+ if ( locked ) {
- // If not repeatable but with memory, clear out spent callbacks
- } else if ( memory ) {
+ // Keep an empty list if we have data for future add calls
+ if ( memory ) {
list = [];
- // Else, disable
+ // Otherwise, this object is spent
} else {
- self.disable();
+ list = "";
}
}
},
+
// Actual Callbacks object
self = {
+
// Add a callback or a collection of callbacks to the list
add: function() {
if ( list ) {
- // First, we save the current length
- var start = list.length;
+
+ // If we have memory from a past run, we should fire after adding
+ if ( memory && !firing ) {
+ firingIndex = list.length - 1;
+ queue.push( memory );
+ }
+
(function add( args ) {
jQuery.each( args, function( _, arg ) {
- var type = jQuery.type( arg );
- if ( type === "function" ) {
+ if ( jQuery.isFunction( arg ) ) {
if ( !options.unique || !self.has( arg ) ) {
list.push( arg );
}
- } else if ( arg && arg.length && type !== "string" ) {
+ } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
// Inspect recursively
add( arg );
}
});
})( arguments );
- // Do we need to add the callbacks to the
- // current firing batch?
- if ( firing ) {
- firingLength = list.length;
- // With memory, if we're not firing then
- // we should call right away
- } else if ( memory ) {
- firingStart = start;
- fire( memory );
+
+ if ( memory && !firing ) {
+ fire();
}
}
return this;
// Remove a callback from the list
remove: function() {
- if ( list ) {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
- // Handle firing indexes
- if ( firing ) {
- if ( index <= firingLength ) {
- firingLength--;
- }
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
+ jQuery.each( arguments, function( _, arg ) {
+ var index;
+ while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
+ list.splice( index, 1 );
+
+ // Handle firing indexes
+ if ( index <= firingIndex ) {
+ firingIndex--;
}
- });
- }
+ }
+ });
return this;
},
// Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) {
- return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
+ return fn ?
+ jQuery.inArray( fn, list ) > -1 :
+ list.length > 0;
},
// Remove all callbacks from the list
empty: function() {
if ( list ) {
list = [];
- firingLength = 0;
}
return this;
},
// Abort any current/pending executions
// Clear all callbacks and values
disable: function() {
- list = stack = memory = undefined;
- locked = true;
+ locked = queue = [];
+ list = memory = "";
return this;
},
disabled: function() {
// Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions
lock: function() {
- stack = undefined;
- locked = true;
+ locked = queue = [];
if ( !memory && !firing ) {
- self.disable();
+ list = memory = "";
}
return this;
},
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
- if ( firing ) {
- stack.push( args );
- } else {
- fire( args );
+ queue.push( args );
+ if ( !firing ) {
+ fire();
}
}
return this;