blob: 6cf54031e39182183a2a64a0e13bdb136053cf52 [file] [log] [blame]
Copybara botbe50d492023-11-30 00:16:42 +01001define( [
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
11function 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 */
41jQuery.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
235return jQuery;
236} );