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