Callbacks: Reduce size

This commit is contained in:
Richard Gibson 2015-01-03 20:48:54 -05:00
parent fc7477f492
commit 18baae2efb

View File

@ -3,12 +3,9 @@ define([
"./var/rnotwhite" "./var/rnotwhite"
], function( jQuery, rnotwhite ) { ], function( jQuery, rnotwhite ) {
// String to Object options format cache // Convert String-formatted options into Object-formatted ones
var optionsCache = {};
// Convert String-formatted options into Object-formatted ones and store in cache
function createOptions( options ) { function createOptions( options ) {
var object = optionsCache[ options ] = {}; var object = {};
jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) {
object[ flag ] = true; object[ flag ] = true;
}); });
@ -42,94 +39,96 @@ jQuery.Callbacks = function( options ) {
// Convert options from String-formatted to Object-formatted if needed // Convert options from String-formatted to Object-formatted if needed
// (we check in cache first) // (we check in cache first)
options = typeof options === "string" ? options = typeof options === "string" ?
( optionsCache[ options ] || createOptions( options ) ) : createOptions( options ) :
jQuery.extend( {}, options ); jQuery.extend( {}, options );
var // Flag to know if list is currently firing var // Flag to know if list is currently firing
firing, firing,
// Last fire value (for non-forgettable lists) // Last fire value for non-forgettable lists
memory, memory,
// Flag to know if list was already fired // Flag to know if list was already fired
fired, fired,
// Flag to prevent .fire/.fireWith // Flag to prevent firing
locked, 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 // Actual callback list
list = [], list = [],
// Stack of fire calls for repeatable lists // Queue of execution data for repeatable lists
stack = !options.once && [], queue = [],
// Index of currently firing callback (modified by add/remove as needed)
firingIndex = -1,
// Fire callbacks // Fire callbacks
fire = function( data ) { fire = function() {
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 // Enforce single-firing
break; locked = options.once;
// 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;
}
} }
} }
// Forget the data if we're done with it
if ( !options.memory ) {
memory = false;
}
firing = false; firing = false;
// If not disabled, // Clean up if we're done firing for good
if ( list ) { if ( locked ) {
// If repeatable, check for pending execution // Keep an empty list if we have data for future add calls
if ( stack ) { if ( memory ) {
if ( stack.length ) {
fire( stack.shift() );
}
// If not repeatable but with memory, clear out spent callbacks
} else if ( memory ) {
list = []; list = [];
// Else, disable // Otherwise, this object is spent
} else { } else {
self.disable(); list = "";
} }
} }
}, },
// Actual Callbacks object // Actual Callbacks object
self = { self = {
// Add a callback or a collection of callbacks to the list // Add a callback or a collection of callbacks to the list
add: function() { add: function() {
if ( list ) { 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 ) { (function add( args ) {
jQuery.each( args, function( _, arg ) { jQuery.each( args, function( _, arg ) {
var type = jQuery.type( arg ); if ( jQuery.isFunction( arg ) ) {
if ( type === "function" ) {
if ( !options.unique || !self.has( arg ) ) { if ( !options.unique || !self.has( arg ) ) {
list.push( arg ); list.push( arg );
} }
} else if ( arg && arg.length && type !== "string" ) { } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) {
// Inspect recursively // Inspect recursively
add( arg ); add( arg );
} }
}); });
})( arguments ); })( arguments );
// Do we need to add the callbacks to the
// current firing batch? if ( memory && !firing ) {
if ( firing ) { fire();
firingLength = list.length;
// With memory, if we're not firing then
// we should call right away
} else if ( memory ) {
firingStart = start;
fire( memory );
} }
} }
return this; return this;
@ -137,37 +136,32 @@ jQuery.Callbacks = function( options ) {
// Remove a callback from the list // Remove a callback from the list
remove: function() { remove: function() {
if ( list ) { jQuery.each( arguments, function( _, arg ) {
jQuery.each( arguments, function( _, arg ) { var index;
var index; while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 );
list.splice( index, 1 );
// Handle firing indexes // Handle firing indexes
if ( firing ) { if ( index <= firingIndex ) {
if ( index <= firingLength ) { firingIndex--;
firingLength--;
}
if ( index <= firingIndex ) {
firingIndex--;
}
}
} }
}); }
} });
return this; return this;
}, },
// Check if a given callback is in the list. // Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached. // If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) { 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 // Remove all callbacks from the list
empty: function() { empty: function() {
if ( list ) { if ( list ) {
list = []; list = [];
firingLength = 0;
} }
return this; return this;
}, },
@ -176,8 +170,8 @@ jQuery.Callbacks = function( options ) {
// Abort any current/pending executions // Abort any current/pending executions
// Clear all callbacks and values // Clear all callbacks and values
disable: function() { disable: function() {
list = stack = memory = undefined; locked = queue = [];
locked = true; list = memory = "";
return this; return this;
}, },
disabled: function() { disabled: function() {
@ -188,10 +182,9 @@ jQuery.Callbacks = function( options ) {
// Also disable .add unless we have memory (since it would have no effect) // Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions // Abort any pending executions
lock: function() { lock: function() {
stack = undefined; locked = queue = [];
locked = true;
if ( !memory && !firing ) { if ( !memory && !firing ) {
self.disable(); list = memory = "";
} }
return this; return this;
}, },
@ -204,10 +197,9 @@ jQuery.Callbacks = function( options ) {
if ( !locked ) { if ( !locked ) {
args = args || []; args = args || [];
args = [ context, args.slice ? args.slice() : args ]; args = [ context, args.slice ? args.slice() : args ];
if ( firing ) { queue.push( args );
stack.push( args ); if ( !firing ) {
} else { fire();
fire( args );
} }
} }
return this; return this;