Issue #2559299 by tarekdj, vprocessor: Update JS lib: backbone to 1.2.3

8.0.x
webchick 2015-09-04 13:54:03 -07:00
parent e648812d9c
commit c583a57781
5 changed files with 138 additions and 117 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
// Backbone.js 1.2.1 // Backbone.js 1.2.3
// (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
// Backbone may be freely distributed under the MIT license. // Backbone may be freely distributed under the MIT license.
@ -41,10 +41,10 @@
var previousBackbone = root.Backbone; var previousBackbone = root.Backbone;
// Create a local reference to a common array method we'll want to use later. // 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`. // Current version of the library. Keep in sync with `package.json`.
Backbone.VERSION = '1.2.1'; Backbone.VERSION = '1.2.3';
// For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
// the `$` variable. // the `$` variable.
@ -68,8 +68,13 @@
// form param named `model`. // form param named `model`.
Backbone.emulateJSON = false; Backbone.emulateJSON = false;
// Proxy Underscore methods to a Backbone class' prototype using a // Proxy Backbone class methods to Underscore functions, wrapping the model's
// particular attribute as the data argument // `attributes` object or collection's `models` array behind the scenes.
//
// collection.filter(function(model) { return model.get('age') > 10 });
// collection.each(this.addView);
//
// `Function#apply` can be slow so we use the method's arg count, if we know it.
var addMethod = function(length, method, attribute) { var addMethod = function(length, method, attribute) {
switch (length) { switch (length) {
case 1: return function() { case 1: return function() {
@ -79,10 +84,10 @@
return _[method](this[attribute], value); return _[method](this[attribute], value);
}; };
case 3: return function(iteratee, context) { case 3: return function(iteratee, context) {
return _[method](this[attribute], iteratee, context); return _[method](this[attribute], cb(iteratee, this), context);
}; };
case 4: return function(iteratee, defaultVal, context) { case 4: return function(iteratee, defaultVal, context) {
return _[method](this[attribute], iteratee, defaultVal, context); return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
}; };
default: return function() { default: return function() {
var args = slice.call(arguments); var args = slice.call(arguments);
@ -97,12 +102,26 @@
}); });
}; };
// Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
var cb = function(iteratee, instance) {
if (_.isFunction(iteratee)) return iteratee;
if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
return iteratee;
};
var modelMatcher = function(attrs) {
var matcher = _.matches(attrs);
return function(model) {
return matcher(model.attributes);
};
};
// Backbone.Events // Backbone.Events
// --------------- // ---------------
// A module that can be mixed in to *any object* in order to provide it with // A module that can be mixed in to *any object* in order to provide it with
// custom events. You may bind with `on` or remove with `off` callback // a custom event channel. You may bind a callback to an event with `on` or
// functions to an event; `trigger`-ing an event fires all callbacks in // remove with `off`; `trigger`-ing an event fires all callbacks in
// succession. // succession.
// //
// var object = {}; // var object = {};
@ -117,26 +136,25 @@
// Iterates over the standard `event, callback` (as well as the fancy multiple // Iterates over the standard `event, callback` (as well as the fancy multiple
// space-separated events `"change blur", callback` and jQuery-style event // space-separated events `"change blur", callback` and jQuery-style event
// maps `{event: callback}`), reducing them by manipulating `memo`. // maps `{event: callback}`).
// Passes a normalized single event name and callback, as well as any var eventsApi = function(iteratee, events, name, callback, opts) {
// optional `opts`.
var eventsApi = function(iteratee, memo, name, callback, opts) {
var i = 0, names; var i = 0, names;
if (name && typeof name === 'object') { if (name && typeof name === 'object') {
// Handle event maps. // Handle event maps.
if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback; if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
for (names = _.keys(name); i < names.length ; i++) { for (names = _.keys(name); i < names.length ; i++) {
memo = iteratee(memo, names[i], name[names[i]], opts); events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
} }
} else if (name && eventSplitter.test(name)) { } else if (name && eventSplitter.test(name)) {
// Handle space separated event names. // Handle space separated event names by delegating them individually.
for (names = name.split(eventSplitter); i < names.length; i++) { for (names = name.split(eventSplitter); i < names.length; i++) {
memo = iteratee(memo, names[i], callback, opts); events = iteratee(events, names[i], callback, opts);
} }
} else { } else {
memo = iteratee(memo, name, callback, opts); // Finally, standard events.
events = iteratee(events, name, callback, opts);
} }
return memo; return events;
}; };
// Bind an event to a `callback` function. Passing `"all"` will bind // Bind an event to a `callback` function. Passing `"all"` will bind
@ -145,8 +163,7 @@
return internalOn(this, name, callback, context); return internalOn(this, name, callback, context);
}; };
// An internal use `on` function, used to guard the `listening` argument from // Guard the `listening` argument from the public API.
// the public API.
var internalOn = function(obj, name, callback, context, listening) { var internalOn = function(obj, name, callback, context, listening) {
obj._events = eventsApi(onApi, obj._events || {}, name, callback, { obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
context: context, context: context,
@ -163,7 +180,8 @@
}; };
// Inversion-of-control versions of `on`. Tell *this* object to listen to // Inversion-of-control versions of `on`. Tell *this* object to listen to
// an event in another object... keeping track of what it's listening to. // an event in another object... keeping track of what it's listening to
// for easier unbinding later.
Events.listenTo = function(obj, name, callback) { Events.listenTo = function(obj, name, callback) {
if (!obj) return this; if (!obj) return this;
var id = obj._listenId || (obj._listenId = _.uniqueId('l')); var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
@ -231,7 +249,6 @@
// The reducing API that removes a callback from the `events` object. // The reducing API that removes a callback from the `events` object.
var offApi = function(events, name, callback, options) { var offApi = function(events, name, callback, options) {
// No events to consider.
if (!events) return; if (!events) return;
var i = 0, listening; var i = 0, listening;
@ -286,9 +303,9 @@
}; };
// Bind an event to only be triggered a single time. After the first time // Bind an event to only be triggered a single time. After the first time
// the callback is invoked, it will be removed. When multiple events are // the callback is invoked, its listener will be removed. If multiple events
// passed in using the space-separated syntax, the event will fire once for every // are passed in using the space-separated syntax, the handler will fire
// event you passed in, not once for a combination of all events // once for each event, not once for a combination of all events.
Events.once = function(name, callback, context) { Events.once = function(name, callback, context) {
// Map the event into a `{event: once}` object. // Map the event into a `{event: once}` object.
var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this)); var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
@ -476,9 +493,6 @@
var changed = this.changed; var changed = this.changed;
var prev = this._previousAttributes; var prev = this._previousAttributes;
// Check for changes of `id`.
if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
// For each `set` attribute, update or delete the current value. // For each `set` attribute, update or delete the current value.
for (var attr in attrs) { for (var attr in attrs) {
val = attrs[attr]; val = attrs[attr];
@ -491,6 +505,9 @@
unset ? delete current[attr] : current[attr] = val; unset ? delete current[attr] : current[attr] = val;
} }
// Update the `id`.
this.id = this.get(this.idAttribute);
// Trigger all relevant attribute changes. // Trigger all relevant attribute changes.
if (!silent) { if (!silent) {
if (changes.length) this._pending = options; if (changes.length) this._pending = options;
@ -713,7 +730,8 @@
}); });
// Underscore methods that we want to implement on the Model. // Underscore methods that we want to implement on the Model, mapped to the
// number of arguments they take.
var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0, var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
omit: 0, chain: 1, isEmpty: 1 }; omit: 0, chain: 1, isEmpty: 1 };
@ -746,6 +764,16 @@
var setOptions = {add: true, remove: true, merge: true}; var setOptions = {add: true, remove: true, merge: true};
var addOptions = {add: true, remove: false}; var addOptions = {add: true, remove: false};
// Splices `insert` into `array` at index `at`.
var splice = function(array, insert, at) {
at = Math.min(Math.max(at, 0), array.length);
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. // Define the Collection's inheritable methods.
_.extend(Collection.prototype, Events, { _.extend(Collection.prototype, Events, {
@ -768,7 +796,9 @@
return Backbone.sync.apply(this, arguments); return Backbone.sync.apply(this, arguments);
}, },
// Add a model, or list of models to the set. // Add a model, or list of models to the set. `models` may be Backbone
// Models or raw JavaScript objects to be converted to Models, or any
// combination of the two.
add: function(models, options) { add: function(models, options) {
return this.set(models, _.extend({merge: false}, options, addOptions)); return this.set(models, _.extend({merge: false}, options, addOptions));
}, },
@ -788,83 +818,88 @@
// already exist in the collection, as necessary. Similar to **Model#set**, // already exist in the collection, as necessary. Similar to **Model#set**,
// the core operation for updating the data contained by the collection. // the core operation for updating the data contained by the collection.
set: function(models, options) { set: function(models, options) {
if (models == null) return;
options = _.defaults({}, options, setOptions); options = _.defaults({}, options, setOptions);
if (options.parse && !this._isModel(models)) models = this.parse(models, options); if (options.parse && !this._isModel(models)) models = this.parse(models, options);
var singular = !_.isArray(models); var singular = !_.isArray(models);
models = singular ? (models ? [models] : []) : models.slice(); models = singular ? [models] : models.slice();
var id, model, attrs, existing, sort;
var at = options.at; var at = options.at;
if (at != null) at = +at; if (at != null) at = +at;
if (at < 0) at += this.length + 1; 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 sortable = this.comparator && (at == null) && options.sort !== false;
var sortAttr = _.isString(this.comparator) ? this.comparator : null; 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 // Turn bare objects into model references, and prevent invalid models
// from being added. // from being added.
var model;
for (var i = 0; i < models.length; i++) { 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 // If a duplicate is found, prevent it from being added and
// optionally merge it into the existing model. // optionally merge it into the existing model.
if (existing = this.get(attrs)) { var existing = this.get(model);
if (remove) modelMap[existing.cid] = true; if (existing) {
if (merge && attrs !== existing) { if (merge && model !== existing) {
attrs = this._isModel(attrs) ? attrs.attributes : attrs; var attrs = this._isModel(model) ? model.attributes : model;
if (options.parse) attrs = existing.parse(attrs, options); if (options.parse) attrs = existing.parse(attrs, options);
existing.set(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; models[i] = existing;
// If this is a new, valid model, push it to the `toAdd` list. // If this is a new, valid model, push it to the `toAdd` list.
} else if (add) { } else if (add) {
model = models[i] = this._prepareModel(attrs, options); model = models[i] = this._prepareModel(model, options);
if (!model) continue; if (model) {
toAdd.push(model); toAdd.push(model);
this._addReference(model, options); 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 nonexistent models if appropriate. // Remove stale models.
if (remove) { if (remove) {
for (var i = 0; i < this.length; i++) { for (i = 0; i < this.length; i++) {
if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model); model = this.models[i];
if (!modelMap[model.cid]) toRemove.push(model);
} }
if (toRemove.length) this._removeModels(toRemove, options); if (toRemove.length) this._removeModels(toRemove, options);
} }
// See if sorting is needed, update `length` and splice in new models. // 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; if (sortable) sort = true;
this.length += toAdd.length; splice(this.models, toAdd, at == null ? this.length : at);
if (at != null) { this.length = this.models.length;
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]);
}
}
} }
// Silently sort the collection if appropriate. // Silently sort the collection if appropriate.
@ -872,10 +907,10 @@
// Unless silenced, it's time to fire all appropriate add/sort events. // Unless silenced, it's time to fire all appropriate add/sort events.
if (!options.silent) { if (!options.silent) {
var addOpts = at != null ? _.clone(options) : options; for (i = 0; i < toAdd.length; i++) {
for (var i = 0; i < toAdd.length; i++) { if (at != null) options.index = at + i;
if (at != null) addOpts.index = at + i; model = toAdd[i];
(model = toAdd[i]).trigger('add', model, this, addOpts); model.trigger('add', model, this, options);
} }
if (sort || orderChanged) this.trigger('sort', this, options); if (sort || orderChanged) this.trigger('sort', this, options);
if (toAdd.length || toRemove.length) this.trigger('update', this, options); if (toAdd.length || toRemove.length) this.trigger('update', this, options);
@ -944,10 +979,7 @@
// Return models with matching attributes. Useful for simple cases of // Return models with matching attributes. Useful for simple cases of
// `filter`. // `filter`.
where: function(attrs, first) { where: function(attrs, first) {
var matches = _.matches(attrs); return this[first ? 'find' : 'filter'](attrs);
return this[first ? 'find' : 'filter'](function(model) {
return matches(model.attributes);
});
}, },
// Return the first model with matching attributes. Useful for simple cases // Return the first model with matching attributes. Useful for simple cases
@ -960,16 +992,19 @@
// normal circumstances, as the set will maintain sort order as each item // normal circumstances, as the set will maintain sort order as each item
// is added. // is added.
sort: function(options) { sort: function(options) {
if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); var comparator = this.comparator;
if (!comparator) throw new Error('Cannot sort a set without a comparator');
options || (options = {}); options || (options = {});
// Run sort based on type of `comparator`. var length = comparator.length;
if (_.isString(this.comparator) || this.comparator.length === 1) { if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
this.models = this.sortBy(this.comparator, this);
} else {
this.models.sort(_.bind(this.comparator, this));
}
// Run sort based on type of `comparator`.
if (length === 1 || _.isString(comparator)) {
this.models = this.sortBy(comparator);
} else {
this.models.sort(comparator);
}
if (!options.silent) this.trigger('sort', this, options); if (!options.silent) this.trigger('sort', this, options);
return this; return this;
}, },
@ -1058,7 +1093,6 @@
}, },
// Internal method called by both remove and set. // Internal method called by both remove and set.
// Returns removed models, or false if nothing is removed.
_removeModels: function(models, options) { _removeModels: function(models, options) {
var removed = []; var removed = [];
for (var i = 0; i < models.length; i++) { for (var i = 0; i < models.length; i++) {
@ -1128,29 +1162,16 @@
// right here: // right here:
var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4, var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3, foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 2, select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
contains: 2, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3, contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3, head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3, without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
isEmpty: 1, chain: 1, sample: 3, partition: 3 }; isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
sortBy: 3, indexBy: 3};
// Mix in each Underscore method as a proxy to `Collection#models`. // Mix in each Underscore method as a proxy to `Collection#models`.
addUnderscoreMethods(Collection, collectionMethods, 'models'); addUnderscoreMethods(Collection, collectionMethods, 'models');
// Underscore methods that take a property name as an argument.
var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
// Use attributes instead of properties.
_.each(attributeMethods, function(method) {
if (!_[method]) return;
Collection.prototype[method] = function(value, context) {
var iterator = _.isFunction(value) ? value : function(model) {
return model.get(value);
};
return _[method](this.models, iterator, context);
};
});
// Backbone.View // Backbone.View
// ------------- // -------------
@ -1174,7 +1195,7 @@
// Cached regex to split keys for `delegate`. // Cached regex to split keys for `delegate`.
var delegateEventSplitter = /^(\S+)\s*(.*)$/; var delegateEventSplitter = /^(\S+)\s*(.*)$/;
// List of view options to be merged as properties. // List of view options to be set as properties.
var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events']; var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
// Set up all inheritable **Backbone.View** properties and methods. // Set up all inheritable **Backbone.View** properties and methods.
@ -1518,7 +1539,7 @@
// falls back to polling. // falls back to polling.
var History = Backbone.History = function() { var History = Backbone.History = function() {
this.handlers = []; this.handlers = [];
_.bindAll(this, 'checkUrl'); this.checkUrl = _.bind(this.checkUrl, this);
// Ensure that `History` can be used outside of the browser. // Ensure that `History` can be used outside of the browser.
if (typeof window !== 'undefined') { if (typeof window !== 'undefined') {
@ -1611,7 +1632,7 @@
this.options = _.extend({root: '/'}, this.options, options); this.options = _.extend({root: '/'}, this.options, options);
this.root = this.options.root; this.root = this.options.root;
this._wantsHashChange = this.options.hashChange !== false; this._wantsHashChange = this.options.hashChange !== false;
this._hasHashChange = 'onhashchange' in window; this._hasHashChange = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
this._useHashChange = this._wantsHashChange && this._hasHashChange; this._useHashChange = this._wantsHashChange && this._hasHashChange;
this._wantsPushState = !!this.options.pushState; this._wantsPushState = !!this.options.pushState;
this._hasPushState = !!(this.history && this.history.pushState); this._hasPushState = !!(this.history && this.history.pushState);
@ -1730,7 +1751,7 @@
// If the root doesn't match, no routes can match either. // If the root doesn't match, no routes can match either.
if (!this.matchRoot()) return false; if (!this.matchRoot()) return false;
fragment = this.fragment = this.getFragment(fragment); fragment = this.fragment = this.getFragment(fragment);
return _.any(this.handlers, function(handler) { return _.some(this.handlers, function(handler) {
if (handler.route.test(fragment)) { if (handler.route.test(fragment)) {
handler.callback(fragment); handler.callback(fragment);
return true; return true;

View File

@ -2,10 +2,10 @@
backbone: backbone:
remote: https://github.com/jashkenas/backbone remote: https://github.com/jashkenas/backbone
version: "1.2.1" version: "1.2.3"
license: license:
name: MIT name: MIT
url: https://github.com/jashkenas/backbone/blob/1.2.1/LICENSE url: https://github.com/jashkenas/backbone/blob/1.2.3/LICENSE
gpl-compatible: true gpl-compatible: true
js: js:
assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true } assets/vendor/backbone/backbone-min.js: { weight: -19, minified: true }

View File

@ -288,7 +288,7 @@ class AttachedAssetsTest extends KernelTestBase {
$js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js); $js_render_array = \Drupal::service('asset.js.collection_renderer')->render($js);
$rendered_js = $this->renderer->renderPlain($js_render_array); $rendered_js = $this->renderer->renderPlain($js_render_array);
$this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.2.1') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0 , 'JavaScript version identifiers correctly appended to URLs'); $this->assertTrue(strpos($rendered_js, 'core/assets/vendor/backbone/backbone-min.js?v=1.2.3') > 0 && strpos($rendered_js, 'core/assets/vendor/domready/ready.min.js?v=1.0.8') > 0 , 'JavaScript version identifiers correctly appended to URLs');
} }
/** /**