Update to Backbone 1.4.0 and use fork of Backbone.NativeView

with `_setElement` fix.

See https://github.com/akre54/Backbone.NativeView/pull/29
This commit is contained in:
JC Brand 2019-03-01 08:56:46 +01:00
parent 310b2c723b
commit b15ebdde40
5 changed files with 714 additions and 510 deletions

View File

@ -2,6 +2,8 @@
## 4.1.3 (Unreleased) ## 4.1.3 (Unreleased)
- Upgrade to Backbone 1.4.0
- Fix "flashing" of roster filter when you have less than 5 roster contacts.
- Allow setting of debug mode via URL with `/#converse?debug=true` - Allow setting of debug mode via URL with `/#converse?debug=true`
- New config setting [locked_muc_domain](https://conversejs.org/docs/html/configuration.html#locked-muc-domain) - New config setting [locked_muc_domain](https://conversejs.org/docs/html/configuration.html#locked-muc-domain)
- New config setting [show_client_info](https://conversejs.org/docs/html/configuration.html#show-client-info) - New config setting [show_client_info](https://conversejs.org/docs/html/configuration.html#show-client-info)

474
dist/converse.js vendored
View File

@ -893,12 +893,12 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
// https://github.com/akre54/Backbone.NativeView // https://github.com/akre54/Backbone.NativeView
(function (factory) { (function (factory) {
if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory), if (true) { !(__WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(/*! underscore */ "./src/underscore-shim.js"), __webpack_require__(/*! backbone */ "./node_modules/backbone/backbone.js")], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ? __WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__), (__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)); __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
} else {} } else {}
}(function (Backbone) { }(function (_, Backbone) {
// Cached regex to match an opening '<' of an HTML tag, possibly left-padded // Cached regex to match an opening '<' of an HTML tag, possibly left-padded
// with whitespace. // with whitespace.
var paddedLt = /^\s*</; var paddedLt = /^\s*</;
@ -907,10 +907,15 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
var ElementProto = (typeof Element !== 'undefined' && Element.prototype) || {}; var ElementProto = (typeof Element !== 'undefined' && Element.prototype) || {};
// Cross-browser event listener shims // Cross-browser event listener shims
var elementAddEventListener = ElementProto.addEventListener || function(eventName, listener) { var elementAddEventListener = ElementProto.addEventListener ? function(eventName, listener) {
return this.addEventListener(eventName, listener, false);
} : function(eventName, listener) {
return this.attachEvent('on' + eventName, listener); return this.attachEvent('on' + eventName, listener);
} }
var elementRemoveEventListener = ElementProto.removeEventListener || function(eventName, listener) {
var elementRemoveEventListener = ElementProto.removeEventListener ? function(eventName, listener) {
return this.removeEventListener(eventName, listener, false);
} : function(eventName, listener) {
return this.detachEvent('on' + eventName, listener); return this.detachEvent('on' + eventName, listener);
} }
@ -959,7 +964,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
}, },
// Apply the `element` to the view. `element` can be a CSS selector, // Apply the `element` to the view. `element` can be a CSS selector,
// a string of HTML, or an Element node. // a string of HTML, or an Element node. If passed a NodeList or CSS
// selector, uses just the first match.
_setElement: function(element) { _setElement: function(element) {
if (typeof element == 'string') { if (typeof element == 'string') {
if (paddedLt.test(element)) { if (paddedLt.test(element)) {
@ -969,6 +975,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
} else { } else {
this.el = document.querySelector(element); this.el = document.querySelector(element);
} }
} else if (element && !_.isElement(element) && element.length) {
this.el = element[0];
} else { } else {
this.el = element; this.el = element;
} }
@ -991,12 +999,28 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
// result of calling bound `listener` with the parameters given to the // result of calling bound `listener` with the parameters given to the
// handler. // handler.
delegate: function(eventName, selector, listener) { delegate: function(eventName, selector, listener) {
var root = this.el;
if (!root) {
return;
}
if (typeof selector === 'function') { if (typeof selector === 'function') {
listener = selector; listener = selector;
selector = null; selector = null;
} }
var root = this.el; // Given that `focus` and `blur` events do not bubble, do not delegate these events
if (['focus', 'blur'].indexOf(eventName) !== -1) {
var els = this.el.querySelectorAll(selector);
for (var i = 0, len = els.length; i < len; i++) {
var item = els[i];
elementAddEventListener.call(item, eventName, listener, false);
this._domEvents.push({el: item, eventName: eventName, handler: listener});
}
return listener;
}
var handler = selector ? function (e) { var handler = selector ? function (e) {
var node = e.target || e.srcElement; var node = e.target || e.srcElement;
for (; node && node != root; node = node.parentNode) { for (; node && node != root; node = node.parentNode) {
@ -1008,7 +1032,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
} : listener; } : listener;
elementAddEventListener.call(this.el, eventName, handler, false); elementAddEventListener.call(this.el, eventName, handler, false);
this._domEvents.push({eventName: eventName, handler: handler, listener: listener, selector: selector}); this._domEvents.push({el: this.el, eventName: eventName, handler: handler, listener: listener, selector: selector});
return handler; return handler;
}, },
@ -1022,7 +1046,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
if (this.el) { if (this.el) {
var handlers = this._domEvents.slice(); var handlers = this._domEvents.slice();
for (var i = 0, len = handlers.length; i < len; i++) { var i = handlers.length;
while (i--) {
var item = handlers[i]; var item = handlers[i];
var match = item.eventName === eventName && var match = item.eventName === eventName &&
@ -1031,8 +1056,8 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
if (!match) continue; if (!match) continue;
elementRemoveEventListener.call(this.el, item.eventName, item.handler, false); elementRemoveEventListener.call(item.el, item.eventName, item.handler, false);
this._domEvents.splice(indexOf(handlers, item), 1); this._domEvents.splice(i, 1);
} }
} }
return this; return this;
@ -1043,7 +1068,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
if (this.el) { if (this.el) {
for (var i = 0, len = this._domEvents.length; i < len; i++) { for (var i = 0, len = this._domEvents.length; i < len; i++) {
var item = this._domEvents[i]; var item = this._domEvents[i];
elementRemoveEventListener.call(this.el, item.eventName, item.handler, false); elementRemoveEventListener.call(item.el, item.eventName, item.handler, false);
}; };
this._domEvents.length = 0; this._domEvents.length = 0;
} }
@ -1057,6 +1082,7 @@ var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_
})); }));
/***/ }), /***/ }),
/***/ "./node_modules/backbone.overview/backbone.orderedlistview.js": /***/ "./node_modules/backbone.overview/backbone.orderedlistview.js":
@ -1478,9 +1504,9 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
/*! no static exports found */ /*! no static exports found */
/***/ (function(module, exports, __webpack_require__) { /***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// Backbone.js 1.3.3 /* WEBPACK VAR INJECTION */(function(global) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// 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
@ -1489,8 +1515,8 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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 (true) { if (true) {
@ -1517,7 +1543,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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.
@ -1541,54 +1567,6 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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
// --------------- // ---------------
@ -1607,6 +1585,9 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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}`).
@ -1633,23 +1614,21 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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
@ -1659,17 +1638,23 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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;
}; };
@ -1685,6 +1670,16 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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
@ -1695,6 +1690,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
context: context, context: context,
listeners: this._listeners listeners: this._listeners
}); });
return this; return this;
}; };
@ -1705,7 +1701,6 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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]];
@ -1714,7 +1709,9 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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;
}; };
@ -1723,21 +1720,18 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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];
@ -1745,7 +1739,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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];
@ -1756,21 +1750,19 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
) { ) {
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;
}; };
@ -1780,7 +1772,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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);
}; };
@ -1788,7 +1780,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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);
}; };
@ -1846,6 +1838,44 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
} }
}; };
// 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;
@ -1867,6 +1897,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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;
@ -1895,6 +1926,10 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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(){},
@ -2035,12 +2070,14 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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
@ -2116,7 +2153,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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);
@ -2204,14 +2241,6 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
}); });
// 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
// ------------------- // -------------------
@ -2227,6 +2256,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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();
@ -2256,6 +2286,11 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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(){},
@ -2458,7 +2493,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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];
}, },
@ -2494,7 +2529,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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)) {
@ -2566,6 +2601,21 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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() {
@ -2662,20 +2712,71 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
}); });
// 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
// ------------- // -------------
@ -2692,6 +2793,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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);
@ -2715,6 +2817,10 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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(){},
@ -2782,7 +2888,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
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;
}, },
@ -2840,6 +2946,94 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
}); });
// 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
// ------------- // -------------
@ -2920,11 +3114,11 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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 `$`.
@ -2940,6 +3134,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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);
@ -2955,6 +3150,10 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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(){},
@ -3044,7 +3243,7 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
// 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') {
@ -3285,11 +3484,14 @@ backbone.nativeview = __webpack_require__(/*! backbone.nativeview */ "./node_mod
} }
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) {

732
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -39,7 +39,7 @@
"@fortawesome/fontawesome-free": "5.3.1", "@fortawesome/fontawesome-free": "5.3.1",
"awesomplete-avoid-xss": "^1.1.2", "awesomplete-avoid-xss": "^1.1.2",
"babel-loader": "^8.0.4", "babel-loader": "^8.0.4",
"backbone.nativeview": "^0.3.3", "backbone.nativeview": "conversejs/Backbone.NativeView#5997c8197ca594e6b8469447f28310c78bd1d95e",
"backbone.overview": "^1.0.2", "backbone.overview": "^1.0.2",
"backbone.vdomview": "^1.0.1", "backbone.vdomview": "^1.0.1",
"bootstrap": "^4.0.0", "bootstrap": "^4.0.0",
@ -63,7 +63,7 @@
"jed": "1.1.1", "jed": "1.1.1",
"jquery": "3.2.1", "jquery": "3.2.1",
"jsdoc": "^3.5.5", "jsdoc": "^3.5.5",
"lerna": "^3.11.1", "lerna": "^3.13.1",
"lodash-template-loader": "^2.0.0", "lodash-template-loader": "^2.0.0",
"lodash-template-webpack-loader": "jcbrand/lodash-template-webpack-loader", "lodash-template-webpack-loader": "jcbrand/lodash-template-webpack-loader",
"long": "^3.1.0", "long": "^3.1.0",

View File

@ -22,7 +22,7 @@
}, },
"gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2", "gitHead": "9641dcdc820e029b05930479c242d2b707bbe8e2",
"devDependencies": { "devDependencies": {
"backbone": "1.3.3", "backbone": "1.4",
"backbone.browserStorage": "0.0.5", "backbone.browserStorage": "0.0.5",
"es6-promise": "^4.1.0", "es6-promise": "^4.1.0",
"filesize": "^3.6.1", "filesize": "^3.6.1",