@converse/skeletor 0.0.3 → 0.0.6
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.
- package/README.md +70 -7
- package/dist/skeletor.js +508 -1048
- package/dist/skeletor.js.map +1 -1
- package/dist/skeletor.min.js +4 -93
- package/dist/skeletor.min.js.map +1 -1
- package/package.json +6 -7
- package/src/collection.js +1 -1
- package/src/drivers/indexeddb-getitems.js~ +1116 -0
- package/src/drivers/indexeddb.js~ +1115 -0
- package/src/storage.js +11 -8
- package/src/storage.js~ +13 -9
- package/CHANGES.md +0 -26
- package/dist/skeletor.js~ +0 -2291
- package/src/.events.js.swp +0 -0
- package/src/.helpers.js.swp +0 -0
- package/src/.history.js.swp +0 -0
- package/src/.model.js.swp +0 -0
- package/src/.overview.js.swp +0 -0
- package/src/.router.js.swp +0 -0
- package/src/.view.js.swp +0 -0
- package/src/collection.js~ +0 -737
- package/src/drivers/.sessionStorage.js.swp +0 -0
- package/src/drivers/sessionStorage.js~ +0 -316
- package/src/element-overview.js~ +0 -4
- package/src/element.js~ +0 -190
- package/src/events.js~ +0 -334
- package/src/helpers.js~ +0 -151
- package/src/history.js~ +0 -301
- package/src/htmlview.js~ +0 -14
- package/src/model.js~ +0 -439
- package/src/orderedlist.js~ +0 -88
- package/src/overview.js~ +0 -111
- package/src/router.js~ +0 -119
- package/src/vdomview.js~ +0 -15
- package/src/view.js~ +0 -283
package/src/collection.js~
DELETED
|
@@ -1,737 +0,0 @@
|
|
|
1
|
-
// Backbone.js 1.4.0
|
|
2
|
-
// (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
|
|
3
|
-
// Backbone may be freely distributed under the MIT license.
|
|
4
|
-
|
|
5
|
-
// Collection
|
|
6
|
-
// ----------
|
|
7
|
-
|
|
8
|
-
// If models tend to represent a single row of data, a Collection is
|
|
9
|
-
// more analogous to a table full of data ... or a small slice or page of that
|
|
10
|
-
// table, or a collection of rows that belong together for a particular reason
|
|
11
|
-
// -- all of the messages in this particular folder, all of the documents
|
|
12
|
-
// belonging to this particular author, and so on. Collections maintain
|
|
13
|
-
// indexes of their models, both in order, and for lookup by `id`.
|
|
14
|
-
|
|
15
|
-
import { inherits, getResolveablePromise, getSyncMethod, wrapError } from './helpers.js';
|
|
16
|
-
import { Events } from './events.js';
|
|
17
|
-
import { Model } from './model.js';
|
|
18
|
-
import clone from "lodash-es/clone.js";
|
|
19
|
-
import countBy from 'lodash-es/countBy.js';
|
|
20
|
-
import difference from 'lodash-es/difference.js';
|
|
21
|
-
import every from 'lodash-es/every.js';
|
|
22
|
-
import extend from "lodash-es/extend.js";
|
|
23
|
-
import findIndex from 'lodash-es/findIndex.js';
|
|
24
|
-
import findLastIndex from 'lodash-es/findLastIndex.js';
|
|
25
|
-
import groupBy from 'lodash-es/groupBy.js';
|
|
26
|
-
import indexOf from 'lodash-es/indexOf.js';
|
|
27
|
-
import isEmpty from "lodash-es/isEmpty.js";
|
|
28
|
-
import isFunction from "lodash-es/isFunction.js";
|
|
29
|
-
import isString from 'lodash-es/isString.js';
|
|
30
|
-
import keyBy from 'lodash-es/keyBy.js';
|
|
31
|
-
import lastIndexOf from 'lodash-es/lastIndexOf.js';
|
|
32
|
-
import some from 'lodash-es/some.js';
|
|
33
|
-
import sortBy from 'lodash-es/sortBy.js';
|
|
34
|
-
|
|
35
|
-
const slice = Array.prototype.slice;
|
|
36
|
-
|
|
37
|
-
// Create a new **Collection**, perhaps to contain a specific type of `model`.
|
|
38
|
-
// If a `comparator` is specified, the Collection will maintain
|
|
39
|
-
// its models in sort order, as they're added and removed.
|
|
40
|
-
export const Collection = function(models, options) {
|
|
41
|
-
options || (options = {});
|
|
42
|
-
this.preinitialize.apply(this, arguments);
|
|
43
|
-
if (options.model) this.model = options.model;
|
|
44
|
-
if (options.comparator !== undefined) this.comparator = options.comparator;
|
|
45
|
-
this._reset();
|
|
46
|
-
this.initialize.apply(this, arguments);
|
|
47
|
-
if (models) this.reset(models, extend({silent: true}, options));
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
Collection.extend = inherits;
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
// Default options for `Collection#set`.
|
|
54
|
-
const setOptions = {add: true, remove: true, merge: true};
|
|
55
|
-
const addOptions = {add: true, remove: false};
|
|
56
|
-
|
|
57
|
-
// Splices `insert` into `array` at index `at`.
|
|
58
|
-
const splice = function(array, insert, at) {
|
|
59
|
-
at = Math.min(Math.max(at, 0), array.length);
|
|
60
|
-
const tail = Array(array.length - at);
|
|
61
|
-
const length = insert.length;
|
|
62
|
-
let i;
|
|
63
|
-
for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
|
|
64
|
-
for (i = 0; i < length; i++) array[i + at] = insert[i];
|
|
65
|
-
for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
// Define the Collection's inheritable methods.
|
|
69
|
-
Object.assign(Collection.prototype, Events, {
|
|
70
|
-
|
|
71
|
-
// The default model for a collection is just a **Backbone.Model**.
|
|
72
|
-
// This should be overridden in most cases.
|
|
73
|
-
model: Model,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// preinitialize is an empty function by default. You can override it with a function
|
|
77
|
-
// or object. preinitialize will run before any instantiation logic is run in the Collection.
|
|
78
|
-
preinitialize: function(){},
|
|
79
|
-
|
|
80
|
-
// Initialize is an empty function by default. Override it with your own
|
|
81
|
-
// initialization logic.
|
|
82
|
-
initialize: function(){},
|
|
83
|
-
|
|
84
|
-
// The JSON representation of a Collection is an array of the
|
|
85
|
-
// models' attributes.
|
|
86
|
-
toJSON: function(options) {
|
|
87
|
-
return this.map(function(model) { return model.toJSON(options); });
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
// Proxy `Backbone.sync` by default.
|
|
91
|
-
sync: function(method, model, options) {
|
|
92
|
-
return getSyncMethod(this)(method, model, options);
|
|
93
|
-
},
|
|
94
|
-
|
|
95
|
-
// Add a model, or list of models to the set. `models` may be Backbone
|
|
96
|
-
// Models or raw JavaScript objects to be converted to Models, or any
|
|
97
|
-
// combination of the two.
|
|
98
|
-
add: function(models, options) {
|
|
99
|
-
return this.set(models, extend({merge: false}, options, addOptions));
|
|
100
|
-
},
|
|
101
|
-
|
|
102
|
-
// Remove a model, or a list of models from the set.
|
|
103
|
-
remove: function(models, options) {
|
|
104
|
-
options = extend({}, options);
|
|
105
|
-
const singular = !Array.isArray(models);
|
|
106
|
-
models = singular ? [models] : models.slice();
|
|
107
|
-
const removed = this._removeModels(models, options);
|
|
108
|
-
if (!options.silent && removed.length) {
|
|
109
|
-
options.changes = {added: [], merged: [], removed: removed};
|
|
110
|
-
this.trigger('update', this, options);
|
|
111
|
-
}
|
|
112
|
-
return singular ? removed[0] : removed;
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
// Update a collection by `set`-ing a new list of models, adding new ones,
|
|
116
|
-
// removing models that are no longer present, and merging models that
|
|
117
|
-
// already exist in the collection, as necessary. Similar to **Model#set**,
|
|
118
|
-
// the core operation for updating the data contained by the collection.
|
|
119
|
-
set: function(models, options) {
|
|
120
|
-
if (models == null) return;
|
|
121
|
-
|
|
122
|
-
options = extend({}, setOptions, options);
|
|
123
|
-
if (options.parse && !this._isModel(models)) {
|
|
124
|
-
models = this.parse(models, options) || [];
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const singular = !Array.isArray(models);
|
|
128
|
-
models = singular ? [models] : models.slice();
|
|
129
|
-
|
|
130
|
-
let at = options.at;
|
|
131
|
-
if (at != null) at = +at;
|
|
132
|
-
if (at > this.length) at = this.length;
|
|
133
|
-
if (at < 0) at += this.length + 1;
|
|
134
|
-
|
|
135
|
-
const set = [];
|
|
136
|
-
const toAdd = [];
|
|
137
|
-
const toMerge = [];
|
|
138
|
-
const toRemove = [];
|
|
139
|
-
const modelMap = {};
|
|
140
|
-
|
|
141
|
-
const add = options.add;
|
|
142
|
-
const merge = options.merge;
|
|
143
|
-
const remove = options.remove;
|
|
144
|
-
|
|
145
|
-
let sort = false;
|
|
146
|
-
const sortable = this.comparator && at == null && options.sort !== false;
|
|
147
|
-
const sortAttr = isString(this.comparator) ? this.comparator : null;
|
|
148
|
-
|
|
149
|
-
// Turn bare objects into model references, and prevent invalid models
|
|
150
|
-
// from being added.
|
|
151
|
-
let model, i;
|
|
152
|
-
for (i = 0; i < models.length; i++) {
|
|
153
|
-
model = models[i];
|
|
154
|
-
|
|
155
|
-
// If a duplicate is found, prevent it from being added and
|
|
156
|
-
// optionally merge it into the existing model.
|
|
157
|
-
const existing = this.get(model);
|
|
158
|
-
if (existing) {
|
|
159
|
-
if (merge && model !== existing) {
|
|
160
|
-
let attrs = this._isModel(model) ? model.attributes : model;
|
|
161
|
-
if (options.parse) attrs = existing.parse(attrs, options);
|
|
162
|
-
existing.set(attrs, options);
|
|
163
|
-
toMerge.push(existing);
|
|
164
|
-
if (sortable && !sort) sort = existing.hasChanged(sortAttr);
|
|
165
|
-
}
|
|
166
|
-
if (!modelMap[existing.cid]) {
|
|
167
|
-
modelMap[existing.cid] = true;
|
|
168
|
-
set.push(existing);
|
|
169
|
-
}
|
|
170
|
-
models[i] = existing;
|
|
171
|
-
|
|
172
|
-
// If this is a new, valid model, push it to the `toAdd` list.
|
|
173
|
-
} else if (add) {
|
|
174
|
-
model = models[i] = this._prepareModel(model, options);
|
|
175
|
-
if (model) {
|
|
176
|
-
toAdd.push(model);
|
|
177
|
-
this._addReference(model, options);
|
|
178
|
-
modelMap[model.cid] = true;
|
|
179
|
-
set.push(model);
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// Remove stale models.
|
|
185
|
-
if (remove) {
|
|
186
|
-
for (i = 0; i < this.length; i++) {
|
|
187
|
-
model = this.models[i];
|
|
188
|
-
if (!modelMap[model.cid]) toRemove.push(model);
|
|
189
|
-
}
|
|
190
|
-
if (toRemove.length) this._removeModels(toRemove, options);
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
// See if sorting is needed, update `length` and splice in new models.
|
|
194
|
-
let orderChanged = false;
|
|
195
|
-
const replace = !sortable && add && remove;
|
|
196
|
-
if (set.length && replace) {
|
|
197
|
-
orderChanged = this.length !== set.length || some(this.models, (m, index) => m !== set[index]);
|
|
198
|
-
this.models.length = 0;
|
|
199
|
-
splice(this.models, set, 0);
|
|
200
|
-
this.length = this.models.length;
|
|
201
|
-
} else if (toAdd.length) {
|
|
202
|
-
if (sortable) sort = true;
|
|
203
|
-
splice(this.models, toAdd, at == null ? this.length : at);
|
|
204
|
-
this.length = this.models.length;
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
// Silently sort the collection if appropriate.
|
|
208
|
-
if (sort) this.sort({silent: true});
|
|
209
|
-
|
|
210
|
-
// Unless silenced, it's time to fire all appropriate add/sort/update events.
|
|
211
|
-
if (!options.silent) {
|
|
212
|
-
for (i = 0; i < toAdd.length; i++) {
|
|
213
|
-
if (at != null) options.index = at + i;
|
|
214
|
-
model = toAdd[i];
|
|
215
|
-
model.trigger('add', model, this, options);
|
|
216
|
-
}
|
|
217
|
-
if (sort || orderChanged) this.trigger('sort', this, options);
|
|
218
|
-
if (toAdd.length || toRemove.length || toMerge.length) {
|
|
219
|
-
options.changes = {
|
|
220
|
-
added: toAdd,
|
|
221
|
-
removed: toRemove,
|
|
222
|
-
merged: toMerge
|
|
223
|
-
};
|
|
224
|
-
this.trigger('update', this, options);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
// Return the added (or merged) model (or models).
|
|
229
|
-
return singular ? models[0] : models;
|
|
230
|
-
},
|
|
231
|
-
|
|
232
|
-
clearStore: async function(options={}) {
|
|
233
|
-
await Promise.all(Array.from(this.models).map(m => {
|
|
234
|
-
return new Promise(
|
|
235
|
-
resolve => {
|
|
236
|
-
m.destroy(Object.assign(options, {
|
|
237
|
-
'success': resolve,
|
|
238
|
-
'error': (m, e) => { console.error(e); resolve() }
|
|
239
|
-
}));
|
|
240
|
-
}
|
|
241
|
-
);
|
|
242
|
-
}));
|
|
243
|
-
await this.browserStorage.clear();
|
|
244
|
-
this.reset();
|
|
245
|
-
},
|
|
246
|
-
|
|
247
|
-
// When you have more items than you want to add or remove individually,
|
|
248
|
-
// you can reset the entire set with a new list of models, without firing
|
|
249
|
-
// any granular `add` or `remove` events. Fires `reset` when finished.
|
|
250
|
-
// Useful for bulk operations and optimizations.
|
|
251
|
-
reset: function(models, options) {
|
|
252
|
-
options = options ? clone(options) : {};
|
|
253
|
-
for (let i = 0; i < this.models.length; i++) {
|
|
254
|
-
this._removeReference(this.models[i], options);
|
|
255
|
-
}
|
|
256
|
-
options.previousModels = this.models;
|
|
257
|
-
this._reset();
|
|
258
|
-
models = this.add(models, extend({silent: true}, options));
|
|
259
|
-
if (!options.silent) this.trigger('reset', this, options);
|
|
260
|
-
return models;
|
|
261
|
-
},
|
|
262
|
-
|
|
263
|
-
// Add a model to the end of the collection.
|
|
264
|
-
push: function(model, options) {
|
|
265
|
-
return this.add(model, extend({at: this.length}, options));
|
|
266
|
-
},
|
|
267
|
-
|
|
268
|
-
// Remove a model from the end of the collection.
|
|
269
|
-
pop: function(options) {
|
|
270
|
-
const model = this.at(this.length - 1);
|
|
271
|
-
return this.remove(model, options);
|
|
272
|
-
},
|
|
273
|
-
|
|
274
|
-
// Add a model to the beginning of the collection.
|
|
275
|
-
unshift: function(model, options) {
|
|
276
|
-
return this.add(model, extend({at: 0}, options));
|
|
277
|
-
},
|
|
278
|
-
|
|
279
|
-
// Remove a model from the beginning of the collection.
|
|
280
|
-
shift: function(options) {
|
|
281
|
-
const model = this.at(0);
|
|
282
|
-
return this.remove(model, options);
|
|
283
|
-
},
|
|
284
|
-
|
|
285
|
-
// Slice out a sub-array of models from the collection.
|
|
286
|
-
slice: function() {
|
|
287
|
-
return slice.apply(this.models, arguments);
|
|
288
|
-
},
|
|
289
|
-
|
|
290
|
-
filter: function(callback, thisArg) {
|
|
291
|
-
return this.models.filter(
|
|
292
|
-
isFunction(callback) ? callback : m => m.matches(callback),
|
|
293
|
-
thisArg
|
|
294
|
-
);
|
|
295
|
-
},
|
|
296
|
-
|
|
297
|
-
every: function(pred) {
|
|
298
|
-
return every(this.models.map(m => m.attributes), pred);
|
|
299
|
-
},
|
|
300
|
-
|
|
301
|
-
difference: function(values) {
|
|
302
|
-
return difference(this.models, values);
|
|
303
|
-
},
|
|
304
|
-
|
|
305
|
-
max: function() {
|
|
306
|
-
return Math.max.apply(Math, this.models);
|
|
307
|
-
},
|
|
308
|
-
|
|
309
|
-
min: function() {
|
|
310
|
-
return Math.min.apply(Math, this.models);
|
|
311
|
-
},
|
|
312
|
-
|
|
313
|
-
drop: function(n=1) {
|
|
314
|
-
return this.models.slice(n);
|
|
315
|
-
},
|
|
316
|
-
|
|
317
|
-
some: function(pred) {
|
|
318
|
-
return some(this.models.map(m => m.attributes), pred);
|
|
319
|
-
},
|
|
320
|
-
|
|
321
|
-
sortBy: function(iteratee) {
|
|
322
|
-
return sortBy(
|
|
323
|
-
this.models,
|
|
324
|
-
isFunction(iteratee) ? iteratee : m => isString(iteratee) ? m.get(iteratee) : m.matches(iteratee),
|
|
325
|
-
);
|
|
326
|
-
},
|
|
327
|
-
|
|
328
|
-
isEmpty: function() {
|
|
329
|
-
return isEmpty(this.models);
|
|
330
|
-
},
|
|
331
|
-
|
|
332
|
-
keyBy: function(iteratee) {
|
|
333
|
-
return keyBy(this.models, iteratee);
|
|
334
|
-
},
|
|
335
|
-
|
|
336
|
-
each: function(callback, thisArg) {
|
|
337
|
-
return this.forEach(callback, thisArg);
|
|
338
|
-
},
|
|
339
|
-
|
|
340
|
-
forEach: function(callback, thisArg) {
|
|
341
|
-
return this.models.forEach(callback, thisArg);
|
|
342
|
-
},
|
|
343
|
-
|
|
344
|
-
includes: function(item) {
|
|
345
|
-
return this.models.includes(item);
|
|
346
|
-
},
|
|
347
|
-
|
|
348
|
-
size: function() {
|
|
349
|
-
return this.models.length;
|
|
350
|
-
},
|
|
351
|
-
|
|
352
|
-
countBy: function(f) {
|
|
353
|
-
return countBy(
|
|
354
|
-
this.models,
|
|
355
|
-
isFunction(f) ? f : m => isString(f) ? m.get(f) : m.matches(f),
|
|
356
|
-
);
|
|
357
|
-
},
|
|
358
|
-
|
|
359
|
-
groupBy: function(pred) {
|
|
360
|
-
return groupBy(
|
|
361
|
-
this.models,
|
|
362
|
-
isFunction(pred) ? pred : m => isString(pred) ? m.get(pred) : m.matches(pred),
|
|
363
|
-
);
|
|
364
|
-
},
|
|
365
|
-
|
|
366
|
-
indexOf: function(fromIndex) {
|
|
367
|
-
return indexOf(this.models, fromIndex);
|
|
368
|
-
},
|
|
369
|
-
|
|
370
|
-
findLastIndex: function(pred, fromIndex) {
|
|
371
|
-
return findLastIndex(
|
|
372
|
-
this.models,
|
|
373
|
-
isFunction(pred) ? pred : m => isString(pred) ? m.get(pred) : m.matches(pred),
|
|
374
|
-
fromIndex
|
|
375
|
-
);
|
|
376
|
-
},
|
|
377
|
-
|
|
378
|
-
lastIndexOf: function(fromIndex) {
|
|
379
|
-
return lastIndexOf(this.models, fromIndex);
|
|
380
|
-
},
|
|
381
|
-
|
|
382
|
-
findIndex: function(pred) {
|
|
383
|
-
return findIndex(
|
|
384
|
-
this.models,
|
|
385
|
-
isFunction(pred) ? pred : m => isString(pred) ? m.get(pred) : m.matches(pred),
|
|
386
|
-
);
|
|
387
|
-
},
|
|
388
|
-
|
|
389
|
-
last: function() {
|
|
390
|
-
const length = this.models == null ? 0 : this.models.length;
|
|
391
|
-
return length ? this.models[length - 1] : undefined;
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
head: function() {
|
|
395
|
-
return this.models[0];
|
|
396
|
-
},
|
|
397
|
-
|
|
398
|
-
first: function() {
|
|
399
|
-
return this.head();
|
|
400
|
-
},
|
|
401
|
-
|
|
402
|
-
map: function(cb, thisArg) {
|
|
403
|
-
return this.models.map(
|
|
404
|
-
isFunction(cb) ? cb : m => isString(cb) ? m.get(cb) : m.matches(cb),
|
|
405
|
-
thisArg
|
|
406
|
-
);
|
|
407
|
-
},
|
|
408
|
-
|
|
409
|
-
reduce: function(callback, initialValue) {
|
|
410
|
-
return this.models.reduce(callback, initialValue || this.models[0]);
|
|
411
|
-
},
|
|
412
|
-
|
|
413
|
-
reduceRight: function(callback, initialValue) {
|
|
414
|
-
return this.models.reduceRight(callback, initialValue || this.models[0]);
|
|
415
|
-
},
|
|
416
|
-
|
|
417
|
-
toArray: function() {
|
|
418
|
-
return Array.from(this.models);
|
|
419
|
-
},
|
|
420
|
-
|
|
421
|
-
// Get a model from the set by id, cid, model object with id or cid
|
|
422
|
-
// properties, or an attributes object that is transformed through modelId.
|
|
423
|
-
get: function(obj) {
|
|
424
|
-
if (obj == null) return undefined;
|
|
425
|
-
return this._byId[obj] ||
|
|
426
|
-
this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] ||
|
|
427
|
-
obj.cid && this._byId[obj.cid];
|
|
428
|
-
},
|
|
429
|
-
|
|
430
|
-
// Returns `true` if the model is in the collection.
|
|
431
|
-
has: function(obj) {
|
|
432
|
-
return this.get(obj) != null;
|
|
433
|
-
},
|
|
434
|
-
|
|
435
|
-
// Get the model at the given index.
|
|
436
|
-
at: function(index) {
|
|
437
|
-
if (index < 0) index += this.length;
|
|
438
|
-
return this.models[index];
|
|
439
|
-
},
|
|
440
|
-
|
|
441
|
-
// Return models with matching attributes. Useful for simple cases of
|
|
442
|
-
// `filter`.
|
|
443
|
-
where: function(attrs, first) {
|
|
444
|
-
return this[first ? 'find' : 'filter'](attrs);
|
|
445
|
-
},
|
|
446
|
-
|
|
447
|
-
// Return the first model with matching attributes. Useful for simple cases
|
|
448
|
-
// of `find`.
|
|
449
|
-
findWhere: function(attrs) {
|
|
450
|
-
return this.where(attrs, true);
|
|
451
|
-
},
|
|
452
|
-
|
|
453
|
-
find: function(predicate, fromIndex) {
|
|
454
|
-
const pred = isFunction(predicate) ? predicate : m => m.matches(predicate);
|
|
455
|
-
return this.models.find(pred, fromIndex);
|
|
456
|
-
},
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
// Force the collection to re-sort itself. You don't need to call this under
|
|
460
|
-
// normal circumstances, as the set will maintain sort order as each item
|
|
461
|
-
// is added.
|
|
462
|
-
sort: function(options) {
|
|
463
|
-
let comparator = this.comparator;
|
|
464
|
-
if (!comparator) throw new Error('Cannot sort a set without a comparator');
|
|
465
|
-
options || (options = {});
|
|
466
|
-
|
|
467
|
-
const length = comparator.length;
|
|
468
|
-
if (isFunction(comparator)) comparator = comparator.bind(this);
|
|
469
|
-
|
|
470
|
-
// Run sort based on type of `comparator`.
|
|
471
|
-
if (length === 1 || isString(comparator)) {
|
|
472
|
-
this.models = this.sortBy(comparator);
|
|
473
|
-
} else {
|
|
474
|
-
this.models.sort(comparator);
|
|
475
|
-
}
|
|
476
|
-
if (!options.silent) this.trigger('sort', this, options);
|
|
477
|
-
return this;
|
|
478
|
-
},
|
|
479
|
-
|
|
480
|
-
// Pluck an attribute from each model in the collection.
|
|
481
|
-
pluck: function(attr) {
|
|
482
|
-
return this.map(attr + '');
|
|
483
|
-
},
|
|
484
|
-
|
|
485
|
-
// Fetch the default set of models for this collection, resetting the
|
|
486
|
-
// collection when they arrive. If `reset: true` is passed, the response
|
|
487
|
-
// data will be passed through the `reset` method instead of `set`.
|
|
488
|
-
fetch: function(options) {
|
|
489
|
-
options = extend({parse: true}, options);
|
|
490
|
-
const success = options.success;
|
|
491
|
-
const collection = this;
|
|
492
|
-
const promise = options.promise && getResolveablePromise();
|
|
493
|
-
options.success = function(resp) {
|
|
494
|
-
const method = options.reset ? 'reset' : 'set';
|
|
495
|
-
collection[method](resp, options);
|
|
496
|
-
if (success) success.call(options.context, collection, resp, options);
|
|
497
|
-
promise && promise.resolve();
|
|
498
|
-
collection.trigger('sync', collection, resp, options);
|
|
499
|
-
};
|
|
500
|
-
wrapError(this, options);
|
|
501
|
-
return promise ? promise : this.sync('read', this, options);
|
|
502
|
-
},
|
|
503
|
-
|
|
504
|
-
// Create a new instance of a model in this collection. Add the model to the
|
|
505
|
-
// collection immediately, unless `wait: true` is passed, in which case we
|
|
506
|
-
// wait for the server to agree.
|
|
507
|
-
create: function(model, options) {
|
|
508
|
-
options = options ? clone(options) : {};
|
|
509
|
-
const wait = options.wait;
|
|
510
|
-
const return_promise = options.promise;
|
|
511
|
-
const promise = return_promise && getResolveablePromise();
|
|
512
|
-
|
|
513
|
-
model = this._prepareModel(model, options);
|
|
514
|
-
if (!model) return false;
|
|
515
|
-
if (!wait) this.add(model, options);
|
|
516
|
-
const collection = this;
|
|
517
|
-
const success = options.success;
|
|
518
|
-
const error = options.error;
|
|
519
|
-
options.success = function(m, resp, callbackOpts) {
|
|
520
|
-
if (wait) {
|
|
521
|
-
collection.add(m, callbackOpts);
|
|
522
|
-
}
|
|
523
|
-
if (success) {
|
|
524
|
-
success.call(callbackOpts.context, m, resp, callbackOpts);
|
|
525
|
-
}
|
|
526
|
-
if (return_promise) {
|
|
527
|
-
promise.resolve(m);
|
|
528
|
-
}
|
|
529
|
-
};
|
|
530
|
-
options.error = function(model, e, options) {
|
|
531
|
-
error && error.call(options.context, model, e, options);
|
|
532
|
-
return_promise && promise.reject(e);
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
model.save(null, Object.assign(options, {'promise': false}));
|
|
536
|
-
if (return_promise) {
|
|
537
|
-
return promise;
|
|
538
|
-
} else {
|
|
539
|
-
return model;
|
|
540
|
-
}
|
|
541
|
-
},
|
|
542
|
-
|
|
543
|
-
// **parse** converts a response into a list of models to be added to the
|
|
544
|
-
// collection. The default implementation is just to pass it through.
|
|
545
|
-
parse: function(resp, options) {
|
|
546
|
-
return resp;
|
|
547
|
-
},
|
|
548
|
-
|
|
549
|
-
// Create a new collection with an identical list of models as this one.
|
|
550
|
-
clone: function() {
|
|
551
|
-
return new this.constructor(this.models, {
|
|
552
|
-
model: this.model,
|
|
553
|
-
comparator: this.comparator
|
|
554
|
-
});
|
|
555
|
-
},
|
|
556
|
-
|
|
557
|
-
// Define how to uniquely identify models in the collection.
|
|
558
|
-
modelId: function(attrs) {
|
|
559
|
-
return attrs[this.model.prototype.idAttribute || 'id'];
|
|
560
|
-
},
|
|
561
|
-
|
|
562
|
-
// Get an iterator of all models in this collection.
|
|
563
|
-
values: function() {
|
|
564
|
-
return new CollectionIterator(this, ITERATOR_VALUES);
|
|
565
|
-
},
|
|
566
|
-
|
|
567
|
-
// Get an iterator of all model IDs in this collection.
|
|
568
|
-
keys: function() {
|
|
569
|
-
return new CollectionIterator(this, ITERATOR_KEYS);
|
|
570
|
-
},
|
|
571
|
-
|
|
572
|
-
// Get an iterator of all [ID, model] tuples in this collection.
|
|
573
|
-
entries: function() {
|
|
574
|
-
return new CollectionIterator(this, ITERATOR_KEYSVALUES);
|
|
575
|
-
},
|
|
576
|
-
|
|
577
|
-
// Private method to reset all internal state. Called when the collection
|
|
578
|
-
// is first initialized or reset.
|
|
579
|
-
_reset: function() {
|
|
580
|
-
this.length = 0;
|
|
581
|
-
this.models = [];
|
|
582
|
-
this._byId = {};
|
|
583
|
-
},
|
|
584
|
-
|
|
585
|
-
// Prepare a hash of attributes (or other model) to be added to this
|
|
586
|
-
// collection.
|
|
587
|
-
_prepareModel: function(attrs, options) {
|
|
588
|
-
if (this._isModel(attrs)) {
|
|
589
|
-
if (!attrs.collection) attrs.collection = this;
|
|
590
|
-
return attrs;
|
|
591
|
-
}
|
|
592
|
-
options = options ? clone(options) : {};
|
|
593
|
-
options.collection = this;
|
|
594
|
-
const model = new this.model(attrs, options);
|
|
595
|
-
if (!model.validationError) return model;
|
|
596
|
-
this.trigger('invalid', this, model.validationError, options);
|
|
597
|
-
return false;
|
|
598
|
-
},
|
|
599
|
-
|
|
600
|
-
// Internal method called by both remove and set.
|
|
601
|
-
_removeModels: function(models, options) {
|
|
602
|
-
const removed = [];
|
|
603
|
-
for (let i = 0; i < models.length; i++) {
|
|
604
|
-
const model = this.get(models[i]);
|
|
605
|
-
if (!model) continue;
|
|
606
|
-
|
|
607
|
-
const index = this.indexOf(model);
|
|
608
|
-
this.models.splice(index, 1);
|
|
609
|
-
this.length--;
|
|
610
|
-
|
|
611
|
-
// Remove references before triggering 'remove' event to prevent an
|
|
612
|
-
// infinite loop. #3693
|
|
613
|
-
delete this._byId[model.cid];
|
|
614
|
-
const id = this.modelId(model.attributes);
|
|
615
|
-
if (id != null) delete this._byId[id];
|
|
616
|
-
|
|
617
|
-
if (!options.silent) {
|
|
618
|
-
options.index = index;
|
|
619
|
-
model.trigger('remove', model, this, options);
|
|
620
|
-
}
|
|
621
|
-
|
|
622
|
-
removed.push(model);
|
|
623
|
-
this._removeReference(model, options);
|
|
624
|
-
}
|
|
625
|
-
return removed;
|
|
626
|
-
},
|
|
627
|
-
|
|
628
|
-
// Method for checking whether an object should be considered a model for
|
|
629
|
-
// the purposes of adding to the collection.
|
|
630
|
-
_isModel: function(model) {
|
|
631
|
-
return model instanceof Model;
|
|
632
|
-
},
|
|
633
|
-
|
|
634
|
-
// Internal method to create a model's ties to a collection.
|
|
635
|
-
_addReference: function(model, options) {
|
|
636
|
-
this._byId[model.cid] = model;
|
|
637
|
-
const id = this.modelId(model.attributes);
|
|
638
|
-
if (id != null) this._byId[id] = model;
|
|
639
|
-
model.on('all', this._onModelEvent, this);
|
|
640
|
-
},
|
|
641
|
-
|
|
642
|
-
// Internal method to sever a model's ties to a collection.
|
|
643
|
-
_removeReference: function(model, options) {
|
|
644
|
-
delete this._byId[model.cid];
|
|
645
|
-
const id = this.modelId(model.attributes);
|
|
646
|
-
if (id != null) delete this._byId[id];
|
|
647
|
-
if (this === model.collection) delete model.collection;
|
|
648
|
-
model.off('all', this._onModelEvent, this);
|
|
649
|
-
},
|
|
650
|
-
|
|
651
|
-
// Internal method called every time a model in the set fires an event.
|
|
652
|
-
// Sets need to update their indexes when models change ids. All other
|
|
653
|
-
// events simply proxy through. "add" and "remove" events that originate
|
|
654
|
-
// in other collections are ignored.
|
|
655
|
-
_onModelEvent: function(event, model, collection, options) {
|
|
656
|
-
if (model) {
|
|
657
|
-
if ((event === 'add' || event === 'remove') && collection !== this) return;
|
|
658
|
-
if (event === 'destroy') this.remove(model, options);
|
|
659
|
-
if (event === 'change') {
|
|
660
|
-
const prevId = this.modelId(model.previousAttributes());
|
|
661
|
-
const id = this.modelId(model.attributes);
|
|
662
|
-
if (prevId !== id) {
|
|
663
|
-
if (prevId != null) delete this._byId[prevId];
|
|
664
|
-
if (id != null) this._byId[id] = model;
|
|
665
|
-
}
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
this.trigger.apply(this, arguments);
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
});
|
|
672
|
-
|
|
673
|
-
// Defining an @@iterator method implements JavaScript's Iterable protocol.
|
|
674
|
-
// In modern ES2015 browsers, this value is found at Symbol.iterator.
|
|
675
|
-
/* global Symbol */
|
|
676
|
-
const $$iterator = typeof Symbol === 'function' && Symbol.iterator;
|
|
677
|
-
if ($$iterator) {
|
|
678
|
-
Collection.prototype[$$iterator] = Collection.prototype.values;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
// CollectionIterator
|
|
682
|
-
// ------------------
|
|
683
|
-
|
|
684
|
-
// A CollectionIterator implements JavaScript's Iterator protocol, allowing the
|
|
685
|
-
// use of `for of` loops in modern browsers and interoperation between
|
|
686
|
-
// Collection and other JavaScript functions and third-party libraries
|
|
687
|
-
// which can operate on Iterables.
|
|
688
|
-
const CollectionIterator = function(collection, kind) {
|
|
689
|
-
this._collection = collection;
|
|
690
|
-
this._kind = kind;
|
|
691
|
-
this._index = 0;
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
// This "enum" defines the three possible kinds of values which can be emitted
|
|
695
|
-
// by a CollectionIterator that correspond to the values(), keys() and entries()
|
|
696
|
-
// methods on Collection, respectively.
|
|
697
|
-
const ITERATOR_VALUES = 1;
|
|
698
|
-
const ITERATOR_KEYS = 2;
|
|
699
|
-
const ITERATOR_KEYSVALUES = 3;
|
|
700
|
-
|
|
701
|
-
// All Iterators should themselves be Iterable.
|
|
702
|
-
if ($$iterator) {
|
|
703
|
-
CollectionIterator.prototype[$$iterator] = function() {
|
|
704
|
-
return this;
|
|
705
|
-
};
|
|
706
|
-
}
|
|
707
|
-
|
|
708
|
-
CollectionIterator.prototype.next = function() {
|
|
709
|
-
if (this._collection) {
|
|
710
|
-
|
|
711
|
-
// Only continue iterating if the iterated collection is long enough.
|
|
712
|
-
if (this._index < this._collection.length) {
|
|
713
|
-
const model = this._collection.at(this._index);
|
|
714
|
-
this._index++;
|
|
715
|
-
|
|
716
|
-
// Construct a value depending on what kind of values should be iterated.
|
|
717
|
-
let value;
|
|
718
|
-
if (this._kind === ITERATOR_VALUES) {
|
|
719
|
-
value = model;
|
|
720
|
-
} else {
|
|
721
|
-
const id = this._collection.modelId(model.attributes);
|
|
722
|
-
if (this._kind === ITERATOR_KEYS) {
|
|
723
|
-
value = id;
|
|
724
|
-
} else { // ITERATOR_KEYSVALUES
|
|
725
|
-
value = [id, model];
|
|
726
|
-
}
|
|
727
|
-
}
|
|
728
|
-
return {value: value, done: false};
|
|
729
|
-
}
|
|
730
|
-
|
|
731
|
-
// Once exhausted, remove the reference to the collection so future
|
|
732
|
-
// calls to the next method always return done.
|
|
733
|
-
this._collection = undefined;
|
|
734
|
-
}
|
|
735
|
-
|
|
736
|
-
return {value: undefined, done: true};
|
|
737
|
-
};
|