parsejs-rails 1.2.2.0 → 1.2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +6 -14
- data/lib/parsejs-rails/version.rb +1 -1
- data/vendor/assets/javascripts/parse.js +1421 -842
- metadata +5 -5
@@ -1,7 +1,7 @@
|
|
1
1
|
/*!
|
2
2
|
* Parse JavaScript SDK
|
3
|
-
* Version: 1.2.
|
4
|
-
* Built:
|
3
|
+
* Version: 1.2.7
|
4
|
+
* Built: Wed Apr 17 2013 14:48:27
|
5
5
|
* http://parse.com
|
6
6
|
*
|
7
7
|
* Copyright 2013 Parse, Inc.
|
@@ -13,17 +13,12 @@
|
|
13
13
|
*/
|
14
14
|
(function(root) {
|
15
15
|
root.Parse = root.Parse || {};
|
16
|
-
root.Parse.VERSION = "js1.2.
|
16
|
+
root.Parse.VERSION = "js1.2.7";
|
17
17
|
}(this));
|
18
|
-
|
19
|
-
|
20
|
-
//
|
21
|
-
//
|
22
|
-
// Underscore is freely distributable under the MIT license.
|
23
|
-
// Portions of Underscore are inspired or borrowed from Prototype,
|
24
|
-
// Oliver Steele's Functional, and John Resig's Micro-Templating.
|
25
|
-
// For all details and documentation:
|
26
|
-
// http://documentcloud.github.com/underscore
|
18
|
+
// Underscore.js 1.4.4
|
19
|
+
// http://underscorejs.org
|
20
|
+
// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.
|
21
|
+
// Underscore may be freely distributed under the MIT license.
|
27
22
|
|
28
23
|
(function() {
|
29
24
|
|
@@ -43,8 +38,9 @@
|
|
43
38
|
var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
|
44
39
|
|
45
40
|
// Create quick reference variables for speed access to core prototypes.
|
46
|
-
var
|
47
|
-
|
41
|
+
var push = ArrayProto.push,
|
42
|
+
slice = ArrayProto.slice,
|
43
|
+
concat = ArrayProto.concat,
|
48
44
|
toString = ObjProto.toString,
|
49
45
|
hasOwnProperty = ObjProto.hasOwnProperty;
|
50
46
|
|
@@ -65,7 +61,11 @@
|
|
65
61
|
nativeBind = FuncProto.bind;
|
66
62
|
|
67
63
|
// Create a safe reference to the Underscore object for use below.
|
68
|
-
var _ = function(obj) {
|
64
|
+
var _ = function(obj) {
|
65
|
+
if (obj instanceof _) return obj;
|
66
|
+
if (!(this instanceof _)) return new _(obj);
|
67
|
+
this._wrapped = obj;
|
68
|
+
};
|
69
69
|
|
70
70
|
// Export the Underscore object for **Node.js**, with
|
71
71
|
// backwards-compatibility for the old `require()` API. If we're in
|
@@ -77,11 +77,11 @@
|
|
77
77
|
}
|
78
78
|
exports._ = _;
|
79
79
|
} else {
|
80
|
-
root
|
80
|
+
root._ = _;
|
81
81
|
}
|
82
82
|
|
83
83
|
// Current version.
|
84
|
-
_.VERSION = '1.
|
84
|
+
_.VERSION = '1.4.4';
|
85
85
|
|
86
86
|
// Collection Functions
|
87
87
|
// --------------------
|
@@ -95,7 +95,7 @@
|
|
95
95
|
obj.forEach(iterator, context);
|
96
96
|
} else if (obj.length === +obj.length) {
|
97
97
|
for (var i = 0, l = obj.length; i < l; i++) {
|
98
|
-
if (
|
98
|
+
if (iterator.call(context, obj[i], i, obj) === breaker) return;
|
99
99
|
}
|
100
100
|
} else {
|
101
101
|
for (var key in obj) {
|
@@ -115,10 +115,11 @@
|
|
115
115
|
each(obj, function(value, index, list) {
|
116
116
|
results[results.length] = iterator.call(context, value, index, list);
|
117
117
|
});
|
118
|
-
if (obj.length === +obj.length) results.length = obj.length;
|
119
118
|
return results;
|
120
119
|
};
|
121
120
|
|
121
|
+
var reduceError = 'Reduce of empty array with no initial value';
|
122
|
+
|
122
123
|
// **Reduce** builds up a single result from a list of values, aka `inject`,
|
123
124
|
// or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.
|
124
125
|
_.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {
|
@@ -136,7 +137,7 @@
|
|
136
137
|
memo = iterator.call(context, memo, value, index, list);
|
137
138
|
}
|
138
139
|
});
|
139
|
-
if (!initial) throw new TypeError(
|
140
|
+
if (!initial) throw new TypeError(reduceError);
|
140
141
|
return memo;
|
141
142
|
};
|
142
143
|
|
@@ -149,9 +150,22 @@
|
|
149
150
|
if (context) iterator = _.bind(iterator, context);
|
150
151
|
return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);
|
151
152
|
}
|
152
|
-
var
|
153
|
-
if (
|
154
|
-
|
153
|
+
var length = obj.length;
|
154
|
+
if (length !== +length) {
|
155
|
+
var keys = _.keys(obj);
|
156
|
+
length = keys.length;
|
157
|
+
}
|
158
|
+
each(obj, function(value, index, list) {
|
159
|
+
index = keys ? keys[--length] : --length;
|
160
|
+
if (!initial) {
|
161
|
+
memo = obj[index];
|
162
|
+
initial = true;
|
163
|
+
} else {
|
164
|
+
memo = iterator.call(context, memo, obj[index], index, list);
|
165
|
+
}
|
166
|
+
});
|
167
|
+
if (!initial) throw new TypeError(reduceError);
|
168
|
+
return memo;
|
155
169
|
};
|
156
170
|
|
157
171
|
// Return the first value which passes a truth test. Aliased as `detect`.
|
@@ -181,18 +195,16 @@
|
|
181
195
|
|
182
196
|
// Return all the elements for which a truth test fails.
|
183
197
|
_.reject = function(obj, iterator, context) {
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
if (!iterator.call(context, value, index, list)) results[results.length] = value;
|
188
|
-
});
|
189
|
-
return results;
|
198
|
+
return _.filter(obj, function(value, index, list) {
|
199
|
+
return !iterator.call(context, value, index, list);
|
200
|
+
}, context);
|
190
201
|
};
|
191
202
|
|
192
203
|
// Determine whether all of the elements match a truth test.
|
193
204
|
// Delegates to **ECMAScript 5**'s native `every` if available.
|
194
205
|
// Aliased as `all`.
|
195
206
|
_.every = _.all = function(obj, iterator, context) {
|
207
|
+
iterator || (iterator = _.identity);
|
196
208
|
var result = true;
|
197
209
|
if (obj == null) return result;
|
198
210
|
if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);
|
@@ -216,23 +228,22 @@
|
|
216
228
|
return !!result;
|
217
229
|
};
|
218
230
|
|
219
|
-
// Determine if
|
220
|
-
// Aliased as `
|
221
|
-
_.
|
222
|
-
|
223
|
-
if (obj == null) return found;
|
231
|
+
// Determine if the array or object contains a given value (using `===`).
|
232
|
+
// Aliased as `include`.
|
233
|
+
_.contains = _.include = function(obj, target) {
|
234
|
+
if (obj == null) return false;
|
224
235
|
if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;
|
225
|
-
|
236
|
+
return any(obj, function(value) {
|
226
237
|
return value === target;
|
227
238
|
});
|
228
|
-
return found;
|
229
239
|
};
|
230
240
|
|
231
241
|
// Invoke a method (with arguments) on every item in a collection.
|
232
242
|
_.invoke = function(obj, method) {
|
233
243
|
var args = slice.call(arguments, 2);
|
244
|
+
var isFunc = _.isFunction(method);
|
234
245
|
return _.map(obj, function(value) {
|
235
|
-
return (
|
246
|
+
return (isFunc ? method : value[method]).apply(value, args);
|
236
247
|
});
|
237
248
|
};
|
238
249
|
|
@@ -241,11 +252,33 @@
|
|
241
252
|
return _.map(obj, function(value){ return value[key]; });
|
242
253
|
};
|
243
254
|
|
255
|
+
// Convenience version of a common use case of `filter`: selecting only objects
|
256
|
+
// containing specific `key:value` pairs.
|
257
|
+
_.where = function(obj, attrs, first) {
|
258
|
+
if (_.isEmpty(attrs)) return first ? null : [];
|
259
|
+
return _[first ? 'find' : 'filter'](obj, function(value) {
|
260
|
+
for (var key in attrs) {
|
261
|
+
if (attrs[key] !== value[key]) return false;
|
262
|
+
}
|
263
|
+
return true;
|
264
|
+
});
|
265
|
+
};
|
266
|
+
|
267
|
+
// Convenience version of a common use case of `find`: getting the first object
|
268
|
+
// containing specific `key:value` pairs.
|
269
|
+
_.findWhere = function(obj, attrs) {
|
270
|
+
return _.where(obj, attrs, true);
|
271
|
+
};
|
272
|
+
|
244
273
|
// Return the maximum element or (element-based computation).
|
274
|
+
// Can't optimize arrays of integers longer than 65,535 elements.
|
275
|
+
// See: https://bugs.webkit.org/show_bug.cgi?id=80797
|
245
276
|
_.max = function(obj, iterator, context) {
|
246
|
-
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]
|
277
|
+
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
278
|
+
return Math.max.apply(Math, obj);
|
279
|
+
}
|
247
280
|
if (!iterator && _.isEmpty(obj)) return -Infinity;
|
248
|
-
var result = {computed : -Infinity};
|
281
|
+
var result = {computed : -Infinity, value: -Infinity};
|
249
282
|
each(obj, function(value, index, list) {
|
250
283
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
251
284
|
computed >= result.computed && (result = {value : value, computed : computed});
|
@@ -255,9 +288,11 @@
|
|
255
288
|
|
256
289
|
// Return the minimum element (or element-based computation).
|
257
290
|
_.min = function(obj, iterator, context) {
|
258
|
-
if (!iterator && _.isArray(obj) && obj[0] === +obj[0]
|
291
|
+
if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {
|
292
|
+
return Math.min.apply(Math, obj);
|
293
|
+
}
|
259
294
|
if (!iterator && _.isEmpty(obj)) return Infinity;
|
260
|
-
var result = {computed : Infinity};
|
295
|
+
var result = {computed : Infinity, value: Infinity};
|
261
296
|
each(obj, function(value, index, list) {
|
262
297
|
var computed = iterator ? iterator.call(context, value, index, list) : value;
|
263
298
|
computed < result.computed && (result = {value : value, computed : computed});
|
@@ -267,67 +302,96 @@
|
|
267
302
|
|
268
303
|
// Shuffle an array.
|
269
304
|
_.shuffle = function(obj) {
|
270
|
-
var
|
271
|
-
|
272
|
-
|
273
|
-
|
305
|
+
var rand;
|
306
|
+
var index = 0;
|
307
|
+
var shuffled = [];
|
308
|
+
each(obj, function(value) {
|
309
|
+
rand = _.random(index++);
|
310
|
+
shuffled[index - 1] = shuffled[rand];
|
274
311
|
shuffled[rand] = value;
|
275
312
|
});
|
276
313
|
return shuffled;
|
277
314
|
};
|
278
315
|
|
316
|
+
// An internal function to generate lookup iterators.
|
317
|
+
var lookupIterator = function(value) {
|
318
|
+
return _.isFunction(value) ? value : function(obj){ return obj[value]; };
|
319
|
+
};
|
320
|
+
|
279
321
|
// Sort the object's values by a criterion produced by an iterator.
|
280
|
-
_.sortBy = function(obj,
|
281
|
-
var iterator =
|
322
|
+
_.sortBy = function(obj, value, context) {
|
323
|
+
var iterator = lookupIterator(value);
|
282
324
|
return _.pluck(_.map(obj, function(value, index, list) {
|
283
325
|
return {
|
284
326
|
value : value,
|
327
|
+
index : index,
|
285
328
|
criteria : iterator.call(context, value, index, list)
|
286
329
|
};
|
287
330
|
}).sort(function(left, right) {
|
288
|
-
var a = left.criteria
|
289
|
-
|
290
|
-
if (
|
291
|
-
|
331
|
+
var a = left.criteria;
|
332
|
+
var b = right.criteria;
|
333
|
+
if (a !== b) {
|
334
|
+
if (a > b || a === void 0) return 1;
|
335
|
+
if (a < b || b === void 0) return -1;
|
336
|
+
}
|
337
|
+
return left.index < right.index ? -1 : 1;
|
292
338
|
}), 'value');
|
293
339
|
};
|
294
340
|
|
295
|
-
//
|
296
|
-
|
297
|
-
_.groupBy = function(obj, val) {
|
341
|
+
// An internal function used for aggregate "group by" operations.
|
342
|
+
var group = function(obj, value, context, behavior) {
|
298
343
|
var result = {};
|
299
|
-
var iterator =
|
344
|
+
var iterator = lookupIterator(value || _.identity);
|
300
345
|
each(obj, function(value, index) {
|
301
|
-
var key = iterator(value, index);
|
302
|
-
(result
|
346
|
+
var key = iterator.call(context, value, index, obj);
|
347
|
+
behavior(result, key, value);
|
303
348
|
});
|
304
349
|
return result;
|
305
350
|
};
|
306
351
|
|
307
|
-
//
|
308
|
-
//
|
309
|
-
_.
|
310
|
-
|
352
|
+
// Groups the object's values by a criterion. Pass either a string attribute
|
353
|
+
// to group by, or a function that returns the criterion.
|
354
|
+
_.groupBy = function(obj, value, context) {
|
355
|
+
return group(obj, value, context, function(result, key, value) {
|
356
|
+
(_.has(result, key) ? result[key] : (result[key] = [])).push(value);
|
357
|
+
});
|
358
|
+
};
|
359
|
+
|
360
|
+
// Counts instances of an object that group by a certain criterion. Pass
|
361
|
+
// either a string attribute to count by, or a function that returns the
|
362
|
+
// criterion.
|
363
|
+
_.countBy = function(obj, value, context) {
|
364
|
+
return group(obj, value, context, function(result, key) {
|
365
|
+
if (!_.has(result, key)) result[key] = 0;
|
366
|
+
result[key]++;
|
367
|
+
});
|
368
|
+
};
|
369
|
+
|
370
|
+
// Use a comparator function to figure out the smallest index at which
|
371
|
+
// an object should be inserted so as to maintain order. Uses binary search.
|
372
|
+
_.sortedIndex = function(array, obj, iterator, context) {
|
373
|
+
iterator = iterator == null ? _.identity : lookupIterator(iterator);
|
374
|
+
var value = iterator.call(context, obj);
|
311
375
|
var low = 0, high = array.length;
|
312
376
|
while (low < high) {
|
313
|
-
var mid = (low + high)
|
314
|
-
iterator(array[mid]) <
|
377
|
+
var mid = (low + high) >>> 1;
|
378
|
+
iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;
|
315
379
|
}
|
316
380
|
return low;
|
317
381
|
};
|
318
382
|
|
319
383
|
// Safely convert anything iterable into a real, live array.
|
320
384
|
_.toArray = function(obj) {
|
321
|
-
if (!obj)
|
322
|
-
if (_.isArray(obj))
|
323
|
-
if (
|
324
|
-
if (obj.toArray && _.isFunction(obj.toArray)) return obj.toArray();
|
385
|
+
if (!obj) return [];
|
386
|
+
if (_.isArray(obj)) return slice.call(obj);
|
387
|
+
if (obj.length === +obj.length) return _.map(obj, _.identity);
|
325
388
|
return _.values(obj);
|
326
389
|
};
|
327
390
|
|
328
391
|
// Return the number of elements in an object.
|
329
392
|
_.size = function(obj) {
|
330
|
-
|
393
|
+
if (obj == null) return 0;
|
394
|
+
return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;
|
331
395
|
};
|
332
396
|
|
333
397
|
// Array Functions
|
@@ -337,10 +401,11 @@
|
|
337
401
|
// values in the array. Aliased as `head` and `take`. The **guard** check
|
338
402
|
// allows it to work with `_.map`.
|
339
403
|
_.first = _.head = _.take = function(array, n, guard) {
|
404
|
+
if (array == null) return void 0;
|
340
405
|
return (n != null) && !guard ? slice.call(array, 0, n) : array[0];
|
341
406
|
};
|
342
407
|
|
343
|
-
// Returns everything but the last entry of the array.
|
408
|
+
// Returns everything but the last entry of the array. Especially useful on
|
344
409
|
// the arguments object. Passing **n** will return all the values in
|
345
410
|
// the array, excluding the last N. The **guard** check allows it to work with
|
346
411
|
// `_.map`.
|
@@ -351,6 +416,7 @@
|
|
351
416
|
// Get the last element of an array. Passing **n** will return the last N
|
352
417
|
// values in the array. The **guard** check allows it to work with `_.map`.
|
353
418
|
_.last = function(array, n, guard) {
|
419
|
+
if (array == null) return void 0;
|
354
420
|
if ((n != null) && !guard) {
|
355
421
|
return slice.call(array, Math.max(array.length - n, 0));
|
356
422
|
} else {
|
@@ -358,26 +424,34 @@
|
|
358
424
|
}
|
359
425
|
};
|
360
426
|
|
361
|
-
// Returns everything but the first entry of the array. Aliased as `tail`.
|
362
|
-
// Especially useful on the arguments object. Passing an **
|
363
|
-
// the rest
|
427
|
+
// Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
|
428
|
+
// Especially useful on the arguments object. Passing an **n** will return
|
429
|
+
// the rest N values in the array. The **guard**
|
364
430
|
// check allows it to work with `_.map`.
|
365
|
-
_.rest = _.tail = function(array,
|
366
|
-
return slice.call(array, (
|
431
|
+
_.rest = _.tail = _.drop = function(array, n, guard) {
|
432
|
+
return slice.call(array, (n == null) || guard ? 1 : n);
|
367
433
|
};
|
368
434
|
|
369
435
|
// Trim out all falsy values from an array.
|
370
436
|
_.compact = function(array) {
|
371
|
-
return _.filter(array,
|
437
|
+
return _.filter(array, _.identity);
|
438
|
+
};
|
439
|
+
|
440
|
+
// Internal implementation of a recursive `flatten` function.
|
441
|
+
var flatten = function(input, shallow, output) {
|
442
|
+
each(input, function(value) {
|
443
|
+
if (_.isArray(value)) {
|
444
|
+
shallow ? push.apply(output, value) : flatten(value, shallow, output);
|
445
|
+
} else {
|
446
|
+
output.push(value);
|
447
|
+
}
|
448
|
+
});
|
449
|
+
return output;
|
372
450
|
};
|
373
451
|
|
374
452
|
// Return a completely flattened version of an array.
|
375
453
|
_.flatten = function(array, shallow) {
|
376
|
-
return
|
377
|
-
if (_.isArray(value)) return memo.concat(shallow ? value : _.flatten(value));
|
378
|
-
memo[memo.length] = value;
|
379
|
-
return memo;
|
380
|
-
}, []);
|
454
|
+
return flatten(array, shallow, []);
|
381
455
|
};
|
382
456
|
|
383
457
|
// Return a version of the array that does not contain the specified value(s).
|
@@ -388,30 +462,33 @@
|
|
388
462
|
// Produce a duplicate-free version of the array. If the array has already
|
389
463
|
// been sorted, you have the option of using a faster algorithm.
|
390
464
|
// Aliased as `unique`.
|
391
|
-
_.uniq = _.unique = function(array, isSorted, iterator) {
|
392
|
-
|
465
|
+
_.uniq = _.unique = function(array, isSorted, iterator, context) {
|
466
|
+
if (_.isFunction(isSorted)) {
|
467
|
+
context = iterator;
|
468
|
+
iterator = isSorted;
|
469
|
+
isSorted = false;
|
470
|
+
}
|
471
|
+
var initial = iterator ? _.map(array, iterator, context) : array;
|
393
472
|
var results = [];
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
memo.push(value);
|
473
|
+
var seen = [];
|
474
|
+
each(initial, function(value, index) {
|
475
|
+
if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {
|
476
|
+
seen.push(value);
|
399
477
|
results.push(array[index]);
|
400
478
|
}
|
401
|
-
|
402
|
-
}, []);
|
479
|
+
});
|
403
480
|
return results;
|
404
481
|
};
|
405
482
|
|
406
483
|
// Produce an array that contains the union: each distinct element from all of
|
407
484
|
// the passed-in arrays.
|
408
485
|
_.union = function() {
|
409
|
-
return _.uniq(
|
486
|
+
return _.uniq(concat.apply(ArrayProto, arguments));
|
410
487
|
};
|
411
488
|
|
412
489
|
// Produce an array that contains every item shared between all the
|
413
|
-
// passed-in arrays.
|
414
|
-
_.intersection =
|
490
|
+
// passed-in arrays.
|
491
|
+
_.intersection = function(array) {
|
415
492
|
var rest = slice.call(arguments, 1);
|
416
493
|
return _.filter(_.uniq(array), function(item) {
|
417
494
|
return _.every(rest, function(other) {
|
@@ -423,8 +500,8 @@
|
|
423
500
|
// Take the difference between one array and a number of other arrays.
|
424
501
|
// Only the elements present in just the first array will remain.
|
425
502
|
_.difference = function(array) {
|
426
|
-
var rest =
|
427
|
-
return _.filter(array, function(value){ return !_.
|
503
|
+
var rest = concat.apply(ArrayProto, slice.call(arguments, 1));
|
504
|
+
return _.filter(array, function(value){ return !_.contains(rest, value); });
|
428
505
|
};
|
429
506
|
|
430
507
|
// Zip together multiple lists into a single array -- elements that share
|
@@ -433,10 +510,28 @@
|
|
433
510
|
var args = slice.call(arguments);
|
434
511
|
var length = _.max(_.pluck(args, 'length'));
|
435
512
|
var results = new Array(length);
|
436
|
-
for (var i = 0; i < length; i++)
|
513
|
+
for (var i = 0; i < length; i++) {
|
514
|
+
results[i] = _.pluck(args, "" + i);
|
515
|
+
}
|
437
516
|
return results;
|
438
517
|
};
|
439
518
|
|
519
|
+
// Converts lists into objects. Pass either a single array of `[key, value]`
|
520
|
+
// pairs, or two parallel arrays of the same length -- one of keys, and one of
|
521
|
+
// the corresponding values.
|
522
|
+
_.object = function(list, values) {
|
523
|
+
if (list == null) return {};
|
524
|
+
var result = {};
|
525
|
+
for (var i = 0, l = list.length; i < l; i++) {
|
526
|
+
if (values) {
|
527
|
+
result[list[i]] = values[i];
|
528
|
+
} else {
|
529
|
+
result[list[i][0]] = list[i][1];
|
530
|
+
}
|
531
|
+
}
|
532
|
+
return result;
|
533
|
+
};
|
534
|
+
|
440
535
|
// If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),
|
441
536
|
// we need this function. Return the position of the first occurrence of an
|
442
537
|
// item in an array, or -1 if the item is not included in the array.
|
@@ -445,22 +540,29 @@
|
|
445
540
|
// for **isSorted** to use binary search.
|
446
541
|
_.indexOf = function(array, item, isSorted) {
|
447
542
|
if (array == null) return -1;
|
448
|
-
var i, l;
|
543
|
+
var i = 0, l = array.length;
|
449
544
|
if (isSorted) {
|
450
|
-
|
451
|
-
|
545
|
+
if (typeof isSorted == 'number') {
|
546
|
+
i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);
|
547
|
+
} else {
|
548
|
+
i = _.sortedIndex(array, item);
|
549
|
+
return array[i] === item ? i : -1;
|
550
|
+
}
|
452
551
|
}
|
453
|
-
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item);
|
454
|
-
for (
|
552
|
+
if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);
|
553
|
+
for (; i < l; i++) if (array[i] === item) return i;
|
455
554
|
return -1;
|
456
555
|
};
|
457
556
|
|
458
557
|
// Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.
|
459
|
-
_.lastIndexOf = function(array, item) {
|
558
|
+
_.lastIndexOf = function(array, item, from) {
|
460
559
|
if (array == null) return -1;
|
461
|
-
|
462
|
-
|
463
|
-
|
560
|
+
var hasIndex = from != null;
|
561
|
+
if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {
|
562
|
+
return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);
|
563
|
+
}
|
564
|
+
var i = (hasIndex ? from : array.length);
|
565
|
+
while (i--) if (array[i] === item) return i;
|
464
566
|
return -1;
|
465
567
|
};
|
466
568
|
|
@@ -489,25 +591,23 @@
|
|
489
591
|
// Function (ahem) Functions
|
490
592
|
// ------------------
|
491
593
|
|
492
|
-
// Reusable constructor function for prototype setting.
|
493
|
-
var ctor = function(){};
|
494
|
-
|
495
594
|
// Create a function bound to a given object (assigning `this`, and arguments,
|
496
|
-
// optionally).
|
497
|
-
//
|
498
|
-
|
499
|
-
_.bind = function bind(func, context) {
|
500
|
-
var bound, args;
|
595
|
+
// optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
|
596
|
+
// available.
|
597
|
+
_.bind = function(func, context) {
|
501
598
|
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
599
|
+
var args = slice.call(arguments, 2);
|
600
|
+
return function() {
|
601
|
+
return func.apply(context, args.concat(slice.call(arguments)));
|
602
|
+
};
|
603
|
+
};
|
604
|
+
|
605
|
+
// Partially apply a function by creating a version that has had some of its
|
606
|
+
// arguments pre-filled, without changing its dynamic `this` context.
|
607
|
+
_.partial = function(func) {
|
608
|
+
var args = slice.call(arguments, 1);
|
609
|
+
return function() {
|
610
|
+
return func.apply(this, args.concat(slice.call(arguments)));
|
511
611
|
};
|
512
612
|
};
|
513
613
|
|
@@ -515,7 +615,7 @@
|
|
515
615
|
// all callbacks defined on an object belong to it.
|
516
616
|
_.bindAll = function(obj) {
|
517
617
|
var funcs = slice.call(arguments, 1);
|
518
|
-
if (funcs.length
|
618
|
+
if (funcs.length === 0) funcs = _.functions(obj);
|
519
619
|
each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });
|
520
620
|
return obj;
|
521
621
|
};
|
@@ -546,23 +646,26 @@
|
|
546
646
|
// Returns a function, that, when invoked, will only be triggered at most once
|
547
647
|
// during a given window of time.
|
548
648
|
_.throttle = function(func, wait) {
|
549
|
-
var context, args, timeout,
|
550
|
-
var
|
649
|
+
var context, args, timeout, result;
|
650
|
+
var previous = 0;
|
651
|
+
var later = function() {
|
652
|
+
previous = new Date;
|
653
|
+
timeout = null;
|
654
|
+
result = func.apply(context, args);
|
655
|
+
};
|
551
656
|
return function() {
|
552
|
-
|
553
|
-
var
|
657
|
+
var now = new Date;
|
658
|
+
var remaining = wait - (now - previous);
|
659
|
+
context = this;
|
660
|
+
args = arguments;
|
661
|
+
if (remaining <= 0) {
|
662
|
+
clearTimeout(timeout);
|
554
663
|
timeout = null;
|
555
|
-
|
556
|
-
whenDone();
|
557
|
-
};
|
558
|
-
if (!timeout) timeout = setTimeout(later, wait);
|
559
|
-
if (throttling) {
|
560
|
-
more = true;
|
561
|
-
} else {
|
664
|
+
previous = now;
|
562
665
|
result = func.apply(context, args);
|
666
|
+
} else if (!timeout) {
|
667
|
+
timeout = setTimeout(later, remaining);
|
563
668
|
}
|
564
|
-
whenDone();
|
565
|
-
throttling = true;
|
566
669
|
return result;
|
567
670
|
};
|
568
671
|
};
|
@@ -572,16 +675,18 @@
|
|
572
675
|
// N milliseconds. If `immediate` is passed, trigger the function on the
|
573
676
|
// leading edge, instead of the trailing.
|
574
677
|
_.debounce = function(func, wait, immediate) {
|
575
|
-
var timeout;
|
678
|
+
var timeout, result;
|
576
679
|
return function() {
|
577
680
|
var context = this, args = arguments;
|
578
681
|
var later = function() {
|
579
682
|
timeout = null;
|
580
|
-
if (!immediate) func.apply(context, args);
|
683
|
+
if (!immediate) result = func.apply(context, args);
|
581
684
|
};
|
582
|
-
|
685
|
+
var callNow = immediate && !timeout;
|
583
686
|
clearTimeout(timeout);
|
584
687
|
timeout = setTimeout(later, wait);
|
688
|
+
if (callNow) result = func.apply(context, args);
|
689
|
+
return result;
|
585
690
|
};
|
586
691
|
};
|
587
692
|
|
@@ -592,7 +697,9 @@
|
|
592
697
|
return function() {
|
593
698
|
if (ran) return memo;
|
594
699
|
ran = true;
|
595
|
-
|
700
|
+
memo = func.apply(this, arguments);
|
701
|
+
func = null;
|
702
|
+
return memo;
|
596
703
|
};
|
597
704
|
};
|
598
705
|
|
@@ -601,7 +708,8 @@
|
|
601
708
|
// conditionally execute the original function.
|
602
709
|
_.wrap = function(func, wrapper) {
|
603
710
|
return function() {
|
604
|
-
var args = [func]
|
711
|
+
var args = [func];
|
712
|
+
push.apply(args, arguments);
|
605
713
|
return wrapper.apply(this, args);
|
606
714
|
};
|
607
715
|
};
|
@@ -623,7 +731,9 @@
|
|
623
731
|
_.after = function(times, func) {
|
624
732
|
if (times <= 0) return func();
|
625
733
|
return function() {
|
626
|
-
if (--times < 1) {
|
734
|
+
if (--times < 1) {
|
735
|
+
return func.apply(this, arguments);
|
736
|
+
}
|
627
737
|
};
|
628
738
|
};
|
629
739
|
|
@@ -641,7 +751,23 @@
|
|
641
751
|
|
642
752
|
// Retrieve the values of an object's properties.
|
643
753
|
_.values = function(obj) {
|
644
|
-
|
754
|
+
var values = [];
|
755
|
+
for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);
|
756
|
+
return values;
|
757
|
+
};
|
758
|
+
|
759
|
+
// Convert an object into a list of `[key, value]` pairs.
|
760
|
+
_.pairs = function(obj) {
|
761
|
+
var pairs = [];
|
762
|
+
for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);
|
763
|
+
return pairs;
|
764
|
+
};
|
765
|
+
|
766
|
+
// Invert the keys and values of an object. The values must be serializable.
|
767
|
+
_.invert = function(obj) {
|
768
|
+
var result = {};
|
769
|
+
for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;
|
770
|
+
return result;
|
645
771
|
};
|
646
772
|
|
647
773
|
// Return a sorted list of the function names available on the object.
|
@@ -657,8 +783,10 @@
|
|
657
783
|
// Extend a given object with all the properties in passed-in object(s).
|
658
784
|
_.extend = function(obj) {
|
659
785
|
each(slice.call(arguments, 1), function(source) {
|
660
|
-
|
661
|
-
|
786
|
+
if (source) {
|
787
|
+
for (var prop in source) {
|
788
|
+
obj[prop] = source[prop];
|
789
|
+
}
|
662
790
|
}
|
663
791
|
});
|
664
792
|
return obj;
|
@@ -666,18 +794,31 @@
|
|
666
794
|
|
667
795
|
// Return a copy of the object only containing the whitelisted properties.
|
668
796
|
_.pick = function(obj) {
|
669
|
-
var
|
670
|
-
|
671
|
-
|
797
|
+
var copy = {};
|
798
|
+
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
799
|
+
each(keys, function(key) {
|
800
|
+
if (key in obj) copy[key] = obj[key];
|
672
801
|
});
|
673
|
-
return
|
802
|
+
return copy;
|
803
|
+
};
|
804
|
+
|
805
|
+
// Return a copy of the object without the blacklisted properties.
|
806
|
+
_.omit = function(obj) {
|
807
|
+
var copy = {};
|
808
|
+
var keys = concat.apply(ArrayProto, slice.call(arguments, 1));
|
809
|
+
for (var key in obj) {
|
810
|
+
if (!_.contains(keys, key)) copy[key] = obj[key];
|
811
|
+
}
|
812
|
+
return copy;
|
674
813
|
};
|
675
814
|
|
676
815
|
// Fill in a given object with default properties.
|
677
816
|
_.defaults = function(obj) {
|
678
817
|
each(slice.call(arguments, 1), function(source) {
|
679
|
-
|
680
|
-
|
818
|
+
if (source) {
|
819
|
+
for (var prop in source) {
|
820
|
+
if (obj[prop] == null) obj[prop] = source[prop];
|
821
|
+
}
|
681
822
|
}
|
682
823
|
});
|
683
824
|
return obj;
|
@@ -697,19 +838,16 @@
|
|
697
838
|
return obj;
|
698
839
|
};
|
699
840
|
|
700
|
-
// Internal recursive comparison function
|
701
|
-
|
841
|
+
// Internal recursive comparison function for `isEqual`.
|
842
|
+
var eq = function(a, b, aStack, bStack) {
|
702
843
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
703
844
|
// See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.
|
704
845
|
if (a === b) return a !== 0 || 1 / a == 1 / b;
|
705
846
|
// A strict comparison is necessary because `null == undefined`.
|
706
847
|
if (a == null || b == null) return a === b;
|
707
848
|
// Unwrap any wrapped objects.
|
708
|
-
if (a
|
709
|
-
if (b
|
710
|
-
// Invoke a custom `isEqual` method if one is provided.
|
711
|
-
if (a.isEqual && _.isFunction(a.isEqual)) return a.isEqual(b);
|
712
|
-
if (b.isEqual && _.isFunction(b.isEqual)) return b.isEqual(a);
|
849
|
+
if (a instanceof _) a = a._wrapped;
|
850
|
+
if (b instanceof _) b = b._wrapped;
|
713
851
|
// Compare `[[Class]]` names.
|
714
852
|
var className = toString.call(a);
|
715
853
|
if (className != toString.call(b)) return false;
|
@@ -739,14 +877,15 @@
|
|
739
877
|
if (typeof a != 'object' || typeof b != 'object') return false;
|
740
878
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
741
879
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
742
|
-
var length =
|
880
|
+
var length = aStack.length;
|
743
881
|
while (length--) {
|
744
882
|
// Linear search. Performance is inversely proportional to the number of
|
745
883
|
// unique nested structures.
|
746
|
-
if (
|
884
|
+
if (aStack[length] == a) return bStack[length] == b;
|
747
885
|
}
|
748
886
|
// Add the first object to the stack of traversed objects.
|
749
|
-
|
887
|
+
aStack.push(a);
|
888
|
+
bStack.push(b);
|
750
889
|
var size = 0, result = true;
|
751
890
|
// Recursively compare objects and arrays.
|
752
891
|
if (className == '[object Array]') {
|
@@ -756,20 +895,24 @@
|
|
756
895
|
if (result) {
|
757
896
|
// Deep compare the contents, ignoring non-numeric properties.
|
758
897
|
while (size--) {
|
759
|
-
|
760
|
-
if (!(result = size in a == size in b && eq(a[size], b[size], stack))) break;
|
898
|
+
if (!(result = eq(a[size], b[size], aStack, bStack))) break;
|
761
899
|
}
|
762
900
|
}
|
763
901
|
} else {
|
764
|
-
// Objects with different constructors are not equivalent
|
765
|
-
|
902
|
+
// Objects with different constructors are not equivalent, but `Object`s
|
903
|
+
// from different frames are.
|
904
|
+
var aCtor = a.constructor, bCtor = b.constructor;
|
905
|
+
if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&
|
906
|
+
_.isFunction(bCtor) && (bCtor instanceof bCtor))) {
|
907
|
+
return false;
|
908
|
+
}
|
766
909
|
// Deep compare objects.
|
767
910
|
for (var key in a) {
|
768
911
|
if (_.has(a, key)) {
|
769
912
|
// Count the expected number of properties.
|
770
913
|
size++;
|
771
914
|
// Deep compare each member.
|
772
|
-
if (!(result = _.has(b, key) && eq(a[key], b[key],
|
915
|
+
if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;
|
773
916
|
}
|
774
917
|
}
|
775
918
|
// Ensure that both objects contain the same number of properties.
|
@@ -781,13 +924,14 @@
|
|
781
924
|
}
|
782
925
|
}
|
783
926
|
// Remove the first object from the stack of traversed objects.
|
784
|
-
|
927
|
+
aStack.pop();
|
928
|
+
bStack.pop();
|
785
929
|
return result;
|
786
|
-
}
|
930
|
+
};
|
787
931
|
|
788
932
|
// Perform a deep comparison to check if two objects are equal.
|
789
933
|
_.isEqual = function(a, b) {
|
790
|
-
return eq(a, b, []);
|
934
|
+
return eq(a, b, [], []);
|
791
935
|
};
|
792
936
|
|
793
937
|
// Is a given array, string, or object empty?
|
@@ -801,7 +945,7 @@
|
|
801
945
|
|
802
946
|
// Is a given value a DOM element?
|
803
947
|
_.isElement = function(obj) {
|
804
|
-
return !!(obj && obj.nodeType
|
948
|
+
return !!(obj && obj.nodeType === 1);
|
805
949
|
};
|
806
950
|
|
807
951
|
// Is a given value an array?
|
@@ -815,40 +959,36 @@
|
|
815
959
|
return obj === Object(obj);
|
816
960
|
};
|
817
961
|
|
818
|
-
//
|
819
|
-
|
820
|
-
|
821
|
-
|
962
|
+
// Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.
|
963
|
+
each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {
|
964
|
+
_['is' + name] = function(obj) {
|
965
|
+
return toString.call(obj) == '[object ' + name + ']';
|
966
|
+
};
|
967
|
+
});
|
968
|
+
|
969
|
+
// Define a fallback version of the method in browsers (ahem, IE), where
|
970
|
+
// there isn't any inspectable "Arguments" type.
|
822
971
|
if (!_.isArguments(arguments)) {
|
823
972
|
_.isArguments = function(obj) {
|
824
973
|
return !!(obj && _.has(obj, 'callee'));
|
825
974
|
};
|
826
975
|
}
|
827
976
|
|
828
|
-
//
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
_.isString = function(obj) {
|
835
|
-
return toString.call(obj) == '[object String]';
|
836
|
-
};
|
837
|
-
|
838
|
-
// Is a given value a number?
|
839
|
-
_.isNumber = function(obj) {
|
840
|
-
return toString.call(obj) == '[object Number]';
|
841
|
-
};
|
977
|
+
// Optimize `isFunction` if appropriate.
|
978
|
+
if (typeof (/./) !== 'function') {
|
979
|
+
_.isFunction = function(obj) {
|
980
|
+
return typeof obj === 'function';
|
981
|
+
};
|
982
|
+
}
|
842
983
|
|
843
984
|
// Is a given object a finite number?
|
844
985
|
_.isFinite = function(obj) {
|
845
|
-
return
|
986
|
+
return isFinite(obj) && !isNaN(parseFloat(obj));
|
846
987
|
};
|
847
988
|
|
848
|
-
// Is the given value `NaN`?
|
989
|
+
// Is the given value `NaN`? (NaN is the only number which does not equal itself).
|
849
990
|
_.isNaN = function(obj) {
|
850
|
-
|
851
|
-
return obj !== obj;
|
991
|
+
return _.isNumber(obj) && obj != +obj;
|
852
992
|
};
|
853
993
|
|
854
994
|
// Is a given value a boolean?
|
@@ -856,16 +996,6 @@
|
|
856
996
|
return obj === true || obj === false || toString.call(obj) == '[object Boolean]';
|
857
997
|
};
|
858
998
|
|
859
|
-
// Is a given value a date?
|
860
|
-
_.isDate = function(obj) {
|
861
|
-
return toString.call(obj) == '[object Date]';
|
862
|
-
};
|
863
|
-
|
864
|
-
// Is the given value a regular expression?
|
865
|
-
_.isRegExp = function(obj) {
|
866
|
-
return toString.call(obj) == '[object RegExp]';
|
867
|
-
};
|
868
|
-
|
869
999
|
// Is a given value equal to null?
|
870
1000
|
_.isNull = function(obj) {
|
871
1001
|
return obj === null;
|
@@ -876,7 +1006,8 @@
|
|
876
1006
|
return obj === void 0;
|
877
1007
|
};
|
878
1008
|
|
879
|
-
//
|
1009
|
+
// Shortcut function for checking if an object has a given property directly
|
1010
|
+
// on itself (in other words, not on a prototype).
|
880
1011
|
_.has = function(obj, key) {
|
881
1012
|
return hasOwnProperty.call(obj, key);
|
882
1013
|
};
|
@@ -897,15 +1028,50 @@
|
|
897
1028
|
};
|
898
1029
|
|
899
1030
|
// Run a function **n** times.
|
900
|
-
_.times = function
|
901
|
-
|
1031
|
+
_.times = function(n, iterator, context) {
|
1032
|
+
var accum = Array(n);
|
1033
|
+
for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);
|
1034
|
+
return accum;
|
902
1035
|
};
|
903
1036
|
|
904
|
-
//
|
905
|
-
_.
|
906
|
-
|
1037
|
+
// Return a random integer between min and max (inclusive).
|
1038
|
+
_.random = function(min, max) {
|
1039
|
+
if (max == null) {
|
1040
|
+
max = min;
|
1041
|
+
min = 0;
|
1042
|
+
}
|
1043
|
+
return min + Math.floor(Math.random() * (max - min + 1));
|
1044
|
+
};
|
1045
|
+
|
1046
|
+
// List of HTML entities for escaping.
|
1047
|
+
var entityMap = {
|
1048
|
+
escape: {
|
1049
|
+
'&': '&',
|
1050
|
+
'<': '<',
|
1051
|
+
'>': '>',
|
1052
|
+
'"': '"',
|
1053
|
+
"'": ''',
|
1054
|
+
'/': '/'
|
1055
|
+
}
|
1056
|
+
};
|
1057
|
+
entityMap.unescape = _.invert(entityMap.escape);
|
1058
|
+
|
1059
|
+
// Regexes containing the keys and values listed immediately above.
|
1060
|
+
var entityRegexes = {
|
1061
|
+
escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),
|
1062
|
+
unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')
|
907
1063
|
};
|
908
1064
|
|
1065
|
+
// Functions for escaping and unescaping strings to/from HTML interpolation.
|
1066
|
+
_.each(['escape', 'unescape'], function(method) {
|
1067
|
+
_[method] = function(string) {
|
1068
|
+
if (string == null) return '';
|
1069
|
+
return ('' + string).replace(entityRegexes[method], function(match) {
|
1070
|
+
return entityMap[method][match];
|
1071
|
+
});
|
1072
|
+
};
|
1073
|
+
});
|
1074
|
+
|
909
1075
|
// If the value of the named property is a function then invoke it;
|
910
1076
|
// otherwise, return it.
|
911
1077
|
_.result = function(object, property) {
|
@@ -914,11 +1080,15 @@
|
|
914
1080
|
return _.isFunction(value) ? value.call(object) : value;
|
915
1081
|
};
|
916
1082
|
|
917
|
-
// Add your own custom functions to the Underscore object
|
918
|
-
// they're correctly added to the OOP wrapper as well.
|
1083
|
+
// Add your own custom functions to the Underscore object.
|
919
1084
|
_.mixin = function(obj) {
|
920
1085
|
each(_.functions(obj), function(name){
|
921
|
-
|
1086
|
+
var func = _[name] = obj[name];
|
1087
|
+
_.prototype[name] = function() {
|
1088
|
+
var args = [this._wrapped];
|
1089
|
+
push.apply(args, arguments);
|
1090
|
+
return result.call(this, func.apply(_, args));
|
1091
|
+
};
|
922
1092
|
});
|
923
1093
|
};
|
924
1094
|
|
@@ -926,7 +1096,7 @@
|
|
926
1096
|
// Useful for temporary DOM ids.
|
927
1097
|
var idCounter = 0;
|
928
1098
|
_.uniqueId = function(prefix) {
|
929
|
-
var id = idCounter
|
1099
|
+
var id = ++idCounter + '';
|
930
1100
|
return prefix ? prefix + id : id;
|
931
1101
|
};
|
932
1102
|
|
@@ -941,72 +1111,78 @@
|
|
941
1111
|
// When customizing `templateSettings`, if you don't want to define an
|
942
1112
|
// interpolation, evaluation or escaping regex, we need one that is
|
943
1113
|
// guaranteed not to match.
|
944
|
-
var noMatch =
|
1114
|
+
var noMatch = /(.)^/;
|
945
1115
|
|
946
1116
|
// Certain characters need to be escaped so that they can be put into a
|
947
1117
|
// string literal.
|
948
1118
|
var escapes = {
|
949
|
-
'
|
950
|
-
|
951
|
-
'r':
|
952
|
-
'n':
|
953
|
-
't':
|
954
|
-
'u2028': '
|
955
|
-
'u2029': '
|
1119
|
+
"'": "'",
|
1120
|
+
'\\': '\\',
|
1121
|
+
'\r': 'r',
|
1122
|
+
'\n': 'n',
|
1123
|
+
'\t': 't',
|
1124
|
+
'\u2028': 'u2028',
|
1125
|
+
'\u2029': 'u2029'
|
956
1126
|
};
|
957
1127
|
|
958
|
-
for (var p in escapes) escapes[escapes[p]] = p;
|
959
1128
|
var escaper = /\\|'|\r|\n|\t|\u2028|\u2029/g;
|
960
|
-
var unescaper = /\\(\\|'|r|n|t|u2028|u2029)/g;
|
961
|
-
|
962
|
-
// Within an interpolation, evaluation, or escaping, remove HTML escaping
|
963
|
-
// that had been previously added.
|
964
|
-
var unescape = function(code) {
|
965
|
-
return code.replace(unescaper, function(match, escape) {
|
966
|
-
return escapes[escape];
|
967
|
-
});
|
968
|
-
};
|
969
1129
|
|
970
1130
|
// JavaScript micro-templating, similar to John Resig's implementation.
|
971
1131
|
// Underscore templating handles arbitrary delimiters, preserves whitespace,
|
972
1132
|
// and correctly escapes quotes within interpolated code.
|
973
1133
|
_.template = function(text, data, settings) {
|
974
|
-
|
975
|
-
|
976
|
-
|
977
|
-
//
|
978
|
-
|
979
|
-
|
980
|
-
.
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
989
|
-
|
990
|
-
|
991
|
-
|
1134
|
+
var render;
|
1135
|
+
settings = _.defaults({}, settings, _.templateSettings);
|
1136
|
+
|
1137
|
+
// Combine delimiters into one regular expression via alternation.
|
1138
|
+
var matcher = new RegExp([
|
1139
|
+
(settings.escape || noMatch).source,
|
1140
|
+
(settings.interpolate || noMatch).source,
|
1141
|
+
(settings.evaluate || noMatch).source
|
1142
|
+
].join('|') + '|$', 'g');
|
1143
|
+
|
1144
|
+
// Compile the template source, escaping string literals appropriately.
|
1145
|
+
var index = 0;
|
1146
|
+
var source = "__p+='";
|
1147
|
+
text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
|
1148
|
+
source += text.slice(index, offset)
|
1149
|
+
.replace(escaper, function(match) { return '\\' + escapes[match]; });
|
1150
|
+
|
1151
|
+
if (escape) {
|
1152
|
+
source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
|
1153
|
+
}
|
1154
|
+
if (interpolate) {
|
1155
|
+
source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
|
1156
|
+
}
|
1157
|
+
if (evaluate) {
|
1158
|
+
source += "';\n" + evaluate + "\n__p+='";
|
1159
|
+
}
|
1160
|
+
index = offset + match.length;
|
1161
|
+
return match;
|
1162
|
+
});
|
1163
|
+
source += "';\n";
|
992
1164
|
|
993
1165
|
// If a variable is not specified, place data values in local scope.
|
994
1166
|
if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
|
995
1167
|
|
996
|
-
source = "var __p=''
|
997
|
-
"
|
1168
|
+
source = "var __t,__p='',__j=Array.prototype.join," +
|
1169
|
+
"print=function(){__p+=__j.call(arguments,'');};\n" +
|
998
1170
|
source + "return __p;\n";
|
999
1171
|
|
1000
|
-
|
1172
|
+
try {
|
1173
|
+
render = new Function(settings.variable || 'obj', '_', source);
|
1174
|
+
} catch (e) {
|
1175
|
+
e.source = source;
|
1176
|
+
throw e;
|
1177
|
+
}
|
1178
|
+
|
1001
1179
|
if (data) return render(data, _);
|
1002
1180
|
var template = function(data) {
|
1003
1181
|
return render.call(this, data, _);
|
1004
1182
|
};
|
1005
1183
|
|
1006
|
-
// Provide the compiled function source as a convenience for
|
1007
|
-
|
1008
|
-
template.source = 'function(' + (settings.variable || 'obj') + '){\n' +
|
1009
|
-
source + '}';
|
1184
|
+
// Provide the compiled function source as a convenience for precompilation.
|
1185
|
+
template.source = 'function(' + (settings.variable || 'obj') + '){\n' + source + '}';
|
1010
1186
|
|
1011
1187
|
return template;
|
1012
1188
|
};
|
@@ -1016,29 +1192,15 @@
|
|
1016
1192
|
return _(obj).chain();
|
1017
1193
|
};
|
1018
1194
|
|
1019
|
-
//
|
1195
|
+
// OOP
|
1020
1196
|
// ---------------
|
1021
|
-
|
1022
1197
|
// If Underscore is called as a function, it returns a wrapped object that
|
1023
1198
|
// can be used OO-style. This wrapper holds altered versions of all the
|
1024
1199
|
// underscore functions. Wrapped objects may be chained.
|
1025
|
-
var wrapper = function(obj) { this._wrapped = obj; };
|
1026
|
-
|
1027
|
-
// Expose `wrapper.prototype` as `_.prototype`
|
1028
|
-
_.prototype = wrapper.prototype;
|
1029
1200
|
|
1030
1201
|
// Helper function to continue chaining intermediate results.
|
1031
|
-
var result = function(obj
|
1032
|
-
return
|
1033
|
-
};
|
1034
|
-
|
1035
|
-
// A method to easily add functions to the OOP wrapper.
|
1036
|
-
var addToWrapper = function(name, func) {
|
1037
|
-
wrapper.prototype[name] = function() {
|
1038
|
-
var args = slice.call(arguments);
|
1039
|
-
unshift.call(args, this._wrapped);
|
1040
|
-
return result(func.apply(_, args), this._chain);
|
1041
|
-
};
|
1202
|
+
var result = function(obj) {
|
1203
|
+
return this._chain ? _(obj).chain() : obj;
|
1042
1204
|
};
|
1043
1205
|
|
1044
1206
|
// Add all of the Underscore functions to the wrapper object.
|
@@ -1047,38 +1209,42 @@
|
|
1047
1209
|
// Add all mutator Array functions to the wrapper.
|
1048
1210
|
each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
|
1049
1211
|
var method = ArrayProto[name];
|
1050
|
-
|
1051
|
-
var
|
1052
|
-
method.apply(
|
1053
|
-
|
1054
|
-
|
1055
|
-
return result(wrapped, this._chain);
|
1212
|
+
_.prototype[name] = function() {
|
1213
|
+
var obj = this._wrapped;
|
1214
|
+
method.apply(obj, arguments);
|
1215
|
+
if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];
|
1216
|
+
return result.call(this, obj);
|
1056
1217
|
};
|
1057
1218
|
});
|
1058
1219
|
|
1059
1220
|
// Add all accessor Array functions to the wrapper.
|
1060
1221
|
each(['concat', 'join', 'slice'], function(name) {
|
1061
1222
|
var method = ArrayProto[name];
|
1062
|
-
|
1063
|
-
return result(method.apply(this._wrapped, arguments)
|
1223
|
+
_.prototype[name] = function() {
|
1224
|
+
return result.call(this, method.apply(this._wrapped, arguments));
|
1064
1225
|
};
|
1065
1226
|
});
|
1066
1227
|
|
1067
|
-
|
1068
|
-
wrapper.prototype.chain = function() {
|
1069
|
-
this._chain = true;
|
1070
|
-
return this;
|
1071
|
-
};
|
1228
|
+
_.extend(_.prototype, {
|
1072
1229
|
|
1073
|
-
|
1074
|
-
|
1075
|
-
|
1076
|
-
|
1230
|
+
// Start chaining a wrapped Underscore object.
|
1231
|
+
chain: function() {
|
1232
|
+
this._chain = true;
|
1233
|
+
return this;
|
1234
|
+
},
|
1235
|
+
|
1236
|
+
// Extracts the result from a wrapped and chained object.
|
1237
|
+
value: function() {
|
1238
|
+
return this._wrapped;
|
1239
|
+
}
|
1240
|
+
|
1241
|
+
});
|
1077
1242
|
|
1078
1243
|
}).call(this);
|
1079
1244
|
|
1080
|
-
/*global _: false, $: false, localStorage: false,
|
1081
|
-
|
1245
|
+
/*global _: false, $: false, localStorage: false, process: true,
|
1246
|
+
XMLHttpRequest: false, XDomainRequest: false, exports: false,
|
1247
|
+
require: false */
|
1082
1248
|
(function(root) {
|
1083
1249
|
root.Parse = root.Parse || {};
|
1084
1250
|
/**
|
@@ -1167,13 +1333,25 @@
|
|
1167
1333
|
// Set the server for Parse to talk to.
|
1168
1334
|
Parse.serverURL = "https://api.parse.com";
|
1169
1335
|
|
1336
|
+
// Check whether we are running in Node.js.
|
1337
|
+
if (typeof(process) !== "undefined" &&
|
1338
|
+
process.versions &&
|
1339
|
+
process.versions.node) {
|
1340
|
+
Parse._isNode = true;
|
1341
|
+
}
|
1342
|
+
|
1170
1343
|
/**
|
1171
1344
|
* Call this method first to set up your authentication tokens for Parse.
|
1172
1345
|
* You can get your keys from the Data Browser on parse.com.
|
1173
1346
|
* @param {String} applicationId Your Parse Application ID.
|
1174
1347
|
* @param {String} javaScriptKey Your Parse JavaScript Key.
|
1348
|
+
* @param {String} masterKey (optional) Your Parse Master Key. (Node.js only!)
|
1175
1349
|
*/
|
1176
|
-
Parse.initialize = function(applicationId, javaScriptKey) {
|
1350
|
+
Parse.initialize = function(applicationId, javaScriptKey, masterKey) {
|
1351
|
+
if (masterKey) {
|
1352
|
+
throw "Parse.initialize() was passed a Master Key, which is only " +
|
1353
|
+
"allowed from within Node.js.";
|
1354
|
+
}
|
1177
1355
|
Parse._initialize(applicationId, javaScriptKey);
|
1178
1356
|
};
|
1179
1357
|
|
@@ -1191,6 +1369,23 @@
|
|
1191
1369
|
Parse._useMasterKey = false;
|
1192
1370
|
};
|
1193
1371
|
|
1372
|
+
// If we're running in node.js, allow using the master key.
|
1373
|
+
if (Parse._isNode) {
|
1374
|
+
Parse.initialize = Parse._initialize;
|
1375
|
+
|
1376
|
+
Parse.Cloud = Parse.Cloud || {};
|
1377
|
+
/**
|
1378
|
+
* Switches the Parse SDK to using the Master key. The Master key grants
|
1379
|
+
* priveleged access to the data in Parse and can be used to bypass ACLs and
|
1380
|
+
* other restrictions that are applied to the client SDKs.
|
1381
|
+
* <p><strong><em>Available in Cloud Code and Node.js only.</em></strong>
|
1382
|
+
* </p>
|
1383
|
+
*/
|
1384
|
+
Parse.Cloud.useMasterKey = function() {
|
1385
|
+
Parse._useMasterKey = true;
|
1386
|
+
};
|
1387
|
+
}
|
1388
|
+
|
1194
1389
|
/**
|
1195
1390
|
* Returns prefix for localStorage keys used by this instance of Parse.
|
1196
1391
|
* @param {String} path The relative suffix to append to it.
|
@@ -1266,7 +1461,7 @@
|
|
1266
1461
|
return new Date(Date.UTC(year, month, day, hour, minute, second, milli));
|
1267
1462
|
};
|
1268
1463
|
|
1269
|
-
Parse._ajaxIE8 = function(method, url, data
|
1464
|
+
Parse._ajaxIE8 = function(method, url, data) {
|
1270
1465
|
var promise = new Parse.Promise();
|
1271
1466
|
var xdr = new XDomainRequest();
|
1272
1467
|
xdr.onload = function() {
|
@@ -1274,22 +1469,13 @@
|
|
1274
1469
|
try {
|
1275
1470
|
response = JSON.parse(xdr.responseText);
|
1276
1471
|
} catch (e) {
|
1277
|
-
if (error) {
|
1278
|
-
error(xdr);
|
1279
|
-
}
|
1280
1472
|
promise.reject(e);
|
1281
1473
|
}
|
1282
1474
|
if (response) {
|
1283
|
-
if (success) {
|
1284
|
-
success(response, xdr);
|
1285
|
-
}
|
1286
1475
|
promise.resolve(response);
|
1287
1476
|
}
|
1288
1477
|
};
|
1289
1478
|
xdr.onerror = xdr.ontimeout = function() {
|
1290
|
-
if (error) {
|
1291
|
-
error(xdr);
|
1292
|
-
}
|
1293
1479
|
promise.reject(xdr);
|
1294
1480
|
};
|
1295
1481
|
xdr.onprogress = function() {};
|
@@ -1298,9 +1484,15 @@
|
|
1298
1484
|
return promise;
|
1299
1485
|
};
|
1300
1486
|
|
1487
|
+
|
1301
1488
|
Parse._ajax = function(method, url, data, success, error) {
|
1489
|
+
var options = {
|
1490
|
+
success: success,
|
1491
|
+
error: error
|
1492
|
+
};
|
1493
|
+
|
1302
1494
|
if (typeof(XDomainRequest) !== "undefined") {
|
1303
|
-
return Parse._ajaxIE8(method, url, data
|
1495
|
+
return Parse._ajaxIE8(method, url, data)._thenRunCallbacks(options);
|
1304
1496
|
}
|
1305
1497
|
|
1306
1498
|
var promise = new Parse.Promise();
|
@@ -1319,29 +1511,26 @@
|
|
1319
1511
|
try {
|
1320
1512
|
response = JSON.parse(xhr.responseText);
|
1321
1513
|
} catch (e) {
|
1322
|
-
if (error) {
|
1323
|
-
error(xhr);
|
1324
|
-
}
|
1325
1514
|
promise.reject(e);
|
1326
1515
|
}
|
1327
1516
|
if (response) {
|
1328
|
-
if (success) {
|
1329
|
-
success(response, xhr.status, xhr);
|
1330
|
-
}
|
1331
1517
|
promise.resolve(response, xhr.status, xhr);
|
1332
1518
|
}
|
1333
1519
|
} else {
|
1334
|
-
if (error) {
|
1335
|
-
error(xhr);
|
1336
|
-
}
|
1337
1520
|
promise.reject(xhr);
|
1338
1521
|
}
|
1339
1522
|
}
|
1340
1523
|
};
|
1341
1524
|
xhr.open(method, url, true);
|
1342
1525
|
xhr.setRequestHeader("Content-Type", "text/plain"); // avoid pre-flight.
|
1526
|
+
if (Parse._isNode) {
|
1527
|
+
// Add a special user agent just for request from node.js.
|
1528
|
+
xhr.setRequestHeader("User-Agent",
|
1529
|
+
"Parse/" + Parse.VERSION +
|
1530
|
+
" (NodeJS " + process.versions.node + ")");
|
1531
|
+
}
|
1343
1532
|
xhr.send(data);
|
1344
|
-
return promise;
|
1533
|
+
return promise._thenRunCallbacks(options);
|
1345
1534
|
};
|
1346
1535
|
|
1347
1536
|
// A self-propagating extend function.
|
@@ -1356,11 +1545,9 @@
|
|
1356
1545
|
* objectId is null if there is no associated objectId.
|
1357
1546
|
* method is the http method for the REST API.
|
1358
1547
|
* dataObject is the payload as an object, or null if there is none.
|
1359
|
-
* options is just a success/error callback hash.
|
1360
1548
|
* @ignore
|
1361
1549
|
*/
|
1362
|
-
Parse._request = function(route, className, objectId, method, dataObject
|
1363
|
-
options) {
|
1550
|
+
Parse._request = function(route, className, objectId, method, dataObject) {
|
1364
1551
|
if (!Parse.applicationId) {
|
1365
1552
|
throw "You must specify your applicationId using Parse.initialize";
|
1366
1553
|
}
|
@@ -1372,6 +1559,7 @@
|
|
1372
1559
|
|
1373
1560
|
if (route !== "batch" &&
|
1374
1561
|
route !== "classes" &&
|
1562
|
+
route !== "files" &&
|
1375
1563
|
route !== "functions" &&
|
1376
1564
|
route !== "login" &&
|
1377
1565
|
route !== "push" &&
|
@@ -1397,6 +1585,7 @@
|
|
1397
1585
|
dataObject._method = method;
|
1398
1586
|
method = "POST";
|
1399
1587
|
}
|
1588
|
+
|
1400
1589
|
dataObject._ApplicationId = Parse.applicationId;
|
1401
1590
|
if (!Parse._useMasterKey) {
|
1402
1591
|
dataObject._JavaScriptKey = Parse.javaScriptKey;
|
@@ -1413,8 +1602,25 @@
|
|
1413
1602
|
}
|
1414
1603
|
var data = JSON.stringify(dataObject);
|
1415
1604
|
|
1416
|
-
|
1417
|
-
|
1605
|
+
return Parse._ajax(method, url, data).then(null, function(response) {
|
1606
|
+
// Transform the error into an instance of Parse.Error by trying to parse
|
1607
|
+
// the error string as JSON.
|
1608
|
+
var error;
|
1609
|
+
if (response && response.responseText) {
|
1610
|
+
try {
|
1611
|
+
var errorJSON = JSON.parse(response.responseText);
|
1612
|
+
if (errorJSON) {
|
1613
|
+
error = new Parse.Error(errorJSON.code, errorJSON.error);
|
1614
|
+
}
|
1615
|
+
} catch (e) {
|
1616
|
+
// If we fail to parse the error text, that's okay.
|
1617
|
+
}
|
1618
|
+
}
|
1619
|
+
error = error || new Parse.Error(-1, response.responseText);
|
1620
|
+
// By explicitly returning a rejected Promise, this will work with
|
1621
|
+
// either jQuery or Promises/A semantics.
|
1622
|
+
return Parse.Promise.error(error);
|
1623
|
+
});
|
1418
1624
|
};
|
1419
1625
|
|
1420
1626
|
// Helper function to get a value from a Backbone object as a property
|
@@ -1443,39 +1649,56 @@
|
|
1443
1649
|
}
|
1444
1650
|
if (!seenObjects || _.include(seenObjects, value) || !value._hasData) {
|
1445
1651
|
return value._toPointer();
|
1446
|
-
}
|
1652
|
+
}
|
1653
|
+
if (!value.dirty()) {
|
1447
1654
|
seenObjects = seenObjects.concat(value);
|
1448
1655
|
return Parse._encode(value._toFullJSON(seenObjects),
|
1449
1656
|
seenObjects,
|
1450
1657
|
disallowObjects);
|
1451
|
-
} else {
|
1452
|
-
throw "Can't fully embed a dirty object";
|
1453
1658
|
}
|
1454
|
-
|
1659
|
+
throw "Tried to save an object with a pointer to a new, unsaved object.";
|
1660
|
+
}
|
1661
|
+
if (value instanceof Parse.ACL) {
|
1455
1662
|
return value.toJSON();
|
1456
|
-
}
|
1663
|
+
}
|
1664
|
+
if (_.isDate(value)) {
|
1457
1665
|
return { "__type": "Date", "iso": value.toJSON() };
|
1458
|
-
}
|
1666
|
+
}
|
1667
|
+
if (value instanceof Parse.GeoPoint) {
|
1459
1668
|
return value.toJSON();
|
1460
|
-
}
|
1669
|
+
}
|
1670
|
+
if (_.isArray(value)) {
|
1461
1671
|
return _.map(value, function(x) {
|
1462
1672
|
return Parse._encode(x, seenObjects, disallowObjects);
|
1463
1673
|
});
|
1464
|
-
}
|
1674
|
+
}
|
1675
|
+
if (_.isRegExp(value)) {
|
1465
1676
|
return value.source;
|
1466
|
-
}
|
1677
|
+
}
|
1678
|
+
if (value instanceof Parse.Relation) {
|
1467
1679
|
return value.toJSON();
|
1468
|
-
}
|
1680
|
+
}
|
1681
|
+
if (value instanceof Parse.Op) {
|
1469
1682
|
return value.toJSON();
|
1470
|
-
}
|
1683
|
+
}
|
1684
|
+
if (value instanceof Parse.File) {
|
1685
|
+
if (!value.url()) {
|
1686
|
+
throw "Tried to save an object containing an unsaved file.";
|
1687
|
+
}
|
1688
|
+
return {
|
1689
|
+
__type: "File",
|
1690
|
+
name: value.name(),
|
1691
|
+
url: value.url()
|
1692
|
+
};
|
1693
|
+
}
|
1694
|
+
if (_.isObject(value)) {
|
1471
1695
|
var output = {};
|
1472
|
-
Parse.
|
1696
|
+
Parse._objectEach(value, function(v, k) {
|
1473
1697
|
output[k] = Parse._encode(v, seenObjects, disallowObjects);
|
1474
1698
|
});
|
1475
1699
|
return output;
|
1476
|
-
} else {
|
1477
|
-
return value;
|
1478
1700
|
}
|
1701
|
+
return value;
|
1479
1702
|
};
|
1480
1703
|
|
1481
1704
|
/**
|
@@ -1486,23 +1709,31 @@
|
|
1486
1709
|
var _ = Parse._;
|
1487
1710
|
if (!_.isObject(value)) {
|
1488
1711
|
return value;
|
1489
|
-
}
|
1490
|
-
|
1712
|
+
}
|
1713
|
+
if (_.isArray(value)) {
|
1714
|
+
Parse._arrayEach(value, function(v, k) {
|
1491
1715
|
value[k] = Parse._decode(k, v);
|
1492
1716
|
});
|
1493
1717
|
return value;
|
1494
|
-
}
|
1718
|
+
}
|
1719
|
+
if (value instanceof Parse.Object) {
|
1720
|
+
return value;
|
1721
|
+
}
|
1722
|
+
if (value instanceof Parse.File) {
|
1495
1723
|
return value;
|
1496
|
-
}
|
1724
|
+
}
|
1725
|
+
if (value instanceof Parse.Op) {
|
1497
1726
|
return value;
|
1498
|
-
}
|
1499
|
-
|
1727
|
+
}
|
1728
|
+
if (value.__op) {
|
1500
1729
|
return Parse.Op._decode(value);
|
1501
|
-
}
|
1730
|
+
}
|
1731
|
+
if (value.__type === "Pointer") {
|
1502
1732
|
var pointer = Parse.Object._create(value.className);
|
1503
1733
|
pointer._finishFetch({ objectId: value.objectId }, false);
|
1504
1734
|
return pointer;
|
1505
|
-
}
|
1735
|
+
}
|
1736
|
+
if (value.__type === "Object") {
|
1506
1737
|
// It's an Object included in a query result.
|
1507
1738
|
var className = value.className;
|
1508
1739
|
delete value.__type;
|
@@ -1510,69 +1741,83 @@
|
|
1510
1741
|
var object = Parse.Object._create(className);
|
1511
1742
|
object._finishFetch(value, true);
|
1512
1743
|
return object;
|
1513
|
-
}
|
1744
|
+
}
|
1745
|
+
if (value.__type === "Date") {
|
1514
1746
|
return Parse._parseDate(value.iso);
|
1515
|
-
}
|
1747
|
+
}
|
1748
|
+
if (value.__type === "GeoPoint") {
|
1516
1749
|
return new Parse.GeoPoint({
|
1517
1750
|
latitude: value.latitude,
|
1518
1751
|
longitude: value.longitude
|
1519
1752
|
});
|
1520
|
-
}
|
1753
|
+
}
|
1754
|
+
if (key === "ACL") {
|
1521
1755
|
if (value instanceof Parse.ACL) {
|
1522
1756
|
return value;
|
1523
|
-
} else {
|
1524
|
-
return new Parse.ACL(value);
|
1525
1757
|
}
|
1526
|
-
|
1758
|
+
return new Parse.ACL(value);
|
1759
|
+
}
|
1760
|
+
if (value.__type === "Relation") {
|
1527
1761
|
var relation = new Parse.Relation(null, key);
|
1528
1762
|
relation.targetClassName = value.className;
|
1529
1763
|
return relation;
|
1530
|
-
} else {
|
1531
|
-
Parse._each(value, function(v, k) {
|
1532
|
-
value[k] = Parse._decode(k, v);
|
1533
|
-
});
|
1534
|
-
return value;
|
1535
1764
|
}
|
1765
|
+
if (value.__type === "File") {
|
1766
|
+
var file = new Parse.File(value.name);
|
1767
|
+
file._url = value.url;
|
1768
|
+
return file;
|
1769
|
+
}
|
1770
|
+
Parse._objectEach(value, function(v, k) {
|
1771
|
+
value[k] = Parse._decode(k, v);
|
1772
|
+
});
|
1773
|
+
return value;
|
1536
1774
|
};
|
1537
1775
|
|
1776
|
+
Parse._arrayEach = Parse._.each;
|
1777
|
+
|
1538
1778
|
/**
|
1539
|
-
* Does a deep traversal of every item in object, calling func on
|
1540
|
-
* instances of Parse.Object.
|
1779
|
+
* Does a deep traversal of every item in object, calling func on every one.
|
1541
1780
|
* @param {Object} object The object or array to traverse deeply.
|
1542
|
-
* @param {Function} func The function to call for every
|
1543
|
-
* be passed the
|
1544
|
-
* will replace the
|
1545
|
-
* @returns {}
|
1546
|
-
* returns the result of calling func on that object.
|
1781
|
+
* @param {Function} func The function to call for every item. It will
|
1782
|
+
* be passed the item as an argument. If it returns a truthy value, that
|
1783
|
+
* value will replace the item in its parent container.
|
1784
|
+
* @returns {} the result of calling func on the top-level object itself.
|
1547
1785
|
*/
|
1548
|
-
Parse._traverse = function(object, func) {
|
1786
|
+
Parse._traverse = function(object, func, seen) {
|
1549
1787
|
if (object instanceof Parse.Object) {
|
1550
|
-
|
1788
|
+
seen = seen || [];
|
1789
|
+
if (Parse._.indexOf(seen, object) >= 0) {
|
1790
|
+
// We've already visited this object in this call.
|
1791
|
+
return;
|
1792
|
+
}
|
1793
|
+
seen.push(object);
|
1794
|
+
Parse._traverse(object.attributes, func, seen);
|
1551
1795
|
return func(object);
|
1552
1796
|
}
|
1553
1797
|
if (object instanceof Parse.Relation) {
|
1554
1798
|
// Nothing needs to be done, but we don't want to recurse into the
|
1555
1799
|
// relation's parent infinitely, so we catch this case.
|
1556
|
-
return;
|
1800
|
+
return func(object);
|
1557
1801
|
}
|
1558
1802
|
if (Parse._.isArray(object)) {
|
1559
1803
|
Parse._.each(object, function(child, index) {
|
1560
|
-
var newChild = Parse._traverse(child, func);
|
1804
|
+
var newChild = Parse._traverse(child, func, seen);
|
1561
1805
|
if (newChild) {
|
1562
1806
|
object[index] = newChild;
|
1563
1807
|
}
|
1564
1808
|
});
|
1565
|
-
return;
|
1809
|
+
return func(object);
|
1566
1810
|
}
|
1567
1811
|
if (Parse._.isObject(object)) {
|
1568
1812
|
Parse._each(object, function(child, key) {
|
1569
|
-
var newChild = Parse._traverse(child, func);
|
1813
|
+
var newChild = Parse._traverse(child, func, seen);
|
1570
1814
|
if (newChild) {
|
1571
1815
|
object[key] = newChild;
|
1572
1816
|
}
|
1573
1817
|
});
|
1574
|
-
return;
|
1818
|
+
return func(object);
|
1575
1819
|
}
|
1820
|
+
return func(object);
|
1576
1821
|
};
|
1577
1822
|
|
1578
1823
|
/**
|
@@ -1580,7 +1825,7 @@
|
|
1580
1825
|
* * it doesn't work for so-called array-like objects,
|
1581
1826
|
* * it does work for dictionaries with a "length" attribute.
|
1582
1827
|
*/
|
1583
|
-
Parse._each = function(obj, callback) {
|
1828
|
+
Parse._objectEach = Parse._each = function(obj, callback) {
|
1584
1829
|
var _ = Parse._;
|
1585
1830
|
if (_.isObject(obj)) {
|
1586
1831
|
_.each(_.keys(obj), function(key) {
|
@@ -2196,20 +2441,18 @@
|
|
2196
2441
|
* @param {Object} options An object with success and error callbacks.
|
2197
2442
|
*/
|
2198
2443
|
Parse.GeoPoint.current = function(options) {
|
2199
|
-
var
|
2200
|
-
|
2201
|
-
|
2202
|
-
|
2203
|
-
|
2204
|
-
|
2205
|
-
|
2206
|
-
}
|
2207
|
-
|
2208
|
-
|
2209
|
-
|
2210
|
-
|
2211
|
-
};
|
2212
|
-
navigator.geolocation.getCurrentPosition(success, error);
|
2444
|
+
var promise = new Parse.Promise();
|
2445
|
+
navigator.geolocation.getCurrentPosition(function(location) {
|
2446
|
+
promise.resolve(new Parse.GeoPoint({
|
2447
|
+
latitude: location.coords.latitude,
|
2448
|
+
longitude: location.coords.longitude
|
2449
|
+
}));
|
2450
|
+
|
2451
|
+
}, function(error) {
|
2452
|
+
promise.reject(error);
|
2453
|
+
});
|
2454
|
+
|
2455
|
+
return promise._thenRunCallbacks(options);
|
2213
2456
|
};
|
2214
2457
|
|
2215
2458
|
Parse.GeoPoint.prototype = {
|
@@ -2302,12 +2545,12 @@
|
|
2302
2545
|
if (_.isFunction(arg1)) {
|
2303
2546
|
throw "Parse.ACL() called with a function. Did you forget ()?";
|
2304
2547
|
}
|
2305
|
-
Parse.
|
2548
|
+
Parse._objectEach(arg1, function(accessList, userId) {
|
2306
2549
|
if (!_.isString(userId)) {
|
2307
2550
|
throw "Tried to create an ACL with an invalid userId.";
|
2308
2551
|
}
|
2309
2552
|
self.permissionsById[userId] = {};
|
2310
|
-
Parse.
|
2553
|
+
Parse._objectEach(accessList, function(allowed, permission) {
|
2311
2554
|
if (permission !== "read" && permission !== "write") {
|
2312
2555
|
throw "Tried to create an ACL with an invalid permission type.";
|
2313
2556
|
}
|
@@ -2591,7 +2834,7 @@
|
|
2591
2834
|
*/
|
2592
2835
|
Parse.Op._registerDecoder("Batch", function(json) {
|
2593
2836
|
var op = null;
|
2594
|
-
|
2837
|
+
Parse._arrayEach(json.ops, function(nextOp) {
|
2595
2838
|
nextOp = Parse.Op._decode(nextOp);
|
2596
2839
|
op = nextOp._mergeWithPrevious(op);
|
2597
2840
|
});
|
@@ -2877,7 +3120,7 @@
|
|
2877
3120
|
} else {
|
2878
3121
|
var newValue = _.difference(oldValue, this.objects());
|
2879
3122
|
// If there are saved Parse Objects being removed, also remove them.
|
2880
|
-
|
3123
|
+
Parse._arrayEach(this.objects(), function(obj) {
|
2881
3124
|
if (obj instanceof Parse.Object && obj.id) {
|
2882
3125
|
newValue = _.reject(newValue, function(other) {
|
2883
3126
|
return (other instanceof Parse.Object) && (other.id === obj.id);
|
@@ -3245,7 +3488,7 @@
|
|
3245
3488
|
}
|
3246
3489
|
};
|
3247
3490
|
|
3248
|
-
|
3491
|
+
Parse._arrayEach(objects, function(object, i) {
|
3249
3492
|
if (Parse.Promise.is(object)) {
|
3250
3493
|
object.then(function(result) {
|
3251
3494
|
results[i] = result;
|
@@ -3295,7 +3538,7 @@
|
|
3295
3538
|
this._resolved = true;
|
3296
3539
|
this._result = arguments;
|
3297
3540
|
var results = arguments;
|
3298
|
-
|
3541
|
+
Parse._arrayEach(this._resolvedCallbacks, function(resolvedCallback) {
|
3299
3542
|
resolvedCallback.apply(this, results);
|
3300
3543
|
});
|
3301
3544
|
this._resolvedCallbacks = [];
|
@@ -3313,7 +3556,7 @@
|
|
3313
3556
|
}
|
3314
3557
|
this._rejected = true;
|
3315
3558
|
this._error = error;
|
3316
|
-
|
3559
|
+
Parse._arrayEach(this._rejectedCallbacks, function(rejectedCallback) {
|
3317
3560
|
rejectedCallback(error);
|
3318
3561
|
});
|
3319
3562
|
this._resolvedCallbacks = [];
|
@@ -3395,10 +3638,12 @@
|
|
3395
3638
|
* @param optionsOrCallback {} A Backbone-style options callback, or a
|
3396
3639
|
* callback function. If this is an options object and contains a "model"
|
3397
3640
|
* attributes, that will be passed to error callbacks as the first argument.
|
3641
|
+
* @param model {} If truthy, this will be passed as the first result of
|
3642
|
+
* error callbacks. This is for Backbone-compatability.
|
3398
3643
|
* @return {Parse.Promise} A promise that will be resolved after the
|
3399
3644
|
* callbacks are run, with the same result as this.
|
3400
3645
|
*/
|
3401
|
-
_thenRunCallbacks: function(optionsOrCallback) {
|
3646
|
+
_thenRunCallbacks: function(optionsOrCallback, model) {
|
3402
3647
|
var options;
|
3403
3648
|
if (_.isFunction(optionsOrCallback)) {
|
3404
3649
|
var callback = optionsOrCallback;
|
@@ -3417,38 +3662,423 @@
|
|
3417
3662
|
|
3418
3663
|
return this.then(function(result) {
|
3419
3664
|
if (options.success) {
|
3420
|
-
options.success(
|
3665
|
+
options.success.apply(this, arguments);
|
3666
|
+
} else if (model) {
|
3667
|
+
// When there's no callback, a sync event should be triggered.
|
3668
|
+
model.trigger('sync', model, result, options);
|
3421
3669
|
}
|
3422
|
-
return
|
3670
|
+
return Parse.Promise.as.apply(Parse.Promise, arguments);
|
3423
3671
|
}, function(error) {
|
3424
3672
|
if (options.error) {
|
3425
|
-
if (
|
3426
|
-
options.error(
|
3673
|
+
if (!_.isUndefined(model)) {
|
3674
|
+
options.error(model, error);
|
3427
3675
|
} else {
|
3428
3676
|
options.error(error);
|
3429
3677
|
}
|
3678
|
+
} else if (model) {
|
3679
|
+
// When there's no error callback, an error event should be triggered.
|
3680
|
+
model.trigger('error', model, error, options);
|
3430
3681
|
}
|
3682
|
+
// By explicitly returning a rejected Promise, this will work with
|
3683
|
+
// either jQuery or Promises/A semantics.
|
3431
3684
|
return Parse.Promise.error(error);
|
3432
3685
|
});
|
3433
3686
|
},
|
3434
3687
|
|
3435
3688
|
/**
|
3436
|
-
* Adds a callback function that should be called regardless of whether
|
3437
|
-
* this promise failed or succeeded. The callback will be given either the
|
3438
|
-
* array of results for its first argument, or the error as its second,
|
3439
|
-
* depending on whether this Promise was rejected or resolved. Returns a
|
3440
|
-
* new Promise, like "then" would.
|
3441
|
-
* @param {Function} continuation the callback.
|
3689
|
+
* Adds a callback function that should be called regardless of whether
|
3690
|
+
* this promise failed or succeeded. The callback will be given either the
|
3691
|
+
* array of results for its first argument, or the error as its second,
|
3692
|
+
* depending on whether this Promise was rejected or resolved. Returns a
|
3693
|
+
* new Promise, like "then" would.
|
3694
|
+
* @param {Function} continuation the callback.
|
3695
|
+
*/
|
3696
|
+
_continueWith: function(continuation) {
|
3697
|
+
return this.then(function() {
|
3698
|
+
return continuation(arguments, null);
|
3699
|
+
}, function(error) {
|
3700
|
+
return continuation(null, error);
|
3701
|
+
});
|
3702
|
+
}
|
3703
|
+
|
3704
|
+
});
|
3705
|
+
|
3706
|
+
}(this));
|
3707
|
+
|
3708
|
+
/*jshint bitwise:false *//*global FileReader: true, File: true */
|
3709
|
+
(function(root) {
|
3710
|
+
root.Parse = root.Parse || {};
|
3711
|
+
var Parse = root.Parse;
|
3712
|
+
var _ = Parse._;
|
3713
|
+
|
3714
|
+
var b64Digit = function(number) {
|
3715
|
+
if (number < 26) {
|
3716
|
+
return String.fromCharCode(65 + number);
|
3717
|
+
}
|
3718
|
+
if (number < 52) {
|
3719
|
+
return String.fromCharCode(97 + (number - 26));
|
3720
|
+
}
|
3721
|
+
if (number < 62) {
|
3722
|
+
return String.fromCharCode(48 + (number - 52));
|
3723
|
+
}
|
3724
|
+
if (number === 62) {
|
3725
|
+
return "+";
|
3726
|
+
}
|
3727
|
+
if (number === 63) {
|
3728
|
+
return "/";
|
3729
|
+
}
|
3730
|
+
throw "Tried to encode large digit " + number + " in base64.";
|
3731
|
+
};
|
3732
|
+
|
3733
|
+
var encodeBase64 = function(array) {
|
3734
|
+
var chunks = [];
|
3735
|
+
chunks.length = Math.ceil(array.length / 3);
|
3736
|
+
_.times(chunks.length, function(i) {
|
3737
|
+
var b1 = array[i * 3];
|
3738
|
+
var b2 = array[i * 3 + 1] || 0;
|
3739
|
+
var b3 = array[i * 3 + 2] || 0;
|
3740
|
+
|
3741
|
+
var has2 = (i * 3 + 1) < array.length;
|
3742
|
+
var has3 = (i * 3 + 2) < array.length;
|
3743
|
+
|
3744
|
+
chunks[i] = [
|
3745
|
+
b64Digit((b1 >> 2) & 0x3F),
|
3746
|
+
b64Digit(((b1 << 4) & 0x30) | ((b2 >> 4) & 0x0F)),
|
3747
|
+
has2 ? b64Digit(((b2 << 2) & 0x3C) | ((b3 >> 6) & 0x03)) : "=",
|
3748
|
+
has3 ? b64Digit(b3 & 0x3F) : "="
|
3749
|
+
].join("");
|
3750
|
+
});
|
3751
|
+
return chunks.join("");
|
3752
|
+
};
|
3753
|
+
|
3754
|
+
|
3755
|
+
// A list of file extensions to mime types as found here:
|
3756
|
+
// http://stackoverflow.com/questions/58510/using-net-how-can-you-find-the-
|
3757
|
+
// mime-type-of-a-file-based-on-the-file-signature
|
3758
|
+
var mimeTypes = {
|
3759
|
+
ai: "application/postscript",
|
3760
|
+
aif: "audio/x-aiff",
|
3761
|
+
aifc: "audio/x-aiff",
|
3762
|
+
aiff: "audio/x-aiff",
|
3763
|
+
asc: "text/plain",
|
3764
|
+
atom: "application/atom+xml",
|
3765
|
+
au: "audio/basic",
|
3766
|
+
avi: "video/x-msvideo",
|
3767
|
+
bcpio: "application/x-bcpio",
|
3768
|
+
bin: "application/octet-stream",
|
3769
|
+
bmp: "image/bmp",
|
3770
|
+
cdf: "application/x-netcdf",
|
3771
|
+
cgm: "image/cgm",
|
3772
|
+
"class": "application/octet-stream",
|
3773
|
+
cpio: "application/x-cpio",
|
3774
|
+
cpt: "application/mac-compactpro",
|
3775
|
+
csh: "application/x-csh",
|
3776
|
+
css: "text/css",
|
3777
|
+
dcr: "application/x-director",
|
3778
|
+
dif: "video/x-dv",
|
3779
|
+
dir: "application/x-director",
|
3780
|
+
djv: "image/vnd.djvu",
|
3781
|
+
djvu: "image/vnd.djvu",
|
3782
|
+
dll: "application/octet-stream",
|
3783
|
+
dmg: "application/octet-stream",
|
3784
|
+
dms: "application/octet-stream",
|
3785
|
+
doc: "application/msword",
|
3786
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml." +
|
3787
|
+
"document",
|
3788
|
+
dotx: "application/vnd.openxmlformats-officedocument.wordprocessingml." +
|
3789
|
+
"template",
|
3790
|
+
docm: "application/vnd.ms-word.document.macroEnabled.12",
|
3791
|
+
dotm: "application/vnd.ms-word.template.macroEnabled.12",
|
3792
|
+
dtd: "application/xml-dtd",
|
3793
|
+
dv: "video/x-dv",
|
3794
|
+
dvi: "application/x-dvi",
|
3795
|
+
dxr: "application/x-director",
|
3796
|
+
eps: "application/postscript",
|
3797
|
+
etx: "text/x-setext",
|
3798
|
+
exe: "application/octet-stream",
|
3799
|
+
ez: "application/andrew-inset",
|
3800
|
+
gif: "image/gif",
|
3801
|
+
gram: "application/srgs",
|
3802
|
+
grxml: "application/srgs+xml",
|
3803
|
+
gtar: "application/x-gtar",
|
3804
|
+
hdf: "application/x-hdf",
|
3805
|
+
hqx: "application/mac-binhex40",
|
3806
|
+
htm: "text/html",
|
3807
|
+
html: "text/html",
|
3808
|
+
ice: "x-conference/x-cooltalk",
|
3809
|
+
ico: "image/x-icon",
|
3810
|
+
ics: "text/calendar",
|
3811
|
+
ief: "image/ief",
|
3812
|
+
ifb: "text/calendar",
|
3813
|
+
iges: "model/iges",
|
3814
|
+
igs: "model/iges",
|
3815
|
+
jnlp: "application/x-java-jnlp-file",
|
3816
|
+
jp2: "image/jp2",
|
3817
|
+
jpe: "image/jpeg",
|
3818
|
+
jpeg: "image/jpeg",
|
3819
|
+
jpg: "image/jpeg",
|
3820
|
+
js: "application/x-javascript",
|
3821
|
+
kar: "audio/midi",
|
3822
|
+
latex: "application/x-latex",
|
3823
|
+
lha: "application/octet-stream",
|
3824
|
+
lzh: "application/octet-stream",
|
3825
|
+
m3u: "audio/x-mpegurl",
|
3826
|
+
m4a: "audio/mp4a-latm",
|
3827
|
+
m4b: "audio/mp4a-latm",
|
3828
|
+
m4p: "audio/mp4a-latm",
|
3829
|
+
m4u: "video/vnd.mpegurl",
|
3830
|
+
m4v: "video/x-m4v",
|
3831
|
+
mac: "image/x-macpaint",
|
3832
|
+
man: "application/x-troff-man",
|
3833
|
+
mathml: "application/mathml+xml",
|
3834
|
+
me: "application/x-troff-me",
|
3835
|
+
mesh: "model/mesh",
|
3836
|
+
mid: "audio/midi",
|
3837
|
+
midi: "audio/midi",
|
3838
|
+
mif: "application/vnd.mif",
|
3839
|
+
mov: "video/quicktime",
|
3840
|
+
movie: "video/x-sgi-movie",
|
3841
|
+
mp2: "audio/mpeg",
|
3842
|
+
mp3: "audio/mpeg",
|
3843
|
+
mp4: "video/mp4",
|
3844
|
+
mpe: "video/mpeg",
|
3845
|
+
mpeg: "video/mpeg",
|
3846
|
+
mpg: "video/mpeg",
|
3847
|
+
mpga: "audio/mpeg",
|
3848
|
+
ms: "application/x-troff-ms",
|
3849
|
+
msh: "model/mesh",
|
3850
|
+
mxu: "video/vnd.mpegurl",
|
3851
|
+
nc: "application/x-netcdf",
|
3852
|
+
oda: "application/oda",
|
3853
|
+
ogg: "application/ogg",
|
3854
|
+
pbm: "image/x-portable-bitmap",
|
3855
|
+
pct: "image/pict",
|
3856
|
+
pdb: "chemical/x-pdb",
|
3857
|
+
pdf: "application/pdf",
|
3858
|
+
pgm: "image/x-portable-graymap",
|
3859
|
+
pgn: "application/x-chess-pgn",
|
3860
|
+
pic: "image/pict",
|
3861
|
+
pict: "image/pict",
|
3862
|
+
png: "image/png",
|
3863
|
+
pnm: "image/x-portable-anymap",
|
3864
|
+
pnt: "image/x-macpaint",
|
3865
|
+
pntg: "image/x-macpaint",
|
3866
|
+
ppm: "image/x-portable-pixmap",
|
3867
|
+
ppt: "application/vnd.ms-powerpoint",
|
3868
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml." +
|
3869
|
+
"presentation",
|
3870
|
+
potx: "application/vnd.openxmlformats-officedocument.presentationml." +
|
3871
|
+
"template",
|
3872
|
+
ppsx: "application/vnd.openxmlformats-officedocument.presentationml." +
|
3873
|
+
"slideshow",
|
3874
|
+
ppam: "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
3875
|
+
pptm: "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
3876
|
+
potm: "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
3877
|
+
ppsm: "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
3878
|
+
ps: "application/postscript",
|
3879
|
+
qt: "video/quicktime",
|
3880
|
+
qti: "image/x-quicktime",
|
3881
|
+
qtif: "image/x-quicktime",
|
3882
|
+
ra: "audio/x-pn-realaudio",
|
3883
|
+
ram: "audio/x-pn-realaudio",
|
3884
|
+
ras: "image/x-cmu-raster",
|
3885
|
+
rdf: "application/rdf+xml",
|
3886
|
+
rgb: "image/x-rgb",
|
3887
|
+
rm: "application/vnd.rn-realmedia",
|
3888
|
+
roff: "application/x-troff",
|
3889
|
+
rtf: "text/rtf",
|
3890
|
+
rtx: "text/richtext",
|
3891
|
+
sgm: "text/sgml",
|
3892
|
+
sgml: "text/sgml",
|
3893
|
+
sh: "application/x-sh",
|
3894
|
+
shar: "application/x-shar",
|
3895
|
+
silo: "model/mesh",
|
3896
|
+
sit: "application/x-stuffit",
|
3897
|
+
skd: "application/x-koan",
|
3898
|
+
skm: "application/x-koan",
|
3899
|
+
skp: "application/x-koan",
|
3900
|
+
skt: "application/x-koan",
|
3901
|
+
smi: "application/smil",
|
3902
|
+
smil: "application/smil",
|
3903
|
+
snd: "audio/basic",
|
3904
|
+
so: "application/octet-stream",
|
3905
|
+
spl: "application/x-futuresplash",
|
3906
|
+
src: "application/x-wais-source",
|
3907
|
+
sv4cpio: "application/x-sv4cpio",
|
3908
|
+
sv4crc: "application/x-sv4crc",
|
3909
|
+
svg: "image/svg+xml",
|
3910
|
+
swf: "application/x-shockwave-flash",
|
3911
|
+
t: "application/x-troff",
|
3912
|
+
tar: "application/x-tar",
|
3913
|
+
tcl: "application/x-tcl",
|
3914
|
+
tex: "application/x-tex",
|
3915
|
+
texi: "application/x-texinfo",
|
3916
|
+
texinfo: "application/x-texinfo",
|
3917
|
+
tif: "image/tiff",
|
3918
|
+
tiff: "image/tiff",
|
3919
|
+
tr: "application/x-troff",
|
3920
|
+
tsv: "text/tab-separated-values",
|
3921
|
+
txt: "text/plain",
|
3922
|
+
ustar: "application/x-ustar",
|
3923
|
+
vcd: "application/x-cdlink",
|
3924
|
+
vrml: "model/vrml",
|
3925
|
+
vxml: "application/voicexml+xml",
|
3926
|
+
wav: "audio/x-wav",
|
3927
|
+
wbmp: "image/vnd.wap.wbmp",
|
3928
|
+
wbmxl: "application/vnd.wap.wbxml",
|
3929
|
+
wml: "text/vnd.wap.wml",
|
3930
|
+
wmlc: "application/vnd.wap.wmlc",
|
3931
|
+
wmls: "text/vnd.wap.wmlscript",
|
3932
|
+
wmlsc: "application/vnd.wap.wmlscriptc",
|
3933
|
+
wrl: "model/vrml",
|
3934
|
+
xbm: "image/x-xbitmap",
|
3935
|
+
xht: "application/xhtml+xml",
|
3936
|
+
xhtml: "application/xhtml+xml",
|
3937
|
+
xls: "application/vnd.ms-excel",
|
3938
|
+
xml: "application/xml",
|
3939
|
+
xpm: "image/x-xpixmap",
|
3940
|
+
xsl: "application/xml",
|
3941
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
3942
|
+
xltx: "application/vnd.openxmlformats-officedocument.spreadsheetml." +
|
3943
|
+
"template",
|
3944
|
+
xlsm: "application/vnd.ms-excel.sheet.macroEnabled.12",
|
3945
|
+
xltm: "application/vnd.ms-excel.template.macroEnabled.12",
|
3946
|
+
xlam: "application/vnd.ms-excel.addin.macroEnabled.12",
|
3947
|
+
xlsb: "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
3948
|
+
xslt: "application/xslt+xml",
|
3949
|
+
xul: "application/vnd.mozilla.xul+xml",
|
3950
|
+
xwd: "image/x-xwindowdump",
|
3951
|
+
xyz: "chemical/x-xyz",
|
3952
|
+
zip: "application/zip"
|
3953
|
+
};
|
3954
|
+
|
3955
|
+
/**
|
3956
|
+
* Reads a File using a FileReader.
|
3957
|
+
* @param file {File} the File to read.
|
3958
|
+
* @param type {String} (optional) the mimetype to override with.
|
3959
|
+
* @return {Parse.Promise} A Promise that will be fulfilled with a
|
3960
|
+
* base64-encoded string of the data and its mime type.
|
3961
|
+
*/
|
3962
|
+
var readAsync = function(file, type) {
|
3963
|
+
var promise = new Parse.Promise();
|
3964
|
+
|
3965
|
+
if (typeof(FileReader) === "undefined") {
|
3966
|
+
return Parse.Promise.error(new Parse.Error(
|
3967
|
+
-1, "Attempted to use a FileReader on an unsupported browser."));
|
3968
|
+
}
|
3969
|
+
|
3970
|
+
var reader = new FileReader();
|
3971
|
+
reader.onloadend = function() {
|
3972
|
+
if (reader.readyState !== 2) {
|
3973
|
+
promise.reject(new Parse.Error(-1, "Error reading file."));
|
3974
|
+
return;
|
3975
|
+
}
|
3976
|
+
|
3977
|
+
var dataURL = reader.result;
|
3978
|
+
var matches = /^data:([^;]*);base64,(.*)$/.exec(dataURL);
|
3979
|
+
if (!matches) {
|
3980
|
+
promise.reject(
|
3981
|
+
new Parse.Error(-1, "Unable to interpret data URL: " + dataURL));
|
3982
|
+
return;
|
3983
|
+
}
|
3984
|
+
|
3985
|
+
promise.resolve(matches[2], type || matches[1]);
|
3986
|
+
};
|
3987
|
+
reader.readAsDataURL(file);
|
3988
|
+
return promise;
|
3989
|
+
};
|
3990
|
+
|
3991
|
+
/**
|
3992
|
+
* A Parse.File is a local representation of a file that is saved to the Parse
|
3993
|
+
* cloud.
|
3994
|
+
* @param name {String} The file's name. This will change to a unique value
|
3995
|
+
* once the file has finished saving.
|
3996
|
+
* @param data {Array} The data for the file, as either:
|
3997
|
+
* 1. an Array of byte value Numbers, or
|
3998
|
+
* 2. an Object like { base64: "..." } with a base64-encoded String.
|
3999
|
+
* 3. a File object selected with a file upload control. (3) only works
|
4000
|
+
* in Firefox 3.6+, Safari 6.0.2+, Chrome 7+, and IE 10+.
|
4001
|
+
* For example:<pre>
|
4002
|
+
* var fileUploadControl = $("#profilePhotoFileUpload")[0];
|
4003
|
+
* if (fileUploadControl.files.length > 0) {
|
4004
|
+
* var file = fileUploadControl.files[0];
|
4005
|
+
* var name = "photo.jpg";
|
4006
|
+
* var parseFile = new Parse.File(name, file);
|
4007
|
+
* parseFile.save().then(function() {
|
4008
|
+
* // The file has been saved to Parse.
|
4009
|
+
* }, function(error) {
|
4010
|
+
* // The file either could not be read, or could not be saved to Parse.
|
4011
|
+
* });
|
4012
|
+
* }</pre>
|
4013
|
+
* @param type {String} Optional Content-Type header to use for the file. If
|
4014
|
+
* this is omitted, the content type will be inferred from the name's
|
4015
|
+
* extension.
|
4016
|
+
*/
|
4017
|
+
Parse.File = function(name, data, type) {
|
4018
|
+
this._name = name;
|
4019
|
+
|
4020
|
+
// Guess the content type from the extension if we need to.
|
4021
|
+
var extension = /\.([^.]*)$/.exec(name);
|
4022
|
+
if (extension) {
|
4023
|
+
extension = extension[1].toLowerCase();
|
4024
|
+
}
|
4025
|
+
var guessedType = type || mimeTypes[extension] || "text/plain";
|
4026
|
+
|
4027
|
+
if (_.isArray(data)) {
|
4028
|
+
this._source = Parse.Promise.as(encodeBase64(data), guessedType);
|
4029
|
+
} else if (data && data.base64) {
|
4030
|
+
this._source = Parse.Promise.as(data.base64, guessedType);
|
4031
|
+
} else if (typeof(File) !== "undefined" && data instanceof File) {
|
4032
|
+
this._source = readAsync(data, type);
|
4033
|
+
} else if (_.isString(data)) {
|
4034
|
+
throw "Creating a Parse.File from a String is not yet supported.";
|
4035
|
+
}
|
4036
|
+
};
|
4037
|
+
|
4038
|
+
Parse.File.prototype = {
|
4039
|
+
|
4040
|
+
/**
|
4041
|
+
* Gets the name of the file. Before save is called, this is the filename
|
4042
|
+
* given by the user. After save is called, that name gets prefixed with a
|
4043
|
+
* unique identifier.
|
4044
|
+
*/
|
4045
|
+
name: function() {
|
4046
|
+
return this._name;
|
4047
|
+
},
|
4048
|
+
|
4049
|
+
/**
|
4050
|
+
* Gets the url of the file. It is only available after you save the file or
|
4051
|
+
* after you get the file from a Parse.Object.
|
4052
|
+
* @return {String}
|
3442
4053
|
*/
|
3443
|
-
|
3444
|
-
return this.
|
3445
|
-
|
3446
|
-
}, function(error) {
|
3447
|
-
return continuation(null, error);
|
3448
|
-
});
|
3449
|
-
}
|
4054
|
+
url: function() {
|
4055
|
+
return this._url;
|
4056
|
+
},
|
3450
4057
|
|
3451
|
-
|
4058
|
+
/**
|
4059
|
+
* Saves the file to the Parse cloud.
|
4060
|
+
* @param {Object} options A Backbone-style options object.
|
4061
|
+
* @return {Parse.Promise} Promise that is resolved when the save finishes.
|
4062
|
+
*/
|
4063
|
+
save: function(options) {
|
4064
|
+
var self = this;
|
4065
|
+
if (!self._previousSave) {
|
4066
|
+
self._previousSave = self._source.then(function(base64, type) {
|
4067
|
+
var data = {
|
4068
|
+
base64: base64,
|
4069
|
+
_ContentType: type
|
4070
|
+
};
|
4071
|
+
return Parse._request("files", self._name, null, 'POST', data);
|
4072
|
+
|
4073
|
+
}).then(function(response) {
|
4074
|
+
self._name = response.name;
|
4075
|
+
self._url = response.url;
|
4076
|
+
return self;
|
4077
|
+
});
|
4078
|
+
}
|
4079
|
+
return self._previousSave._thenRunCallbacks(options);
|
4080
|
+
}
|
4081
|
+
};
|
3452
4082
|
|
3453
4083
|
}(this));
|
3454
4084
|
|
@@ -3580,17 +4210,17 @@
|
|
3580
4210
|
*/
|
3581
4211
|
toJSON: function() {
|
3582
4212
|
var json = this._toFullJSON();
|
3583
|
-
|
3584
|
-
|
4213
|
+
Parse._arrayEach(["__type", "className"],
|
4214
|
+
function(key) { delete json[key]; });
|
3585
4215
|
return json;
|
3586
4216
|
},
|
3587
4217
|
|
3588
4218
|
_toFullJSON: function(seenObjects) {
|
3589
4219
|
var json = _.clone(this.attributes);
|
3590
|
-
Parse.
|
4220
|
+
Parse._objectEach(json, function(val, key) {
|
3591
4221
|
json[key] = Parse._encode(val, seenObjects);
|
3592
4222
|
});
|
3593
|
-
Parse.
|
4223
|
+
Parse._objectEach(this._operations, function(val, key) {
|
3594
4224
|
json[key] = val;
|
3595
4225
|
});
|
3596
4226
|
|
@@ -3623,7 +4253,11 @@
|
|
3623
4253
|
*/
|
3624
4254
|
_refreshCache: function() {
|
3625
4255
|
var self = this;
|
3626
|
-
|
4256
|
+
if (self._refreshingCache) {
|
4257
|
+
return;
|
4258
|
+
}
|
4259
|
+
self._refreshingCache = true;
|
4260
|
+
Parse._objectEach(this.attributes, function(value, key) {
|
3627
4261
|
if (value instanceof Parse.Object) {
|
3628
4262
|
value._refreshCache();
|
3629
4263
|
} else if (_.isObject(value)) {
|
@@ -3632,6 +4266,7 @@
|
|
3632
4266
|
}
|
3633
4267
|
}
|
3634
4268
|
});
|
4269
|
+
delete self._refreshingCache;
|
3635
4270
|
},
|
3636
4271
|
|
3637
4272
|
/**
|
@@ -3732,7 +4367,8 @@
|
|
3732
4367
|
_mergeMagicFields: function(attrs) {
|
3733
4368
|
// Check for changes of magic fields.
|
3734
4369
|
var model = this;
|
3735
|
-
|
4370
|
+
var specialFields = ["id", "objectId", "createdAt", "updatedAt"];
|
4371
|
+
Parse._arrayEach(specialFields, function(attr) {
|
3736
4372
|
if (attrs[attr]) {
|
3737
4373
|
if (attr === "objectId") {
|
3738
4374
|
model.id = attrs[attr];
|
@@ -3771,7 +4407,7 @@
|
|
3771
4407
|
var failedChanges = _.first(this._opSetQueue);
|
3772
4408
|
this._opSetQueue = _.rest(this._opSetQueue);
|
3773
4409
|
var nextChanges = _.first(this._opSetQueue);
|
3774
|
-
Parse.
|
4410
|
+
Parse._objectEach(failedChanges, function(op, key) {
|
3775
4411
|
var op1 = failedChanges[key];
|
3776
4412
|
var op2 = nextChanges[key];
|
3777
4413
|
if (op1 && op2) {
|
@@ -3795,7 +4431,7 @@
|
|
3795
4431
|
// same object, but that's a risk we have to take.
|
3796
4432
|
var fetchedObjects = {};
|
3797
4433
|
Parse._traverse(this.attributes, function(object) {
|
3798
|
-
if (object.id && object._hasData) {
|
4434
|
+
if (object instanceof Parse.Object && object.id && object._hasData) {
|
3799
4435
|
fetchedObjects[object.id] = object;
|
3800
4436
|
}
|
3801
4437
|
});
|
@@ -3805,13 +4441,13 @@
|
|
3805
4441
|
this._applyOpSet(savedChanges, this._serverData);
|
3806
4442
|
this._mergeMagicFields(serverData);
|
3807
4443
|
var self = this;
|
3808
|
-
Parse.
|
4444
|
+
Parse._objectEach(serverData, function(value, key) {
|
3809
4445
|
self._serverData[key] = Parse._decode(key, value);
|
3810
4446
|
|
3811
4447
|
// Look for any objects that might have become unfetched and fix them
|
3812
4448
|
// by replacing their values with the previously observed values.
|
3813
4449
|
var fetched = Parse._traverse(self._serverData[key], function(object) {
|
3814
|
-
if (fetchedObjects[object.id]) {
|
4450
|
+
if (object instanceof Parse.Object && fetchedObjects[object.id]) {
|
3815
4451
|
return fetchedObjects[object.id];
|
3816
4452
|
}
|
3817
4453
|
});
|
@@ -3834,7 +4470,7 @@
|
|
3834
4470
|
// Bring in all the new server data.
|
3835
4471
|
this._mergeMagicFields(serverData);
|
3836
4472
|
var self = this;
|
3837
|
-
Parse.
|
4473
|
+
Parse._objectEach(serverData, function(value, key) {
|
3838
4474
|
self._serverData[key] = Parse._decode(key, value);
|
3839
4475
|
});
|
3840
4476
|
|
@@ -3853,7 +4489,7 @@
|
|
3853
4489
|
*/
|
3854
4490
|
_applyOpSet: function(opSet, target) {
|
3855
4491
|
var self = this;
|
3856
|
-
Parse.
|
4492
|
+
Parse._objectEach(opSet, function(change, key) {
|
3857
4493
|
target[key] = change._estimate(target[key], self, key);
|
3858
4494
|
if (target[key] === Parse.Op._UNSET) {
|
3859
4495
|
delete target[key];
|
@@ -3867,7 +4503,9 @@
|
|
3867
4503
|
*/
|
3868
4504
|
_resetCacheForKey: function(key) {
|
3869
4505
|
var value = this.attributes[key];
|
3870
|
-
if (_.isObject(value) &&
|
4506
|
+
if (_.isObject(value) &&
|
4507
|
+
!(value instanceof Parse.Object) &&
|
4508
|
+
!(value instanceof Parse.File)) {
|
3871
4509
|
value = value.toJSON ? value.toJSON() : value;
|
3872
4510
|
var json = JSON.stringify(value);
|
3873
4511
|
if (this._hashedJSON[key] !== json) {
|
@@ -3889,7 +4527,7 @@
|
|
3889
4527
|
if (this._serverData[key]) {
|
3890
4528
|
this.attributes[key] = this._serverData[key];
|
3891
4529
|
}
|
3892
|
-
|
4530
|
+
Parse._arrayEach(this._opSetQueue, function(opSet) {
|
3893
4531
|
var op = opSet[key];
|
3894
4532
|
if (op) {
|
3895
4533
|
self.attributes[key] = op._estimate(self.attributes[key], self, key);
|
@@ -3913,20 +4551,20 @@
|
|
3913
4551
|
var previousAttributes = _.clone(this.attributes);
|
3914
4552
|
|
3915
4553
|
this.attributes = _.clone(this._serverData);
|
3916
|
-
|
4554
|
+
Parse._arrayEach(this._opSetQueue, function(opSet) {
|
3917
4555
|
self._applyOpSet(opSet, self.attributes);
|
3918
|
-
|
4556
|
+
Parse._objectEach(opSet, function(op, key) {
|
3919
4557
|
self._resetCacheForKey(key);
|
3920
4558
|
});
|
3921
4559
|
});
|
3922
4560
|
|
3923
4561
|
// Trigger change events for anything that changed because of the fetch.
|
3924
|
-
|
4562
|
+
Parse._objectEach(previousAttributes, function(oldValue, key) {
|
3925
4563
|
if (self.attributes[key] !== oldValue) {
|
3926
4564
|
self.trigger('change:' + key, self, self.attributes[key], {});
|
3927
4565
|
}
|
3928
4566
|
});
|
3929
|
-
|
4567
|
+
Parse._objectEach(this.attributes, function(newValue, key) {
|
3930
4568
|
if (!_.has(previousAttributes, key)) {
|
3931
4569
|
self.trigger('change:' + key, self, newValue, {});
|
3932
4570
|
}
|
@@ -3969,7 +4607,7 @@
|
|
3969
4607
|
var attrs, attr;
|
3970
4608
|
if (_.isObject(key) || Parse._isNullOrUndefined(key)) {
|
3971
4609
|
attrs = key;
|
3972
|
-
Parse.
|
4610
|
+
Parse._objectEach(attrs, function(v, k) {
|
3973
4611
|
attrs[k] = Parse._decode(k, v);
|
3974
4612
|
});
|
3975
4613
|
options = value;
|
@@ -3989,7 +4627,7 @@
|
|
3989
4627
|
|
3990
4628
|
// If the unset option is used, every attribute should be a Unset.
|
3991
4629
|
if (options.unset) {
|
3992
|
-
Parse.
|
4630
|
+
Parse._objectEach(attrs, function(unused_value, key) {
|
3993
4631
|
attrs[key] = new Parse.Op.Unset();
|
3994
4632
|
});
|
3995
4633
|
}
|
@@ -3997,7 +4635,7 @@
|
|
3997
4635
|
// Apply all the attributes to get the estimated values.
|
3998
4636
|
var dataToValidate = _.clone(attrs);
|
3999
4637
|
var self = this;
|
4000
|
-
Parse.
|
4638
|
+
Parse._objectEach(dataToValidate, function(value, key) {
|
4001
4639
|
if (value instanceof Parse.Op) {
|
4002
4640
|
dataToValidate[key] = value._estimate(self.attributes[key],
|
4003
4641
|
self, key);
|
@@ -4019,7 +4657,7 @@
|
|
4019
4657
|
var prev = this._previousAttributes || {};
|
4020
4658
|
|
4021
4659
|
// Update attributes.
|
4022
|
-
Parse.
|
4660
|
+
Parse._arrayEach(_.keys(attrs), function(attr) {
|
4023
4661
|
var val = attrs[attr];
|
4024
4662
|
|
4025
4663
|
// If this is a relation object we need to set the parent correctly,
|
@@ -4158,7 +4796,7 @@
|
|
4158
4796
|
*/
|
4159
4797
|
_getSaveJSON: function() {
|
4160
4798
|
var json = _.clone(_.first(this._opSetQueue));
|
4161
|
-
Parse.
|
4799
|
+
Parse._objectEach(json, function(op, key) {
|
4162
4800
|
json[key] = op.toJSON();
|
4163
4801
|
});
|
4164
4802
|
return json;
|
@@ -4179,22 +4817,12 @@
|
|
4179
4817
|
* completes.
|
4180
4818
|
*/
|
4181
4819
|
fetch: function(options) {
|
4182
|
-
var
|
4183
|
-
|
4184
|
-
|
4185
|
-
|
4186
|
-
|
4187
|
-
|
4188
|
-
if (success) {
|
4189
|
-
success(model, resp);
|
4190
|
-
}
|
4191
|
-
promise.resolve(model);
|
4192
|
-
};
|
4193
|
-
options.error = Parse.Object._wrapError(options.error, model, options,
|
4194
|
-
promise);
|
4195
|
-
Parse._request(
|
4196
|
-
"classes", model.className, model.id, 'GET', null, options);
|
4197
|
-
return promise;
|
4820
|
+
var self = this;
|
4821
|
+
var request = Parse._request("classes", this.className, this.id, 'GET');
|
4822
|
+
return request.then(function(response, status, xhr) {
|
4823
|
+
self._finishFetch(self.parse(response, status, xhr), true);
|
4824
|
+
return self;
|
4825
|
+
})._thenRunCallbacks(options, this);
|
4198
4826
|
},
|
4199
4827
|
|
4200
4828
|
/**
|
@@ -4236,8 +4864,6 @@
|
|
4236
4864
|
* @see Parse.Error
|
4237
4865
|
*/
|
4238
4866
|
save: function(arg1, arg2, arg3) {
|
4239
|
-
var promise = new Parse.Promise();
|
4240
|
-
|
4241
4867
|
var i, attrs, current, options, saved;
|
4242
4868
|
if (_.isObject(arg1) || Parse._isNullOrUndefined(arg1)) {
|
4243
4869
|
attrs = arg1;
|
@@ -4269,25 +4895,22 @@
|
|
4269
4895
|
}
|
4270
4896
|
}
|
4271
4897
|
|
4272
|
-
options =
|
4898
|
+
options = _.clone(options) || {};
|
4273
4899
|
if (options.wait) {
|
4274
4900
|
current = _.clone(this.attributes);
|
4275
4901
|
}
|
4276
|
-
|
4902
|
+
|
4903
|
+
var setOptions = _.clone(options) || {};
|
4277
4904
|
if (setOptions.wait) {
|
4278
4905
|
setOptions.silent = true;
|
4279
4906
|
}
|
4907
|
+
var setError;
|
4280
4908
|
setOptions.error = function(model, error) {
|
4281
|
-
|
4282
|
-
options.error.apply(this, arguments);
|
4283
|
-
}
|
4284
|
-
promise.reject(error);
|
4909
|
+
setError = error;
|
4285
4910
|
};
|
4286
4911
|
if (attrs && !this.set(attrs, setOptions)) {
|
4287
|
-
return
|
4912
|
+
return Parse.Promise.error(setError)._thenRunCallbacks(options, this);
|
4288
4913
|
}
|
4289
|
-
var oldOptions = options; // Psuedonym more accurate in some contexts.
|
4290
|
-
var newOptions = _.clone(options);
|
4291
4914
|
|
4292
4915
|
var model = this;
|
4293
4916
|
|
@@ -4296,53 +4919,24 @@
|
|
4296
4919
|
|
4297
4920
|
|
4298
4921
|
|
4299
|
-
var unsavedChildren =
|
4300
|
-
|
4922
|
+
var unsavedChildren = [];
|
4923
|
+
var unsavedFiles = [];
|
4924
|
+
Parse.Object._findUnsavedChildren(model.attributes,
|
4925
|
+
unsavedChildren,
|
4926
|
+
unsavedFiles);
|
4927
|
+
if (unsavedChildren.length + unsavedFiles.length > 0) {
|
4301
4928
|
return Parse.Object._deepSaveAsync(this.attributes).then(function() {
|
4302
|
-
return model.save(null,
|
4929
|
+
return model.save(null, options);
|
4303
4930
|
}, function(error) {
|
4304
|
-
|
4305
|
-
options.error.apply(this, arguments);
|
4306
|
-
}
|
4307
|
-
return Parse.Promise.error(error);
|
4931
|
+
return Parse.Promise.error(error)._thenRunCallbacks(options, model);
|
4308
4932
|
});
|
4309
4933
|
}
|
4310
4934
|
|
4311
|
-
/** ignore */
|
4312
|
-
newOptions.success = function(resp, status, xhr) {
|
4313
|
-
var serverAttrs = model.parse(resp, status, xhr);
|
4314
|
-
if (newOptions.wait) {
|
4315
|
-
serverAttrs = _.extend(attrs || {}, serverAttrs);
|
4316
|
-
}
|
4317
|
-
|
4318
|
-
model._finishSave(serverAttrs);
|
4319
|
-
|
4320
|
-
if (oldOptions.success) {
|
4321
|
-
oldOptions.success(model, resp);
|
4322
|
-
} else {
|
4323
|
-
model.trigger('sync', model, resp, newOptions);
|
4324
|
-
}
|
4325
|
-
|
4326
|
-
promise.resolve(model);
|
4327
|
-
};
|
4328
|
-
|
4329
|
-
newOptions.error = function(model, error) {
|
4330
|
-
model._cancelSave();
|
4331
|
-
if (oldOptions.error) {
|
4332
|
-
oldOptions.error.apply(this, arguments);
|
4333
|
-
}
|
4334
|
-
|
4335
|
-
promise.reject(error);
|
4336
|
-
};
|
4337
|
-
|
4338
|
-
newOptions.error = Parse.Object._wrapError(newOptions.error, model,
|
4339
|
-
newOptions);
|
4340
|
-
|
4341
4935
|
this._startSave();
|
4342
4936
|
this._saving = (this._saving || 0) + 1;
|
4343
4937
|
|
4344
4938
|
this._allPreviousSaves = this._allPreviousSaves || Parse.Promise.as();
|
4345
|
-
this._allPreviousSaves._continueWith(function() {
|
4939
|
+
this._allPreviousSaves = this._allPreviousSaves._continueWith(function() {
|
4346
4940
|
var method = model.id ? 'PUT' : 'POST';
|
4347
4941
|
|
4348
4942
|
var json = model._getSaveJSON();
|
@@ -4354,17 +4948,28 @@
|
|
4354
4948
|
route = "users";
|
4355
4949
|
className = null;
|
4356
4950
|
}
|
4357
|
-
var request =
|
4358
|
-
Parse._request(route, className, model.id, method, json, newOptions);
|
4359
|
-
if (newOptions.wait) {
|
4360
|
-
model.set(current, setOptions);
|
4361
|
-
}
|
4362
|
-
return request;
|
4951
|
+
var request = Parse._request(route, className, model.id, method, json);
|
4363
4952
|
|
4364
|
-
|
4365
|
-
|
4953
|
+
request = request.then(function(resp, status, xhr) {
|
4954
|
+
var serverAttrs = model.parse(resp, status, xhr);
|
4955
|
+
if (options.wait) {
|
4956
|
+
serverAttrs = _.extend(attrs || {}, serverAttrs);
|
4957
|
+
}
|
4958
|
+
model._finishSave(serverAttrs);
|
4959
|
+
if (options.wait) {
|
4960
|
+
model.set(current, setOptions);
|
4961
|
+
}
|
4962
|
+
return model;
|
4366
4963
|
|
4367
|
-
|
4964
|
+
}, function(error) {
|
4965
|
+
model._cancelSave();
|
4966
|
+
return Parse.Promise.error(error);
|
4967
|
+
|
4968
|
+
})._thenRunCallbacks(options, model);
|
4969
|
+
|
4970
|
+
return request;
|
4971
|
+
});
|
4972
|
+
return this._allPreviousSaves;
|
4368
4973
|
},
|
4369
4974
|
|
4370
4975
|
/**
|
@@ -4377,10 +4982,8 @@
|
|
4377
4982
|
* completes.
|
4378
4983
|
*/
|
4379
4984
|
destroy: function(options) {
|
4380
|
-
|
4381
|
-
options = options ? _.clone(options) : {};
|
4985
|
+
options = options || {};
|
4382
4986
|
var model = this;
|
4383
|
-
var success = options.success;
|
4384
4987
|
|
4385
4988
|
var triggerDestroy = function() {
|
4386
4989
|
model.trigger('destroy', model, model.collection, options);
|
@@ -4389,27 +4992,19 @@
|
|
4389
4992
|
if (!this.id) {
|
4390
4993
|
return triggerDestroy();
|
4391
4994
|
}
|
4392
|
-
/** ignore */
|
4393
|
-
options.success = function(resp) {
|
4394
|
-
if (options.wait) {
|
4395
|
-
triggerDestroy();
|
4396
|
-
}
|
4397
|
-
if (success) {
|
4398
|
-
success(model, resp);
|
4399
|
-
} else {
|
4400
|
-
model.trigger('sync', model, resp, options);
|
4401
|
-
}
|
4402
|
-
promise.resolve(model);
|
4403
|
-
};
|
4404
|
-
options.error = Parse.Object._wrapError(options.error, model, options,
|
4405
|
-
promise);
|
4406
4995
|
|
4407
|
-
Parse._request("classes", this.className, this.id, 'DELETE', null,
|
4408
|
-
options);
|
4409
4996
|
if (!options.wait) {
|
4410
4997
|
triggerDestroy();
|
4411
4998
|
}
|
4412
|
-
|
4999
|
+
|
5000
|
+
var request =
|
5001
|
+
Parse._request("classes", this.className, this.id, 'DELETE');
|
5002
|
+
return request.then(function() {
|
5003
|
+
if (options.wait) {
|
5004
|
+
triggerDestroy();
|
5005
|
+
}
|
5006
|
+
return model;
|
5007
|
+
})._thenRunCallbacks(options, this);
|
4413
5008
|
},
|
4414
5009
|
|
4415
5010
|
/**
|
@@ -4460,14 +5055,14 @@
|
|
4460
5055
|
|
4461
5056
|
// Silent changes become pending changes.
|
4462
5057
|
var self = this;
|
4463
|
-
Parse.
|
5058
|
+
Parse._objectEach(this._silent, function(attr) {
|
4464
5059
|
self._pending[attr] = true;
|
4465
5060
|
});
|
4466
5061
|
|
4467
5062
|
// Silent changes are triggered.
|
4468
5063
|
var changes = _.extend({}, options.changes, this._silent);
|
4469
5064
|
this._silent = {};
|
4470
|
-
Parse.
|
5065
|
+
Parse._objectEach(changes, function(unused_value, attr) {
|
4471
5066
|
self.trigger('change:' + attr, self, self.get(attr), options);
|
4472
5067
|
});
|
4473
5068
|
if (changing) {
|
@@ -4486,7 +5081,7 @@
|
|
4486
5081
|
this._pending = {};
|
4487
5082
|
this.trigger('change', this, options);
|
4488
5083
|
// Pending and silent changes still remain.
|
4489
|
-
Parse.
|
5084
|
+
Parse._objectEach(this.changed, deleteChanged);
|
4490
5085
|
self._previousAttributes = _.clone(this.attributes);
|
4491
5086
|
}
|
4492
5087
|
|
@@ -4531,7 +5126,7 @@
|
|
4531
5126
|
}
|
4532
5127
|
var changed = {};
|
4533
5128
|
var old = this._previousAttributes;
|
4534
|
-
Parse.
|
5129
|
+
Parse._objectEach(diff, function(diffVal, attr) {
|
4535
5130
|
if (!_.isEqual(old[attr], diffVal)) {
|
4536
5131
|
changed[attr] = diffVal;
|
4537
5132
|
}
|
@@ -4732,41 +5327,23 @@
|
|
4732
5327
|
return NewClassObject;
|
4733
5328
|
};
|
4734
5329
|
|
4735
|
-
|
4736
|
-
|
4737
|
-
|
4738
|
-
|
4739
|
-
|
4740
|
-
|
4741
|
-
response = model;
|
4742
|
-
}
|
4743
|
-
var error = new Parse.Error(-1, response.responseText);
|
4744
|
-
if (response.responseText) {
|
4745
|
-
var errorJSON = JSON.parse(response.responseText);
|
4746
|
-
if (errorJSON) {
|
4747
|
-
error = new Parse.Error(errorJSON.code, errorJSON.error);
|
5330
|
+
Parse.Object._findUnsavedChildren = function(object, children, files) {
|
5331
|
+
Parse._traverse(object, function(object) {
|
5332
|
+
if (object instanceof Parse.Object) {
|
5333
|
+
object._refreshCache();
|
5334
|
+
if (object.dirty()) {
|
5335
|
+
children.push(object);
|
4748
5336
|
}
|
5337
|
+
return;
|
4749
5338
|
}
|
4750
|
-
if (onError) {
|
4751
|
-
onError(originalModel, error, options);
|
4752
|
-
} else {
|
4753
|
-
originalModel.trigger('error', originalModel, error, options);
|
4754
|
-
}
|
4755
|
-
if (promise) {
|
4756
|
-
promise.reject(error);
|
4757
|
-
}
|
4758
|
-
};
|
4759
|
-
};
|
4760
5339
|
|
4761
|
-
|
4762
|
-
|
4763
|
-
|
4764
|
-
|
4765
|
-
|
4766
|
-
results.push(object);
|
5340
|
+
if (object instanceof Parse.File) {
|
5341
|
+
if (!object.url()) {
|
5342
|
+
files.push(object);
|
5343
|
+
}
|
5344
|
+
return;
|
4767
5345
|
}
|
4768
5346
|
});
|
4769
|
-
return results;
|
4770
5347
|
};
|
4771
5348
|
|
4772
5349
|
Parse.Object._canBeSerializedAsValue = function(object) {
|
@@ -4776,14 +5353,14 @@
|
|
4776
5353
|
canBeSerializedAsValue = !!object.id;
|
4777
5354
|
|
4778
5355
|
} else if (_.isArray(object)) {
|
4779
|
-
|
5356
|
+
Parse._arrayEach(object, function(child) {
|
4780
5357
|
if (!Parse.Object._canBeSerializedAsValue(child)) {
|
4781
5358
|
canBeSerializedAsValue = false;
|
4782
5359
|
}
|
4783
5360
|
});
|
4784
5361
|
|
4785
5362
|
} else if (_.isObject(object)) {
|
4786
|
-
Parse.
|
5363
|
+
Parse._objectEach(object, function(child) {
|
4787
5364
|
if (!Parse.Object._canBeSerializedAsValue(child)) {
|
4788
5365
|
canBeSerializedAsValue = false;
|
4789
5366
|
}
|
@@ -4794,97 +5371,108 @@
|
|
4794
5371
|
};
|
4795
5372
|
|
4796
5373
|
Parse.Object._deepSaveAsync = function(object) {
|
4797
|
-
var
|
5374
|
+
var unsavedChildren = [];
|
5375
|
+
var unsavedFiles = [];
|
5376
|
+
Parse.Object._findUnsavedChildren(object, unsavedChildren, unsavedFiles);
|
5377
|
+
|
5378
|
+
var promise = Parse.Promise.as();
|
5379
|
+
_.each(unsavedFiles, function(file) {
|
5380
|
+
promise = promise.then(function() {
|
5381
|
+
return file.save();
|
5382
|
+
});
|
5383
|
+
});
|
5384
|
+
|
5385
|
+
var objects = _.uniq(unsavedChildren);
|
4798
5386
|
var remaining = _.uniq(objects);
|
4799
5387
|
|
4800
|
-
return
|
4801
|
-
return
|
4802
|
-
|
4803
|
-
|
4804
|
-
|
4805
|
-
|
4806
|
-
|
4807
|
-
|
4808
|
-
|
4809
|
-
|
4810
|
-
|
4811
|
-
|
4812
|
-
|
5388
|
+
return promise.then(function() {
|
5389
|
+
return Parse.Promise._continueWhile(function() {
|
5390
|
+
return remaining.length > 0;
|
5391
|
+
}, function() {
|
5392
|
+
|
5393
|
+
// Gather up all the objects that can be saved in this batch.
|
5394
|
+
var batch = [];
|
5395
|
+
var newRemaining = [];
|
5396
|
+
Parse._arrayEach(remaining, function(object) {
|
5397
|
+
// Limit batches to 20 objects.
|
5398
|
+
if (batch.length > 20) {
|
5399
|
+
newRemaining.push(object);
|
5400
|
+
return;
|
5401
|
+
}
|
4813
5402
|
|
4814
|
-
|
4815
|
-
|
4816
|
-
|
4817
|
-
|
4818
|
-
|
4819
|
-
|
4820
|
-
|
5403
|
+
if (object._canBeSerialized()) {
|
5404
|
+
batch.push(object);
|
5405
|
+
} else {
|
5406
|
+
newRemaining.push(object);
|
5407
|
+
}
|
5408
|
+
});
|
5409
|
+
remaining = newRemaining;
|
4821
5410
|
|
4822
|
-
|
4823
|
-
|
4824
|
-
|
4825
|
-
|
4826
|
-
|
4827
|
-
|
5411
|
+
// If we can't save any objects, there must be a circular reference.
|
5412
|
+
if (batch.length === 0) {
|
5413
|
+
return Parse.Promise.error(
|
5414
|
+
new Parse.Error(Parse.Error.OTHER_CAUSE,
|
5415
|
+
"Tried to save a batch with a cycle."));
|
5416
|
+
}
|
4828
5417
|
|
4829
|
-
|
4830
|
-
|
4831
|
-
|
4832
|
-
|
4833
|
-
|
4834
|
-
|
4835
|
-
|
4836
|
-
|
5418
|
+
// Reserve a spot in every object's save queue.
|
5419
|
+
var readyToStart = Parse.Promise.when(_.map(batch, function(object) {
|
5420
|
+
return object._allPreviousSaves || Parse.Promise.as();
|
5421
|
+
}));
|
5422
|
+
var batchFinished = new Parse.Promise();
|
5423
|
+
Parse._arrayEach(batch, function(object) {
|
5424
|
+
object._allPreviousSaves = batchFinished;
|
5425
|
+
});
|
4837
5426
|
|
4838
|
-
|
4839
|
-
|
4840
|
-
|
4841
|
-
|
4842
|
-
|
4843
|
-
|
4844
|
-
|
4845
|
-
|
4846
|
-
|
4847
|
-
|
4848
|
-
|
5427
|
+
// Save a single batch, whether previous saves succeeded or failed.
|
5428
|
+
return readyToStart._continueWith(function() {
|
5429
|
+
return Parse._request("batch", null, null, "POST", {
|
5430
|
+
requests: _.map(batch, function(object) {
|
5431
|
+
var json = object._getSaveJSON();
|
5432
|
+
var method = "POST";
|
5433
|
+
|
5434
|
+
var path = "/1/classes/" + object.className;
|
5435
|
+
if (object.id) {
|
5436
|
+
path = path + "/" + object.id;
|
5437
|
+
method = "PUT";
|
5438
|
+
}
|
5439
|
+
|
5440
|
+
object._startSave();
|
5441
|
+
|
5442
|
+
return {
|
5443
|
+
method: method,
|
5444
|
+
path: path,
|
5445
|
+
body: json
|
5446
|
+
};
|
5447
|
+
})
|
5448
|
+
|
5449
|
+
}).then(function(response, status, xhr) {
|
5450
|
+
var error;
|
5451
|
+
Parse._arrayEach(batch, function(object, i) {
|
5452
|
+
if (response[i].success) {
|
5453
|
+
object._finishSave(
|
5454
|
+
object.parse(response[i].success, status, xhr));
|
5455
|
+
} else {
|
5456
|
+
error = error || response[i].error;
|
5457
|
+
object._cancelSave();
|
5458
|
+
}
|
5459
|
+
});
|
5460
|
+
if (error) {
|
5461
|
+
return Parse.Promise.error(
|
5462
|
+
new Parse.Error(error.code, error.error));
|
4849
5463
|
}
|
4850
5464
|
|
4851
|
-
|
4852
|
-
|
4853
|
-
return
|
4854
|
-
|
4855
|
-
|
4856
|
-
|
4857
|
-
};
|
4858
|
-
})
|
4859
|
-
|
4860
|
-
}).then(function(response, status, xhr) {
|
4861
|
-
var error;
|
4862
|
-
_.each(batch, function(object, i) {
|
4863
|
-
if (response[i].success) {
|
4864
|
-
object._finishSave(
|
4865
|
-
object.parse(response[i].success, status, xhr));
|
4866
|
-
} else {
|
4867
|
-
error = error || response[i].error;
|
4868
|
-
object._cancelSave();
|
4869
|
-
}
|
5465
|
+
}).then(function(results) {
|
5466
|
+
batchFinished.resolve(results);
|
5467
|
+
return results;
|
5468
|
+
}, function(error) {
|
5469
|
+
batchFinished.reject(error);
|
5470
|
+
return Parse.Promise.error(error);
|
4870
5471
|
});
|
4871
|
-
if (error) {
|
4872
|
-
return Parse.Promise.error(
|
4873
|
-
new Parse.Error(error.code, error.error));
|
4874
|
-
}
|
4875
|
-
|
4876
|
-
}).then(function(results) {
|
4877
|
-
batchFinished.resolve(results);
|
4878
|
-
return results;
|
4879
|
-
}, function(error) {
|
4880
|
-
batchFinished.reject(error);
|
4881
|
-
return Parse.Promise.error(error);
|
4882
|
-
|
4883
5472
|
});
|
4884
5473
|
});
|
4885
5474
|
}).then(function() {
|
4886
5475
|
return object;
|
4887
|
-
|
4888
5476
|
});
|
4889
5477
|
};
|
4890
5478
|
|
@@ -5237,7 +5825,7 @@
|
|
5237
5825
|
var self = this;
|
5238
5826
|
models = models || [];
|
5239
5827
|
options = options || {};
|
5240
|
-
|
5828
|
+
Parse._arrayEach(this.models, function(model) {
|
5241
5829
|
self._removeReference(model);
|
5242
5830
|
});
|
5243
5831
|
this._reset();
|
@@ -5254,26 +5842,20 @@
|
|
5254
5842
|
* models to the collection instead of resetting.
|
5255
5843
|
*/
|
5256
5844
|
fetch: function(options) {
|
5257
|
-
options =
|
5845
|
+
options = _.clone(options) || {};
|
5258
5846
|
if (options.parse === undefined) {
|
5259
5847
|
options.parse = true;
|
5260
5848
|
}
|
5261
5849
|
var collection = this;
|
5262
|
-
var
|
5263
|
-
|
5850
|
+
var query = this.query || new Parse.Query(this.model);
|
5851
|
+
return query.find().then(function(results) {
|
5264
5852
|
if (options.add) {
|
5265
5853
|
collection.add(results, options);
|
5266
5854
|
} else {
|
5267
5855
|
collection.reset(results, options);
|
5268
5856
|
}
|
5269
|
-
|
5270
|
-
|
5271
|
-
}
|
5272
|
-
};
|
5273
|
-
options.error = Parse.Object._wrapError(options.error, collection,
|
5274
|
-
options);
|
5275
|
-
var query = this.query || new Parse.Query(this.model);
|
5276
|
-
query.find(options);
|
5857
|
+
return collection;
|
5858
|
+
})._thenRunCallbacks(options, this);
|
5277
5859
|
},
|
5278
5860
|
|
5279
5861
|
/**
|
@@ -5391,7 +5973,7 @@
|
|
5391
5973
|
'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy'];
|
5392
5974
|
|
5393
5975
|
// Mix in each Underscore method as a proxy to `Collection#models`.
|
5394
|
-
|
5976
|
+
Parse._arrayEach(methods, function(method) {
|
5395
5977
|
Parse.Collection.prototype[method] = function() {
|
5396
5978
|
return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
|
5397
5979
|
};
|
@@ -5556,7 +6138,7 @@
|
|
5556
6138
|
}
|
5557
6139
|
this.undelegateEvents();
|
5558
6140
|
var self = this;
|
5559
|
-
Parse.
|
6141
|
+
Parse._objectEach(events, function(method, key) {
|
5560
6142
|
if (!_.isFunction(method)) {
|
5561
6143
|
method = self[events[key]];
|
5562
6144
|
}
|
@@ -5679,7 +6261,7 @@
|
|
5679
6261
|
if (!authData) {
|
5680
6262
|
return;
|
5681
6263
|
}
|
5682
|
-
|
6264
|
+
Parse._objectEach(this.get('authData'), function(value, key) {
|
5683
6265
|
if (!authData[key]) {
|
5684
6266
|
delete authData[key];
|
5685
6267
|
}
|
@@ -5696,7 +6278,7 @@
|
|
5696
6278
|
}
|
5697
6279
|
|
5698
6280
|
var self = this;
|
5699
|
-
|
6281
|
+
Parse._objectEach(this.get('authData'), function(value, key) {
|
5700
6282
|
self._synchronizeAuthData(key);
|
5701
6283
|
});
|
5702
6284
|
},
|
@@ -5760,7 +6342,7 @@
|
|
5760
6342
|
this.set('authData', authData);
|
5761
6343
|
|
5762
6344
|
// Overridden so that the user can be made the current user.
|
5763
|
-
var newOptions = _.clone(options);
|
6345
|
+
var newOptions = _.clone(options) || {};
|
5764
6346
|
newOptions.success = function(model) {
|
5765
6347
|
model._handleSaveResult(true);
|
5766
6348
|
if (options.success) {
|
@@ -5770,20 +6352,25 @@
|
|
5770
6352
|
return this.save({'authData': authData}, newOptions);
|
5771
6353
|
} else {
|
5772
6354
|
var self = this;
|
5773
|
-
|
6355
|
+
var promise = new Parse.Promise();
|
6356
|
+
provider.authenticate({
|
5774
6357
|
success: function(provider, result) {
|
5775
6358
|
self._linkWith(provider, {
|
5776
6359
|
authData: result,
|
5777
6360
|
success: options.success,
|
5778
6361
|
error: options.error
|
6362
|
+
}).then(function() {
|
6363
|
+
promise.resolve(self);
|
5779
6364
|
});
|
5780
6365
|
},
|
5781
6366
|
error: function(provider, error) {
|
5782
6367
|
if (options.error) {
|
5783
6368
|
options.error(self, error);
|
5784
6369
|
}
|
6370
|
+
promise.reject(error);
|
5785
6371
|
}
|
5786
6372
|
});
|
6373
|
+
return promise;
|
5787
6374
|
}
|
5788
6375
|
},
|
5789
6376
|
|
@@ -5833,7 +6420,7 @@
|
|
5833
6420
|
return;
|
5834
6421
|
}
|
5835
6422
|
var self = this;
|
5836
|
-
|
6423
|
+
Parse._objectEach(this.get('authData'), function(value, key) {
|
5837
6424
|
self._logOutWith(key);
|
5838
6425
|
});
|
5839
6426
|
},
|
@@ -5922,24 +6509,14 @@
|
|
5922
6509
|
* the login is complete.
|
5923
6510
|
*/
|
5924
6511
|
logIn: function(options) {
|
5925
|
-
var promise = new Parse.Promise();
|
5926
6512
|
var model = this;
|
5927
|
-
var
|
5928
|
-
|
6513
|
+
var request = Parse._request("login", null, null, "GET", this.toJSON());
|
6514
|
+
return request.then(function(resp, status, xhr) {
|
5929
6515
|
var serverAttrs = model.parse(resp, status, xhr);
|
5930
6516
|
model._finishFetch(serverAttrs);
|
5931
6517
|
model._handleSaveResult(true);
|
5932
|
-
|
5933
|
-
|
5934
|
-
} else {
|
5935
|
-
model.trigger('sync', model, resp, newOptions);
|
5936
|
-
}
|
5937
|
-
promise.resolve(model);
|
5938
|
-
};
|
5939
|
-
newOptions.error = Parse.Object._wrapError(newOptions.error, model,
|
5940
|
-
newOptions, promise);
|
5941
|
-
Parse._request("login", null, null, "GET", this.toJSON(), newOptions);
|
5942
|
-
return promise;
|
6518
|
+
return model;
|
6519
|
+
})._thenRunCallbacks(options, this);
|
5943
6520
|
},
|
5944
6521
|
|
5945
6522
|
/**
|
@@ -5971,10 +6548,10 @@
|
|
5971
6548
|
* @see Parse.Object#fetch
|
5972
6549
|
*/
|
5973
6550
|
fetch: function(options) {
|
5974
|
-
var newOptions = _.clone(options);
|
6551
|
+
var newOptions = options ? _.clone(options) : {};
|
5975
6552
|
newOptions.success = function(model) {
|
5976
6553
|
model._handleSaveResult(false);
|
5977
|
-
if (options.success) {
|
6554
|
+
if (options && options.success) {
|
5978
6555
|
options.success.apply(this, arguments);
|
5979
6556
|
}
|
5980
6557
|
};
|
@@ -6142,8 +6719,9 @@
|
|
6142
6719
|
*/
|
6143
6720
|
requestPasswordReset: function(email, options) {
|
6144
6721
|
var json = { email: email };
|
6145
|
-
|
6146
|
-
|
6722
|
+
var request = Parse._request("requestPasswordReset", null, null, "POST",
|
6723
|
+
json);
|
6724
|
+
return request._thenRunCallbacks(options);
|
6147
6725
|
},
|
6148
6726
|
|
6149
6727
|
/**
|
@@ -6313,7 +6891,7 @@
|
|
6313
6891
|
Parse.Query.or = function() {
|
6314
6892
|
var queries = _.toArray(arguments);
|
6315
6893
|
var className = null;
|
6316
|
-
|
6894
|
+
Parse._arrayEach(queries, function(q) {
|
6317
6895
|
if (_.isNull(className)) {
|
6318
6896
|
className = q.className;
|
6319
6897
|
}
|
@@ -6338,32 +6916,18 @@
|
|
6338
6916
|
*/
|
6339
6917
|
get: function(objectId, options) {
|
6340
6918
|
var self = this;
|
6341
|
-
|
6342
|
-
var error = options.error || function() {};
|
6343
|
-
var promise = new Parse.Promise();
|
6919
|
+
self.equalTo('objectId', objectId);
|
6344
6920
|
|
6345
|
-
|
6346
|
-
|
6347
|
-
|
6348
|
-
error(null, errorObject);
|
6349
|
-
promise.reject(errorObject);
|
6350
|
-
},
|
6351
|
-
success: function(response) {
|
6352
|
-
if (response) {
|
6353
|
-
success(response);
|
6354
|
-
promise.resolve(response);
|
6355
|
-
} else {
|
6356
|
-
var errorObject = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
|
6357
|
-
"Object not found.");
|
6358
|
-
error(null, errorObject);
|
6359
|
-
promise.reject(errorObject);
|
6360
|
-
}
|
6921
|
+
return self.first().then(function(response) {
|
6922
|
+
if (response) {
|
6923
|
+
return response;
|
6361
6924
|
}
|
6362
|
-
};
|
6363
6925
|
|
6364
|
-
|
6365
|
-
|
6366
|
-
|
6926
|
+
var errorObject = new Parse.Error(Parse.Error.OBJECT_NOT_FOUND,
|
6927
|
+
"Object not found.");
|
6928
|
+
return Parse.Promise.error(errorObject);
|
6929
|
+
|
6930
|
+
})._thenRunCallbacks(options, null);
|
6367
6931
|
},
|
6368
6932
|
|
6369
6933
|
/**
|
@@ -6378,6 +6942,9 @@
|
|
6378
6942
|
if (this._include.length > 0) {
|
6379
6943
|
params.include = this._include.join(",");
|
6380
6944
|
}
|
6945
|
+
if (this._select) {
|
6946
|
+
params.keys = this._select.join(",");
|
6947
|
+
}
|
6381
6948
|
if (this._limit >= 0) {
|
6382
6949
|
params.limit = this._limit;
|
6383
6950
|
}
|
@@ -6388,7 +6955,7 @@
|
|
6388
6955
|
params.order = this._order;
|
6389
6956
|
}
|
6390
6957
|
|
6391
|
-
Parse.
|
6958
|
+
Parse._objectEach(this._extraOptions, function(v, k) {
|
6392
6959
|
params[k] = v;
|
6393
6960
|
});
|
6394
6961
|
|
@@ -6406,35 +6973,22 @@
|
|
6406
6973
|
*/
|
6407
6974
|
find: function(options) {
|
6408
6975
|
var self = this;
|
6409
|
-
options = options || {};
|
6410
|
-
var success = options.success || function() {};
|
6411
|
-
var promise = new Parse.Promise();
|
6412
6976
|
|
6413
|
-
|
6414
|
-
|
6415
|
-
error: options.error,
|
6416
|
-
success: function(response) {
|
6417
|
-
var results = _.map(response.results, function(json) {
|
6418
|
-
var obj;
|
6419
|
-
if (response.className) {
|
6420
|
-
obj = new Parse.Object(response.className);
|
6421
|
-
} else {
|
6422
|
-
obj = new self.objectClass();
|
6423
|
-
}
|
6424
|
-
obj._finishFetch(json, true);
|
6425
|
-
return obj;
|
6426
|
-
});
|
6427
|
-
success(results);
|
6428
|
-
promise.resolve(results);
|
6429
|
-
}
|
6430
|
-
};
|
6977
|
+
var request = Parse._request("classes", this.className, null, "GET",
|
6978
|
+
this.toJSON());
|
6431
6979
|
|
6432
|
-
|
6433
|
-
|
6434
|
-
|
6435
|
-
|
6436
|
-
|
6437
|
-
|
6980
|
+
return request.then(function(response) {
|
6981
|
+
return _.map(response.results, function(json) {
|
6982
|
+
var obj;
|
6983
|
+
if (response.className) {
|
6984
|
+
obj = new Parse.Object(response.className);
|
6985
|
+
} else {
|
6986
|
+
obj = new self.objectClass();
|
6987
|
+
}
|
6988
|
+
obj._finishFetch(json, true);
|
6989
|
+
return obj;
|
6990
|
+
});
|
6991
|
+
})._thenRunCallbacks(options);
|
6438
6992
|
},
|
6439
6993
|
|
6440
6994
|
/**
|
@@ -6447,28 +7001,15 @@
|
|
6447
7001
|
* the query completes.
|
6448
7002
|
*/
|
6449
7003
|
count: function(options) {
|
6450
|
-
var self = this;
|
6451
|
-
options = options || {};
|
6452
|
-
var success = options.success || function() {};
|
6453
|
-
var promise = new Parse.Promise();
|
6454
|
-
|
6455
|
-
/** ignore */
|
6456
|
-
var ajaxOptions = {
|
6457
|
-
error: options.error,
|
6458
|
-
success: function(response) {
|
6459
|
-
success(response.count);
|
6460
|
-
promise.resolve(response.count);
|
6461
|
-
}
|
6462
|
-
};
|
6463
|
-
|
6464
7004
|
var params = this.toJSON();
|
6465
7005
|
params.limit = 0;
|
6466
7006
|
params.count = 1;
|
6467
|
-
|
6468
|
-
|
6469
|
-
|
6470
|
-
|
6471
|
-
|
7007
|
+
var request = Parse._request("classes", this.className, null, "GET",
|
7008
|
+
params);
|
7009
|
+
|
7010
|
+
return request.then(function(response) {
|
7011
|
+
return response.count;
|
7012
|
+
})._thenRunCallbacks(options);
|
6472
7013
|
},
|
6473
7014
|
|
6474
7015
|
/**
|
@@ -6483,31 +7024,19 @@
|
|
6483
7024
|
*/
|
6484
7025
|
first: function(options) {
|
6485
7026
|
var self = this;
|
6486
|
-
options = options || {};
|
6487
|
-
var success = options.success || function() {};
|
6488
|
-
var promise = new Parse.Promise();
|
6489
|
-
|
6490
|
-
/** ignore */
|
6491
|
-
var ajaxOptions = {
|
6492
|
-
error: options.error,
|
6493
|
-
success: function(response) {
|
6494
|
-
var result = _.map(response.results, function(json) {
|
6495
|
-
var obj = new self.objectClass();
|
6496
|
-
obj._finishFetch(json, true);
|
6497
|
-
return obj;
|
6498
|
-
})[0];
|
6499
|
-
success(result);
|
6500
|
-
promise.resolve(result);
|
6501
|
-
}
|
6502
|
-
};
|
6503
7027
|
|
6504
7028
|
var params = this.toJSON();
|
6505
7029
|
params.limit = 1;
|
6506
|
-
|
6507
|
-
|
6508
|
-
|
6509
|
-
|
6510
|
-
|
7030
|
+
var request = Parse._request("classes", this.className, null, "GET",
|
7031
|
+
params);
|
7032
|
+
|
7033
|
+
return request.then(function(response) {
|
7034
|
+
return _.map(response.results, function(json) {
|
7035
|
+
var obj = new self.objectClass();
|
7036
|
+
obj._finishFetch(json, true);
|
7037
|
+
return obj;
|
7038
|
+
})[0];
|
7039
|
+
})._thenRunCallbacks(options);
|
6511
7040
|
},
|
6512
7041
|
|
6513
7042
|
/**
|
@@ -6944,45 +7473,108 @@
|
|
6944
7473
|
* @param {String} key The name of the key to include.
|
6945
7474
|
* @return {Parse.Query} Returns the query, so you can chain this call.
|
6946
7475
|
*/
|
6947
|
-
include: function(
|
6948
|
-
|
6949
|
-
|
6950
|
-
|
6951
|
-
|
6952
|
-
|
7476
|
+
include: function() {
|
7477
|
+
var self = this;
|
7478
|
+
Parse._arrayEach(arguments, function(key) {
|
7479
|
+
if (_.isArray(key)) {
|
7480
|
+
self._include = self._include.concat(key);
|
7481
|
+
} else {
|
7482
|
+
self._include.push(key);
|
7483
|
+
}
|
7484
|
+
});
|
6953
7485
|
return this;
|
6954
|
-
}
|
6955
|
-
};
|
7486
|
+
},
|
6956
7487
|
|
6957
|
-
|
6958
|
-
|
6959
|
-
|
6960
|
-
|
6961
|
-
|
6962
|
-
|
6963
|
-
|
6964
|
-
|
7488
|
+
/**
|
7489
|
+
* Restrict the fields of the returned Parse.Objects to include only the
|
7490
|
+
* provided keys. If this is called multiple times, then all of the keys
|
7491
|
+
* specified in each of the calls will be included.
|
7492
|
+
* @param {Array} keys The names of the keys to include.
|
7493
|
+
* @return {Parse.Query} Returns the query, so you can chain this call.
|
7494
|
+
*/
|
7495
|
+
select: function() {
|
7496
|
+
var self = this;
|
7497
|
+
this._select = this._select || [];
|
7498
|
+
Parse._arrayEach(arguments, function(key) {
|
7499
|
+
if (_.isArray(key)) {
|
7500
|
+
self._select = self._select.concat(key);
|
7501
|
+
} else {
|
7502
|
+
self._select.push(key);
|
6965
7503
|
}
|
7504
|
+
});
|
7505
|
+
return this;
|
7506
|
+
},
|
7507
|
+
|
7508
|
+
/**
|
7509
|
+
* Iterates over each result of a query, calling a callback for each one. If
|
7510
|
+
* the callback returns a promise, the iteration will not continue until
|
7511
|
+
* that promise has been fulfilled. If the callback returns a rejected
|
7512
|
+
* promise, then iteration will stop with that error. The items are
|
7513
|
+
* processed in an unspecified order. The query may not have any sort order,
|
7514
|
+
* and may not use limit or skip.
|
7515
|
+
* @param callback {Function} Callback that will be called with each result
|
7516
|
+
* of the query.
|
7517
|
+
* @param options {Object} An optional Backbone-like options object with
|
7518
|
+
* success and error callbacks that will be invoked once the iteration
|
7519
|
+
* has finished.
|
7520
|
+
* @return {Parse.Promise} A promise that will be fulfilled once the
|
7521
|
+
* iteration has completed.
|
7522
|
+
*/
|
7523
|
+
each: function(callback, options) {
|
7524
|
+
options = options || {};
|
7525
|
+
|
7526
|
+
if (this._order || this._skip || (this._limit >= 0)) {
|
7527
|
+
var error =
|
7528
|
+
"Cannot iterate on a query with sort, skip, or limit.";
|
7529
|
+
return Parse.Promise.error(error)._thenRunCallbacks(options);
|
6966
7530
|
}
|
6967
|
-
|
6968
|
-
|
6969
|
-
|
6970
|
-
|
6971
|
-
|
6972
|
-
|
6973
|
-
|
6974
|
-
|
7531
|
+
|
7532
|
+
var promise = new Parse.Promise();
|
7533
|
+
|
7534
|
+
var query = new Parse.Query(this.objectClass);
|
7535
|
+
// We can override the batch size from the options.
|
7536
|
+
// This is undocumented, but useful for testing.
|
7537
|
+
query._limit = options.batchSize || 100;
|
7538
|
+
query._where = _.clone(this._where);
|
7539
|
+
query._include = _.clone(this._include);
|
7540
|
+
|
7541
|
+
query.ascending('objectId');
|
7542
|
+
|
7543
|
+
var finished = false;
|
7544
|
+
return Parse.Promise._continueWhile(function() {
|
7545
|
+
return !finished;
|
7546
|
+
|
7547
|
+
}, function() {
|
7548
|
+
return query.find().then(function(results) {
|
7549
|
+
var callbacksDone = Parse.Promise.as();
|
7550
|
+
Parse._.each(results, function(result) {
|
7551
|
+
callbacksDone = callbacksDone.then(function() {
|
7552
|
+
return callback(result);
|
7553
|
+
});
|
7554
|
+
});
|
7555
|
+
|
7556
|
+
return callbacksDone.then(function() {
|
7557
|
+
if (results.length >= query._limit) {
|
7558
|
+
query.greaterThan("objectId", results[results.length - 1].id);
|
7559
|
+
} else {
|
7560
|
+
finished = true;
|
7561
|
+
}
|
7562
|
+
});
|
7563
|
+
});
|
7564
|
+
})._thenRunCallbacks(options);
|
7565
|
+
}
|
6975
7566
|
};
|
7567
|
+
|
6976
7568
|
}(this));
|
6977
7569
|
|
6978
|
-
/*global FB: false */
|
7570
|
+
/*global FB: false , console: false*/
|
6979
7571
|
(function(root) {
|
6980
7572
|
root.Parse = root.Parse || {};
|
6981
7573
|
var Parse = root.Parse;
|
6982
7574
|
var _ = Parse._;
|
6983
7575
|
|
6984
7576
|
var PUBLIC_KEY = "*";
|
6985
|
-
|
7577
|
+
|
6986
7578
|
var initialized = false;
|
6987
7579
|
var requestedPermissions;
|
6988
7580
|
var initOptions;
|
@@ -7018,6 +7610,9 @@
|
|
7018
7610
|
};
|
7019
7611
|
var newOptions = _.clone(initOptions);
|
7020
7612
|
newOptions.authResponse = authResponse;
|
7613
|
+
|
7614
|
+
// Suppress checks for login status from the browser.
|
7615
|
+
newOptions.status = false;
|
7021
7616
|
FB.init(newOptions);
|
7022
7617
|
}
|
7023
7618
|
return true;
|
@@ -7049,18 +7644,28 @@
|
|
7049
7644
|
* @param {Object} options Facebook options argument as described here:
|
7050
7645
|
* <a href=
|
7051
7646
|
* "https://developers.facebook.com/docs/reference/javascript/FB.init/">
|
7052
|
-
* FB.init()</a
|
7647
|
+
* FB.init()</a>. The status flag will be coerced to 'false' because it
|
7648
|
+
* interferes with Parse Facebook integration. Call FB.getLoginStatus()
|
7649
|
+
* explicitly if this behavior is required by your application.
|
7053
7650
|
*/
|
7054
7651
|
init: function(options) {
|
7055
7652
|
if (typeof(FB) === 'undefined') {
|
7056
|
-
throw "The
|
7653
|
+
throw "The Facebook JavaScript SDK must be loaded before calling init.";
|
7057
7654
|
}
|
7058
|
-
initOptions = _.clone(options);
|
7655
|
+
initOptions = _.clone(options) || {};
|
7656
|
+
if (initOptions.status && typeof(console) !== "undefined") {
|
7657
|
+
var warn = console.warn || console.log || function() {};
|
7658
|
+
warn.call(console, "The 'status' flag passed into" +
|
7659
|
+
" FB.init, when set to true, can interfere with Parse Facebook" +
|
7660
|
+
" integration, so it has been suppressed. Please call" +
|
7661
|
+
" FB.getLoginStatus() explicitly if you require this behavior.");
|
7662
|
+
}
|
7663
|
+
initOptions.status = false;
|
7059
7664
|
FB.init(initOptions);
|
7060
7665
|
Parse.User._registerAuthenticationProvider(provider);
|
7061
7666
|
initialized = true;
|
7062
7667
|
},
|
7063
|
-
|
7668
|
+
|
7064
7669
|
/**
|
7065
7670
|
* Gets whether the user has their account linked to Facebook.
|
7066
7671
|
*
|
@@ -7072,7 +7677,7 @@
|
|
7072
7677
|
isLinked: function(user) {
|
7073
7678
|
return user._isLinked("facebook");
|
7074
7679
|
},
|
7075
|
-
|
7680
|
+
|
7076
7681
|
/**
|
7077
7682
|
* Logs in a user using Facebook. This method delegates to the Facebook
|
7078
7683
|
* SDK to authenticate the user, and then automatically logs in (or
|
@@ -7094,12 +7699,12 @@
|
|
7094
7699
|
requestedPermissions = permissions;
|
7095
7700
|
return Parse.User._logInWith("facebook", options);
|
7096
7701
|
} else {
|
7097
|
-
var newOptions = _.clone(options);
|
7702
|
+
var newOptions = _.clone(options) || {};
|
7098
7703
|
newOptions.authData = permissions;
|
7099
7704
|
return Parse.User._logInWith("facebook", newOptions);
|
7100
7705
|
}
|
7101
7706
|
},
|
7102
|
-
|
7707
|
+
|
7103
7708
|
/**
|
7104
7709
|
* Links Facebook to an existing PFUser. This method delegates to the
|
7105
7710
|
* Facebook SDK to authenticate the user, and then automatically links
|
@@ -7123,12 +7728,12 @@
|
|
7123
7728
|
requestedPermissions = permissions;
|
7124
7729
|
return user._linkWith("facebook", options);
|
7125
7730
|
} else {
|
7126
|
-
var newOptions = _.clone(options);
|
7731
|
+
var newOptions = _.clone(options) || {};
|
7127
7732
|
newOptions.authData = permissions;
|
7128
7733
|
return user._linkWith("facebook", newOptions);
|
7129
7734
|
}
|
7130
7735
|
},
|
7131
|
-
|
7736
|
+
|
7132
7737
|
/**
|
7133
7738
|
* Unlinks the Parse.User from a Facebook account.
|
7134
7739
|
*
|
@@ -7323,7 +7928,7 @@
|
|
7323
7928
|
this.navigate(current);
|
7324
7929
|
}
|
7325
7930
|
if (!this.loadUrl()) {
|
7326
|
-
|
7931
|
+
this.loadUrl(this.getHash());
|
7327
7932
|
}
|
7328
7933
|
},
|
7329
7934
|
|
@@ -7537,15 +8142,15 @@
|
|
7537
8142
|
var _ = Parse._;
|
7538
8143
|
|
7539
8144
|
/**
|
7540
|
-
*
|
7541
|
-
*
|
7542
8145
|
* @namespace Contains functions for calling and declaring
|
7543
8146
|
* <a href="/docs/cloud_code_guide#functions">cloud functions</a>.
|
7544
8147
|
* <p><strong><em>
|
7545
8148
|
* Some functions are only available from Cloud Code.
|
7546
8149
|
* </em></strong></p>
|
7547
8150
|
*/
|
7548
|
-
Parse.Cloud = {
|
8151
|
+
Parse.Cloud = Parse.Cloud || {};
|
8152
|
+
|
8153
|
+
_.extend(Parse.Cloud, /** @lends Parse.Cloud */ {
|
7549
8154
|
/**
|
7550
8155
|
* Makes a call to a cloud function.
|
7551
8156
|
* @param {String} name The function name.
|
@@ -7555,41 +8160,18 @@
|
|
7555
8160
|
* call to a cloud function. options.error should be a function that
|
7556
8161
|
* handles an error running the cloud function. Both functions are
|
7557
8162
|
* optional. Both functions take a single argument.
|
8163
|
+
* @return {Parse.Promise} A promise that will be resolved with the result
|
8164
|
+
* of the function.
|
7558
8165
|
*/
|
7559
8166
|
run: function(name, data, options) {
|
7560
|
-
var
|
7561
|
-
|
7562
|
-
newOptions.success = function(resp) {
|
7563
|
-
var results = Parse._decode(null, resp);
|
7564
|
-
if (oldOptions.success) {
|
7565
|
-
oldOptions.success(results.result);
|
7566
|
-
}
|
7567
|
-
};
|
8167
|
+
var request = Parse._request("functions", name, null, 'POST',
|
8168
|
+
Parse._encode(data, null, true));
|
7568
8169
|
|
7569
|
-
|
7570
|
-
|
7571
|
-
|
7572
|
-
null,
|
7573
|
-
'POST',
|
7574
|
-
Parse._encode(data, null, true),
|
7575
|
-
newOptions);
|
7576
|
-
},
|
7577
|
-
|
7578
|
-
_wrapError: function(onError, options) {
|
7579
|
-
return function(response) {
|
7580
|
-
if (onError) {
|
7581
|
-
var error = new Parse.Error(-1, response.responseText);
|
7582
|
-
if (response.responseText) {
|
7583
|
-
var errorJSON = JSON.parse(response.responseText);
|
7584
|
-
if (errorJSON) {
|
7585
|
-
error = new Parse.Error(errorJSON.code, errorJSON.error);
|
7586
|
-
}
|
7587
|
-
}
|
7588
|
-
onError(error, options);
|
7589
|
-
}
|
7590
|
-
};
|
8170
|
+
return request.then(function(resp) {
|
8171
|
+
return Parse._decode(null, resp).result;
|
8172
|
+
})._thenRunCallbacks(options);
|
7591
8173
|
}
|
7592
|
-
};
|
8174
|
+
});
|
7593
8175
|
}(this));
|
7594
8176
|
|
7595
8177
|
(function(root) {
|
@@ -7640,11 +8222,8 @@
|
|
7640
8222
|
if (data.expiration_time && data.expiration_time_interval) {
|
7641
8223
|
throw "Both expiration_time and expiration_time_interval can't be set";
|
7642
8224
|
}
|
7643
|
-
|
7644
|
-
|
7645
|
-
|
7646
|
-
};
|
7647
|
-
ajaxOptions.error = Parse.Query._wrapError(options.error, ajaxOptions);
|
7648
|
-
Parse._request('push', null, null, 'POST', data, ajaxOptions);
|
8225
|
+
|
8226
|
+
var request = Parse._request('push', null, null, 'POST', data);
|
8227
|
+
return request._thenRunCallbacks(options);
|
7649
8228
|
};
|
7650
8229
|
}(this));
|