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