Commit f5a8c649 authored by Richard Gibson's avatar Richard Gibson

Callbacks: No object starts out locked

Fixes gh-1989
parent 6f0db531
...@@ -51,6 +51,8 @@ jQuery.Callbacks = function( options ) { ...@@ -51,6 +51,8 @@ jQuery.Callbacks = function( options ) {
memory, memory,
// Flag to know if list was already fired // Flag to know if list was already fired
fired, fired,
// Flag to prevent .fire/.fireWith
locked,
// End of the loop when firing // End of the loop when firing
firingLength, firingLength,
// Index of currently firing callback (modified by remove if needed) // Index of currently firing callback (modified by remove if needed)
...@@ -63,6 +65,7 @@ jQuery.Callbacks = function( options ) { ...@@ -63,6 +65,7 @@ jQuery.Callbacks = function( options ) {
stack = !options.once && [], stack = !options.once && [],
// Fire callbacks // Fire callbacks
fire = function( data ) { fire = function( data ) {
locked = options.once;
memory = options.memory && data; memory = options.memory && data;
fired = true; fired = true;
firingIndex = firingStart || 0; firingIndex = firingStart || 0;
...@@ -78,13 +81,21 @@ jQuery.Callbacks = function( options ) { ...@@ -78,13 +81,21 @@ jQuery.Callbacks = function( options ) {
} }
} }
firing = false; firing = false;
// If not disabled,
if ( list ) { if ( list ) {
// If repeatable, check for pending execution
if ( stack ) { if ( stack ) {
if ( stack.length ) { if ( stack.length ) {
fire( stack.shift() ); fire( stack.shift() );
} }
// If not repeatable but with memory, clear out spent callbacks
} else if ( memory ) { } else if ( memory ) {
list = []; list = [];
// Else, disable
} else { } else {
self.disable(); self.disable();
} }
...@@ -123,6 +134,7 @@ jQuery.Callbacks = function( options ) { ...@@ -123,6 +134,7 @@ jQuery.Callbacks = function( options ) {
} }
return this; return this;
}, },
// Remove a callback from the list // Remove a callback from the list
remove: function() { remove: function() {
if ( list ) { if ( list ) {
...@@ -144,11 +156,13 @@ jQuery.Callbacks = function( options ) { ...@@ -144,11 +156,13 @@ jQuery.Callbacks = function( options ) {
} }
return this; return this;
}, },
// Check if a given callback is in the list. // Check if a given callback is in the list.
// If no argument is given, return whether or not list has callbacks attached. // If no argument is given, return whether or not list has callbacks attached.
has: function( fn ) { has: function( fn ) {
return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length ); return fn ? jQuery.inArray( fn, list ) > -1 : !!( list && list.length );
}, },
// Remove all callbacks from the list // Remove all callbacks from the list
empty: function() { empty: function() {
if ( list ) { if ( list ) {
...@@ -157,30 +171,37 @@ jQuery.Callbacks = function( options ) { ...@@ -157,30 +171,37 @@ jQuery.Callbacks = function( options ) {
} }
return this; return this;
}, },
// Have the list do nothing anymore
// Disable .fire and .add
// Abort any current/pending executions
// Clear all callbacks and values
disable: function() { disable: function() {
list = stack = memory = undefined; list = stack = memory = undefined;
locked = true;
return this; return this;
}, },
// Is it disabled?
disabled: function() { disabled: function() {
return !list; return !list;
}, },
// Lock the list in its current state
// Disable .fire
// Also disable .add unless we have memory (since it would have no effect)
// Abort any pending executions
lock: function() { lock: function() {
stack = undefined; stack = undefined;
locked = true;
if ( !memory ) { if ( !memory ) {
self.disable(); self.disable();
} }
return this; return this;
}, },
// Is it locked?
locked: function() { locked: function() {
return !stack; return !!locked;
}, },
// Call all callbacks with the given context and arguments // Call all callbacks with the given context and arguments
fireWith: function( context, args ) { fireWith: function( context, args ) {
if ( list && ( !fired || stack ) ) { if ( !locked ) {
args = args || []; args = args || [];
args = [ context, args.slice ? args.slice() : args ]; args = [ context, args.slice ? args.slice() : args ];
if ( firing ) { if ( firing ) {
...@@ -191,11 +212,13 @@ jQuery.Callbacks = function( options ) { ...@@ -191,11 +212,13 @@ jQuery.Callbacks = function( options ) {
} }
return this; return this;
}, },
// Call all the callbacks with the given arguments // Call all the callbacks with the given arguments
fire: function() { fire: function() {
self.fireWith( this, arguments ); self.fireWith( this, arguments );
return this; return this;
}, },
// To know if the callbacks have already been called at least once // To know if the callbacks have already been called at least once
fired: function() { fired: function() {
return !!fired; return !!fired;
......
...@@ -65,11 +65,7 @@ jQuery.each( tests, function( strFlags, resultString ) { ...@@ -65,11 +65,7 @@ jQuery.each( tests, function( strFlags, resultString ) {
test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function() { test( "jQuery.Callbacks( " + showFlags( flags ) + " ) - " + filterLabel, function() {
expect( 21 ); expect( 28 );
// Give qunit a little breathing room
stop();
setTimeout( start, 0 );
var cblist, var cblist,
results = resultString.split( /\s+/ ); results = resultString.split( /\s+/ );
...@@ -77,10 +73,14 @@ jQuery.each( tests, function( strFlags, resultString ) { ...@@ -77,10 +73,14 @@ jQuery.each( tests, function( strFlags, resultString ) {
// Basic binding and firing // Basic binding and firing
output = "X"; output = "X";
cblist = jQuery.Callbacks( flags ); cblist = jQuery.Callbacks( flags );
strictEqual( cblist.locked(), false, ".locked() initially false" );
strictEqual( cblist.disabled(), false, ".disabled() initially false" );
strictEqual( cblist.fired(), false, ".fired() initially false" );
cblist.add(function( str ) { cblist.add(function( str ) {
output += str; output += str;
}); });
cblist.fire("A"); strictEqual( cblist.fired(), false, ".fired() still false after .add" );
cblist.fire( "A" );
strictEqual( output, "XA", "Basic binding and firing" ); strictEqual( output, "XA", "Basic binding and firing" );
strictEqual( cblist.fired(), true, ".fired() detects firing" ); strictEqual( cblist.fired(), true, ".fired() detects firing" );
output = "X"; output = "X";
...@@ -91,6 +91,8 @@ jQuery.each( tests, function( strFlags, resultString ) { ...@@ -91,6 +91,8 @@ jQuery.each( tests, function( strFlags, resultString ) {
strictEqual( output, "X", "Adding a callback after disabling" ); strictEqual( output, "X", "Adding a callback after disabling" );
cblist.fire("A"); cblist.fire("A");
strictEqual( output, "X", "Firing after disabling" ); strictEqual( output, "X", "Firing after disabling" );
strictEqual( cblist.disabled(), true, ".disabled() becomes true" );
strictEqual( cblist.locked(), true, "disabling locks" );
// #13517 - Emptying while firing // #13517 - Emptying while firing
cblist = jQuery.Callbacks( flags ); cblist = jQuery.Callbacks( flags );
...@@ -160,6 +162,7 @@ jQuery.each( tests, function( strFlags, resultString ) { ...@@ -160,6 +162,7 @@ jQuery.each( tests, function( strFlags, resultString ) {
output += str; output += str;
}); });
strictEqual( output, "X", "Lock early" ); strictEqual( output, "X", "Lock early" );
strictEqual( cblist.locked(), true, "Locking reflected in accessor" );
// Ordering // Ordering
output = "X"; output = "X";
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment