External Libraries: Update Backbone.js to 1.4.0.

Upgrade Backbone to the latest stable version. Fix some issues in Media with `listenTo` which changed in this version, see https://backbonejs.org/#changelog.

Props desrosj, priyankkpatel, pierlo.
Fixes #47478.


Built from https://develop.svn.wordpress.org/trunk@46157


git-svn-id: http://core.svn.wordpress.org/trunk@45969 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
Adam Silverstein 2019-09-17 17:19:54 +00:00
parent 222b400a7d
commit 0cb3860138
8 changed files with 322 additions and 149 deletions

View File

@ -1,6 +1,6 @@
// Backbone.js 1.3.3 // Backbone.js 1.4.0
// (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
// Backbone may be freely distributed under the MIT license. // Backbone may be freely distributed under the MIT license.
// For all details and documentation: // For all details and documentation:
// http://backbonejs.org // http://backbonejs.org
@ -9,8 +9,8 @@
// Establish the root object, `window` (`self`) in the browser, or `global` on the server. // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
// We use `self` instead of `window` for `WebWorker` support. // We use `self` instead of `window` for `WebWorker` support.
var root = (typeof self == 'object' && self.self === self && self) || var root = typeof self == 'object' && self.self === self && self ||
(typeof global == 'object' && global.global === global && global); typeof global == 'object' && global.global === global && global;
// Set up Backbone appropriately for the environment. Start with AMD. // Set up Backbone appropriately for the environment. Start with AMD.
if (typeof define === 'function' && define.amd) { if (typeof define === 'function' && define.amd) {
@ -28,7 +28,7 @@
// Finally, as a browser global. // Finally, as a browser global.
} else { } else {
root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$)); root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$);
} }
})(function(root, Backbone, _, $) { })(function(root, Backbone, _, $) {
@ -44,7 +44,7 @@
var slice = Array.prototype.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.3.3'; Backbone.VERSION = '1.4.0';
// 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,54 +68,6 @@
// form param named `model`. // form param named `model`.
Backbone.emulateJSON = false; Backbone.emulateJSON = false;
// Proxy Backbone class methods to Underscore functions, wrapping the model's
// `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) {
switch (length) {
case 1: return function() {
return _[method](this[attribute]);
};
case 2: return function(value) {
return _[method](this[attribute], value);
};
case 3: return function(iteratee, context) {
return _[method](this[attribute], cb(iteratee, this), context);
};
case 4: return function(iteratee, defaultVal, context) {
return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
};
default: return function() {
var args = slice.call(arguments);
args.unshift(this[attribute]);
return _[method].apply(_, args);
};
}
};
var addUnderscoreMethods = function(Class, methods, attribute) {
_.each(methods, function(length, method) {
if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
});
};
// 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
// --------------- // ---------------
@ -134,6 +86,9 @@
// Regular expression used to split event strings. // Regular expression used to split event strings.
var eventSplitter = /\s+/; var eventSplitter = /\s+/;
// A private global variable to share between listeners and listenees.
var _listening;
// 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}`). // maps `{event: callback}`).
@ -160,23 +115,21 @@
// Bind an event to a `callback` function. Passing `"all"` will bind // Bind an event to a `callback` function. Passing `"all"` will bind
// the callback to all events fired. // the callback to all events fired.
Events.on = function(name, callback, context) { Events.on = function(name, callback, context) {
return internalOn(this, name, callback, context); this._events = eventsApi(onApi, this._events || {}, name, callback, {
};
// Guard the `listening` argument from the public API.
var internalOn = function(obj, name, callback, context, listening) {
obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
context: context, context: context,
ctx: obj, ctx: this,
listening: listening listening: _listening
}); });
if (listening) { if (_listening) {
var listeners = obj._listeners || (obj._listeners = {}); var listeners = this._listeners || (this._listeners = {});
listeners[listening.id] = listening; listeners[_listening.id] = _listening;
// Allow the listening to use a counter, instead of tracking
// callbacks for library interop
_listening.interop = false;
} }
return obj; return this;
}; };
// Inversion-of-control versions of `on`. Tell *this* object to listen to // Inversion-of-control versions of `on`. Tell *this* object to listen to
@ -186,17 +139,23 @@
if (!obj) return this; if (!obj) return this;
var id = obj._listenId || (obj._listenId = _.uniqueId('l')); var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
var listeningTo = this._listeningTo || (this._listeningTo = {}); var listeningTo = this._listeningTo || (this._listeningTo = {});
var listening = listeningTo[id]; var listening = _listening = listeningTo[id];
// This object is not listening to any other events on `obj` yet. // This object is not listening to any other events on `obj` yet.
// Setup the necessary references to track the listening callbacks. // Setup the necessary references to track the listening callbacks.
if (!listening) { if (!listening) {
var thisId = this._listenId || (this._listenId = _.uniqueId('l')); this._listenId || (this._listenId = _.uniqueId('l'));
listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0}; listening = _listening = listeningTo[id] = new Listening(this, obj);
} }
// Bind callbacks on obj, and keep track of them on listening. // Bind callbacks on obj.
internalOn(obj, name, callback, this, listening); var error = tryCatchOn(obj, name, callback, this);
_listening = void 0;
if (error) throw error;
// If the target obj is not Backbone.Events, track events manually.
if (listening.interop) listening.on(name, callback);
return this; return this;
}; };
@ -212,6 +171,16 @@
return events; return events;
}; };
// An try-catch guarded #on function, to prevent poisoning the global
// `_listening` variable.
var tryCatchOn = function(obj, name, callback, context) {
try {
obj.on(name, callback, context);
} catch (e) {
return e;
}
};
// Remove one or many callbacks. If `context` is null, removes all // Remove one or many callbacks. If `context` is null, removes all
// callbacks with that function. If `callback` is null, removes all // callbacks with that function. If `callback` is null, removes all
// callbacks for the event. If `name` is null, removes all bound // callbacks for the event. If `name` is null, removes all bound
@ -222,6 +191,7 @@
context: context, context: context,
listeners: this._listeners listeners: this._listeners
}); });
return this; return this;
}; };
@ -232,7 +202,6 @@
if (!listeningTo) return this; if (!listeningTo) return this;
var ids = obj ? [obj._listenId] : _.keys(listeningTo); var ids = obj ? [obj._listenId] : _.keys(listeningTo);
for (var i = 0; i < ids.length; i++) { for (var i = 0; i < ids.length; i++) {
var listening = listeningTo[ids[i]]; var listening = listeningTo[ids[i]];
@ -241,7 +210,9 @@
if (!listening) break; if (!listening) break;
listening.obj.off(name, callback, this); listening.obj.off(name, callback, this);
if (listening.interop) listening.off(name, callback);
} }
if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
return this; return this;
}; };
@ -250,21 +221,18 @@
var offApi = function(events, name, callback, options) { var offApi = function(events, name, callback, options) {
if (!events) return; if (!events) return;
var i = 0, listening;
var context = options.context, listeners = options.listeners; var context = options.context, listeners = options.listeners;
var i = 0, names;
// Delete all events listeners and "drop" events. // Delete all event listeners and "drop" events.
if (!name && !callback && !context) { if (!name && !context && !callback) {
var ids = _.keys(listeners); for (names = _.keys(listeners); i < names.length; i++) {
for (; i < ids.length; i++) { listeners[names[i]].cleanup();
listening = listeners[ids[i]];
delete listeners[listening.id];
delete listening.listeningTo[listening.objId];
} }
return; return;
} }
var names = name ? [name] : _.keys(events); names = name ? [name] : _.keys(events);
for (; i < names.length; i++) { for (; i < names.length; i++) {
name = names[i]; name = names[i];
var handlers = events[name]; var handlers = events[name];
@ -272,7 +240,7 @@
// Bail out if there are no events stored. // Bail out if there are no events stored.
if (!handlers) break; if (!handlers) break;
// Replace events if there are any remaining. Otherwise, clean up. // Find any remaining events.
var remaining = []; var remaining = [];
for (var j = 0; j < handlers.length; j++) { for (var j = 0; j < handlers.length; j++) {
var handler = handlers[j]; var handler = handlers[j];
@ -283,21 +251,19 @@
) { ) {
remaining.push(handler); remaining.push(handler);
} else { } else {
listening = handler.listening; var listening = handler.listening;
if (listening && --listening.count === 0) { if (listening) listening.off(name, callback);
delete listeners[listening.id];
delete listening.listeningTo[listening.objId];
}
} }
} }
// Update tail event if the list has any events. Otherwise, clean up. // Replace events if there are any remaining. Otherwise, clean up.
if (remaining.length) { if (remaining.length) {
events[name] = remaining; events[name] = remaining;
} else { } else {
delete events[name]; delete events[name];
} }
} }
return events; return events;
}; };
@ -307,7 +273,7 @@
// once for each event, 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, this.off.bind(this));
if (typeof name === 'string' && context == null) callback = void 0; if (typeof name === 'string' && context == null) callback = void 0;
return this.on(events, callback, context); return this.on(events, callback, context);
}; };
@ -315,7 +281,7 @@
// Inversion-of-control versions of `once`. // Inversion-of-control versions of `once`.
Events.listenToOnce = function(obj, name, callback) { Events.listenToOnce = function(obj, name, callback) {
// Map the event into a `{event: once}` object. // Map the event into a `{event: once}` object.
var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj)); var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
return this.listenTo(obj, events); return this.listenTo(obj, events);
}; };
@ -373,6 +339,44 @@
} }
}; };
// A listening class that tracks and cleans up memory bindings
// when all callbacks have been offed.
var Listening = function(listener, obj) {
this.id = listener._listenId;
this.listener = listener;
this.obj = obj;
this.interop = true;
this.count = 0;
this._events = void 0;
};
Listening.prototype.on = Events.on;
// Offs a callback (or several).
// Uses an optimized counter if the listenee uses Backbone.Events.
// Otherwise, falls back to manual tracking to support events
// library interop.
Listening.prototype.off = function(name, callback) {
var cleanup;
if (this.interop) {
this._events = eventsApi(offApi, this._events, name, callback, {
context: void 0,
listeners: void 0
});
cleanup = !this._events;
} else {
this.count--;
cleanup = this.count === 0;
}
if (cleanup) this.cleanup();
};
// Cleans up memory bindings between the listener and the listenee.
Listening.prototype.cleanup = function() {
delete this.listener._listeningTo[this.obj._listenId];
if (!this.interop) delete this.obj._listeners[this.id];
};
// Aliases for backwards compatibility. // Aliases for backwards compatibility.
Events.bind = Events.on; Events.bind = Events.on;
Events.unbind = Events.off; Events.unbind = Events.off;
@ -394,6 +398,7 @@
var Model = Backbone.Model = function(attributes, options) { var Model = Backbone.Model = function(attributes, options) {
var attrs = attributes || {}; var attrs = attributes || {};
options || (options = {}); options || (options = {});
this.preinitialize.apply(this, arguments);
this.cid = _.uniqueId(this.cidPrefix); this.cid = _.uniqueId(this.cidPrefix);
this.attributes = {}; this.attributes = {};
if (options.collection) this.collection = options.collection; if (options.collection) this.collection = options.collection;
@ -422,6 +427,10 @@
// You may want to override this if you're experiencing name clashes with model ids. // You may want to override this if you're experiencing name clashes with model ids.
cidPrefix: 'c', cidPrefix: 'c',
// preinitialize is an empty function by default. You can override it with a function
// or object. preinitialize will run before any instantiation logic is run in the Model.
preinitialize: function(){},
// Initialize is an empty function by default. Override it with your own // Initialize is an empty function by default. Override it with your own
// initialization logic. // initialization logic.
initialize: function(){}, initialize: function(){},
@ -562,12 +571,14 @@
if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
var old = this._changing ? this._previousAttributes : this.attributes; var old = this._changing ? this._previousAttributes : this.attributes;
var changed = {}; var changed = {};
var hasChanged;
for (var attr in diff) { for (var attr in diff) {
var val = diff[attr]; var val = diff[attr];
if (_.isEqual(old[attr], val)) continue; if (_.isEqual(old[attr], val)) continue;
changed[attr] = val; changed[attr] = val;
hasChanged = true;
} }
return _.size(changed) ? changed : false; return hasChanged ? changed : false;
}, },
// Get the previous value of an attribute, recorded at the time the last // Get the previous value of an attribute, recorded at the time the last
@ -643,7 +654,7 @@
// Set temporary attributes if `{wait: true}` to properly find new ids. // Set temporary attributes if `{wait: true}` to properly find new ids.
if (attrs && wait) this.attributes = _.extend({}, attributes, attrs); if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
if (method === 'patch' && !options.attrs) options.attrs = attrs; if (method === 'patch' && !options.attrs) options.attrs = attrs;
var xhr = this.sync(method, this, options); var xhr = this.sync(method, this, options);
@ -731,14 +742,6 @@
}); });
// 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,
omit: 0, chain: 1, isEmpty: 1};
// Mix in each Underscore method as a proxy to `Model#attributes`.
addUnderscoreMethods(Model, modelMethods, 'attributes');
// Backbone.Collection // Backbone.Collection
// ------------------- // -------------------
@ -754,6 +757,7 @@
// its models in sort order, as they're added and removed. // its models in sort order, as they're added and removed.
var Collection = Backbone.Collection = function(models, options) { var Collection = Backbone.Collection = function(models, options) {
options || (options = {}); options || (options = {});
this.preinitialize.apply(this, arguments);
if (options.model) this.model = options.model; if (options.model) this.model = options.model;
if (options.comparator !== void 0) this.comparator = options.comparator; if (options.comparator !== void 0) this.comparator = options.comparator;
this._reset(); this._reset();
@ -783,6 +787,11 @@
// This should be overridden in most cases. // This should be overridden in most cases.
model: Model, model: Model,
// preinitialize is an empty function by default. You can override it with a function
// or object. preinitialize will run before any instantiation logic is run in the Collection.
preinitialize: function(){},
// Initialize is an empty function by default. Override it with your own // Initialize is an empty function by default. Override it with your own
// initialization logic. // initialization logic.
initialize: function(){}, initialize: function(){},
@ -985,7 +994,7 @@
get: function(obj) { get: function(obj) {
if (obj == null) return void 0; if (obj == null) return void 0;
return this._byId[obj] || return this._byId[obj] ||
this._byId[this.modelId(obj.attributes || obj)] || this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] ||
obj.cid && this._byId[obj.cid]; obj.cid && this._byId[obj.cid];
}, },
@ -1021,7 +1030,7 @@
options || (options = {}); options || (options = {});
var length = comparator.length; var length = comparator.length;
if (_.isFunction(comparator)) comparator = _.bind(comparator, this); if (_.isFunction(comparator)) comparator = comparator.bind(this);
// Run sort based on type of `comparator`. // Run sort based on type of `comparator`.
if (length === 1 || _.isString(comparator)) { if (length === 1 || _.isString(comparator)) {
@ -1093,6 +1102,21 @@
return attrs[this.model.prototype.idAttribute || 'id']; return attrs[this.model.prototype.idAttribute || 'id'];
}, },
// Get an iterator of all models in this collection.
values: function() {
return new CollectionIterator(this, ITERATOR_VALUES);
},
// Get an iterator of all model IDs in this collection.
keys: function() {
return new CollectionIterator(this, ITERATOR_KEYS);
},
// Get an iterator of all [ID, model] tuples in this collection.
entries: function() {
return new CollectionIterator(this, ITERATOR_KEYSVALUES);
},
// Private method to reset all internal state. Called when the collection // Private method to reset all internal state. Called when the collection
// is first initialized or reset. // is first initialized or reset.
_reset: function() { _reset: function() {
@ -1189,20 +1213,71 @@
}); });
// Underscore methods that we want to implement on the Collection. // Defining an @@iterator method implements JavaScript's Iterable protocol.
// 90% of the core usefulness of Backbone Collections is actually implemented // In modern ES2015 browsers, this value is found at Symbol.iterator.
// right here: /* global Symbol */
var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0, var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3, if ($$iterator) {
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3, Collection.prototype[$$iterator] = Collection.prototype.values;
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,
without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
// Mix in each Underscore method as a proxy to `Collection#models`. // CollectionIterator
addUnderscoreMethods(Collection, collectionMethods, 'models'); // ------------------
// A CollectionIterator implements JavaScript's Iterator protocol, allowing the
// use of `for of` loops in modern browsers and interoperation between
// Backbone.Collection and other JavaScript functions and third-party libraries
// which can operate on Iterables.
var CollectionIterator = function(collection, kind) {
this._collection = collection;
this._kind = kind;
this._index = 0;
};
// This "enum" defines the three possible kinds of values which can be emitted
// by a CollectionIterator that correspond to the values(), keys() and entries()
// methods on Collection, respectively.
var ITERATOR_VALUES = 1;
var ITERATOR_KEYS = 2;
var ITERATOR_KEYSVALUES = 3;
// All Iterators should themselves be Iterable.
if ($$iterator) {
CollectionIterator.prototype[$$iterator] = function() {
return this;
};
}
CollectionIterator.prototype.next = function() {
if (this._collection) {
// Only continue iterating if the iterated collection is long enough.
if (this._index < this._collection.length) {
var model = this._collection.at(this._index);
this._index++;
// Construct a value depending on what kind of values should be iterated.
var value;
if (this._kind === ITERATOR_VALUES) {
value = model;
} else {
var id = this._collection.modelId(model.attributes);
if (this._kind === ITERATOR_KEYS) {
value = id;
} else { // ITERATOR_KEYSVALUES
value = [id, model];
}
}
return {value: value, done: false};
}
// Once exhausted, remove the reference to the collection so future
// calls to the next method always return done.
this._collection = void 0;
}
return {value: void 0, done: true};
};
// Backbone.View // Backbone.View
// ------------- // -------------
@ -1219,6 +1294,7 @@
// if an existing element is not provided... // if an existing element is not provided...
var View = Backbone.View = function(options) { var View = Backbone.View = function(options) {
this.cid = _.uniqueId('view'); this.cid = _.uniqueId('view');
this.preinitialize.apply(this, arguments);
_.extend(this, _.pick(options, viewOptions)); _.extend(this, _.pick(options, viewOptions));
this._ensureElement(); this._ensureElement();
this.initialize.apply(this, arguments); this.initialize.apply(this, arguments);
@ -1242,6 +1318,10 @@
return this.$el.find(selector); return this.$el.find(selector);
}, },
// preinitialize is an empty function by default. You can override it with a function
// or object. preinitialize will run before any instantiation logic is run in the View
preinitialize: function(){},
// Initialize is an empty function by default. Override it with your own // Initialize is an empty function by default. Override it with your own
// initialization logic. // initialization logic.
initialize: function(){}, initialize: function(){},
@ -1309,7 +1389,7 @@
if (!_.isFunction(method)) method = this[method]; if (!_.isFunction(method)) method = this[method];
if (!method) continue; if (!method) continue;
var match = key.match(delegateEventSplitter); var match = key.match(delegateEventSplitter);
this.delegate(match[1], match[2], _.bind(method, this)); this.delegate(match[1], match[2], method.bind(this));
} }
return this; return this;
}, },
@ -1367,6 +1447,94 @@
}); });
// Proxy Backbone class methods to Underscore functions, wrapping the model's
// `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(base, length, method, attribute) {
switch (length) {
case 1: return function() {
return base[method](this[attribute]);
};
case 2: return function(value) {
return base[method](this[attribute], value);
};
case 3: return function(iteratee, context) {
return base[method](this[attribute], cb(iteratee, this), context);
};
case 4: return function(iteratee, defaultVal, context) {
return base[method](this[attribute], cb(iteratee, this), defaultVal, context);
};
default: return function() {
var args = slice.call(arguments);
args.unshift(this[attribute]);
return base[method].apply(base, args);
};
}
};
var addUnderscoreMethods = function(Class, base, methods, attribute) {
_.each(methods, function(length, method) {
if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute);
});
};
// 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);
};
};
// Underscore methods that we want to implement on the Collection.
// 90% of the core usefulness of Backbone Collections is actually implemented
// right here:
var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 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,
without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
// 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,
omit: 0, chain: 1, isEmpty: 1};
// Mix in each Underscore method as a proxy to `Collection#models`.
_.each([
[Collection, collectionMethods, 'models'],
[Model, modelMethods, 'attributes']
], function(config) {
var Base = config[0],
methods = config[1],
attribute = config[2];
Base.mixin = function(obj) {
var mappings = _.reduce(_.functions(obj), function(memo, name) {
memo[name] = 0;
return memo;
}, {});
addUnderscoreMethods(Base, obj, mappings, attribute);
};
addUnderscoreMethods(Base, _, methods, attribute);
});
// Backbone.sync // Backbone.sync
// ------------- // -------------
@ -1447,11 +1615,11 @@
// Map from CRUD to HTTP for our default `Backbone.sync` implementation. // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
var methodMap = { var methodMap = {
'create': 'POST', create: 'POST',
'update': 'PUT', update: 'PUT',
'patch': 'PATCH', patch: 'PATCH',
'delete': 'DELETE', delete: 'DELETE',
'read': 'GET' read: 'GET'
}; };
// Set the default implementation of `Backbone.ajax` to proxy through to `$`. // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
@ -1467,6 +1635,7 @@
// matched. Creating a new one sets its `routes` hash, if not set statically. // matched. Creating a new one sets its `routes` hash, if not set statically.
var Router = Backbone.Router = function(options) { var Router = Backbone.Router = function(options) {
options || (options = {}); options || (options = {});
this.preinitialize.apply(this, arguments);
if (options.routes) this.routes = options.routes; if (options.routes) this.routes = options.routes;
this._bindRoutes(); this._bindRoutes();
this.initialize.apply(this, arguments); this.initialize.apply(this, arguments);
@ -1482,6 +1651,10 @@
// Set up all inheritable **Backbone.Router** properties and methods. // Set up all inheritable **Backbone.Router** properties and methods.
_.extend(Router.prototype, Events, { _.extend(Router.prototype, Events, {
// preinitialize is an empty function by default. You can override it with a function
// or object. preinitialize will run before any instantiation logic is run in the Router.
preinitialize: function(){},
// Initialize is an empty function by default. Override it with your own // Initialize is an empty function by default. Override it with your own
// initialization logic. // initialization logic.
initialize: function(){}, initialize: function(){},
@ -1539,11 +1712,11 @@
// against the current location hash. // against the current location hash.
_routeToRegExp: function(route) { _routeToRegExp: function(route) {
route = route.replace(escapeRegExp, '\\$&') route = route.replace(escapeRegExp, '\\$&')
.replace(optionalParam, '(?:$1)?') .replace(optionalParam, '(?:$1)?')
.replace(namedParam, function(match, optional) { .replace(namedParam, function(match, optional) {
return optional ? match : '([^/?]+)'; return optional ? match : '([^/?]+)';
}) })
.replace(splatParam, '([^?]*?)'); .replace(splatParam, '([^?]*?)');
return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$'); return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
}, },
@ -1571,7 +1744,7 @@
// falls back to polling. // falls back to polling.
var History = Backbone.History = function() { var History = Backbone.History = function() {
this.handlers = []; this.handlers = [];
this.checkUrl = _.bind(this.checkUrl, this); this.checkUrl = this.checkUrl.bind(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') {
@ -1812,11 +1985,14 @@
} }
var url = rootPath + fragment; var url = rootPath + fragment;
// Strip the hash and decode for matching. // Strip the fragment of the query and hash for matching.
fragment = this.decodeFragment(fragment.replace(pathStripper, '')); fragment = fragment.replace(pathStripper, '');
if (this.fragment === fragment) return; // Decode for matching.
this.fragment = fragment; var decodedFragment = this.decodeFragment(fragment);
if (this.fragment === decodedFragment) return;
this.fragment = decodedFragment;
// If pushState is available, we use it to set the fragment as a real URL. // If pushState is available, we use it to set the fragment as a real URL.
if (this._usePushState) { if (this._usePushState) {

File diff suppressed because one or more lines are too long

View File

@ -891,7 +891,7 @@ MediaDetails = AttachmentDisplay.extend(/** @lends wp.media.view.MediaDetails.pr
initialize: function() { initialize: function() {
_.bindAll(this, 'success'); _.bindAll(this, 'success');
this.players = []; this.players = [];
this.listenTo( this.controller, 'close', wp.media.mixin.unsetPlayers ); this.listenTo( this.controller.states, 'close', wp.media.mixin.unsetPlayers );
this.on( 'ready', this.setPlayer ); this.on( 'ready', this.setPlayer );
this.on( 'media:setting:remove', wp.media.mixin.unsetPlayers, this ); this.on( 'media:setting:remove', wp.media.mixin.unsetPlayers, this );
this.on( 'media:setting:remove', this.render ); this.on( 'media:setting:remove', this.render );

File diff suppressed because one or more lines are too long

View File

@ -451,17 +451,14 @@ module.exports = Region;
* @augments Backbone.Model * @augments Backbone.Model
* @mixin * @mixin
* @mixes Backbone.Events * @mixes Backbone.Events
*
* @param {Array} states
*/ */
var StateMachine = function( states ) { var StateMachine = function() {
// @todo This is dead code. The states collection gets created in media.view.Frame._createStates. return {
this.states = new Backbone.Collection( states ); // Use Backbone's self-propagating `extend` inheritance method.
extend: Backbone.Model.extend
};
}; };
// Use Backbone's self-propagating `extend` inheritance method.
StateMachine.extend = Backbone.Model.extend;
_.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{ _.extend( StateMachine.prototype, Backbone.Events,/** @lends wp.media.controller.StateMachine.prototype */{
/** /**
* Fetch a state. * Fetch a state.
@ -6247,7 +6244,7 @@ Attachment = View.extend(/** @lends wp.media.view.Attachment.prototype */{
this.details( this.model, this.controller.state().get('selection') ); this.details( this.model, this.controller.state().get('selection') );
} }
this.listenTo( this.controller, 'attachment:compat:waiting attachment:compat:ready', this.updateSave ); this.listenTo( this.controller.states, 'attachment:compat:waiting attachment:compat:ready', this.updateSave );
}, },
/** /**
* @returns {wp.media.view.Attachment} Returns itself to allow chaining * @returns {wp.media.view.Attachment} Returns itself to allow chaining

File diff suppressed because one or more lines are too long

View File

@ -1170,7 +1170,7 @@ function wp_default_scripts( &$scripts ) {
did_action( 'init' ) && $scripts->add_data( 'json2', 'conditional', 'lt IE 8' ); did_action( 'init' ) && $scripts->add_data( 'json2', 'conditional', 'lt IE 8' );
$scripts->add( 'underscore', "/wp-includes/js/underscore$dev_suffix.js", array(), '1.8.3', 1 ); $scripts->add( 'underscore', "/wp-includes/js/underscore$dev_suffix.js", array(), '1.8.3', 1 );
$scripts->add( 'backbone', "/wp-includes/js/backbone$dev_suffix.js", array( 'underscore', 'jquery' ), '1.2.3', 1 ); $scripts->add( 'backbone', "/wp-includes/js/backbone$dev_suffix.js", array( 'underscore', 'jquery' ), '1.4.0', 1 );
$scripts->add( 'wp-util', "/wp-includes/js/wp-util$suffix.js", array( 'underscore', 'jquery' ), false, 1 ); $scripts->add( 'wp-util', "/wp-includes/js/wp-util$suffix.js", array( 'underscore', 'jquery' ), false, 1 );
did_action( 'init' ) && $scripts->localize( did_action( 'init' ) && $scripts->localize(

View File

@ -13,7 +13,7 @@
* *
* @global string $wp_version * @global string $wp_version
*/ */
$wp_version = '5.3-alpha-46156'; $wp_version = '5.3-alpha-46157';
/** /**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema. * Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.