| 1 | define( [
|
| 2 | "./core",
|
| 3 | "./core/toType",
|
| 4 | "./var/isFunction",
|
| 5 | "./var/rnothtmlwhite"
|
| 6 | ], function( jQuery, toType, isFunction, rnothtmlwhite ) {
|
| 7 |
|
| 8 | ;
|
| 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 | } );
|