callbacks.js (5552B)
1 define( [ 2 "./core", 3 "./core/toType", 4 "./var/isFunction", 5 "./var/rnothtmlwhite" 6 ], function( jQuery, toType, isFunction, rnothtmlwhite ) { 7 8 "use strict"; 9 10 // Convert String-formatted options into Object-formatted ones 11 function createOptions( options ) { 12 var object = {}; 13 jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) { 14 object[ flag ] = true; 15 } ); 16 return object; 17 } 18 19 /* 20 * Create a callback list using the following parameters: 21 * 22 * options: an optional list of space-separated options that will change how 23 * the callback list behaves or a more traditional option object 24 * 25 * By default a callback list will act like an event callback list and can be 26 * "fired" multiple times. 27 * 28 * Possible options: 29 * 30 * once: will ensure the callback list can only be fired once (like a Deferred) 31 * 32 * memory: will keep track of previous values and will call any callback added 33 * after the list has been fired right away with the latest "memorized" 34 * values (like a Deferred) 35 * 36 * unique: will ensure a callback can only be added once (no duplicate in the list) 37 * 38 * stopOnFalse: interrupt callings when a callback returns false 39 * 40 */ 41 jQuery.Callbacks = function( options ) { 42 43 // Convert options from String-formatted to Object-formatted if needed 44 // (we check in cache first) 45 options = typeof options === "string" ? 46 createOptions( options ) : 47 jQuery.extend( {}, options ); 48 49 var // Flag to know if list is currently firing 50 firing, 51 52 // Last fire value for non-forgettable lists 53 memory, 54 55 // Flag to know if list was already fired 56 fired, 57 58 // Flag to prevent firing 59 locked, 60 61 // Actual callback list 62 list = [], 63 64 // Queue of execution data for repeatable lists 65 queue = [], 66 67 // Index of currently firing callback (modified by add/remove as needed) 68 firingIndex = -1, 69 70 // Fire callbacks 71 fire = function() { 72 73 // Enforce single-firing 74 locked = locked || options.once; 75 76 // Execute callbacks for all pending executions, 77 // respecting firingIndex overrides and runtime changes 78 fired = firing = true; 79 for ( ; queue.length; firingIndex = -1 ) { 80 memory = queue.shift(); 81 while ( ++firingIndex < list.length ) { 82 83 // Run callback and check for early termination 84 if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && 85 options.stopOnFalse ) { 86 87 // Jump to end and forget the data so .add doesn't re-fire 88 firingIndex = list.length; 89 memory = false; 90 } 91 } 92 } 93 94 // Forget the data if we're done with it 95 if ( !options.memory ) { 96 memory = false; 97 } 98 99 firing = false; 100 101 // Clean up if we're done firing for good 102 if ( locked ) { 103 104 // Keep an empty list if we have data for future add calls 105 if ( memory ) { 106 list = []; 107 108 // Otherwise, this object is spent 109 } else { 110 list = ""; 111 } 112 } 113 }, 114 115 // Actual Callbacks object 116 self = { 117 118 // Add a callback or a collection of callbacks to the list 119 add: function() { 120 if ( list ) { 121 122 // If we have memory from a past run, we should fire after adding 123 if ( memory && !firing ) { 124 firingIndex = list.length - 1; 125 queue.push( memory ); 126 } 127 128 ( function add( args ) { 129 jQuery.each( args, function( _, arg ) { 130 if ( isFunction( arg ) ) { 131 if ( !options.unique || !self.has( arg ) ) { 132 list.push( arg ); 133 } 134 } else if ( arg && arg.length && toType( arg ) !== "string" ) { 135 136 // Inspect recursively 137 add( arg ); 138 } 139 } ); 140 } )( arguments ); 141 142 if ( memory && !firing ) { 143 fire(); 144 } 145 } 146 return this; 147 }, 148 149 // Remove a callback from the list 150 remove: function() { 151 jQuery.each( arguments, function( _, arg ) { 152 var index; 153 while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { 154 list.splice( index, 1 ); 155 156 // Handle firing indexes 157 if ( index <= firingIndex ) { 158 firingIndex--; 159 } 160 } 161 } ); 162 return this; 163 }, 164 165 // Check if a given callback is in the list. 166 // If no argument is given, return whether or not list has callbacks attached. 167 has: function( fn ) { 168 return fn ? 169 jQuery.inArray( fn, list ) > -1 : 170 list.length > 0; 171 }, 172 173 // Remove all callbacks from the list 174 empty: function() { 175 if ( list ) { 176 list = []; 177 } 178 return this; 179 }, 180 181 // Disable .fire and .add 182 // Abort any current/pending executions 183 // Clear all callbacks and values 184 disable: function() { 185 locked = queue = []; 186 list = memory = ""; 187 return this; 188 }, 189 disabled: function() { 190 return !list; 191 }, 192 193 // Disable .fire 194 // Also disable .add unless we have memory (since it would have no effect) 195 // Abort any pending executions 196 lock: function() { 197 locked = queue = []; 198 if ( !memory && !firing ) { 199 list = memory = ""; 200 } 201 return this; 202 }, 203 locked: function() { 204 return !!locked; 205 }, 206 207 // Call all callbacks with the given context and arguments 208 fireWith: function( context, args ) { 209 if ( !locked ) { 210 args = args || []; 211 args = [ context, args.slice ? args.slice() : args ]; 212 queue.push( args ); 213 if ( !firing ) { 214 fire(); 215 } 216 } 217 return this; 218 }, 219 220 // Call all the callbacks with the given arguments 221 fire: function() { 222 self.fireWith( this, arguments ); 223 return this; 224 }, 225 226 // To know if the callbacks have already been called at least once 227 fired: function() { 228 return !!fired; 229 } 230 }; 231 232 return self; 233 }; 234 235 return jQuery; 236 } );