From f0532a29e38d01d6f3e44435198527323d507cf9 Mon Sep 17 00:00:00 2001 From: Oleg Gaidarenko Date: Fri, 13 Nov 2015 17:22:36 +0300 Subject: [PATCH] Callbacks: bring back size reduction Ref 4cbf02df84dbcaa44b75a64ed832f7dbff2231dd --- src/callbacks.js | 158 +++++++++++++++++++++-------------------------- 1 file changed, 71 insertions(+), 87 deletions(-) diff --git a/src/callbacks.js b/src/callbacks.js index ce21571ed..df7c7cf0e 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,71 +39,71 @@ 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 ) { + 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; + } + + firing = false; - // If repeatable, check for pending execution - if ( stack ) { - if ( stack.length ) { - fire( stack.shift() ); - } + // 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 = ""; } } }, @@ -118,16 +115,19 @@ jQuery.Callbacks = function( options ) { 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 ); @@ -135,16 +135,8 @@ jQuery.Callbacks = function( options ) { } ); } )( 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; @@ -152,38 +144,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; }, @@ -192,8 +178,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() { @@ -204,10 +190,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 ) { - self.disable(); + list = memory = ""; } return this; }, @@ -220,10 +205,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; -- 2.39.5