Skip to content

Commit a31195f

Browse files
committed
Replaces jQuery._Deferred with the much more flexible (and public) jQuery.Callbacks. Hopefully, jQuery.Callbacks can be used as a base for all callback lists needs in jQuery. Also adds progress callbacks to Deferreds (handled in jQuery.when too). Needs more unit tests.
1 parent 9d4033d commit a31195f

File tree

15 files changed

+316
-272
lines changed

15 files changed

+316
-272
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ COMPILER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js --unsafe
1010
POST_COMPILER = ${JS_ENGINE} ${BUILD_DIR}/post-compile.js
1111

1212
BASE_FILES = ${SRC_DIR}/core.js\
13+
${SRC_DIR}/callbacks.js\
1314
${SRC_DIR}/deferred.js\
1415
${SRC_DIR}/support.js\
1516
${SRC_DIR}/data.js\

src/ajax.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,7 @@ jQuery.extend({
382382
jQuery( callbackContext ) : jQuery.event,
383383
// Deferreds
384384
deferred = jQuery.Deferred(),
385-
completeDeferred = jQuery._Deferred(),
385+
completeCallbacks = jQuery.Callbacks( "once memory" ),
386386
// Status-dependent callbacks
387387
statusCode = s.statusCode || {},
388388
// ifModified key
@@ -560,7 +560,7 @@ jQuery.extend({
560560
}
561561

562562
// Complete
563-
completeDeferred.resolveWith( callbackContext, [ jqXHR, statusText ] );
563+
completeCallbacks.fireWith( callbackContext, [ jqXHR, statusText ] );
564564

565565
if ( fireGlobals ) {
566566
globalEventContext.trigger( "ajaxComplete", [ jqXHR, s] );
@@ -575,7 +575,7 @@ jQuery.extend({
575575
deferred.promise( jqXHR );
576576
jqXHR.success = jqXHR.done;
577577
jqXHR.error = jqXHR.fail;
578-
jqXHR.complete = completeDeferred.done;
578+
jqXHR.complete = completeCallbacks.add;
579579

580580
// Status-dependent callbacks
581581
jqXHR.statusCode = function( map ) {

src/callbacks.js

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
(function( jQuery ) {
2+
3+
// String to Object flags format cache
4+
var flagsCache = {};
5+
6+
// Convert String-formatted flags into Object-formatted ones and store in cache
7+
function createFlags( flags ) {
8+
var object = flagsCache[ flags ] = {},
9+
i, length;
10+
flags = flags.split( /\s+/ );
11+
for ( i = 0, length = flags.length; i < length; i++ ) {
12+
object[ flags[i] ] = true;
13+
}
14+
return object;
15+
}
16+
17+
/*
18+
* Create a callback list using the following parameters:
19+
*
20+
* flags: an optional list of space-separated flags that will change how
21+
* the callback list behaves
22+
*
23+
* filter: an optional function that will be applied to each added callbacks,
24+
* what filter returns will then be added provided it is not falsy.
25+
*
26+
* By default a callback list will act like an event callback list and can be
27+
* "fired" multiple times.
28+
*
29+
* Possible flags:
30+
*
31+
* once: will ensure the callback list can only be fired once (like a Deferred)
32+
*
33+
* memory: will keep track of previous values and will call any callback added
34+
* after the list has been fired right away with the latest "memorized"
35+
* values (like a Deferred)
36+
*
37+
* unique: will ensure a callback can only be added once (no duplicate in the list)
38+
*
39+
* relocate: like "unique" but will relocate the callback at the end of the list
40+
*
41+
*/
42+
jQuery.Callbacks = function( flags, filter ) {
43+
44+
// flags are optional
45+
if ( typeof flags !== "string" ) {
46+
filter = flags;
47+
flags = undefined;
48+
}
49+
50+
// Convert flags from String-formatted to Object-formatted
51+
// (we check in cache first)
52+
flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
53+
54+
var // Actual callback list
55+
list = [],
56+
// Stack of fire calls for repeatable lists
57+
stack = !flags.once && [],
58+
// Last fire value (for non-forgettable lists)
59+
memory,
60+
// Flag to know if list is currently firing
61+
firing,
62+
// Add a list of callbacks to the list
63+
add = function( args ) {
64+
var i,
65+
length,
66+
elem,
67+
type,
68+
actual;
69+
for ( i = 0, length = args.length; i < length; i++ ) {
70+
elem = args[ i ];
71+
type = jQuery.type( elem );
72+
if ( type === "array" ) {
73+
// Inspect recursively
74+
add( elem );
75+
} else if ( type === "function" ) {
76+
// If we have to relocate, we remove the callback
77+
// if it already exists
78+
if ( flags.relocate ) {
79+
object.remove( elem );
80+
}
81+
// Unless we have a list with unicity and the
82+
// function is already there, add it
83+
if ( !( flags.unique && object.has( elem ) ) ) {
84+
// Get the filtered function if needs be
85+
actual = filter ? filter(elem) : elem;
86+
if ( actual ) {
87+
list.push( [ elem, actual ] );
88+
}
89+
}
90+
}
91+
}
92+
},
93+
object = {
94+
// Add a callback or a collection of callbacks to the list
95+
add: function() {
96+
if ( list ) {
97+
add( arguments );
98+
// If we're not firing and we should call right away
99+
if ( !firing && flags.memory && memory ) {
100+
// Trick the list into thinking it needs to be fired
101+
var tmp = memory;
102+
memory = undefined;
103+
object.fireWith( tmp[ 0 ], tmp[ 1 ] );
104+
}
105+
}
106+
return this;
107+
},
108+
// Remove a callback from the list
109+
remove: function( fn ) {
110+
if ( list ) {
111+
var i = 0,
112+
length = list.length;
113+
for ( ; i < length; i++ ) {
114+
if ( fn === list[ i ][ 0 ] ) {
115+
list.splice( i, 1 );
116+
i--;
117+
// If we have some unicity property then
118+
// we only need to do this once
119+
if ( flags.unique || flags.relocate ) {
120+
break;
121+
}
122+
}
123+
}
124+
}
125+
return this;
126+
},
127+
// Control if a given callback is in the list
128+
has: function( fn ) {
129+
if ( list ) {
130+
var i = 0,
131+
length = list.length;
132+
for ( ; i < length; i++ ) {
133+
if ( fn === list[ i ][ 0 ] ) {
134+
return true;
135+
}
136+
}
137+
}
138+
return false;
139+
},
140+
// Remove all callbacks from the list
141+
empty: function() {
142+
list = [];
143+
return this;
144+
},
145+
// Have the list do nothing anymore
146+
disable: function() {
147+
list = stack = memory = undefined;
148+
return this;
149+
},
150+
// Lock the list in its current state
151+
lock: function() {
152+
stack = undefined;
153+
if ( !memory ) {
154+
object.disable();
155+
}
156+
return this;
157+
},
158+
// Call all callbacks with the given context and arguments
159+
fireWith: function( context, args ) {
160+
var i;
161+
if ( list && ( flags.once ? !memory && !firing : stack ) ) {
162+
if ( firing ) {
163+
stack.push( [ context, args ] );
164+
} else {
165+
args = args || [];
166+
memory = !flags.memory || [ context, args ];
167+
firing = true;
168+
try {
169+
for ( i = 0; list && i < list.length; i++ ) {
170+
if ( list[ i ][ 1 ].apply( context, args ) === false && flags.stopOnFalse ) {
171+
break;
172+
}
173+
}
174+
} finally {
175+
firing = false;
176+
if ( list ) {
177+
if ( !flags.once ) {
178+
if ( i >= list.length && stack.length ) {
179+
object.fire.apply( this, stack.shift() );
180+
}
181+
} else if ( !flags.memory ) {
182+
object.destroy();
183+
} else {
184+
list = [];
185+
}
186+
}
187+
}
188+
}
189+
}
190+
return this;
191+
},
192+
// Call all the callbacks with the given arguments
193+
fire: function() {
194+
object.fireWith( this, arguments );
195+
return this;
196+
},
197+
// To know if the callbacks have already been called at least once
198+
fired: function() {
199+
return !!memory;
200+
}
201+
};
202+
203+
return object;
204+
};
205+
206+
})( jQuery );

src/core.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ var jQuery = function( selector, context ) {
5858
// For matching the engine and version of the browser
5959
browserMatch,
6060

61-
// The deferred used on DOM ready
61+
// The callback list used on DOM ready
6262
readyList,
6363

6464
// The ready event handler
@@ -257,7 +257,7 @@ jQuery.fn = jQuery.prototype = {
257257
jQuery.bindReady();
258258

259259
// Add the callback
260-
readyList.done( fn );
260+
readyList.add( fn );
261261

262262
return this;
263263
},
@@ -412,7 +412,7 @@ jQuery.extend({
412412
}
413413

414414
// If there are functions bound, to execute
415-
readyList.resolveWith( document, [ jQuery ] );
415+
readyList.fireWith( document, [ jQuery ] );
416416

417417
// Trigger any bound ready events
418418
if ( jQuery.fn.trigger ) {
@@ -426,7 +426,7 @@ jQuery.extend({
426426
return;
427427
}
428428

429-
readyList = jQuery._Deferred();
429+
readyList = jQuery.Callbacks( "once memory" );
430430

431431
// Catch cases where $(document).ready() is called after the
432432
// browser event has already occurred.

0 commit comments

Comments
 (0)