From 18baae2efb36a6c759c0dddac7d25da9c554dff7 Mon Sep 17 00:00:00 2001 From: Richard Gibson Date: Sat, 3 Jan 2015 20:48:54 -0500 Subject: [PATCH] Callbacks: Reduce size --- src/callbacks.js | 156 ++++++++++++++++++++++------------------------- 1 file changed, 74 insertions(+), 82 deletions(-) diff --git a/src/callbacks.js b/src/callbacks.js index 4ce1bcdc5..bec37ef54 100644 --- a/src/callbacks.js +++ b/src/callbacks.js @@ -3,12 +3,9 @@ define([ "./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; }); @@ -42,94 +39,96 @@ jQuery.Callbacks = function( options ) { // 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 ) { - 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 ) { + fire = function() { - memory = false; // To prevent further calls using add - break; + // Enforce single-firing + 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; - // If not disabled, - if ( list ) { + // Clean up if we're done firing for good + if ( locked ) { - // If repeatable, check for pending execution - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } - - // 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; @@ -137,37 +136,32 @@ jQuery.Callbacks = function( options ) { // 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; }, @@ -176,8 +170,8 @@ jQuery.Callbacks = function( options ) { // 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() { @@ -188,10 +182,9 @@ jQuery.Callbacks = function( options ) { // 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; }, @@ -204,10 +197,9 @@ jQuery.Callbacks = function( options ) { 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;