Skip to content

Commit 0d829f0

Browse files
committed
Callbacks: No object starts out locked
Fixes jquerygh-1989 (cherry picked from commit f5a8c64)
1 parent 0c9d018 commit 0d829f0

File tree

2 files changed

+43
-17
lines changed

2 files changed

+43
-17
lines changed

src/callbacks.js

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,24 +45,27 @@ jQuery.Callbacks = function( options ) {
4545
( optionsCache[ options ] || createOptions( options ) ) :
4646
jQuery.extend( {}, options );
4747

48-
var // Last fire value (for non-forgettable lists)
48+
var // Flag to know if list is currently firing
49+
firing,
50+
// Last fire value (for non-forgettable lists)
4951
memory,
5052
// Flag to know if list was already fired
5153
fired,
52-
// Flag to know if list is currently firing
53-
firing,
54-
// First callback to fire (used internally by add and fireWith)
55-
firingStart,
54+
// Flag to prevent .fire/.fireWith
55+
locked,
5656
// End of the loop when firing
5757
firingLength,
5858
// Index of currently firing callback (modified by remove if needed)
5959
firingIndex,
60+
// First callback to fire (used internally by add and fireWith)
61+
firingStart,
6062
// Actual callback list
6163
list = [],
6264
// Stack of fire calls for repeatable lists
6365
stack = !options.once && [],
6466
// Fire callbacks
6567
fire = function( data ) {
68+
locked = options.once;
6669
memory = options.memory && data;
6770
fired = true;
6871
firingIndex = firingStart || 0;
@@ -78,13 +81,21 @@ jQuery.Callbacks = function( options ) {
7881
}
7982
}
8083
firing = false;
84+
85+
// If not disabled,
8186
if ( list ) {
87+
88+
// If repeatable, check for pending execution
8289
if ( stack ) {
8390
if ( stack.length ) {
8491
fire( stack.shift() );
8592
}
93+
94+
// If not repeatable but with memory, clear out spent callbacks
8695
} else if ( memory ) {
8796
list = [];
97+
98+
// Else, disable
8899
} else {
89100
self.disable();
90101
}
@@ -123,6 +134,7 @@ jQuery.Callbacks = function( options ) {
123134
}
124135
return this;
125136
},
137+
126138
// Remove a callback from the list
127139
remove: function() {
128140
if ( list ) {
@@ -144,11 +156,13 @@ jQuery.Callbacks = function( options ) {
144156
}
145157
return this;
146158
},
159+
147160
// Check if a given callback is in the list.
148161
// If no argument is given, return whether or not list has callbacks attached.
149162
has: function( fn ) {
150163
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
151164
},
165+
152166
// Remove all callbacks from the list
153167
empty: function() {
154168
if ( list ) {
@@ -157,30 +171,37 @@ jQuery.Callbacks = function( options ) {
157171
}
158172
return this;
159173
},
160-
// Have the list do nothing anymore
174+
175+
// Disable .fire and .add
176+
// Abort any current/pending executions
177+
// Clear all callbacks and values
161178
disable: function() {
162179
list = stack = memory = undefined;
180+
locked = true;
163181
return this;
164182
},
165-
// Is it disabled?
166183
disabled: function() {
167184
return !list;
168185
},
169-
// Lock the list in its current state
186+
187+
// Disable .fire
188+
// Also disable .add unless we have memory (since it would have no effect)
189+
// Abort any pending executions
170190
lock: function() {
171191
stack = undefined;
192+
locked = true;
172193
if ( !memory ) {
173194
self.disable();
174195
}
175196
return this;
176197
},
177-
// Is it locked?
178198
locked: function() {
179-
return !stack;
199+
return !!locked;
180200
},
201+
181202
// Call all callbacks with the given context and arguments
182203
fireWith: function( context, args ) {
183-
if ( list && ( !fired || stack ) ) {
204+
if ( !locked ) {
184205
args = args || [];
185206
args = [ context, args.slice ? args.slice() : args ];
186207
if ( firing ) {
@@ -191,11 +212,13 @@ jQuery.Callbacks = function( options ) {
191212
}
192213
return this;
193214
},
215+
194216
// Call all the callbacks with the given arguments
195217
fire: function() {
196218
self.fireWith( this, arguments );
197219
return this;
198220
},
221+
199222
// To know if the callbacks have already been called at least once
200223
fired: function() {
201224
return !!fired;

test/unit/callbacks.js

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,22 @@ jQuery.each( tests, function( strFlags, resultString ) {
6565

6666
test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function() {
6767

68-
expect( 21 );
69-
70-
// Give qunit a little breathing room
71-
stop();
72-
setTimeout( start, 0 );
68+
expect( 28 );
7369

7470
var cblist,
7571
results = resultString.split( /\s+/ );
7672

7773
// Basic binding and firing
7874
output = "X";
7975
cblist = jQuery.Callbacks( flags );
76+
strictEqual( cblist.locked(), false, ".locked() initially false" );
77+
strictEqual( cblist.disabled(), false, ".disabled() initially false" );
78+
strictEqual( cblist.fired(), false, ".fired() initially false" );
8079
cblist.add(function( str ) {
8180
output += str;
8281
});
83-
cblist.fire("A");
82+
strictEqual( cblist.fired(), false, ".fired() still false after .add" );
83+
cblist.fire( "A" );
8484
strictEqual( output, "XA", "Basic binding and firing" );
8585
strictEqual( cblist.fired(), true, ".fired() detects firing" );
8686
output = "X";
@@ -91,6 +91,8 @@ jQuery.each( tests, function( strFlags, resultString ) {
9191
strictEqual( output, "X", "Adding a callback after disabling" );
9292
cblist.fire("A");
9393
strictEqual( output, "X", "Firing after disabling" );
94+
strictEqual( cblist.disabled(), true, ".disabled() becomes true" );
95+
strictEqual( cblist.locked(), true, "disabling locks" );
9496

9597
// #13517 - Emptying while firing
9698
cblist = jQuery.Callbacks( flags );
@@ -160,6 +162,7 @@ jQuery.each( tests, function( strFlags, resultString ) {
160162
output += str;
161163
});
162164
strictEqual( output, "X", "Lock early" );
165+
strictEqual( cblist.locked(), true, "Locking reflected in accessor" );
163166

164167
// Ordering
165168
output = "X";

0 commit comments

Comments
 (0)