-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjquery.object_utils.js
303 lines (257 loc) · 9.41 KB
/
jquery.object_utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
// Object Utils - v0.91 - 3/28/2009
// http://benalman.com/
//
// Copyright (c) 2009 "Cowboy" Ben Alman
// Licensed under the MIT license
// http://benalman.com/about/license/
//
// Note: the setObject, getObject, and exists methods were inspired by Dojo,
// which is Copyright (c) 2005-2009, The Dojo Foundation.
// ========================================================================== //
// Script: Object Utils
//
// Version: 0.91, Date: 3/28/2009
//
// Tested with jQuery 1.3.2 in Internet Explorer 6-8, Firefox 3, Safari 3-4,
// Chrome, Opera 9.
//
// Source - http://benalman.com/code/javascript/jquery/jquery.ba-object.js
// (Minified) - http://benalman.com/code/javascript/jquery/jquery.ba-object.min.js (1.4kb)
// Unit Tests - http://benalman.com/code/javascript/jquery/test/object.html
//
//
// About: License
//
// Copyright (c) 2009 "Cowboy" Ben Alman
//
// Licensed under the MIT license
//
// http://benalman.com/about/license/
//
// * Note: the setObject, getObject, and exists methods were inspired by Dojo,
// which is Copyright (c) 2005-2009, The Dojo Foundation.
// http://dojofoundation.org/
//
// Topic: Why this plugin exists
//
// Long story short.. well, slightly shorter. I had been working on a standalone
// exec_when_exists method to deal with some asynchronous frame loading issues,
// when Peter Higgins (Dojo Project Lead) suggested that I should generalize the
// code and implement it more like Dojo's getObject method. After looking at the
// Dojo code, I realized that he was absolutely right.. and since I clearly have
// some kind of code-generalization OCD, I just couldn't not write this plugin.
// So after much tweaking, many sanity checks (thanks to all the regulars in
// irc.freenode.net #jquery), and a few complete rewrites, this Object Utils
// plugin is what I ended up with.
//
// Topic: Note for Dojo users
//
// The setObject, getObject, and exists methods are similar to their Dojo
// counterparts, but different in a few ways. The optional context argument
// is always passed before the name string, not afterwards, as in Dojo. This
// arguably provides a more intuitive interface. Also, exists returns true or
// false based on whether or not a property is defined, not whether it is
// truthy, like Dojo. Finally, getObject can poll for the existence of a
// property and execute a callback when it comes into existence.
(function($) {
// Method: jQuery.setObject
//
// Set a property of an object via dot-delimited name string, creating any
// ancestor properties that do not already exist.
//
// Usage:
//
// jQuery.setObject( [ context, ] name, value ); - -
//
// Arguments:
//
// context - (Object) Optional context in which to evaluate name. Defaults to
// window if omitted.
// name - (String) Dot-delimited string representing a property name, for
// example: 'document', 'location.href', 'window.open' or 'foo.bar.baz'.
// value - (Anything) Any valid javascript expression.
//
// Returns:
//
// (Anything) The value if set successfully, otherwise undefined.
$.setObject = function() {
var a = get_args( arguments ),
prop = a.parts.pop(),
obj = get_prop( a.context, a.parts, true );
// Only return the value if it is set successfully.
return obj && typeof obj === 'object' && prop
? ( obj[prop] = a.remain )
: undefined;
};
// Method: jQuery.getObject
//
// Get a property of an object via dot-delimited name string, and optionally
// create the property and any ancestor properties that do not already exist.
//
// Usage:
//
// jQuery.getObject( [ context, ] name [ , create ] ); - -
//
// Arguments:
//
// context - (Object) Optional context in which to evaluate name. Defaults to
// window if omitted.
// name - (String) Dot-delimited string representing a property name, for
// example: 'document', 'location.href', 'window.open' or 'foo.bar.baz'.
// create - (Boolean) Create final and intermediate properties if they don't
// exist. Defaults to false.
//
// Returns:
//
// (Object) An object reference or value on success, otherwise undefined.
// Method: jQuery.getObject (polling)
//
// Get a property of an object via dot-delimited name string, and pass it to
// callback if it exists, otherwise pass it to callback when it comes into
// existence. Polling loop may be canceled with <jQuery.getObject (cancel)>.
//
// Usage:
//
// jQuery.getObject( callback, [ delay, ] [ context, ] name ); - -
//
// Arguments:
//
// callback - (Function) Will be executed synchronously if property exists,
// otherwise a polling loop will be started and callback will be executed
// when property comes into existence. Callback executes in the context
// scope, and is passed the property as its one argument.
// delay - (Number) Optional zero-or-greater numeric polling loop delay in
// milliseconds. Defaults to 100 if omitted.
// context - (Object) Optional context in which to evaluate name. Defaults to
// window if omitted.
// name - (String) Dot-delimited string representing a property name, for
// example: 'document', 'location.href', 'window.open' or 'foo.bar.baz'.
//
// Returns:
//
// (Number) A non-zero numeric poll_id if property does not exist and a
// polling loop was started, otherwise undefined if property exists and
// callback was executed synchronously.
// Method: jQuery.getObject (cancel)
//
// Cancel a running <jQuery.getObject (polling)> polling loop.
//
// Usage:
//
// jQuery.getObject( poll_id ); - -
//
// Arguments:
//
// poll_id - (Number) Poll id returned by an <jQuery.getObject> call that was
// passed a callback and did not execute immediately.
//
// Returns:
//
// (Boolean) True if polling loop was running and is now stopped, false if
// polling loop was already stopped.
$.getObject = function() {
var a,
args = arguments;
if ( !args.length || typeof args[0] === 'number' ) {
// Clear the polling loop, if it exists.
return clear_poll( args[0] );
} else {
a = get_args( args );
// If callback was passed, start polling if necessary, otherwise return
// the property if it exists.
return a.callback
? start_poll( a )
: get_prop( a.context, a.parts, a.remain );
}
};
// Store all getObject polling loops so they can be managed.
var polls = [];
// Clear an existing polling loop, if it exists.
function clear_poll( id ) {
var poll = typeof id === 'number' && polls[id - 1];
if ( poll && poll.timeout_id ) {
clearTimeout( poll.timeout_id );
poll.timeout_id = null;
return true;
}
return false;
};
// Start a new polling loop.
function start_poll(a) {
var result,
id,
poll = {};
return (function(){
result = get_prop( a.context, $.extend([], a.parts) );
if (result === undefined) {
// Property doesn't exist, so check again after a delay.
poll.timeout_id = setTimeout( arguments.callee, a.delay );
// Return poll id so this polling loop can be managed.
return id = id || polls.push( poll );
} else {
// Property exists, so clear poll timeout_id and execute callback.
poll.timeout_id = null;
a.callback.call( a.context, result );
// Return undefined.
return;
}
})();
};
// Method: jQuery.exists
//
// Using dot-delimited name string, return whether a property of an object
// exists.
//
// Usage:
//
// jQuery.exists( [ context, ] name ); - -
//
// Arguments:
//
// context - (Object) Optional context in which to evaluate name. Defaults to
// window if omitted.
// name - (String) Dot-delimited string representing a property name, for
// example: 'document', 'location.href', 'window.open' or 'foo.bar.baz'.
//
// Returns:
//
// (Boolean) Whether or not the property exists.
$.exists = function() {
var a = get_args( arguments );
return get_prop( a.context, a.parts ) !== undefined;
};
// Traverse context object + dot-delimited name string, returning something
// useful, if possible. Start at obj and drill down, one name part at a time,
// creating interim objects if necessary.
function get_prop( obj, parts, create ) {
var p;
while ( obj && parts.length ) {
p = parts.shift();
if ( !obj[p] && create ) {
obj[p] = {};
}
obj = obj[p];
}
return obj;
};
// All Object Utils methods arguments need to be handled in the same way, so
// call this function as necessary to do the dirty work.
function get_args( args ) {
var a = {};
args = Array.prototype.slice.call( args );
// Used only in getObject polling.
a.callback = $.isFunction( args[0] )
? args.shift()
: null;
a.delay = a.callback && typeof args[0] === 'number'
? Math.max( args.shift(), 0 )
: 50;
// Used everywhere else!
a.context = typeof args[0] !== 'string'
? args.shift()
: window;
a.parts = args.shift().split('.');
a.remain = args.shift();
return a;
};
})(jQuery);