mirror of
https://github.com/jashkenas/backbone.git
synced 2026-04-30 03:00:06 -04:00
Merge pull request #3663 from jridgewell/collection-set-style-compat
Collection#set style (Backwards Compatible)
This commit is contained in:
112
backbone.js
112
backbone.js
@@ -41,7 +41,7 @@
|
||||
var previousBackbone = root.Backbone;
|
||||
|
||||
// Create a local reference to a common array method we'll want to use later.
|
||||
var slice = [].slice;
|
||||
var slice = Array.prototype.slice;
|
||||
|
||||
// Current version of the library. Keep in sync with `package.json`.
|
||||
Backbone.VERSION = '1.2.1';
|
||||
@@ -764,6 +764,15 @@
|
||||
var setOptions = {add: true, remove: true, merge: true};
|
||||
var addOptions = {add: true, remove: false};
|
||||
|
||||
// Splices `insert` into `array` at index `at`.
|
||||
var splice = function(array, insert, at) {
|
||||
var tail = Array(array.length - at);
|
||||
var length = insert.length;
|
||||
for (var i = 0; i < tail.length; i++) tail[i] = array[i + at];
|
||||
for (i = 0; i < length; i++) array[i + at] = insert[i];
|
||||
for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
|
||||
};
|
||||
|
||||
// Define the Collection's inheritable methods.
|
||||
_.extend(Collection.prototype, Events, {
|
||||
|
||||
@@ -808,83 +817,88 @@
|
||||
// already exist in the collection, as necessary. Similar to **Model#set**,
|
||||
// the core operation for updating the data contained by the collection.
|
||||
set: function(models, options) {
|
||||
if (models == null) return;
|
||||
|
||||
options = _.defaults({}, options, setOptions);
|
||||
if (options.parse && !this._isModel(models)) models = this.parse(models, options);
|
||||
|
||||
var singular = !_.isArray(models);
|
||||
models = singular ? (models ? [models] : []) : models.slice();
|
||||
var id, model, attrs, existing, sort;
|
||||
models = singular ? [models] : models.slice();
|
||||
|
||||
var at = options.at;
|
||||
if (at != null) at = +at;
|
||||
if (at < 0) at += this.length + 1;
|
||||
|
||||
var set = [];
|
||||
var toAdd = [];
|
||||
var toRemove = [];
|
||||
var modelMap = {};
|
||||
|
||||
var add = options.add;
|
||||
var merge = options.merge;
|
||||
var remove = options.remove;
|
||||
|
||||
var sort = false;
|
||||
var sortable = this.comparator && (at == null) && options.sort !== false;
|
||||
var sortAttr = _.isString(this.comparator) ? this.comparator : null;
|
||||
var toAdd = [], toRemove = [], modelMap = {};
|
||||
var add = options.add, merge = options.merge, remove = options.remove;
|
||||
var order = !sortable && add && remove ? [] : false;
|
||||
var orderChanged = false;
|
||||
|
||||
// Turn bare objects into model references, and prevent invalid models
|
||||
// from being added.
|
||||
var model;
|
||||
for (var i = 0; i < models.length; i++) {
|
||||
attrs = models[i];
|
||||
model = models[i];
|
||||
|
||||
// If a duplicate is found, prevent it from being added and
|
||||
// optionally merge it into the existing model.
|
||||
if (existing = this.get(attrs)) {
|
||||
if (remove) modelMap[existing.cid] = true;
|
||||
if (merge && attrs !== existing) {
|
||||
attrs = this._isModel(attrs) ? attrs.attributes : attrs;
|
||||
var existing = this.get(model);
|
||||
if (existing) {
|
||||
if (merge && model !== existing) {
|
||||
var attrs = this._isModel(model) ? model.attributes : model;
|
||||
if (options.parse) attrs = existing.parse(attrs, options);
|
||||
existing.set(attrs, options);
|
||||
if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
|
||||
if (sortable && !sort) sort = existing.hasChanged(sortAttr);
|
||||
}
|
||||
if (!modelMap[existing.cid]) {
|
||||
modelMap[existing.cid] = true;
|
||||
set.push(existing);
|
||||
}
|
||||
models[i] = existing;
|
||||
|
||||
// If this is a new, valid model, push it to the `toAdd` list.
|
||||
} else if (add) {
|
||||
model = models[i] = this._prepareModel(attrs, options);
|
||||
if (!model) continue;
|
||||
toAdd.push(model);
|
||||
this._addReference(model, options);
|
||||
model = models[i] = this._prepareModel(model, options);
|
||||
if (model) {
|
||||
toAdd.push(model);
|
||||
this._addReference(model, options);
|
||||
modelMap[model.cid] = true;
|
||||
set.push(model);
|
||||
}
|
||||
}
|
||||
|
||||
// Do not add multiple models with the same `id`.
|
||||
model = existing || model;
|
||||
if (!model) continue;
|
||||
id = this.modelId(model.attributes);
|
||||
if (order && (model.isNew() || !modelMap[id])) {
|
||||
order.push(model);
|
||||
|
||||
// Check to see if this is actually a new model at this index.
|
||||
orderChanged = orderChanged || !this.models[i] || model.cid !== this.models[i].cid;
|
||||
}
|
||||
|
||||
modelMap[id] = true;
|
||||
}
|
||||
|
||||
// Remove stale models.
|
||||
if (remove) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
|
||||
for (i = 0; i < this.length; i++) {
|
||||
model = this.models[i];
|
||||
if (!modelMap[model.cid]) toRemove.push(model);
|
||||
}
|
||||
if (toRemove.length) this._removeModels(toRemove, options);
|
||||
}
|
||||
|
||||
// See if sorting is needed, update `length` and splice in new models.
|
||||
if (toAdd.length || orderChanged) {
|
||||
var orderChanged = false;
|
||||
var replace = !sortable && add && remove;
|
||||
if (set.length && replace) {
|
||||
orderChanged = this.length != set.length || _.some(this.models, function(model, index) {
|
||||
return model !== set[index];
|
||||
});
|
||||
this.models.length = 0;
|
||||
splice(this.models, set, 0);
|
||||
this.length = this.models.length;
|
||||
} else if (toAdd.length) {
|
||||
if (sortable) sort = true;
|
||||
this.length += toAdd.length;
|
||||
if (at != null) {
|
||||
for (var i = 0; i < toAdd.length; i++) {
|
||||
this.models.splice(at + i, 0, toAdd[i]);
|
||||
}
|
||||
} else {
|
||||
if (order) this.models.length = 0;
|
||||
var orderedModels = order || toAdd;
|
||||
for (var i = 0; i < orderedModels.length; i++) {
|
||||
this.models.push(orderedModels[i]);
|
||||
}
|
||||
}
|
||||
splice(this.models, toAdd, at == null ? this.length : at);
|
||||
this.length = this.models.length;
|
||||
}
|
||||
|
||||
// Silently sort the collection if appropriate.
|
||||
@@ -892,10 +906,10 @@
|
||||
|
||||
// Unless silenced, it's time to fire all appropriate add/sort events.
|
||||
if (!options.silent) {
|
||||
var addOpts = at != null ? _.clone(options) : options;
|
||||
for (var i = 0; i < toAdd.length; i++) {
|
||||
if (at != null) addOpts.index = at + i;
|
||||
(model = toAdd[i]).trigger('add', model, this, addOpts);
|
||||
for (i = 0; i < toAdd.length; i++) {
|
||||
if (at != null) options.index = at + i;
|
||||
model = toAdd[i];
|
||||
model.trigger('add', model, this, options);
|
||||
}
|
||||
if (sort || orderChanged) this.trigger('sort', this, options);
|
||||
if (toAdd.length || toRemove.length) this.trigger('update', this, options);
|
||||
|
||||
@@ -506,7 +506,7 @@
|
||||
};
|
||||
collection.url = '/test';
|
||||
collection.fetch();
|
||||
this.syncArgs.options.success();
|
||||
this.syncArgs.options.success([]);
|
||||
equal(counter, 1);
|
||||
});
|
||||
|
||||
@@ -1106,6 +1106,12 @@
|
||||
});
|
||||
c.set([]);
|
||||
strictEqual(c.length, 0);
|
||||
|
||||
// Test null models on set doesn't clear collection
|
||||
c.off();
|
||||
c.set([{id: 1}]);
|
||||
c.set();
|
||||
strictEqual(c.length, 1);
|
||||
});
|
||||
|
||||
test("set with only cids", 3, function() {
|
||||
@@ -1272,7 +1278,7 @@
|
||||
}));
|
||||
var ajax = Backbone.ajax;
|
||||
Backbone.ajax = function (params) {
|
||||
_.defer(params.success);
|
||||
_.defer(params.success, []);
|
||||
return {someHeader: 'headerValue'};
|
||||
};
|
||||
collection.fetch({
|
||||
@@ -1602,7 +1608,7 @@
|
||||
collection.on('sort', function() {
|
||||
ok(true);
|
||||
});
|
||||
collection.set([{id: 3}, {id: 2}, {id: 1}, {id: 0}]);
|
||||
collection.set([{id: 1}, {id: 2}, {id: 3}, {id: 0}]);
|
||||
})
|
||||
|
||||
test('#3199 - Order not changing should not trigger a sort', 0, function() {
|
||||
|
||||
Reference in New Issue
Block a user