mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +00:00
Update deps.
This commit is contained in:
298
vendor/backbone/backbone.js
vendored
298
vendor/backbone/backbone.js
vendored
@@ -36,7 +36,9 @@
|
||||
|
||||
// Create local references to array methods we'll want to use later.
|
||||
var array = [];
|
||||
var push = array.push;
|
||||
var slice = array.slice;
|
||||
var splice = array.splice;
|
||||
|
||||
// Current version of the library. Keep in sync with `package.json`.
|
||||
Backbone.VERSION = '1.1.2';
|
||||
@@ -106,46 +108,27 @@
|
||||
// callbacks for the event. If `name` is null, removes all bound
|
||||
// callbacks for all events.
|
||||
off: function(name, callback, context) {
|
||||
var retain, ev, events, names, i, l, j, k;
|
||||
if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
|
||||
|
||||
// Remove all callbacks for all events.
|
||||
if (!name && !callback && !context) {
|
||||
this._events = void 0;
|
||||
return this;
|
||||
}
|
||||
|
||||
var names = name ? [name] : _.keys(this._events);
|
||||
for (var i = 0, length = names.length; i < length; i++) {
|
||||
names = name ? [name] : _.keys(this._events);
|
||||
for (i = 0, l = names.length; i < l; i++) {
|
||||
name = names[i];
|
||||
|
||||
// Bail out if there are no events stored.
|
||||
var events = this._events[name];
|
||||
if (!events) continue;
|
||||
|
||||
// Remove all callbacks for this event.
|
||||
if (!callback && !context) {
|
||||
delete this._events[name];
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find any remaining events.
|
||||
var remaining = [];
|
||||
for (var j = 0, k = events.length; j < k; j++) {
|
||||
var event = events[j];
|
||||
if (
|
||||
callback && callback !== event.callback &&
|
||||
callback !== event.callback._callback ||
|
||||
context && context !== event.context
|
||||
) {
|
||||
remaining.push(event);
|
||||
if (events = this._events[name]) {
|
||||
this._events[name] = retain = [];
|
||||
if (callback || context) {
|
||||
for (j = 0, k = events.length; j < k; j++) {
|
||||
ev = events[j];
|
||||
if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
|
||||
(context && context !== ev.context)) {
|
||||
retain.push(ev);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace events if there are any remaining. Otherwise, clean up.
|
||||
if (remaining.length) {
|
||||
this._events[name] = remaining;
|
||||
} else {
|
||||
delete this._events[name];
|
||||
}
|
||||
if (!retain.length) delete this._events[name];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,7 +188,7 @@
|
||||
// Handle space separated event names.
|
||||
if (eventSplitter.test(name)) {
|
||||
var names = name.split(eventSplitter);
|
||||
for (var i = 0, length = names.length; i < length; i++) {
|
||||
for (var i = 0, l = names.length; i < l; i++) {
|
||||
obj[action].apply(obj, [names[i]].concat(rest));
|
||||
}
|
||||
return false;
|
||||
@@ -370,7 +353,7 @@
|
||||
// Trigger all relevant attribute changes.
|
||||
if (!silent) {
|
||||
if (changes.length) this._pending = options;
|
||||
for (var i = 0, length = changes.length; i < length; i++) {
|
||||
for (var i = 0, l = changes.length; i < l; i++) {
|
||||
this.trigger('change:' + changes[i], this, current[changes[i]], options);
|
||||
}
|
||||
}
|
||||
@@ -595,7 +578,6 @@
|
||||
|
||||
// Mix in each Underscore method as a proxy to `Model#attributes`.
|
||||
_.each(modelMethods, function(method) {
|
||||
if (!_[method]) return;
|
||||
Model.prototype[method] = function() {
|
||||
var args = slice.call(arguments);
|
||||
args.unshift(this.attributes);
|
||||
@@ -607,7 +589,7 @@
|
||||
// -------------------
|
||||
|
||||
// If models tend to represent a single row of data, a Backbone Collection is
|
||||
// more analogous to a table full of data ... or a small slice or page of that
|
||||
// more analagous to a table full of data ... or a small slice or page of that
|
||||
// table, or a collection of rows that belong together for a particular reason
|
||||
// -- all of the messages in this particular folder, all of the documents
|
||||
// belonging to this particular author, and so on. Collections maintain
|
||||
@@ -661,12 +643,13 @@
|
||||
var singular = !_.isArray(models);
|
||||
models = singular ? [models] : _.clone(models);
|
||||
options || (options = {});
|
||||
for (var i = 0, length = models.length; i < length; i++) {
|
||||
var model = models[i] = this.get(models[i]);
|
||||
var i, l, index, model;
|
||||
for (i = 0, l = models.length; i < l; i++) {
|
||||
model = models[i] = this.get(models[i]);
|
||||
if (!model) continue;
|
||||
delete this._byId[model.id];
|
||||
delete this._byId[model.cid];
|
||||
var index = this.indexOf(model);
|
||||
index = this.indexOf(model);
|
||||
this.models.splice(index, 1);
|
||||
this.length--;
|
||||
if (!options.silent) {
|
||||
@@ -686,9 +669,10 @@
|
||||
options = _.defaults({}, options, setOptions);
|
||||
if (options.parse) models = this.parse(models, options);
|
||||
var singular = !_.isArray(models);
|
||||
models = singular ? (models ? [models] : []) : models.slice();
|
||||
var id, model, attrs, existing, sort;
|
||||
models = singular ? (models ? [models] : []) : _.clone(models);
|
||||
var i, l, id, model, attrs, existing, sort;
|
||||
var at = options.at;
|
||||
var targetModel = this.model;
|
||||
var sortable = this.comparator && (at == null) && options.sort !== false;
|
||||
var sortAttr = _.isString(this.comparator) ? this.comparator : null;
|
||||
var toAdd = [], toRemove = [], modelMap = {};
|
||||
@@ -697,12 +681,12 @@
|
||||
|
||||
// Turn bare objects into model references, and prevent invalid models
|
||||
// from being added.
|
||||
for (var i = 0, length = models.length; i < length; i++) {
|
||||
for (i = 0, l = models.length; i < l; i++) {
|
||||
attrs = models[i] || {};
|
||||
if (this._isModel(attrs)) {
|
||||
if (attrs instanceof Model) {
|
||||
id = model = attrs;
|
||||
} else {
|
||||
id = attrs[this.model.prototype.idAttribute || 'id'];
|
||||
id = attrs[targetModel.prototype.idAttribute || 'id'];
|
||||
}
|
||||
|
||||
// If a duplicate is found, prevent it from being added and
|
||||
@@ -727,14 +711,13 @@
|
||||
|
||||
// Do not add multiple models with the same `id`.
|
||||
model = existing || model;
|
||||
if (!model) continue;
|
||||
if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
|
||||
modelMap[model.id] = true;
|
||||
}
|
||||
|
||||
// Remove nonexistent models if appropriate.
|
||||
if (remove) {
|
||||
for (var i = 0, length = this.length; i < length; i++) {
|
||||
for (i = 0, l = this.length; i < l; ++i) {
|
||||
if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
|
||||
}
|
||||
if (toRemove.length) this.remove(toRemove, options);
|
||||
@@ -745,13 +728,13 @@
|
||||
if (sortable) sort = true;
|
||||
this.length += toAdd.length;
|
||||
if (at != null) {
|
||||
for (var i = 0, length = toAdd.length; i < length; i++) {
|
||||
for (i = 0, l = toAdd.length; i < l; i++) {
|
||||
this.models.splice(at + i, 0, toAdd[i]);
|
||||
}
|
||||
} else {
|
||||
if (order) this.models.length = 0;
|
||||
var orderedModels = order || toAdd;
|
||||
for (var i = 0, length = orderedModels.length; i < length; i++) {
|
||||
for (i = 0, l = orderedModels.length; i < l; i++) {
|
||||
this.models.push(orderedModels[i]);
|
||||
}
|
||||
}
|
||||
@@ -762,7 +745,7 @@
|
||||
|
||||
// Unless silenced, it's time to fire all appropriate add/sort events.
|
||||
if (!options.silent) {
|
||||
for (var i = 0, length = toAdd.length; i < length; i++) {
|
||||
for (i = 0, l = toAdd.length; i < l; i++) {
|
||||
(model = toAdd[i]).trigger('add', model, this, options);
|
||||
}
|
||||
if (sort || (order && order.length)) this.trigger('sort', this, options);
|
||||
@@ -778,7 +761,7 @@
|
||||
// Useful for bulk operations and optimizations.
|
||||
reset: function(models, options) {
|
||||
options || (options = {});
|
||||
for (var i = 0, length = this.models.length; i < length; i++) {
|
||||
for (var i = 0, l = this.models.length; i < l; i++) {
|
||||
this._removeReference(this.models[i], options);
|
||||
}
|
||||
options.previousModels = this.models;
|
||||
@@ -912,10 +895,7 @@
|
||||
|
||||
// Create a new collection with an identical list of models as this one.
|
||||
clone: function() {
|
||||
return new this.constructor(this.models, {
|
||||
model: this.model,
|
||||
comparator: this.comparator
|
||||
});
|
||||
return new this.constructor(this.models);
|
||||
},
|
||||
|
||||
// Private method to reset all internal state. Called when the collection
|
||||
@@ -929,10 +909,7 @@
|
||||
// Prepare a hash of attributes (or other model) to be added to this
|
||||
// collection.
|
||||
_prepareModel: function(attrs, options) {
|
||||
if (this._isModel(attrs)) {
|
||||
if (!attrs.collection) attrs.collection = this;
|
||||
return attrs;
|
||||
}
|
||||
if (attrs instanceof Model) return attrs;
|
||||
options = options ? _.clone(options) : {};
|
||||
options.collection = this;
|
||||
var model = new this.model(attrs, options);
|
||||
@@ -941,16 +918,11 @@
|
||||
return false;
|
||||
},
|
||||
|
||||
// Method for checking whether an object should be considered a model for
|
||||
// the purposes of adding to the collection.
|
||||
_isModel: function (model) {
|
||||
return model instanceof Model;
|
||||
},
|
||||
|
||||
// Internal method to create a model's ties to a collection.
|
||||
_addReference: function(model, options) {
|
||||
this._byId[model.cid] = model;
|
||||
if (model.id != null) this._byId[model.id] = model;
|
||||
if (!model.collection) model.collection = this;
|
||||
model.on('all', this._onModelEvent, this);
|
||||
},
|
||||
|
||||
@@ -984,11 +956,10 @@
|
||||
'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
|
||||
'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
|
||||
'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
|
||||
'lastIndexOf', 'isEmpty', 'chain', 'sample', 'partition'];
|
||||
'lastIndexOf', 'isEmpty', 'chain', 'sample'];
|
||||
|
||||
// Mix in each Underscore method as a proxy to `Collection#models`.
|
||||
_.each(methods, function(method) {
|
||||
if (!_[method]) return;
|
||||
Collection.prototype[method] = function() {
|
||||
var args = slice.call(arguments);
|
||||
args.unshift(this.models);
|
||||
@@ -1001,7 +972,6 @@
|
||||
|
||||
// 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);
|
||||
@@ -1029,6 +999,7 @@
|
||||
_.extend(this, _.pick(options, viewOptions));
|
||||
this._ensureElement();
|
||||
this.initialize.apply(this, arguments);
|
||||
this.delegateEvents();
|
||||
};
|
||||
|
||||
// Cached regex to split keys for `delegate`.
|
||||
@@ -1063,35 +1034,19 @@
|
||||
// Remove this view by taking the element out of the DOM, and removing any
|
||||
// applicable Backbone.Events listeners.
|
||||
remove: function() {
|
||||
this._removeElement();
|
||||
this.$el.remove();
|
||||
this.stopListening();
|
||||
return this;
|
||||
},
|
||||
|
||||
// Remove this view's element from the document and all event listeners
|
||||
// attached to it. Exposed for subclasses using an alternative DOM
|
||||
// manipulation API.
|
||||
_removeElement: function() {
|
||||
this.$el.remove();
|
||||
},
|
||||
|
||||
// Change the view's element (`this.el` property) and re-delegate the
|
||||
// view's events on the new element.
|
||||
setElement: function(element) {
|
||||
this.undelegateEvents();
|
||||
this._setElement(element);
|
||||
this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
// Creates the `this.el` and `this.$el` references for this view using the
|
||||
// given `el` and a hash of `attributes`. `el` can be a CSS selector or an
|
||||
// HTML string, a jQuery context or an element. Subclasses can override
|
||||
// this to utilize an alternative DOM manipulation API and are only required
|
||||
// to set the `this.el` property.
|
||||
_setElement: function(el) {
|
||||
this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
|
||||
// Change the view's element (`this.el` property), including event
|
||||
// re-delegation.
|
||||
setElement: function(element, delegate) {
|
||||
if (this.$el) this.undelegateEvents();
|
||||
this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
|
||||
this.el = this.$el[0];
|
||||
if (delegate !== false) this.delegateEvents();
|
||||
return this;
|
||||
},
|
||||
|
||||
// Set callbacks, where `this.events` is a hash of
|
||||
@@ -1107,6 +1062,8 @@
|
||||
// pairs. Callbacks will be bound to the view, with `this` set properly.
|
||||
// Uses event delegation for efficiency.
|
||||
// Omitting the selector binds the event to `this.el`.
|
||||
// This only works for delegate-able events: not `focus`, `blur`, and
|
||||
// not `change`, `submit`, and `reset` in Internet Explorer.
|
||||
delegateEvents: function(events) {
|
||||
if (!(events || (events = _.result(this, 'events')))) return this;
|
||||
this.undelegateEvents();
|
||||
@@ -1114,39 +1071,28 @@
|
||||
var method = events[key];
|
||||
if (!_.isFunction(method)) method = this[events[key]];
|
||||
if (!method) continue;
|
||||
|
||||
var match = key.match(delegateEventSplitter);
|
||||
this.delegate(match[1], match[2], _.bind(method, this));
|
||||
var eventName = match[1], selector = match[2];
|
||||
method = _.bind(method, this);
|
||||
eventName += '.delegateEvents' + this.cid;
|
||||
if (selector === '') {
|
||||
this.$el.on(eventName, method);
|
||||
} else {
|
||||
this.$el.on(eventName, selector, method);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
},
|
||||
|
||||
// Add a single event listener to the view's element (or a child element
|
||||
// using `selector`). This only works for delegate-able events: not `focus`,
|
||||
// `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
|
||||
delegate: function(eventName, selector, listener) {
|
||||
this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
|
||||
},
|
||||
|
||||
// Clears all callbacks previously bound to the view by `delegateEvents`.
|
||||
// Clears all callbacks previously bound to the view with `delegateEvents`.
|
||||
// You usually don't need to use this, but may wish to if you have multiple
|
||||
// Backbone views attached to the same DOM element.
|
||||
undelegateEvents: function() {
|
||||
if (this.$el) this.$el.off('.delegateEvents' + this.cid);
|
||||
this.$el.off('.delegateEvents' + this.cid);
|
||||
return this;
|
||||
},
|
||||
|
||||
// A finer-grained `undelegateEvents` for removing a single delegated event.
|
||||
// `selector` and `listener` are both optional.
|
||||
undelegate: function(eventName, selector, listener) {
|
||||
this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
|
||||
},
|
||||
|
||||
// Produces a DOM element to be assigned to your view. Exposed for
|
||||
// subclasses using an alternative DOM manipulation API.
|
||||
_createElement: function(tagName) {
|
||||
return document.createElement(tagName);
|
||||
},
|
||||
|
||||
// Ensure that the View has a DOM element to render into.
|
||||
// If `this.el` is a string, pass it through `$()`, take the first
|
||||
// matching element, and re-assign it to `el`. Otherwise, create
|
||||
@@ -1156,17 +1102,11 @@
|
||||
var attrs = _.extend({}, _.result(this, 'attributes'));
|
||||
if (this.id) attrs.id = _.result(this, 'id');
|
||||
if (this.className) attrs['class'] = _.result(this, 'className');
|
||||
this.setElement(this._createElement(_.result(this, 'tagName')));
|
||||
this._setAttributes(attrs);
|
||||
var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
|
||||
this.setElement($el, false);
|
||||
} else {
|
||||
this.setElement(_.result(this, 'el'));
|
||||
this.setElement(_.result(this, 'el'), false);
|
||||
}
|
||||
},
|
||||
|
||||
// Set attributes from a hash on this view's element. Exposed for
|
||||
// subclasses using an alternative DOM manipulation API.
|
||||
_setAttributes: function(attributes) {
|
||||
this.$el.attr(attributes);
|
||||
}
|
||||
|
||||
});
|
||||
@@ -1244,14 +1184,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Pass along `textStatus` and `errorThrown` from jQuery.
|
||||
var error = options.error;
|
||||
options.error = function(xhr, textStatus, errorThrown) {
|
||||
options.textStatus = textStatus;
|
||||
options.errorThrown = errorThrown;
|
||||
if (error) error.apply(this, arguments);
|
||||
};
|
||||
|
||||
// Make the request, allowing the user to override any Ajax options.
|
||||
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
|
||||
model.trigger('request', model, xhr, options);
|
||||
@@ -1319,18 +1251,17 @@
|
||||
var router = this;
|
||||
Backbone.history.route(route, function(fragment) {
|
||||
var args = router._extractParameters(route, fragment);
|
||||
if (router.execute(callback, args, name) !== false) {
|
||||
router.execute(callback, args);
|
||||
router.trigger.apply(router, ['route:' + name].concat(args));
|
||||
router.trigger('route', name, args);
|
||||
Backbone.history.trigger('route', router, name, args);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
},
|
||||
|
||||
// Execute a route handler with the provided parameters. This is an
|
||||
// excellent place to do pre-route setup or post-route cleanup.
|
||||
execute: function(callback, args, name) {
|
||||
execute: function(callback, args) {
|
||||
if (callback) callback.apply(this, args);
|
||||
},
|
||||
|
||||
@@ -1403,6 +1334,12 @@
|
||||
// Cached regex for stripping leading and trailing slashes.
|
||||
var rootStripper = /^\/+|\/+$/g;
|
||||
|
||||
// Cached regex for detecting MSIE.
|
||||
var isExplorer = /msie [\w.]+/;
|
||||
|
||||
// Cached regex for removing a trailing slash.
|
||||
var trailingSlash = /\/$/;
|
||||
|
||||
// Cached regex for stripping urls of hash.
|
||||
var pathStripper = /#.*$/;
|
||||
|
||||
@@ -1418,8 +1355,7 @@
|
||||
|
||||
// Are we at the app root?
|
||||
atRoot: function() {
|
||||
var path = this.location.pathname.replace(/[^\/]$/, '$&/');
|
||||
return path === this.root && !this.location.search;
|
||||
return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
|
||||
},
|
||||
|
||||
// Gets the true hash value. Cannot use location.hash directly due to bug
|
||||
@@ -1429,19 +1365,14 @@
|
||||
return match ? match[1] : '';
|
||||
},
|
||||
|
||||
// Get the pathname and search params, without the root.
|
||||
getPath: function() {
|
||||
var path = decodeURI(this.location.pathname + this.location.search);
|
||||
var root = this.root.slice(0, -1);
|
||||
if (!path.indexOf(root)) path = path.slice(root.length);
|
||||
return path.slice(1);
|
||||
},
|
||||
|
||||
// Get the cross-browser normalized URL fragment from the path or hash.
|
||||
getFragment: function(fragment) {
|
||||
// Get the cross-browser normalized URL fragment, either from the URL,
|
||||
// the hash, or the override.
|
||||
getFragment: function(fragment, forcePushState) {
|
||||
if (fragment == null) {
|
||||
if (this._hasPushState || !this._wantsHashChange) {
|
||||
fragment = this.getPath();
|
||||
if (this._hasPushState || !this._wantsHashChange || forcePushState) {
|
||||
fragment = decodeURI(this.location.pathname + this.location.search);
|
||||
var root = this.root.replace(trailingSlash, '');
|
||||
if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
|
||||
} else {
|
||||
fragment = this.getHash();
|
||||
}
|
||||
@@ -1460,43 +1391,36 @@
|
||||
this.options = _.extend({root: '/'}, this.options, options);
|
||||
this.root = this.options.root;
|
||||
this._wantsHashChange = this.options.hashChange !== false;
|
||||
this._hasHashChange = 'onhashchange' in window;
|
||||
this._wantsPushState = !!this.options.pushState;
|
||||
this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState);
|
||||
this.fragment = this.getFragment();
|
||||
|
||||
// Add a cross-platform `addEventListener` shim for older browsers.
|
||||
var addEventListener = window.addEventListener || function (eventName, listener) {
|
||||
return attachEvent('on' + eventName, listener);
|
||||
};
|
||||
var fragment = this.getFragment();
|
||||
var docMode = document.documentMode;
|
||||
var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
|
||||
|
||||
// Normalize root to always include a leading and trailing slash.
|
||||
this.root = ('/' + this.root + '/').replace(rootStripper, '/');
|
||||
|
||||
// Proxy an iframe to handle location events if the browser doesn't
|
||||
// support the `hashchange` event, HTML5 history, or the user wants
|
||||
// `hashChange` but not `pushState`.
|
||||
if (!this._hasHashChange && this._wantsHashChange && (!this._wantsPushState || !this._hasPushState)) {
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.src = 'javascript:0';
|
||||
iframe.style.display = 'none';
|
||||
iframe.tabIndex = -1;
|
||||
var body = document.body;
|
||||
// Using `appendChild` will throw on IE < 9 if the document is not ready.
|
||||
this.iframe = body.insertBefore(iframe, body.firstChild).contentWindow;
|
||||
this.navigate(this.fragment);
|
||||
if (oldIE && this._wantsHashChange) {
|
||||
var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
|
||||
this.iframe = frame.hide().appendTo('body')[0].contentWindow;
|
||||
this.navigate(fragment);
|
||||
}
|
||||
|
||||
// Depending on whether we're using pushState or hashes, and whether
|
||||
// 'onhashchange' is supported, determine how we check the URL state.
|
||||
if (this._hasPushState) {
|
||||
addEventListener('popstate', this.checkUrl, false);
|
||||
} else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
|
||||
addEventListener('hashchange', this.checkUrl, false);
|
||||
Backbone.$(window).on('popstate', this.checkUrl);
|
||||
} else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
|
||||
Backbone.$(window).on('hashchange', this.checkUrl);
|
||||
} else if (this._wantsHashChange) {
|
||||
this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
|
||||
}
|
||||
|
||||
// Determine if we need to change the base url, for a pushState link
|
||||
// opened by a non-pushState browser.
|
||||
this.fragment = fragment;
|
||||
var loc = this.location;
|
||||
|
||||
// Transition from hashChange to pushState or vice versa if both are
|
||||
// requested.
|
||||
if (this._wantsHashChange && this._wantsPushState) {
|
||||
@@ -1504,14 +1428,16 @@
|
||||
// If we've started off with a route from a `pushState`-enabled
|
||||
// browser, but we're currently in a browser that doesn't support it...
|
||||
if (!this._hasPushState && !this.atRoot()) {
|
||||
this.location.replace(this.root + '#' + this.getPath());
|
||||
this.fragment = this.getFragment(null, true);
|
||||
this.location.replace(this.root + '#' + this.fragment);
|
||||
// Return immediately as browser will do redirect to new url
|
||||
return true;
|
||||
|
||||
// Or if we've started out with a hash-based route, but we're currently
|
||||
// in a browser where it could be `pushState`-based instead...
|
||||
} else if (this._hasPushState && this.atRoot()) {
|
||||
this.navigate(this.getHash(), {replace: true});
|
||||
} else if (this._hasPushState && this.atRoot() && loc.hash) {
|
||||
this.fragment = this.getHash().replace(routeStripper, '');
|
||||
this.history.replaceState({}, document.title, this.root + this.fragment);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1522,25 +1448,7 @@
|
||||
// Disable Backbone.history, perhaps temporarily. Not useful in a real app,
|
||||
// but possibly useful for unit testing Routers.
|
||||
stop: function() {
|
||||
// Add a cross-platform `removeEventListener` shim for older browsers.
|
||||
var removeEventListener = window.removeEventListener || function (eventName, listener) {
|
||||
return detachEvent('on' + eventName, listener);
|
||||
};
|
||||
|
||||
// Remove window listeners.
|
||||
if (this._hasPushState) {
|
||||
removeEventListener('popstate', this.checkUrl, false);
|
||||
} else if (this._wantsHashChange && this._hasHashChange && !this.iframe) {
|
||||
removeEventListener('hashchange', this.checkUrl, false);
|
||||
}
|
||||
|
||||
// Clean up the iframe if necessary.
|
||||
if (this.iframe) {
|
||||
document.body.removeChild(this.iframe.frameElement);
|
||||
this.iframe = null;
|
||||
}
|
||||
|
||||
// Some environments will throw when clearing an undefined interval.
|
||||
Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
|
||||
if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
|
||||
History.started = false;
|
||||
},
|
||||
@@ -1556,7 +1464,7 @@
|
||||
checkUrl: function(e) {
|
||||
var current = this.getFragment();
|
||||
if (current === this.fragment && this.iframe) {
|
||||
current = this.getHash(this.iframe);
|
||||
current = this.getFragment(this.getHash(this.iframe));
|
||||
}
|
||||
if (current === this.fragment) return false;
|
||||
if (this.iframe) this.navigate(current);
|
||||
@@ -1589,8 +1497,8 @@
|
||||
|
||||
var url = this.root + (fragment = this.getFragment(fragment || ''));
|
||||
|
||||
// Strip the hash and decode for matching.
|
||||
fragment = decodeURI(fragment.replace(pathStripper, ''));
|
||||
// Strip the hash for matching.
|
||||
fragment = fragment.replace(pathStripper, '');
|
||||
|
||||
if (this.fragment === fragment) return;
|
||||
this.fragment = fragment;
|
||||
@@ -1606,7 +1514,7 @@
|
||||
// fragment to store history.
|
||||
} else if (this._wantsHashChange) {
|
||||
this._updateHash(this.location, fragment, options.replace);
|
||||
if (this.iframe && (fragment !== this.getHash(this.iframe))) {
|
||||
if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
|
||||
// Opening and closing the iframe tricks IE7 and earlier to push a
|
||||
// history entry on hash-tag change. When replace is true, we don't
|
||||
// want this.
|
||||
|
||||
30
vendor/backbone/test/collection.js
vendored
30
vendor/backbone/test/collection.js
vendored
@@ -60,20 +60,6 @@
|
||||
strictEqual(collection.last().get('a'), 4);
|
||||
});
|
||||
|
||||
test("clone preserves model and comparator", 3, function() {
|
||||
var Model = Backbone.Model.extend();
|
||||
var comparator = function(model){ return model.id; };
|
||||
|
||||
var collection = new Backbone.Collection([{id: 1}], {
|
||||
model: Model,
|
||||
comparator: comparator
|
||||
}).clone();
|
||||
collection.add({id: 2});
|
||||
ok(collection.at(0) instanceof Model);
|
||||
ok(collection.at(1) instanceof Model);
|
||||
strictEqual(collection.comparator, comparator);
|
||||
});
|
||||
|
||||
test("get", 6, function() {
|
||||
equal(col.get(0), d);
|
||||
equal(col.get(d.clone()), d);
|
||||
@@ -1349,20 +1335,4 @@
|
||||
equal(c.models.length, 1);
|
||||
});
|
||||
|
||||
test('#3020: #set with {add: false} should not throw.', 2, function() {
|
||||
var collection = new Backbone.Collection;
|
||||
collection.set([{id: 1}], {add: false});
|
||||
strictEqual(collection.length, 0);
|
||||
strictEqual(collection.models.length, 0);
|
||||
});
|
||||
|
||||
test("create with wait, model instance, #3028", 1, function() {
|
||||
var collection = new Backbone.Collection();
|
||||
var model = new Backbone.Model({id: 1});
|
||||
model.sync = function(){
|
||||
equal(this.collection, collection);
|
||||
};
|
||||
collection.create(model, {wait: true});
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
79
vendor/backbone/test/router.js
vendored
79
vendor/backbone/test/router.js
vendored
@@ -331,36 +331,6 @@
|
||||
Backbone.history.checkUrl();
|
||||
});
|
||||
|
||||
test("No events are triggered if #execute returns false.", 1, function() {
|
||||
var Router = Backbone.Router.extend({
|
||||
|
||||
routes: {
|
||||
foo: function() {
|
||||
ok(true);
|
||||
}
|
||||
},
|
||||
|
||||
execute: function(callback, args) {
|
||||
callback.apply(this, args);
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
var router = new Router;
|
||||
|
||||
router.on('route route:foo', function() {
|
||||
ok(false);
|
||||
});
|
||||
|
||||
Backbone.history.on('route', function() {
|
||||
ok(false);
|
||||
});
|
||||
|
||||
location.replace('http://example.com#foo');
|
||||
Backbone.history.checkUrl();
|
||||
});
|
||||
|
||||
test("#933, #908 - leading slash", 2, function() {
|
||||
location.replace('http://example.com/root/foo');
|
||||
|
||||
@@ -405,7 +375,7 @@
|
||||
Backbone.history.navigate('charñ', {trigger: true});
|
||||
equal(router.charType, 'UTF');
|
||||
Backbone.history.navigate('char%C3%B1', {trigger: true});
|
||||
equal(router.charType, 'UTF');
|
||||
equal(router.charType, 'escaped');
|
||||
});
|
||||
|
||||
test("#1185 - Use pathname when hashChange is not wanted.", 1, function() {
|
||||
@@ -768,7 +738,7 @@
|
||||
var Router = Backbone.Router.extend({
|
||||
routes: {
|
||||
path: function(params){
|
||||
strictEqual(params, 'x=y z');
|
||||
strictEqual(params, 'x=y%20z');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -837,49 +807,4 @@
|
||||
Backbone.history.start({pushState: true});
|
||||
});
|
||||
|
||||
test('Router#execute receives callback, args, name.', 3, function() {
|
||||
location.replace('http://example.com#foo/123/bar?x=y');
|
||||
Backbone.history.stop();
|
||||
Backbone.history = _.extend(new Backbone.History, {location: location});
|
||||
var Router = Backbone.Router.extend({
|
||||
routes: {'foo/:id/bar': 'foo'},
|
||||
foo: function(){},
|
||||
execute: function(callback, args, name) {
|
||||
strictEqual(callback, this.foo);
|
||||
deepEqual(args, ['123', 'x=y']);
|
||||
strictEqual(name, 'foo');
|
||||
}
|
||||
});
|
||||
var router = new Router;
|
||||
Backbone.history.start();
|
||||
});
|
||||
|
||||
test("pushState to hashChange with only search params.", 1, function() {
|
||||
Backbone.history.stop();
|
||||
location.replace('http://example.com?a=b');
|
||||
location.replace = function(url) {
|
||||
strictEqual(url, '/#?a=b');
|
||||
};
|
||||
Backbone.history = _.extend(new Backbone.History, {
|
||||
location: location,
|
||||
history: null
|
||||
});
|
||||
Backbone.history.start({pushState: true});
|
||||
});
|
||||
|
||||
test("#3123 - History#navigate decodes before comparison.", 1, function() {
|
||||
Backbone.history.stop();
|
||||
location.replace('http://example.com/shop/search?keyword=short%20dress');
|
||||
Backbone.history = _.extend(new Backbone.History, {
|
||||
location: location,
|
||||
history: {
|
||||
pushState: function(){ ok(false); },
|
||||
replaceState: function(){ ok(false); }
|
||||
}
|
||||
});
|
||||
Backbone.history.start({pushState: true});
|
||||
Backbone.history.navigate('shop/search?keyword=short%20dress', true);
|
||||
strictEqual(Backbone.history.fragment, 'shop/search?keyword=short dress');
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
11
vendor/backbone/test/sync.js
vendored
11
vendor/backbone/test/sync.js
vendored
@@ -207,15 +207,4 @@
|
||||
strictEqual(this.ajaxSettings.beforeSend(xhr), false);
|
||||
});
|
||||
|
||||
test('#2928 - Pass along `textStatus` and `errorThrown`.', 2, function() {
|
||||
var model = new Backbone.Model;
|
||||
model.url = '/test';
|
||||
model.on('error', function(model, xhr, options) {
|
||||
strictEqual(options.textStatus, 'textStatus');
|
||||
strictEqual(options.errorThrown, 'errorThrown');
|
||||
});
|
||||
model.fetch();
|
||||
this.ajaxSettings.error({}, 'textStatus', 'errorThrown');
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
128
vendor/backbone/test/view.js
vendored
128
vendor/backbone/test/view.js
vendored
@@ -20,22 +20,10 @@
|
||||
equal(view.el.other, void 0);
|
||||
});
|
||||
|
||||
test("$", 2, function() {
|
||||
test("jQuery", 1, function() {
|
||||
var view = new Backbone.View;
|
||||
view.setElement('<p><a><b>test</b></a></p>');
|
||||
var result = view.$('a b');
|
||||
|
||||
strictEqual(result[0].innerHTML, 'test');
|
||||
ok(result.length === +result.length);
|
||||
});
|
||||
|
||||
test("$el", 3, function() {
|
||||
var view = new Backbone.View;
|
||||
view.setElement('<p><a><b>test</b></a></p>');
|
||||
strictEqual(view.el.nodeType, 1);
|
||||
|
||||
ok(view.$el instanceof Backbone.$);
|
||||
strictEqual(view.$el[0], view.el);
|
||||
strictEqual(view.$('a b').html(), 'test');
|
||||
});
|
||||
|
||||
test("initialize", 1, function() {
|
||||
@@ -72,17 +60,6 @@
|
||||
equal(counter2, 3);
|
||||
});
|
||||
|
||||
test("delegate", 2, function() {
|
||||
var view = new Backbone.View({el: '#testElement'});
|
||||
view.delegate('click', 'h1', function() {
|
||||
ok(true);
|
||||
});
|
||||
view.delegate('click', function() {
|
||||
ok(true);
|
||||
});
|
||||
view.$('h1').trigger('click');
|
||||
});
|
||||
|
||||
test("delegateEvents allows functions for callbacks", 3, function() {
|
||||
var view = new Backbone.View({el: '<p></p>'});
|
||||
view.counter = 0;
|
||||
@@ -137,63 +114,6 @@
|
||||
equal(counter2, 3);
|
||||
});
|
||||
|
||||
test("undelegate", 0, function() {
|
||||
view = new Backbone.View({el: '#testElement'});
|
||||
view.delegate('click', function() { ok(false); });
|
||||
view.delegate('click', 'h1', function() { ok(false); });
|
||||
|
||||
view.undelegate('click');
|
||||
|
||||
view.$('h1').trigger('click');
|
||||
view.$el.trigger('click');
|
||||
});
|
||||
|
||||
test("undelegate with passed handler", 1, function() {
|
||||
view = new Backbone.View({el: '#testElement'});
|
||||
var listener = function() { ok(false); };
|
||||
view.delegate('click', listener);
|
||||
view.delegate('click', function() { ok(true); });
|
||||
view.undelegate('click', listener);
|
||||
view.$el.trigger('click');
|
||||
});
|
||||
|
||||
test("undelegate with selector", 2, function() {
|
||||
view = new Backbone.View({el: '#testElement'});
|
||||
view.delegate('click', function() { ok(true); });
|
||||
view.delegate('click', 'h1', function() { ok(false); });
|
||||
view.undelegate('click', 'h1');
|
||||
view.$('h1').trigger('click');
|
||||
view.$el.trigger('click');
|
||||
});
|
||||
|
||||
test("undelegate with handler and selector", 2, function() {
|
||||
view = new Backbone.View({el: '#testElement'});
|
||||
view.delegate('click', function() { ok(true); });
|
||||
var handler = function(){ ok(false); };
|
||||
view.delegate('click', 'h1', handler);
|
||||
view.undelegate('click', 'h1', handler);
|
||||
view.$('h1').trigger('click');
|
||||
view.$el.trigger('click');
|
||||
});
|
||||
|
||||
test("tagName can be provided as a string", 1, function() {
|
||||
var View = Backbone.View.extend({
|
||||
tagName: 'span'
|
||||
});
|
||||
|
||||
equal(new View().el.tagName, 'SPAN');
|
||||
});
|
||||
|
||||
test("tagName can be provided as a function", 1, function() {
|
||||
var View = Backbone.View.extend({
|
||||
tagName: function() {
|
||||
return 'p';
|
||||
}
|
||||
});
|
||||
|
||||
ok(new View().$el.is('p'));
|
||||
});
|
||||
|
||||
test("_ensureElement with DOM node el", 1, function() {
|
||||
var View = Backbone.View.extend({
|
||||
el: document.body
|
||||
@@ -281,19 +201,26 @@
|
||||
equal(5, count);
|
||||
});
|
||||
|
||||
test("custom events", 2, function() {
|
||||
test("custom events, with namespaces", 2, function() {
|
||||
var count = 0;
|
||||
|
||||
var View = Backbone.View.extend({
|
||||
el: $('body'),
|
||||
events: {
|
||||
"fake$event": function() { ok(true); }
|
||||
events: function() {
|
||||
return {"fake$event.namespaced": "run"};
|
||||
},
|
||||
run: function() {
|
||||
count++;
|
||||
}
|
||||
});
|
||||
|
||||
var view = new View;
|
||||
$('body').trigger('fake$event').trigger('fake$event');
|
||||
equal(count, 2);
|
||||
|
||||
$('body').off('fake$event');
|
||||
$('body').off('.namespaced');
|
||||
$('body').trigger('fake$event');
|
||||
equal(count, 2);
|
||||
});
|
||||
|
||||
test("#1048 - setElement uses provided object.", 2, function() {
|
||||
@@ -337,11 +264,21 @@
|
||||
ok(!view2.el.id);
|
||||
});
|
||||
|
||||
test("#1228 - tagName can be provided as a function", 1, function() {
|
||||
var View = Backbone.View.extend({
|
||||
tagName: function() {
|
||||
return 'p';
|
||||
}
|
||||
});
|
||||
|
||||
ok(new View().$el.is('p'));
|
||||
});
|
||||
|
||||
test("views stopListening", 0, function() {
|
||||
var View = Backbone.View.extend({
|
||||
initialize: function() {
|
||||
this.listenTo(this.model, 'all x', function(){ ok(false); });
|
||||
this.listenTo(this.collection, 'all x', function(){ ok(false); });
|
||||
this.listenTo(this.model, 'all x', function(){ ok(false); }, this);
|
||||
this.listenTo(this.collection, 'all x', function(){ ok(false); }, this);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -387,19 +324,4 @@
|
||||
equal(counter, 2);
|
||||
});
|
||||
|
||||
test("remove", 1, function() {
|
||||
var view = new Backbone.View;
|
||||
document.body.appendChild(view.el);
|
||||
|
||||
view.delegate('click', function() { ok(false); });
|
||||
view.listenTo(view, 'all x', function() { ok(false); });
|
||||
|
||||
view.remove();
|
||||
view.$el.trigger('click');
|
||||
view.trigger('x');
|
||||
|
||||
// In IE8 and below, parentNode still exists but is not document.body.
|
||||
notEqual(view.el.parentNode, document.body);
|
||||
});
|
||||
|
||||
})();
|
||||
|
||||
2
vendor/dojo/LICENSE
vendored
2
vendor/dojo/LICENSE
vendored
@@ -13,7 +13,7 @@ The text of the AFL and BSD licenses is reproduced below.
|
||||
The "New" BSD License:
|
||||
**********************
|
||||
|
||||
Copyright (c) 2005-2013, The Dojo Foundation
|
||||
Copyright (c) 2005-2014, The Dojo Foundation
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
|
||||
39
vendor/dojo/dojo.js
vendored
39
vendor/dojo/dojo.js
vendored
@@ -168,6 +168,36 @@
|
||||
rhinoDojoConfig(defaultConfig, baseUrl, rhinoArgs);
|
||||
}
|
||||
|
||||
has.add("host-webworker", ((typeof WorkerGlobalScope !== 'undefined') && (self instanceof WorkerGlobalScope)));
|
||||
if(has("host-webworker")){
|
||||
mix(defaultConfig.hasCache, {
|
||||
"host-browser": 0,
|
||||
"dom": 0,
|
||||
"dojo-dom-ready-api": 0,
|
||||
"dojo-sniff": 0,
|
||||
"dojo-inject-api": 1,
|
||||
"host-webworker": 1
|
||||
});
|
||||
|
||||
defaultConfig.loaderPatch = {
|
||||
injectUrl: function(url, callback){
|
||||
// TODO:
|
||||
// This is not async, nor can it be in Webworkers. It could be made better by passing
|
||||
// the entire require array into importScripts at. This way the scripts are loaded in
|
||||
// async mode; even if the callbacks are ran in sync. It is not a major issue as webworkers
|
||||
// tend to be long running where initial startup is not a major factor.
|
||||
|
||||
try{
|
||||
importScripts(url);
|
||||
callback();
|
||||
}catch(e){
|
||||
console.info("failed to load resource (" + url + ")");
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// userConfig has tests override defaultConfig has tests; do this after the environment detection because
|
||||
// the environment detection usually sets some has feature values in the hasCache.
|
||||
for(var p in userConfig.has){
|
||||
@@ -237,7 +267,7 @@
|
||||
};
|
||||
};
|
||||
|
||||
if(has("dom")){
|
||||
if(has("dom") || has("host-webworker")){
|
||||
// in legacy sync mode, the loader needs a minimal XHR library
|
||||
|
||||
var locationProtocol = location.protocol,
|
||||
@@ -579,7 +609,7 @@
|
||||
|
||||
// for each packagePath found in any packagePaths config item, augment the packageConfig
|
||||
// packagePaths is deprecated; remove in 2.0
|
||||
for(baseUrl in config.packagePaths){
|
||||
for(var baseUrl in config.packagePaths){
|
||||
forEach(config.packagePaths[baseUrl], function(packageInfo){
|
||||
var location = baseUrl + "/" + packageInfo;
|
||||
if(isString(packageInfo)){
|
||||
@@ -1294,7 +1324,7 @@
|
||||
has.add("dojo-loader-eval-hint-url", 1);
|
||||
}
|
||||
|
||||
var fixupUrl= function(url){
|
||||
var fixupUrl= typeof userConfig.fixupUrl == "function" ? userConfig.fixupUrl : function(url){
|
||||
url += ""; // make sure url is a Javascript string (some paths may be a Java string)
|
||||
return url + (cacheBust ? ((/\?/.test(url) ? "&" : "?") + cacheBust) : "");
|
||||
},
|
||||
@@ -1606,7 +1636,7 @@
|
||||
startTimer = function(){
|
||||
clearTimer();
|
||||
if(req.waitms){
|
||||
timerId = window.setTimeout(function(){
|
||||
timerId = global.setTimeout(function(){
|
||||
clearTimer();
|
||||
signal(error, makeError("timeout", waiting));
|
||||
}, req.waitms);
|
||||
@@ -1953,7 +1983,6 @@
|
||||
"dojo-sync-loader":1,
|
||||
"dojo-test-sniff":1,
|
||||
"config-deferredInstrumentation":1,
|
||||
"config-useDeferredInstrumentation":"report-unhandled-rejections",
|
||||
"config-tlmSiblingOfDojo":1
|
||||
},
|
||||
packages:[{
|
||||
|
||||
1
vendor/platform.js/LICENSE.txt
vendored
1
vendor/platform.js/LICENSE.txt
vendored
@@ -1,3 +1,4 @@
|
||||
Copyright 2014 Benjamin Tan <http://d10.github.io/>
|
||||
Copyright 2011-2014 John-David Dalton <http://allyoucanleet.com/>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
|
||||
70
vendor/platform.js/platform.js
vendored
70
vendor/platform.js/platform.js
vendored
@@ -38,7 +38,7 @@
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
|
||||
/** Opera regexp */
|
||||
var reOpera = /Opera/;
|
||||
var reOpera = /\bOpera/;
|
||||
|
||||
/** Possible global object */
|
||||
var thisBinding = this;
|
||||
@@ -104,16 +104,16 @@
|
||||
|
||||
os = format(
|
||||
os.replace(/ ce$/i, ' CE')
|
||||
.replace(/hpw/i, 'web')
|
||||
.replace(/Macintosh/, 'Mac OS')
|
||||
.replace(/_PowerPC/i, ' OS')
|
||||
.replace(/(OS X) [^ \d]+/i, '$1')
|
||||
.replace(/Mac (OS X)/, '$1')
|
||||
.replace(/(\b)hpw/i, '$1web')
|
||||
.replace(/(\b)Macintosh(\b)/, '$1Mac OS$2')
|
||||
.replace(/_PowerPC(\b)/i, ' OS$1')
|
||||
.replace(/(\bOS X) [^ \d]+/i, '$1')
|
||||
.replace(/(\b)Mac (OS X\b)/, '$1$2')
|
||||
.replace(/\/(\d)/, ' $1')
|
||||
.replace(/_/g, '.')
|
||||
.replace(/(?: BePC|[ .]*fc[ \d.]+)$/i, '')
|
||||
.replace(/x86\.64/gi, 'x86_64')
|
||||
.replace(/(Windows Phone)(?! OS)/, '$1 OS')
|
||||
.replace(/(\b)x86\.64(\b)/gi, '$1x86_64$2')
|
||||
.replace(/(\bWindows Phone)(?! OS\b)/, '$1 OS')
|
||||
.split(' on ')[0]
|
||||
);
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
phantomClass = isCustomContext ? objectClass : 'RuntimeObject';
|
||||
|
||||
/** Detect Java environment */
|
||||
var java = /Java/.test(javaClass) && context.java;
|
||||
var java = /\bJava/.test(javaClass) && context.java;
|
||||
|
||||
/** Detect Rhino */
|
||||
var rhino = java && getClassOf(context.environment) == enviroClass;
|
||||
@@ -597,8 +597,8 @@
|
||||
product = getProduct([manufacturer]);
|
||||
}
|
||||
// clean up Google TV
|
||||
if ((data = /Google TV/.exec(product))) {
|
||||
product = data[0];
|
||||
if ((data = /\b(Google TV)\b/.exec(product))) {
|
||||
product = data[1];
|
||||
}
|
||||
// detect simulators
|
||||
if (/\bSimulator\b/i.test(ua)) {
|
||||
@@ -621,12 +621,12 @@
|
||||
}
|
||||
// detect Android browsers
|
||||
else if (manufacturer && manufacturer != 'Google' &&
|
||||
((/Chrome/.test(name) && !/Mobile Safari/.test(ua)) || /Vita/.test(product))) {
|
||||
((/Chrome/.test(name) && !/\bMobile Safari\b/i.test(ua)) || /\bVita\b/.test(product))) {
|
||||
name = 'Android Browser';
|
||||
os = /Android/.test(os) ? os : 'Android';
|
||||
os = /\bAndroid\b/.test(os) ? os : 'Android';
|
||||
}
|
||||
// detect false positives for Firefox/Safari
|
||||
else if (!name || (data = !/\bMinefield\b|\(Android;/i.test(ua) && /Firefox|Safari/.exec(name))) {
|
||||
else if (!name || (data = !/\bMinefield\b|\(Android;/i.test(ua) && /\b(?:Firefox|Safari)\b/.exec(name))) {
|
||||
// escape the `/` for Firefox 1
|
||||
if (name && !product && /[\/,]|^[^(]+?\)/.test(ua.slice(ua.indexOf(data + '/') + 8))) {
|
||||
// clear name of false positives
|
||||
@@ -634,8 +634,8 @@
|
||||
}
|
||||
// reassign a generic name
|
||||
if ((data = product || manufacturer || os) &&
|
||||
(product || manufacturer || /Android|Symbian OS|Tablet OS|webOS/.test(os))) {
|
||||
name = /[a-z]+(?: Hat)?/i.exec(/Android/.test(os) ? os : data) + ' Browser';
|
||||
(product || manufacturer || /\b(?:Android|Symbian OS|Tablet OS|webOS)\b/.test(os))) {
|
||||
name = /[a-z]+(?: Hat)?/i.exec(/\bAndroid\b/.test(os) ? os : data) + ' Browser';
|
||||
}
|
||||
}
|
||||
// detect Firefox OS
|
||||
@@ -658,7 +658,7 @@
|
||||
if (layout == 'iCab' && parseFloat(version) > 3) {
|
||||
layout = ['WebKit'];
|
||||
} else if ((data =
|
||||
/Opera/.test(name) && (/OPR/.test(ua) ? 'Blink' : 'Presto') ||
|
||||
/\bOpera\b/.test(name) && (/\bOPR\b/.test(ua) ? 'Blink' : 'Presto') ||
|
||||
/\b(?:Midori|Nook|Safari)\b/i.test(ua) && 'WebKit' ||
|
||||
!layout && /\bMSIE\b/i.test(ua) && (os == 'Mac OS' ? 'Tasman' : 'Trident')
|
||||
)) {
|
||||
@@ -743,7 +743,7 @@
|
||||
(prerelease == 'beta' ? beta : alpha) + (/\d+\+?/.exec(data) || '');
|
||||
}
|
||||
// detect Firefox Mobile
|
||||
if (name == 'Fennec' || name == 'Firefox' && /Android|Firefox OS/.test(os)) {
|
||||
if (name == 'Fennec' || name == 'Firefox' && /\b(?:Android|Firefox OS)\b/.test(os)) {
|
||||
name = 'Firefox Mobile';
|
||||
}
|
||||
// obscure Maxthon's unreliable version
|
||||
@@ -752,7 +752,7 @@
|
||||
}
|
||||
// detect Silk desktop/accelerated modes
|
||||
else if (name == 'Silk') {
|
||||
if (!/Mobi/i.test(ua)) {
|
||||
if (!/\bMobi/i.test(ua)) {
|
||||
os = 'Android';
|
||||
description.unshift('desktop mode');
|
||||
}
|
||||
@@ -767,9 +767,9 @@
|
||||
description.unshift('desktop mode');
|
||||
}
|
||||
// detect Xbox 360 and Xbox One
|
||||
else if (/Xbox/i.test(product)) {
|
||||
else if (/\bXbox\b/i.test(product)) {
|
||||
os = null;
|
||||
if (product == 'Xbox 360' && /IEMobile/.test(ua)) {
|
||||
if (product == 'Xbox 360' && /\bIEMobile\b/.test(ua)) {
|
||||
description.unshift('mobile mode');
|
||||
}
|
||||
}
|
||||
@@ -784,7 +784,7 @@
|
||||
}
|
||||
// detect BlackBerry OS version
|
||||
// http://docs.blackberry.com/en/developers/deliverables/18169/HTTP_headers_sent_by_BB_Browser_1234911_11.jsp
|
||||
else if ((/BlackBerry/.test(product) || /BB10/.test(ua)) && (data =
|
||||
else if ((/\bBlackBerry\b/.test(product) || /\bBB10\b/.test(ua)) && (data =
|
||||
(RegExp(product.replace(/ +/g, ' *') + '/([.\\d]+)', 'i').exec(ua) || 0)[1] ||
|
||||
version
|
||||
)) {
|
||||
@@ -798,11 +798,11 @@
|
||||
product != 'Wii' && (
|
||||
(useFeatures && opera) ||
|
||||
(/Opera/.test(name) && /\b(?:MSIE|Firefox)\b/i.test(ua)) ||
|
||||
(name == 'Firefox' && /OS X (?:\d+\.){2,}/.test(os)) ||
|
||||
(name == 'Firefox' && /\bOS X (?:\d+\.){2,}/.test(os)) ||
|
||||
(name == 'IE' && (
|
||||
(os && !/^Win/.test(os) && version > 5.5) ||
|
||||
/Windows XP/.test(os) && version > 8 ||
|
||||
version == 8 && !/Trident/.test(ua)
|
||||
/\bWindows XP\b/.test(os) && version > 8 ||
|
||||
version == 8 && !/\bTrident\b/.test(ua)
|
||||
))
|
||||
)
|
||||
) && !reOpera.test((data = parse.call(forOwn, ua.replace(reOpera, '') + ';'))) && data.name) {
|
||||
@@ -810,7 +810,7 @@
|
||||
// when "indentifying", the UA contains both Opera and the other browser's name
|
||||
data = 'ing as ' + data.name + ((data = data.version) ? ' ' + data : '');
|
||||
if (reOpera.test(name)) {
|
||||
if (/IE/.test(data) && os == 'Mac OS') {
|
||||
if (/\bIE\b/.test(data) && os == 'Mac OS') {
|
||||
os = null;
|
||||
}
|
||||
data = 'identify' + data;
|
||||
@@ -823,7 +823,7 @@
|
||||
} else {
|
||||
name = 'Opera';
|
||||
}
|
||||
if (/IE/.test(data)) {
|
||||
if (/\bIE\b/.test(data)) {
|
||||
os = null;
|
||||
}
|
||||
if (!useFeatures) {
|
||||
@@ -872,10 +872,10 @@
|
||||
}
|
||||
}
|
||||
// detect Opera desktop modes
|
||||
if (name == 'Opera' && (data = /(?:zbov|zvav)$/.exec(os))) {
|
||||
if (name == 'Opera' && (data = /\b(zbov|zvav)$/.exec(os))) {
|
||||
name += ' ';
|
||||
description.unshift('desktop mode');
|
||||
if (data == 'zvav') {
|
||||
if (data[1] == 'zvav') {
|
||||
name += 'Mini';
|
||||
version = null;
|
||||
} else {
|
||||
@@ -883,12 +883,12 @@
|
||||
}
|
||||
}
|
||||
// detect Chrome desktop mode
|
||||
else if (name == 'Safari' && /Chrome/.exec(layout && layout[1])) {
|
||||
else if (name == 'Safari' && /\bChrome\b/.exec(layout && layout[1])) {
|
||||
description.unshift('desktop mode');
|
||||
name = 'Chrome Mobile';
|
||||
version = null;
|
||||
|
||||
if (/OS X/.test(os)) {
|
||||
if (/\bOS X\b/.test(os)) {
|
||||
manufacturer = 'Apple';
|
||||
os = 'iOS 4.3+';
|
||||
} else {
|
||||
@@ -901,7 +901,7 @@
|
||||
os = trim(os.replace(data, ''));
|
||||
}
|
||||
// add layout engine
|
||||
if (layout && !/Avant|Nook/.test(name) && (
|
||||
if (layout && !/\b(?:Avant|Nook)\b/.test(name) && (
|
||||
/Browser|Lunascape|Maxthon/.test(name) ||
|
||||
/^(?:Adobe|Arora|Breach|Midori|Opera|Phantom|Rekonq|Rock|Sleipnir|Web)/.test(name) && layout[1])) {
|
||||
// don't add layout details to description if they are falsey
|
||||
@@ -939,8 +939,10 @@
|
||||
os.architecture = 64;
|
||||
os.family = os.family.replace(RegExp(' *' + data), '');
|
||||
}
|
||||
if (name && (/WOW64/i.test(ua) ||
|
||||
(useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/^win32$/i.test(nav.platform)))) {
|
||||
if (
|
||||
name && (/\bWOW64\b/i.test(ua) ||
|
||||
(useFeatures && /\w(?:86|32)$/.test(nav.cpuClass || nav.platform) && !/\bWin64; x64\b/i.test(ua)))
|
||||
) {
|
||||
description.unshift('32-bit');
|
||||
}
|
||||
}
|
||||
|
||||
126
vendor/underscore/test/arrays.js
vendored
126
vendor/underscore/test/arrays.js
vendored
@@ -8,35 +8,51 @@
|
||||
deepEqual(_.first([1, 2, 3], 0), [], 'can pass an index to first');
|
||||
deepEqual(_.first([1, 2, 3], 2), [1, 2], 'can pass an index to first');
|
||||
deepEqual(_.first([1, 2, 3], 5), [1, 2, 3], 'can pass an index to first');
|
||||
var result = (function(){ return _.first(arguments); })(4, 3, 2, 1);
|
||||
var result = (function(){ return _.first(arguments); }(4, 3, 2, 1));
|
||||
equal(result, 4, 'works on an arguments object.');
|
||||
result = _.map([[1, 2, 3], [1, 2, 3]], _.first);
|
||||
deepEqual(result, [1, 1], 'works well with _.map');
|
||||
result = (function() { return _.take([1, 2, 3], 2); })();
|
||||
deepEqual(result, [1, 2], 'aliased as take');
|
||||
result = (function() { return _.first([1, 2, 3], 2); }());
|
||||
deepEqual(result, [1, 2]);
|
||||
|
||||
equal(_.first(null), undefined, 'handles nulls');
|
||||
strictEqual(_.first([1, 2, 3], -1).length, 0);
|
||||
});
|
||||
|
||||
test('head', function() {
|
||||
strictEqual(_.first, _.head, 'alias for first');
|
||||
});
|
||||
|
||||
test('take', function() {
|
||||
strictEqual(_.first, _.take, 'alias for first');
|
||||
});
|
||||
|
||||
test('rest', function() {
|
||||
var numbers = [1, 2, 3, 4];
|
||||
deepEqual(_.rest(numbers), [2, 3, 4], 'working rest()');
|
||||
deepEqual(_.rest(numbers, 0), [1, 2, 3, 4], 'working rest(0)');
|
||||
deepEqual(_.rest(numbers, 2), [3, 4], 'rest can take an index');
|
||||
var result = (function(){ return _(arguments).tail(); })(1, 2, 3, 4);
|
||||
deepEqual(result, [2, 3, 4], 'aliased as tail and works on arguments object');
|
||||
var result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
|
||||
deepEqual(result, [2, 3, 4], 'works on arguments object');
|
||||
result = _.map([[1, 2, 3], [1, 2, 3]], _.rest);
|
||||
deepEqual(_.flatten(result), [2, 3, 2, 3], 'works well with _.map');
|
||||
result = (function(){ return _(arguments).drop(); })(1, 2, 3, 4);
|
||||
deepEqual(result, [2, 3, 4], 'aliased as drop and works on arguments object');
|
||||
result = (function(){ return _(arguments).rest(); }(1, 2, 3, 4));
|
||||
deepEqual(result, [2, 3, 4], 'works on arguments object');
|
||||
});
|
||||
|
||||
test('tail', function() {
|
||||
strictEqual(_.rest, _.tail, 'alias for rest');
|
||||
});
|
||||
|
||||
test('drop', function() {
|
||||
strictEqual(_.rest, _.drop, 'alias for rest');
|
||||
});
|
||||
|
||||
test('initial', function() {
|
||||
deepEqual(_.initial([1, 2, 3, 4, 5]), [1, 2, 3, 4], 'working initial()');
|
||||
deepEqual(_.initial([1, 2, 3, 4], 2), [1, 2], 'initial can take an index');
|
||||
deepEqual(_.initial([1, 2, 3, 4], 6), [], 'initial can take a large index');
|
||||
var result = (function(){ return _(arguments).initial(); })(1, 2, 3, 4);
|
||||
var result = (function(){ return _(arguments).initial(); }(1, 2, 3, 4));
|
||||
deepEqual(result, [1, 2, 3], 'initial works on arguments object');
|
||||
result = _.map([[1, 2, 3], [1, 2, 3]], _.initial);
|
||||
deepEqual(_.flatten(result), [1, 2, 1, 2], 'initial works with _.map');
|
||||
@@ -47,7 +63,7 @@
|
||||
deepEqual(_.last([1, 2, 3], 0), [], 'can pass an index to last');
|
||||
deepEqual(_.last([1, 2, 3], 2), [2, 3], 'can pass an index to last');
|
||||
deepEqual(_.last([1, 2, 3], 5), [1, 2, 3], 'can pass an index to last');
|
||||
var result = (function(){ return _(arguments).last(); })(1, 2, 3, 4);
|
||||
var result = (function(){ return _(arguments).last(); }(1, 2, 3, 4));
|
||||
equal(result, 4, 'works on an arguments object');
|
||||
result = _.map([[1, 2, 3], [1, 2, 3]], _.last);
|
||||
deepEqual(result, [3, 3], 'works well with _.map');
|
||||
@@ -58,7 +74,7 @@
|
||||
|
||||
test('compact', function() {
|
||||
equal(_.compact([0, 1, false, 2, false, 3]).length, 3, 'can trim out all falsy values');
|
||||
var result = (function(){ return _.compact(arguments).length; })(0, 1, false, 2, false, 3);
|
||||
var result = (function(){ return _.compact(arguments).length; }(0, 1, false, 2, false, 3));
|
||||
equal(result, 3, 'works on an arguments object');
|
||||
});
|
||||
|
||||
@@ -66,7 +82,7 @@
|
||||
var list = [1, [2], [3, [[[4]]]]];
|
||||
deepEqual(_.flatten(list), [1, 2, 3, 4], 'can flatten nested arrays');
|
||||
deepEqual(_.flatten(list, true), [1, 2, 3, [[[4]]]], 'can shallowly flatten nested arrays');
|
||||
var result = (function(){ return _.flatten(arguments); })(1, [2], [3, [[[4]]]]);
|
||||
var result = (function(){ return _.flatten(arguments); }(1, [2], [3, [[[4]]]]));
|
||||
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
|
||||
list = [[1], [2], [3], [[4]]];
|
||||
deepEqual(_.flatten(list, true), [1, 2, 3, [4]], 'can shallowly flatten arrays containing only other arrays');
|
||||
@@ -75,12 +91,12 @@
|
||||
test('without', function() {
|
||||
var list = [1, 2, 1, 0, 3, 1, 4];
|
||||
deepEqual(_.without(list, 0, 1), [2, 3, 4], 'can remove all instances of an object');
|
||||
var result = (function(){ return _.without(arguments, 0, 1); })(1, 2, 1, 0, 3, 1, 4);
|
||||
var result = (function(){ return _.without(arguments, 0, 1); }(1, 2, 1, 0, 3, 1, 4));
|
||||
deepEqual(result, [2, 3, 4], 'works on an arguments object');
|
||||
|
||||
list = [{one : 1}, {two : 2}];
|
||||
ok(_.without(list, {one : 1}).length == 2, 'uses real object identity for comparisons.');
|
||||
ok(_.without(list, list[0]).length == 1, 'ditto.');
|
||||
equal(_.without(list, {one : 1}).length, 2, 'uses real object identity for comparisons.');
|
||||
equal(_.without(list, list[0]).length, 1, 'ditto.');
|
||||
});
|
||||
|
||||
test('uniq', function() {
|
||||
@@ -96,13 +112,16 @@
|
||||
|
||||
deepEqual(_.map(_.uniq(list, iterator), iterator), ['moe', 'curly', 'larry'], 'can find the unique values of an array using a custom iterator without specifying whether array is sorted');
|
||||
|
||||
iterator = function(value) { return value +1; };
|
||||
iterator = function(value) { return value + 1; };
|
||||
list = [1, 2, 2, 3, 4, 4];
|
||||
deepEqual(_.uniq(list, true, iterator), [1, 2, 3, 4], 'iterator works with sorted array');
|
||||
|
||||
var result = (function(){ return _.uniq(arguments); })(1, 2, 1, 3, 1, 4);
|
||||
var result = (function(){ return _.uniq(arguments); }(1, 2, 1, 3, 1, 4));
|
||||
deepEqual(result, [1, 2, 3, 4], 'works on an arguments object');
|
||||
|
||||
var a = {}, b = {}, c = {};
|
||||
deepEqual(_.uniq([a, b, a, b, c]), [a, b, c], 'works on values that can be tested for equivalency but not ordered');
|
||||
|
||||
deepEqual(_.uniq(null), []);
|
||||
|
||||
var context = {};
|
||||
@@ -111,14 +130,22 @@
|
||||
strictEqual(this, context);
|
||||
strictEqual(value, 3);
|
||||
strictEqual(index, 0);
|
||||
strictEqual(array, list);
|
||||
}, context);
|
||||
|
||||
deepEqual(_.uniq([{a: 1, b: 1}, {a: 1, b: 2}, {a: 1, b: 3}, {a: 2, b: 1}], 'a'), [{a: 1, b: 1}, {a: 2, b: 1}], 'can use pluck like iterator');
|
||||
deepEqual(_.uniq([{0: 1, b: 1}, {0: 1, b: 2}, {0: 1, b: 3}, {0: 2, b: 1}], 0), [{0: 1, b: 1}, {0: 2, b: 1}], 'can use falsey pluck like iterator');
|
||||
});
|
||||
|
||||
test('unique', function() {
|
||||
strictEqual(_.uniq, _.unique, 'alias for uniq');
|
||||
});
|
||||
|
||||
test('intersection', function() {
|
||||
var stooges = ['moe', 'curly', 'larry'], leaders = ['moe', 'groucho'];
|
||||
deepEqual(_.intersection(stooges, leaders), ['moe'], 'can take the set intersection of two arrays');
|
||||
deepEqual(_(stooges).intersection(leaders), ['moe'], 'can perform an OO-style intersection');
|
||||
var result = (function(){ return _.intersection(arguments, leaders); })('moe', 'curly', 'larry');
|
||||
var result = (function(){ return _.intersection(arguments, leaders); }('moe', 'curly', 'larry'));
|
||||
deepEqual(result, ['moe'], 'works on an arguments object');
|
||||
var theSixStooges = ['moe', 'moe', 'curly', 'curly', 'larry', 'larry'];
|
||||
deepEqual(_.intersection(theSixStooges, leaders), ['moe'], 'returns a duplicate-free array');
|
||||
@@ -140,7 +167,7 @@
|
||||
deepEqual(result, [1, 2, 3, 30, 40, [1]], 'takes the union of a list of nested arrays');
|
||||
|
||||
var args = null;
|
||||
(function(){ args = arguments; })(1, 2, 3);
|
||||
(function(){ args = arguments; }(1, 2, 3));
|
||||
result = _.union(args, [2, 30, 1], [1, 40]);
|
||||
deepEqual(result, [1, 2, 3, 30, 40], 'takes the union of a list of arrays');
|
||||
|
||||
@@ -177,6 +204,9 @@
|
||||
|
||||
var empty = _.zip([]);
|
||||
deepEqual(empty, [], 'unzipped empty');
|
||||
|
||||
deepEqual(_.zip(null), [], 'handles null');
|
||||
deepEqual(_.zip(), [], '_.zip() returns []');
|
||||
});
|
||||
|
||||
test('object', function() {
|
||||
@@ -196,9 +226,8 @@
|
||||
|
||||
test('indexOf', function() {
|
||||
var numbers = [1, 2, 3];
|
||||
numbers.indexOf = null;
|
||||
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf, even without the native function');
|
||||
var result = (function(){ return _.indexOf(arguments, 2); })(1, 2, 3);
|
||||
equal(_.indexOf(numbers, 2), 1, 'can compute indexOf');
|
||||
var result = (function(){ return _.indexOf(arguments, 2); }(1, 2, 3));
|
||||
equal(result, 1, 'works on an arguments object');
|
||||
equal(_.indexOf(null, 2), -1, 'handles nulls properly');
|
||||
|
||||
@@ -212,8 +241,12 @@
|
||||
equal(index, 3, '40 is in the list');
|
||||
|
||||
numbers = [1, 40, 40, 40, 40, 40, 40, 40, 50, 60, 70]; num = 40;
|
||||
index = _.indexOf(numbers, num, true);
|
||||
equal(index, 1, '40 is in the list');
|
||||
equal(_.indexOf(numbers, num, true), 1, '40 is in the list');
|
||||
equal(_.indexOf(numbers, 6, true), -1, '6 isnt in the list');
|
||||
equal(_.indexOf([1, 2, 5, 4, 6, 7], 5, true), -1, 'sorted indexOf doesn\'t uses binary search');
|
||||
ok(_.every(['1', [], {}, null], function() {
|
||||
return _.indexOf(numbers, num, {}) === 1;
|
||||
}), 'non-nums as fromIndex make indexOf assume sorted');
|
||||
|
||||
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
|
||||
index = _.indexOf(numbers, 2, 5);
|
||||
@@ -221,23 +254,66 @@
|
||||
|
||||
index = _.indexOf([,,,], undefined);
|
||||
equal(index, 0, 'treats sparse arrays as if they were dense');
|
||||
|
||||
var array = [1, 2, 3, 1, 2, 3];
|
||||
strictEqual(_.indexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
|
||||
strictEqual(_.indexOf(array, 1, -2), -1, 'neg `fromIndex` starts at the right index');
|
||||
strictEqual(_.indexOf(array, 2, -3), 4);
|
||||
_.each([-6, -8, -Infinity], function(fromIndex) {
|
||||
strictEqual(_.indexOf(array, 1, fromIndex), 0);
|
||||
});
|
||||
strictEqual(_.indexOf([1, 2, 3], 1, true), 0);
|
||||
});
|
||||
|
||||
test('lastIndexOf', function() {
|
||||
var numbers = [1, 0, 1];
|
||||
var falsey = [void 0, '', 0, false, NaN, null, undefined];
|
||||
equal(_.lastIndexOf(numbers, 1), 2);
|
||||
|
||||
numbers = [1, 0, 1, 0, 0, 1, 0, 0, 0];
|
||||
numbers.lastIndexOf = null;
|
||||
equal(_.lastIndexOf(numbers, 1), 5, 'can compute lastIndexOf, even without the native function');
|
||||
equal(_.lastIndexOf(numbers, 0), 8, 'lastIndexOf the other element');
|
||||
var result = (function(){ return _.lastIndexOf(arguments, 1); })(1, 0, 1, 0, 0, 1, 0, 0, 0);
|
||||
var result = (function(){ return _.lastIndexOf(arguments, 1); }(1, 0, 1, 0, 0, 1, 0, 0, 0));
|
||||
equal(result, 5, 'works on an arguments object');
|
||||
equal(_.lastIndexOf(null, 2), -1, 'handles nulls properly');
|
||||
|
||||
numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3];
|
||||
var index = _.lastIndexOf(numbers, 2, 2);
|
||||
equal(index, 1, 'supports the fromIndex argument');
|
||||
|
||||
var array = [1, 2, 3, 1, 2, 3];
|
||||
|
||||
strictEqual(_.lastIndexOf(array, 1, 0), 0, 'starts at the correct from idx');
|
||||
strictEqual(_.lastIndexOf(array, 3), 5, 'should return the index of the last matched value');
|
||||
strictEqual(_.lastIndexOf(array, 4), -1, 'should return `-1` for an unmatched value');
|
||||
|
||||
strictEqual(_.lastIndexOf(array, 1, 2), 0, 'should work with a positive `fromIndex`');
|
||||
|
||||
_.each([6, 8, Math.pow(2, 32), Infinity], function(fromIndex) {
|
||||
strictEqual(_.lastIndexOf(array, undefined, fromIndex), -1);
|
||||
strictEqual(_.lastIndexOf(array, 1, fromIndex), 3);
|
||||
strictEqual(_.lastIndexOf(array, '', fromIndex), -1);
|
||||
});
|
||||
|
||||
var expected = _.map(falsey, function(value) {
|
||||
return typeof value == 'number' ? -1 : 5;
|
||||
});
|
||||
|
||||
var actual = _.map(falsey, function(fromIndex) {
|
||||
return _.lastIndexOf(array, 3, fromIndex);
|
||||
});
|
||||
|
||||
deepEqual(actual, expected, 'should treat falsey `fromIndex` values, except `0` and `NaN`, as `array.length`');
|
||||
strictEqual(_.lastIndexOf(array, 3, '1'), 5, 'should treat non-number `fromIndex` values as `array.length`');
|
||||
strictEqual(_.lastIndexOf(array, 3, true), 5, 'should treat non-number `fromIndex` values as `array.length`');
|
||||
|
||||
strictEqual(_.lastIndexOf(array, 2, -3), 1, 'should work with a negative `fromIndex`');
|
||||
strictEqual(_.lastIndexOf(array, 1, -3), 3, 'neg `fromIndex` starts at the right index');
|
||||
|
||||
deepEqual(_.map([-6, -8, -Infinity], function(fromIndex) {
|
||||
return _.lastIndexOf(array, 1, fromIndex);
|
||||
}), [0, -1, -1]);
|
||||
});
|
||||
|
||||
test('range', function() {
|
||||
@@ -251,4 +327,4 @@
|
||||
deepEqual(_.range(0, -10, -1), [0, -1, -2, -3, -4, -5, -6, -7, -8, -9], 'final example in the Python docs');
|
||||
});
|
||||
|
||||
})();
|
||||
}());
|
||||
|
||||
13
vendor/underscore/test/chaining.js
vendored
13
vendor/underscore/test/chaining.js
vendored
@@ -4,10 +4,10 @@
|
||||
|
||||
test('map/flatten/reduce', function() {
|
||||
var lyrics = [
|
||||
"I'm a lumberjack and I'm okay",
|
||||
"I sleep all night and I work all day",
|
||||
"He's a lumberjack and he's okay",
|
||||
"He sleeps all night and he works all day"
|
||||
'I\'m a lumberjack and I\'m okay',
|
||||
'I sleep all night and I work all day',
|
||||
'He\'s a lumberjack and he\'s okay',
|
||||
'He sleeps all night and he works all day'
|
||||
];
|
||||
var counts = _(lyrics).chain()
|
||||
.map(function(line) { return line.split(''); })
|
||||
@@ -17,7 +17,8 @@
|
||||
hash[l]++;
|
||||
return hash;
|
||||
}, {}).value();
|
||||
ok(counts.a == 16 && counts.e == 10, 'counted all the letters in the song');
|
||||
equal(counts.a, 16, 'counted all the letters in the song');
|
||||
equal(counts.e, 10, 'counted all the letters in the song');
|
||||
});
|
||||
|
||||
test('select/reject/sortBy', function() {
|
||||
@@ -62,4 +63,4 @@
|
||||
deepEqual(o.filter(function(i) { return i > 2; }).value(), [3, 4]);
|
||||
});
|
||||
|
||||
})();
|
||||
}());
|
||||
|
||||
336
vendor/underscore/test/collections.js
vendored
336
vendor/underscore/test/collections.js
vendored
@@ -12,7 +12,7 @@
|
||||
deepEqual(answers, [5, 10, 15], 'context object property accessed');
|
||||
|
||||
answers = [];
|
||||
_.forEach([1, 2, 3], function(num){ answers.push(num); });
|
||||
_.each([1, 2, 3], function(num){ answers.push(num); });
|
||||
deepEqual(answers, [1, 2, 3], 'aliased as "forEach"');
|
||||
|
||||
answers = [];
|
||||
@@ -43,17 +43,26 @@
|
||||
equal(answers, 100, 'enumerates [0, length)');
|
||||
});
|
||||
|
||||
test('forEach', function() {
|
||||
strictEqual(_.each, _.forEach, 'alias for each');
|
||||
});
|
||||
|
||||
test('lookupIterator with contexts', function() {
|
||||
_.each([true, false, 'yes', '', 0, 1, {}], function(context) {
|
||||
_.each([1], function() {
|
||||
equal(this, context);
|
||||
}, context);
|
||||
});
|
||||
});
|
||||
|
||||
test('map', function() {
|
||||
var doubled = _.map([1, 2, 3], function(num){ return num * 2; });
|
||||
deepEqual(doubled, [2, 4, 6], 'doubled numbers');
|
||||
|
||||
doubled = _.collect([1, 2, 3], function(num){ return num * 2; });
|
||||
deepEqual(doubled, [2, 4, 6], 'aliased as "collect"');
|
||||
|
||||
var tripled = _.map([1, 2, 3], function(num){ return num * this.multiplier; }, {multiplier : 3});
|
||||
deepEqual(tripled, [3, 6, 9], 'tripled numbers with context');
|
||||
|
||||
var doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
|
||||
doubled = _([1, 2, 3]).map(function(num){ return num * 2; });
|
||||
deepEqual(doubled, [2, 4, 6], 'OO-style doubled numbers');
|
||||
|
||||
if (document.querySelectorAll) {
|
||||
@@ -61,13 +70,24 @@
|
||||
deepEqual(ids, ['id1', 'id2'], 'Can use collection methods on NodeLists.');
|
||||
}
|
||||
|
||||
var ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
|
||||
ids = _.map({length: 2, 0: {id: '1'}, 1: {id: '2'}}, function(n){
|
||||
return n.id;
|
||||
});
|
||||
deepEqual(ids, ['1', '2'], 'Can use collection methods on Array-likes.');
|
||||
|
||||
var ifnull = _.map(null, function(){});
|
||||
ok(_.isArray(ifnull) && ifnull.length === 0, 'handles a null properly');
|
||||
deepEqual(_.map(null, _.noop), [], 'handles a null properly');
|
||||
|
||||
deepEqual(_.map([1], function() {
|
||||
return this.length;
|
||||
}, [5]), [1], 'called with context');
|
||||
|
||||
// Passing a property name like _.pluck.
|
||||
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
|
||||
deepEqual(_.map(people, 'name'), ['moe', 'curly'], 'predicate string map to object properties');
|
||||
});
|
||||
|
||||
test('collect', function() {
|
||||
strictEqual(_.map, _.collect, 'alias for map');
|
||||
});
|
||||
|
||||
test('reduce', function() {
|
||||
@@ -84,50 +104,41 @@
|
||||
sum = _([1, 2, 3]).reduce(function(sum, num){ return sum + num; }, 0);
|
||||
equal(sum, 6, 'OO-style reduce');
|
||||
|
||||
var sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
|
||||
sum = _.reduce([1, 2, 3], function(sum, num){ return sum + num; });
|
||||
equal(sum, 6, 'default initial value');
|
||||
|
||||
var prod = _.reduce([1, 2, 3, 4], function(prod, num){ return prod * num; });
|
||||
equal(prod, 24, 'can reduce via multiplication');
|
||||
|
||||
var ifnull;
|
||||
try {
|
||||
_.reduce(null, function(){});
|
||||
} catch (ex) {
|
||||
ifnull = ex;
|
||||
}
|
||||
ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly');
|
||||
ok(_.reduce(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
|
||||
equal(_.reduce([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
|
||||
equal(_.reduce([_], _.noop), _, 'collection of length one with no initial value returns the first item');
|
||||
|
||||
ok(_.reduce(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
|
||||
equal(_.reduce([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
|
||||
raises(function() { _.reduce([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');
|
||||
raises(function() { _.reduce([], _.noop); }, TypeError, 'throws an error for empty arrays with no initial value');
|
||||
raises(function() {_.reduce(null, _.noop);}, TypeError, 'handles a null (without initial value) properly');
|
||||
});
|
||||
|
||||
test('foldl', function() {
|
||||
strictEqual(_.reduce, _.foldl, 'alias for reduce');
|
||||
});
|
||||
|
||||
test('reduceRight', function() {
|
||||
var list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
|
||||
equal(list, 'bazbarfoo', 'can perform right folds');
|
||||
|
||||
var list = _.foldr(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; }, '');
|
||||
equal(list, 'bazbarfoo', 'aliased as "foldr"');
|
||||
|
||||
var list = _.foldr(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
|
||||
list = _.reduceRight(['foo', 'bar', 'baz'], function(memo, str){ return memo + str; });
|
||||
equal(list, 'bazbarfoo', 'default initial value');
|
||||
|
||||
var ifnull;
|
||||
try {
|
||||
_.reduceRight(null, function(){});
|
||||
} catch (ex) {
|
||||
ifnull = ex;
|
||||
}
|
||||
ok(ifnull instanceof TypeError, 'handles a null (without initial value) properly');
|
||||
|
||||
var sum = _.reduceRight({a: 1, b: 2, c: 3}, function(sum, num){ return sum + num; });
|
||||
equal(sum, 6, 'default initial value on object');
|
||||
|
||||
ok(_.reduceRight(null, function(){}, 138) === 138, 'handles a null (with initial value) properly');
|
||||
ok(_.reduceRight(null, _.noop, 138) === 138, 'handles a null (with initial value) properly');
|
||||
equal(_.reduceRight([_], _.noop), _, 'collection of length one with no initial value returns the first item');
|
||||
|
||||
equal(_.reduceRight([], function(){}, undefined), undefined, 'undefined can be passed as a special case');
|
||||
raises(function() { _.reduceRight([], function(){}); }, TypeError, 'throws an error for empty arrays with no initial value');
|
||||
equal(_.reduceRight([], _.noop, undefined), undefined, 'undefined can be passed as a special case');
|
||||
|
||||
raises(function() { _.reduceRight([], _.noop); }, TypeError, 'throws an error for empty arrays with no initial value');
|
||||
raises(function() {_.reduceRight(null, _.noop);}, TypeError, 'handles a null (without initial value) properly');
|
||||
|
||||
// Assert that the correct arguments are being passed.
|
||||
|
||||
@@ -136,12 +147,12 @@
|
||||
object = {a: 1, b: 2},
|
||||
lastKey = _.keys(object).pop();
|
||||
|
||||
var expected = lastKey == 'a'
|
||||
var expected = lastKey === 'a'
|
||||
? [memo, 1, 'a', object]
|
||||
: [memo, 2, 'b', object];
|
||||
|
||||
_.reduceRight(object, function() {
|
||||
args || (args = _.toArray(arguments));
|
||||
if (!args) args = _.toArray(arguments);
|
||||
}, memo);
|
||||
|
||||
deepEqual(args, expected);
|
||||
@@ -152,79 +163,155 @@
|
||||
lastKey = _.keys(object).pop();
|
||||
args = null;
|
||||
|
||||
expected = lastKey == '2'
|
||||
expected = lastKey === '2'
|
||||
? [memo, 'a', '2', object]
|
||||
: [memo, 'b', '1', object];
|
||||
|
||||
_.reduceRight(object, function() {
|
||||
args || (args = _.toArray(arguments));
|
||||
if (!args) args = _.toArray(arguments);
|
||||
}, memo);
|
||||
|
||||
deepEqual(args, expected);
|
||||
});
|
||||
|
||||
test('foldr', function() {
|
||||
strictEqual(_.reduceRight, _.foldr, 'alias for reduceRight');
|
||||
});
|
||||
|
||||
test('find', function() {
|
||||
var array = [1, 2, 3, 4];
|
||||
strictEqual(_.find(array, function(n) { return n > 2; }), 3, 'should return first found `value`');
|
||||
strictEqual(_.find(array, function() { return false; }), void 0, 'should return `undefined` if `value` is not found');
|
||||
});
|
||||
|
||||
test('detect', function() {
|
||||
var result = _.detect([1, 2, 3], function(num){ return num * 2 == 4; });
|
||||
// Matching an object like _.findWhere.
|
||||
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}, {a: 2, b: 4}];
|
||||
deepEqual(_.find(list, {a: 1}), {a: 1, b: 2}, 'can be used as findWhere');
|
||||
deepEqual(_.find(list, {b: 4}), {a: 1, b: 4});
|
||||
ok(!_.find(list, {c: 1}), 'undefined when not found');
|
||||
ok(!_.find([], {c: 1}), 'undefined when searching empty list');
|
||||
|
||||
var result = _.find([1, 2, 3], function(num){ return num * 2 === 4; });
|
||||
equal(result, 2, 'found the first "2" and broke the loop');
|
||||
});
|
||||
|
||||
test('select', function() {
|
||||
var evens = _.select([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
|
||||
deepEqual(evens, [2, 4, 6], 'selected each even number');
|
||||
test('detect', function() {
|
||||
strictEqual(_.detect, _.find, 'alias for detect');
|
||||
});
|
||||
|
||||
evens = _.filter([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
|
||||
deepEqual(evens, [2, 4, 6], 'aliased as "filter"');
|
||||
test('filter', function() {
|
||||
var evenArray = [1, 2, 3, 4, 5, 6];
|
||||
var evenObject = {one: 1, two: 2, three: 3};
|
||||
var isEven = function(num){ return num % 2 === 0; };
|
||||
|
||||
deepEqual(_.filter(evenArray, isEven), [2, 4, 6]);
|
||||
deepEqual(_.filter(evenObject, isEven), [2], 'can filter objects');
|
||||
deepEqual(_.filter([{}, evenObject, []], 'two'), [evenObject], 'predicate string map to object properties');
|
||||
|
||||
_.filter([1], function() {
|
||||
equal(this, evenObject, 'given context');
|
||||
}, evenObject);
|
||||
|
||||
// Can be used like _.where.
|
||||
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
|
||||
deepEqual(_.filter(list, {a: 1}), [{a: 1, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}]);
|
||||
deepEqual(_.filter(list, {b: 2}), [{a: 1, b: 2}, {a: 2, b: 2}]);
|
||||
deepEqual(_.filter(list, {}), list, 'Empty object accepts all items');
|
||||
deepEqual(_(list).filter({}), list, 'OO-filter');
|
||||
});
|
||||
|
||||
test('select', function() {
|
||||
strictEqual(_.filter, _.select, 'alias for filter');
|
||||
});
|
||||
|
||||
test('reject', function() {
|
||||
var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 == 0; });
|
||||
var odds = _.reject([1, 2, 3, 4, 5, 6], function(num){ return num % 2 === 0; });
|
||||
deepEqual(odds, [1, 3, 5], 'rejected each even number');
|
||||
|
||||
var context = 'obj';
|
||||
|
||||
var evens = _.reject([1, 2, 3, 4, 5, 6], function(num){
|
||||
equal(context, 'obj');
|
||||
return num % 2 != 0;
|
||||
return num % 2 !== 0;
|
||||
}, context);
|
||||
deepEqual(evens, [2, 4, 6], 'rejected each odd number');
|
||||
|
||||
deepEqual(_.reject([odds, {one: 1, two: 2, three: 3}], 'two'), [odds], 'predicate string map to object properties');
|
||||
|
||||
// Can be used like _.where.
|
||||
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
|
||||
deepEqual(_.reject(list, {a: 1}), [{a: 2, b: 2}]);
|
||||
deepEqual(_.reject(list, {b: 2}), [{a: 1, b: 3}, {a: 1, b: 4}]);
|
||||
deepEqual(_.reject(list, {}), [], 'Returns empty list given empty object');
|
||||
deepEqual(_.reject(list, []), [], 'Returns empty list given empty array');
|
||||
});
|
||||
|
||||
test('every', function() {
|
||||
ok(_.every([], _.identity), 'the empty set');
|
||||
ok(_.every([true, true, true], _.identity), 'every true values');
|
||||
ok(!_.every([true, false, true], _.identity), 'one false value');
|
||||
ok(_.every([0, 10, 28], function(num){ return num % 2 === 0; }), 'even numbers');
|
||||
ok(!_.every([0, 11, 28], function(num){ return num % 2 === 0; }), 'an odd number');
|
||||
ok(_.every([1], _.identity) === true, 'cast to boolean - true');
|
||||
ok(_.every([0], _.identity) === false, 'cast to boolean - false');
|
||||
ok(!_.every([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
|
||||
|
||||
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
|
||||
ok(!_.every(list, {a: 1, b: 2}), 'Can be called with object');
|
||||
ok(_.every(list, 'a'), 'String mapped to object property');
|
||||
|
||||
list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
|
||||
ok(_.every(list, {b: 2}), 'Can be called with object');
|
||||
ok(!_.every(list, 'c'), 'String mapped to object property');
|
||||
|
||||
ok(_.every({a: 1, b: 2, c: 3, d: 4}, _.isNumber), 'takes objects');
|
||||
ok(!_.every({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
|
||||
ok(_.every(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
|
||||
ok(!_.every(['a', 'b', 'c', 'd', 'f'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
|
||||
});
|
||||
|
||||
test('all', function() {
|
||||
ok(_.all([], _.identity), 'the empty set');
|
||||
ok(_.all([true, true, true], _.identity), 'all true values');
|
||||
ok(!_.all([true, false, true], _.identity), 'one false value');
|
||||
ok(_.all([0, 10, 28], function(num){ return num % 2 == 0; }), 'even numbers');
|
||||
ok(!_.all([0, 11, 28], function(num){ return num % 2 == 0; }), 'an odd number');
|
||||
ok(_.all([1], _.identity) === true, 'cast to boolean - true');
|
||||
ok(_.all([0], _.identity) === false, 'cast to boolean - false');
|
||||
ok(_.every([true, true, true], _.identity), 'aliased as "every"');
|
||||
ok(!_.all([undefined, undefined, undefined], _.identity), 'works with arrays of undefined');
|
||||
strictEqual(_.all, _.every, 'alias for all');
|
||||
});
|
||||
|
||||
test('some', function() {
|
||||
ok(!_.some([]), 'the empty set');
|
||||
ok(!_.some([false, false, false]), 'all false values');
|
||||
ok(_.some([false, false, true]), 'one true value');
|
||||
ok(_.some([null, 0, 'yes', false]), 'a string');
|
||||
ok(!_.some([null, 0, '', false]), 'falsy values');
|
||||
ok(!_.some([1, 11, 29], function(num){ return num % 2 === 0; }), 'all odd numbers');
|
||||
ok(_.some([1, 10, 29], function(num){ return num % 2 === 0; }), 'an even number');
|
||||
ok(_.some([1], _.identity) === true, 'cast to boolean - true');
|
||||
ok(_.some([0], _.identity) === false, 'cast to boolean - false');
|
||||
ok(_.some([false, false, true]));
|
||||
|
||||
var list = [{a: 1, b: 2}, {a: 2, b: 2}, {a: 1, b: 3}, {a: 1, b: 4}];
|
||||
ok(!_.some(list, {a: 5, b: 2}), 'Can be called with object');
|
||||
ok(_.some(list, 'a'), 'String mapped to object property');
|
||||
|
||||
list = [{a: 1, b: 2}, {a: 2, b: 2, c: true}];
|
||||
ok(_.some(list, {b: 2}), 'Can be called with object');
|
||||
ok(!_.some(list, 'd'), 'String mapped to object property');
|
||||
|
||||
ok(_.some({a: '1', b: '2', c: '3', d: '4', e: 6}, _.isNumber), 'takes objects');
|
||||
ok(!_.some({a: 1, b: 2, c: 3, d: 4}, _.isObject), 'takes objects');
|
||||
ok(_.some(['a', 'b', 'c', 'd'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
|
||||
ok(!_.some(['x', 'y', 'z'], _.hasOwnProperty, {a: 1, b: 2, c: 3, d: 4}), 'context works');
|
||||
});
|
||||
|
||||
test('any', function() {
|
||||
ok(!_.any([]), 'the empty set');
|
||||
ok(!_.any([false, false, false]), 'all false values');
|
||||
ok(_.any([false, false, true]), 'one true value');
|
||||
ok(_.any([null, 0, 'yes', false]), 'a string');
|
||||
ok(!_.any([null, 0, '', false]), 'falsy values');
|
||||
ok(!_.any([1, 11, 29], function(num){ return num % 2 == 0; }), 'all odd numbers');
|
||||
ok(_.any([1, 10, 29], function(num){ return num % 2 == 0; }), 'an even number');
|
||||
ok(_.any([1], _.identity) === true, 'cast to boolean - true');
|
||||
ok(_.any([0], _.identity) === false, 'cast to boolean - false');
|
||||
ok(_.some([false, false, true]), 'aliased as "some"');
|
||||
strictEqual(_.any, _.some, 'alias for any');
|
||||
});
|
||||
|
||||
test('contains', function() {
|
||||
ok(_.contains([1, 2, 3], 2), 'two is in the array');
|
||||
ok(!_.contains([1, 3, 9], 2), 'two is not in the array');
|
||||
ok(_.contains({moe: 1, larry: 3, curly: 9}, 3) === true, '_.contains on objects checks their values');
|
||||
ok(_([1, 2, 3]).contains(2), 'OO-style contains');
|
||||
});
|
||||
|
||||
test('include', function() {
|
||||
ok(_.include([1, 2, 3], 2), 'two is in the array');
|
||||
ok(!_.include([1, 3, 9], 2), 'two is not in the array');
|
||||
ok(_.contains({moe: 1, larry: 3, curly: 9}, 3) === true, '_.include on objects checks their values');
|
||||
ok(_([1, 2, 3]).include(2), 'OO-style include');
|
||||
strictEqual(_.contains, _.include, 'alias for contains');
|
||||
});
|
||||
|
||||
test('invoke', function() {
|
||||
@@ -257,8 +344,10 @@
|
||||
});
|
||||
|
||||
test('pluck', function() {
|
||||
var people = [{name : 'moe', age : 30}, {name : 'curly', age : 50}];
|
||||
var people = [{name: 'moe', age: 30}, {name: 'curly', age: 50}];
|
||||
deepEqual(_.pluck(people, 'name'), ['moe', 'curly'], 'pulls names out of objects');
|
||||
//compat: most flexible handling of edge cases
|
||||
deepEqual(_.pluck([{'[object Object]': 1}], {}), [1]);
|
||||
});
|
||||
|
||||
test('where', function() {
|
||||
@@ -271,6 +360,10 @@
|
||||
equal(result[0].a, 1);
|
||||
result = _.where(list, {});
|
||||
equal(result.length, list.length);
|
||||
|
||||
function test() {}
|
||||
test.map = _.map;
|
||||
deepEqual(_.where([_, {a: 1, b: 2}, _], test), [_, _], 'checks properties given function');
|
||||
});
|
||||
|
||||
test('findWhere', function() {
|
||||
@@ -280,14 +373,29 @@
|
||||
result = _.findWhere(list, {b: 4});
|
||||
deepEqual(result, {a: 1, b: 4});
|
||||
|
||||
result = _.findWhere(list, {c: 1})
|
||||
result = _.findWhere(list, {c: 1});
|
||||
ok(_.isUndefined(result), 'undefined when not found');
|
||||
|
||||
result = _.findWhere([], {c: 1});
|
||||
ok(_.isUndefined(result), 'undefined when searching empty list');
|
||||
|
||||
function test() {}
|
||||
test.map = _.map;
|
||||
equal(_.findWhere([_, {a: 1, b: 2}, _], test), _, 'checks properties given function');
|
||||
|
||||
function TestClass() {
|
||||
this.y = 5;
|
||||
this.x = 'foo';
|
||||
}
|
||||
var expect = {c: 1, x: 'foo', y: 5};
|
||||
deepEqual(_.findWhere([{y: 5, b: 6}, expect], new TestClass()), expect, 'uses class instance properties');
|
||||
});
|
||||
|
||||
test('max', function() {
|
||||
equal(-Infinity, _.max(null), 'can handle null/undefined');
|
||||
equal(-Infinity, _.max(undefined), 'can handle null/undefined');
|
||||
equal(-Infinity, _.max(null, _.identity), 'can handle null/undefined');
|
||||
|
||||
equal(3, _.max([1, 2, 3]), 'can perform a regular Math.max');
|
||||
|
||||
var neg = _.max([1, 2, 3], function(num){ return -num; });
|
||||
@@ -306,9 +414,19 @@
|
||||
var b = {x: -Infinity};
|
||||
var iterator = function(o){ return o.x; };
|
||||
equal(_.max([a, b], iterator), a, 'Respects iterator return value of -Infinity');
|
||||
|
||||
deepEqual(_.max([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 4}, 'String keys use property iterator');
|
||||
|
||||
deepEqual(_.max([0, 2], function(a){ return a * this.x; }, {x: 1}), 2, 'Iterator context');
|
||||
deepEqual(_.max([[1], [2, 3], [-1, 4], [5]], 0), [5], 'Lookup falsy iterator');
|
||||
deepEqual(_.max([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: 2}, 'Lookup falsy iterator');
|
||||
});
|
||||
|
||||
test('min', function() {
|
||||
equal(Infinity, _.min(null), 'can handle null/undefined');
|
||||
equal(Infinity, _.min(undefined), 'can handle null/undefined');
|
||||
equal(Infinity, _.min(null, _.identity), 'can handle null/undefined');
|
||||
|
||||
equal(1, _.min([1, 2, 3]), 'can perform a regular Math.min');
|
||||
|
||||
var neg = _.min([1, 2, 3], function(num){ return -num; });
|
||||
@@ -331,6 +449,12 @@
|
||||
var b = {x: Infinity};
|
||||
var iterator = function(o){ return o.x; };
|
||||
equal(_.min([a, b], iterator), a, 'Respects iterator return value of Infinity');
|
||||
|
||||
deepEqual(_.min([{'a': 1}, {'a': 0, 'b': 3}, {'a': 4}, {'a': 2}], 'a'), {'a': 0, 'b': 3}, 'String keys use property iterator');
|
||||
|
||||
deepEqual(_.min([0, 2], function(a){ return a * this.x; }, {x: -1}), 2, 'Iterator context');
|
||||
deepEqual(_.min([[1], [2, 3], [-1, 4], [5]], 0), [-1, 4], 'Lookup falsy iterator');
|
||||
deepEqual(_.min([{0: 1}, {0: 2}, {0: -1}, {a: 1}], 0), {0: -1}, 'Lookup falsy iterator');
|
||||
});
|
||||
|
||||
test('sortBy', function() {
|
||||
@@ -341,7 +465,7 @@
|
||||
var list = [undefined, 4, 1, undefined, 3, 2];
|
||||
deepEqual(_.sortBy(list, _.identity), [1, 2, 3, 4, undefined, undefined], 'sortBy with undefined values');
|
||||
|
||||
var list = ['one', 'two', 'three', 'four', 'five'];
|
||||
list = ['one', 'two', 'three', 'four', 'five'];
|
||||
var sorted = _.sortBy(list, 'length');
|
||||
deepEqual(sorted, ['one', 'two', 'four', 'five', 'three'], 'sorted by length');
|
||||
|
||||
@@ -368,7 +492,9 @@
|
||||
|
||||
deepEqual(actual, collection, 'sortBy should be stable');
|
||||
|
||||
var list = ['q', 'w', 'e', 'r', 't', 'y'];
|
||||
deepEqual(_.sortBy(collection, 'x'), collection, 'sortBy accepts property string');
|
||||
|
||||
list = ['q', 'w', 'e', 'r', 't', 'y'];
|
||||
deepEqual(_.sortBy(list), ['e', 'q', 'r', 't', 'w', 'y'], 'uses _.identity if iterator is not specified');
|
||||
});
|
||||
|
||||
@@ -395,8 +521,8 @@
|
||||
var array = [{}];
|
||||
_.groupBy(array, function(value, index, obj){ ok(obj === array); });
|
||||
|
||||
var array = [1, 2, 1, 2, 3];
|
||||
var grouped = _.groupBy(array);
|
||||
array = [1, 2, 1, 2, 3];
|
||||
grouped = _.groupBy(array);
|
||||
equal(grouped['1'].length, 2);
|
||||
equal(grouped['3'].length, 1);
|
||||
|
||||
@@ -405,14 +531,14 @@
|
||||
[1, 3],
|
||||
[2, 3]
|
||||
];
|
||||
deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]})
|
||||
deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]})
|
||||
deepEqual(_.groupBy(matrix, 0), {1: [[1, 2], [1, 3]], 2: [[2, 3]]});
|
||||
deepEqual(_.groupBy(matrix, 1), {2: [[1, 2]], 3: [[1, 3], [2, 3]]});
|
||||
});
|
||||
|
||||
test('indexBy', function() {
|
||||
var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; });
|
||||
equal(parity['true'], 4);
|
||||
equal(parity['false'], 5);
|
||||
var parity = _.indexBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
|
||||
equal(parity.true, 4);
|
||||
equal(parity.false, 5);
|
||||
|
||||
var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
|
||||
var grouped = _.indexBy(list, 'length');
|
||||
@@ -421,16 +547,16 @@
|
||||
equal(grouped['5'], 'eight');
|
||||
|
||||
var array = [1, 2, 1, 2, 3];
|
||||
var grouped = _.indexBy(array);
|
||||
grouped = _.indexBy(array);
|
||||
equal(grouped['1'], 1);
|
||||
equal(grouped['2'], 2);
|
||||
equal(grouped['3'], 3);
|
||||
});
|
||||
|
||||
test('countBy', function() {
|
||||
var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 == 0; });
|
||||
equal(parity['true'], 2);
|
||||
equal(parity['false'], 3);
|
||||
var parity = _.countBy([1, 2, 3, 4, 5], function(num){ return num % 2 === 0; });
|
||||
equal(parity.true, 2);
|
||||
equal(parity.false, 3);
|
||||
|
||||
var list = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];
|
||||
var grouped = _.countBy(list, 'length');
|
||||
@@ -450,8 +576,8 @@
|
||||
var array = [{}];
|
||||
_.countBy(array, function(value, index, obj){ ok(obj === array); });
|
||||
|
||||
var array = [1, 2, 1, 2, 3];
|
||||
var grouped = _.countBy(array);
|
||||
array = [1, 2, 1, 2, 3];
|
||||
grouped = _.countBy(array);
|
||||
equal(grouped['1'], 2);
|
||||
equal(grouped['3'], 1);
|
||||
});
|
||||
@@ -476,17 +602,23 @@
|
||||
|
||||
test('shuffle', function() {
|
||||
var numbers = _.range(10);
|
||||
var shuffled = _.shuffle(numbers).sort();
|
||||
var shuffled = _.shuffle(numbers);
|
||||
notStrictEqual(numbers, shuffled, 'original object is unmodified');
|
||||
deepEqual(shuffled, numbers, 'contains the same members before and after shuffle');
|
||||
ok(_.every(_.range(10), function() { //appears consistent?
|
||||
return _.every(numbers, _.partial(_.contains, numbers));
|
||||
}), 'contains the same members before and after shuffle');
|
||||
|
||||
shuffled = _.shuffle({a: 1, b: 2, c: 3, d: 4});
|
||||
equal(shuffled.length, 4);
|
||||
deepEqual(shuffled.sort(), [1, 2, 3, 4], 'works on objects');
|
||||
});
|
||||
|
||||
test('sample', function() {
|
||||
var numbers = _.range(10);
|
||||
var all_sampled = _.sample(numbers, 10).sort();
|
||||
deepEqual(all_sampled, numbers, 'contains the same members before and after sample');
|
||||
all_sampled = _.sample(numbers, 20).sort();
|
||||
deepEqual(all_sampled, numbers, 'also works when sampling more objects than are present');
|
||||
var allSampled = _.sample(numbers, 10).sort();
|
||||
deepEqual(allSampled, numbers, 'contains the same members before and after sample');
|
||||
allSampled = _.sample(numbers, 20).sort();
|
||||
deepEqual(allSampled, numbers, 'also works when sampling more objects than are present');
|
||||
ok(_.contains(numbers, _.sample(numbers)), 'sampling a single element returns something from the array');
|
||||
strictEqual(_.sample([]), undefined, 'sampling empty array with no number returns undefined');
|
||||
notStrictEqual(_.sample([], 5), [], 'sampling empty array with a number returns an empty array');
|
||||
@@ -536,7 +668,7 @@
|
||||
deepEqual(_.partition(list, function(x) { return x & 1; }), [[1, 3, 5], [0, 2, 4]], 'handles 0 and 1 return values');
|
||||
deepEqual(_.partition(list, function(x) { return x - 3; }), [[0, 1, 2, 4, 5], [3]], 'handles other numeric return values');
|
||||
deepEqual(_.partition(list, function(x) { return x > 1 ? null : true; }), [[0, 1], [2, 3, 4, 5]], 'handles null return values');
|
||||
deepEqual(_.partition(list, function(x) { if(x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values');
|
||||
deepEqual(_.partition(list, function(x) { if (x < 2) return true; }), [[0, 1], [2, 3, 4, 5]], 'handles undefined return values');
|
||||
deepEqual(_.partition({a: 1, b: 2, c: 3}, function(x) { return x > 1; }), [[2, 3], [1]], 'handles objects');
|
||||
|
||||
deepEqual(_.partition(list, function(x, index) { return index % 2; }), [[1, 3, 5], [0, 2, 4]], 'can reference the array index');
|
||||
@@ -547,8 +679,18 @@
|
||||
deepEqual(_.partition([{x: 1}, {x: 0}, {x: 1}], 'x'), [[{x: 1}, {x: 1}], [{x: 0}]], 'Takes a string');
|
||||
|
||||
// Context
|
||||
var predicate = function(x){ return x === this.x };
|
||||
var predicate = function(x){ return x === this.x; };
|
||||
deepEqual(_.partition([1, 2, 3], predicate, {x: 2}), [[2], [1, 3]], 'partition takes a context argument');
|
||||
|
||||
deepEqual(_.partition([{a: 1}, {b: 2}, {a: 1, b: 2}], {a: 1}), [[{a: 1}, {a: 1, b: 2}], [{b: 2}]], 'predicate can be object');
|
||||
|
||||
var object = {a: 1};
|
||||
_.partition(object, function(val, key, obj) {
|
||||
equal(val, 1);
|
||||
equal(key, 'a');
|
||||
equal(obj, object);
|
||||
equal(this, predicate);
|
||||
}, predicate);
|
||||
});
|
||||
|
||||
})();
|
||||
}());
|
||||
|
||||
122
vendor/underscore/test/functions.js
vendored
122
vendor/underscore/test/functions.js
vendored
@@ -33,10 +33,11 @@
|
||||
// These tests are only meaningful when using a browser without a native bind function
|
||||
// To test this with a modern browser, set underscore's nativeBind to undefined
|
||||
var F = function () { return this; };
|
||||
var Boundf = _.bind(F, {hello: 'moe curly'});
|
||||
var boundf = _.bind(F, {hello: 'moe curly'});
|
||||
var Boundf = boundf; // make eslint happy.
|
||||
var newBoundf = new Boundf();
|
||||
equal(newBoundf.hello, undefined, 'function should not be bound to the context, to comply with ECMAScript 5');
|
||||
equal(Boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
|
||||
equal(boundf().hello, 'moe curly', "When called without the new operator, it's OK to be bound to the context");
|
||||
ok(newBoundf instanceof F, 'a bound instance is an instance of the original function');
|
||||
|
||||
raises(function() { _.bind('notafunction'); }, TypeError, 'throws an error when binding to a non-function');
|
||||
@@ -76,14 +77,20 @@
|
||||
moe = {
|
||||
name : 'moe',
|
||||
getName : function() { return 'name: ' + this.name; },
|
||||
sayHi : function() { return 'hi: ' + this.name; }
|
||||
sayHi : function() { return 'hi: ' + this.name; },
|
||||
sayLast : function() { return this.sayHi(_.last(arguments)); }
|
||||
};
|
||||
|
||||
raises(function() { _.bindAll(moe); }, Error, 'throws an error for bindAll with no functions named');
|
||||
raises(function() { _.bindAll(moe, 'sayBye'); }, TypeError, 'throws an error for bindAll if the given key is undefined');
|
||||
raises(function() { _.bindAll(moe, 'name'); }, TypeError, 'throws an error for bindAll if the given key is not a function');
|
||||
|
||||
_.bindAll(moe, 'sayHi');
|
||||
_.bindAll(moe, 'sayHi', 'sayLast');
|
||||
curly.sayHi = moe.sayHi;
|
||||
equal(curly.sayHi(), 'hi: moe');
|
||||
|
||||
var sayLast = moe.sayLast;
|
||||
equal(sayLast(1, 2, 3, 4, 5, 6, 7, 'Tom'), 'hi: moe', 'createCallback works with any number of arguments');
|
||||
});
|
||||
|
||||
test('memoize', function() {
|
||||
@@ -111,6 +118,28 @@
|
||||
upper.cache = {foo: 'BAR', bar: 'FOO'};
|
||||
equal(upper('foo'), 'BAR');
|
||||
equal(upper('bar'), 'FOO');
|
||||
|
||||
var hashed = _.memoize(function(key) {
|
||||
//https://github.com/jashkenas/underscore/pull/1679#discussion_r13736209
|
||||
ok(/[a-z]+/.test(key), 'hasher doesn\'t change keys');
|
||||
return key;
|
||||
}, function(key) {
|
||||
return key.toUpperCase();
|
||||
});
|
||||
hashed('yep');
|
||||
deepEqual(hashed.cache, {'YEP': 'yep'}, 'takes a hasher');
|
||||
|
||||
// Test that the hash function can be used to swizzle the key.
|
||||
var objCacher = _.memoize(function(value, key) {
|
||||
return {key: key, value: value};
|
||||
}, function(value, key) {
|
||||
return key;
|
||||
});
|
||||
var myObj = objCacher('a', 'alpha');
|
||||
var myObjAlias = objCacher('b', 'alpha');
|
||||
notStrictEqual(myObj, undefined, 'object is created if second argument used as key');
|
||||
strictEqual(myObj, myObjAlias, 'object is cached if second argument used as key');
|
||||
strictEqual(myObj.value, 'a', 'object is not modified if second argument used as key');
|
||||
});
|
||||
|
||||
asyncTest('delay', 2, function() {
|
||||
@@ -170,11 +199,11 @@
|
||||
var incr = function(){ counter++; };
|
||||
var throttledIncr = _.throttle(incr, 30);
|
||||
throttledIncr(); throttledIncr();
|
||||
ok(counter == 1);
|
||||
equal(counter, 1);
|
||||
_.delay(function(){
|
||||
ok(counter == 2);
|
||||
equal(counter, 2);
|
||||
throttledIncr();
|
||||
ok(counter == 3);
|
||||
equal(counter, 3);
|
||||
start();
|
||||
}, 85);
|
||||
});
|
||||
@@ -208,7 +237,7 @@
|
||||
var throttledIncr = _.throttle(incr, 32);
|
||||
|
||||
var stamp = new Date;
|
||||
while ((new Date - stamp) < limit) {
|
||||
while (new Date - stamp < limit) {
|
||||
throttledIncr();
|
||||
}
|
||||
var lastCount = counter;
|
||||
@@ -226,10 +255,10 @@
|
||||
var throttledIncr = _.throttle(incr, 60, {leading: false});
|
||||
|
||||
throttledIncr(); throttledIncr();
|
||||
ok(counter === 0);
|
||||
equal(counter, 0);
|
||||
|
||||
_.delay(function() {
|
||||
ok(counter == 1);
|
||||
equal(counter, 1);
|
||||
start();
|
||||
}, 96);
|
||||
});
|
||||
@@ -243,14 +272,14 @@
|
||||
_.delay(throttledIncr, 50);
|
||||
_.delay(throttledIncr, 60);
|
||||
_.delay(throttledIncr, 200);
|
||||
ok(counter === 0);
|
||||
equal(counter, 0);
|
||||
|
||||
_.delay(function() {
|
||||
ok(counter == 1);
|
||||
equal(counter, 1);
|
||||
}, 250);
|
||||
|
||||
_.delay(function() {
|
||||
ok(counter == 2);
|
||||
equal(counter, 2);
|
||||
start();
|
||||
}, 350);
|
||||
});
|
||||
@@ -276,16 +305,16 @@
|
||||
var throttledIncr = _.throttle(incr, 60, {trailing: false});
|
||||
|
||||
throttledIncr(); throttledIncr(); throttledIncr();
|
||||
ok(counter === 1);
|
||||
equal(counter, 1);
|
||||
|
||||
_.delay(function() {
|
||||
ok(counter == 1);
|
||||
equal(counter, 1);
|
||||
|
||||
throttledIncr(); throttledIncr();
|
||||
ok(counter == 2);
|
||||
equal(counter, 2);
|
||||
|
||||
_.delay(function() {
|
||||
ok(counter == 2);
|
||||
equal(counter, 2);
|
||||
start();
|
||||
}, 96);
|
||||
}, 96);
|
||||
@@ -298,14 +327,14 @@
|
||||
var origNowFunc = _.now;
|
||||
|
||||
throttledIncr();
|
||||
ok(counter == 1);
|
||||
equal(counter, 1);
|
||||
_.now = function () {
|
||||
return new Date(2013, 0, 1, 1, 1, 1);
|
||||
};
|
||||
|
||||
_.delay(function() {
|
||||
throttledIncr();
|
||||
ok(counter == 2);
|
||||
equal(counter, 2);
|
||||
start();
|
||||
_.now = origNowFunc;
|
||||
}, 200);
|
||||
@@ -320,7 +349,7 @@
|
||||
var throttledAppend;
|
||||
var append = function(arg){
|
||||
value += this + arg;
|
||||
var args = sequence.pop()
|
||||
var args = sequence.pop();
|
||||
if (args) {
|
||||
throttledAppend.call(args[0], args[1]);
|
||||
}
|
||||
@@ -400,7 +429,7 @@
|
||||
var debouncedAppend;
|
||||
var append = function(arg){
|
||||
value += this + arg;
|
||||
var args = sequence.pop()
|
||||
var args = sequence.pop();
|
||||
if (args) {
|
||||
debouncedAppend.call(args[0], args[1]);
|
||||
}
|
||||
@@ -416,10 +445,12 @@
|
||||
|
||||
test('once', function() {
|
||||
var num = 0;
|
||||
var increment = _.once(function(){ num++; });
|
||||
var increment = _.once(function(){ return ++num; });
|
||||
increment();
|
||||
increment();
|
||||
equal(num, 1);
|
||||
|
||||
equal(increment(), 1, 'stores a memo to the last value');
|
||||
});
|
||||
|
||||
test('Recursive onced function.', 1, function() {
|
||||
@@ -441,13 +472,13 @@
|
||||
equal(obj.hi(), 'Hello Moe');
|
||||
|
||||
var noop = function(){};
|
||||
var wrapped = _.wrap(noop, function(fn){ return Array.prototype.slice.call(arguments, 0); });
|
||||
var wrapped = _.wrap(noop, function(){ return Array.prototype.slice.call(arguments, 0); });
|
||||
var ret = wrapped(['whats', 'your'], 'vector', 'victor');
|
||||
deepEqual(ret, [noop, ['whats', 'your'], 'vector', 'victor']);
|
||||
});
|
||||
|
||||
test('negate', function() {
|
||||
var isOdd = function(n){ return (n & 1) == 1; };
|
||||
var isOdd = function(n){ return n & 1; };
|
||||
equal(_.negate(isOdd)(2), true, 'should return the complement of the given function');
|
||||
equal(_.negate(isOdd)(3), false, 'should return the complement of the given function');
|
||||
});
|
||||
@@ -460,6 +491,22 @@
|
||||
|
||||
composed = _.compose(greet, exclaim);
|
||||
equal(composed('moe'), 'hi: moe!', 'in this case, the functions are also commutative');
|
||||
|
||||
// f(g(h(x, y, z)))
|
||||
function h(x, y, z) {
|
||||
equal(arguments.length, 3, 'First function called with multiple args');
|
||||
return z * y;
|
||||
}
|
||||
function g(x) {
|
||||
equal(arguments.length, 1, 'Composed function is called with 1 argument');
|
||||
return x;
|
||||
}
|
||||
function f(x) {
|
||||
equal(arguments.length, 1, 'Composed function is called with 1 argument');
|
||||
return x * 2;
|
||||
}
|
||||
composed = _.compose(f, g, h);
|
||||
equal(composed(1, 2, 3), 12);
|
||||
});
|
||||
|
||||
test('after', function() {
|
||||
@@ -478,4 +525,29 @@
|
||||
equal(testAfter(0, 1), 1, 'after(0) should fire when first invoked');
|
||||
});
|
||||
|
||||
})();
|
||||
test('before', function() {
|
||||
var testBefore = function(beforeAmount, timesCalled) {
|
||||
var beforeCalled = 0;
|
||||
var before = _.before(beforeAmount, function() { beforeCalled++; });
|
||||
while (timesCalled--) before();
|
||||
return beforeCalled;
|
||||
};
|
||||
|
||||
equal(testBefore(5, 5), 4, 'before(N) should not fire after being called N times');
|
||||
equal(testBefore(5, 4), 4, 'before(N) should fire before being called N times');
|
||||
equal(testBefore(0, 0), 0, 'before(0) should not fire immediately');
|
||||
equal(testBefore(0, 1), 0, 'before(0) should not fire when first invoked');
|
||||
|
||||
var context = {num: 0};
|
||||
var increment = _.before(3, function(){ return ++this.num; });
|
||||
_.times(10, increment, context);
|
||||
equal(increment(), 2, 'stores a memo to the last value');
|
||||
equal(context.num, 2, 'provides context');
|
||||
});
|
||||
|
||||
test('iteratee', function() {
|
||||
var identity = _.iteratee();
|
||||
equal(identity, _.identity, '_.iteratee is exposed as an external function.');
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
160
vendor/underscore/test/objects.js
vendored
160
vendor/underscore/test/objects.js
vendored
@@ -1,6 +1,7 @@
|
||||
(function() {
|
||||
|
||||
module('Objects');
|
||||
/* global iObject, iElement, iArguments, iFunction, iArray, iString, iNumber, iBoolean, iDate, iRegExp, iNaN, iNull, iUndefined, ActiveXObject */
|
||||
|
||||
test('keys', function() {
|
||||
deepEqual(_.keys({one : 1, two : 2}), ['one', 'two'], 'can extract the keys from an object');
|
||||
@@ -29,12 +30,12 @@
|
||||
deepEqual(_.keys(_.invert(obj)), ['Moe', 'Larry', 'Curly'], 'can invert an object');
|
||||
deepEqual(_.invert(_.invert(obj)), obj, 'two inverts gets you back where you started');
|
||||
|
||||
var obj = {length: 3};
|
||||
ok(_.invert(obj)['3'] == 'length', 'can invert an object with "length"')
|
||||
obj = {length: 3};
|
||||
equal(_.invert(obj)['3'], 'length', 'can invert an object with "length"');
|
||||
});
|
||||
|
||||
test('functions', function() {
|
||||
var obj = {a : 'dash', b : _.map, c : (/yo/), d : _.reduce};
|
||||
var obj = {a : 'dash', b : _.map, c : /yo/, d : _.reduce};
|
||||
deepEqual(['b', 'd'], _.functions(obj), 'can grab the function names of any passed-in object');
|
||||
|
||||
var Animal = function(){};
|
||||
@@ -42,6 +43,10 @@
|
||||
deepEqual(_.functions(new Animal), ['run'], 'also looks up functions on the prototype');
|
||||
});
|
||||
|
||||
test('methods', function() {
|
||||
strictEqual(_.functions, _.methods, 'alias for functions');
|
||||
});
|
||||
|
||||
test('extend', function() {
|
||||
var result;
|
||||
equal(_.extend({}, {a: 'b'}).a, 'b', 'can extend an object with the attributes of another');
|
||||
@@ -54,6 +59,12 @@
|
||||
result = _.extend({}, {a: void 0, b: null});
|
||||
deepEqual(_.keys(result), ['a', 'b'], 'extend copies undefined values');
|
||||
|
||||
var F = function() {};
|
||||
F.prototype = {a: 'b'};
|
||||
var subObj = new F();
|
||||
subObj.c = 'd';
|
||||
deepEqual(_.extend({}, subObj), {c: 'd'}, 'extend ignores any properties but own from source');
|
||||
|
||||
try {
|
||||
result = {};
|
||||
_.extend(result, null, undefined, {a: 1});
|
||||
@@ -76,6 +87,10 @@
|
||||
result = _.pick(['a', 'b'], 1);
|
||||
deepEqual(result, {1: 'b'}, 'can pick numeric properties');
|
||||
|
||||
deepEqual(_.pick(null, 'a', 'b'), {}, 'non objects return empty object');
|
||||
deepEqual(_.pick(undefined, 'toString'), {}, 'null/undefined return empty object');
|
||||
deepEqual(_.pick(5, 'toString', 'b'), {toString: Number.prototype.toString}, 'can iterate primitives');
|
||||
|
||||
var data = {a: 1, b: 2, c: 3};
|
||||
var callback = function(value, key, object) {
|
||||
strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]);
|
||||
@@ -87,7 +102,12 @@
|
||||
|
||||
var Obj = function(){};
|
||||
Obj.prototype = {a: 1, b: 2, c: 3};
|
||||
deepEqual(_.pick(new Obj, 'a', 'c'), {a: 1, c: 3}, 'include prototype props');
|
||||
var instance = new Obj();
|
||||
deepEqual(_.pick(instance, 'a', 'c'), {a: 1, c: 3}, 'include prototype props');
|
||||
|
||||
deepEqual(_.pick(data, function(val, key) {
|
||||
return this[key] === 3 && this === instance;
|
||||
}, instance), {c: 3}, 'function is given context');
|
||||
});
|
||||
|
||||
test('omit', function() {
|
||||
@@ -101,6 +121,10 @@
|
||||
result = _.omit(['a', 'b'], 0);
|
||||
deepEqual(result, {1: 'b'}, 'can omit numeric properties');
|
||||
|
||||
deepEqual(_.omit(null, 'a', 'b'), {}, 'non objects return empty object');
|
||||
deepEqual(_.omit(undefined, 'toString'), {}, 'null/undefined return empty object');
|
||||
deepEqual(_.omit(5, 'toString', 'b'), {}, 'returns empty object for primitives');
|
||||
|
||||
var data = {a: 1, b: 2, c: 3};
|
||||
var callback = function(value, key, object) {
|
||||
strictEqual(key, {1: 'a', 2: 'b', 3: 'c'}[value]);
|
||||
@@ -112,11 +136,15 @@
|
||||
|
||||
var Obj = function(){};
|
||||
Obj.prototype = {a: 1, b: 2, c: 3};
|
||||
deepEqual(_.omit(new Obj, 'b'), {a: 1, c: 3}, 'include prototype props');
|
||||
var instance = new Obj();
|
||||
deepEqual(_.omit(instance, 'b'), {a: 1, c: 3}, 'include prototype props');
|
||||
|
||||
deepEqual(_.omit(data, function(val, key) {
|
||||
return this[key] === 3 && this === instance;
|
||||
}, instance), {a: 1, b: 2}, 'function is given context');
|
||||
});
|
||||
|
||||
test('defaults', function() {
|
||||
var result;
|
||||
var options = {zero: 0, one: 1, empty: '', nan: NaN, nothing: null};
|
||||
|
||||
_.defaults(options, {zero: 1, one: 10, twenty: 20, nothing: 'str'});
|
||||
@@ -147,7 +175,7 @@
|
||||
equal(clone.name, 'moe', 'the clone as the attributes of the original');
|
||||
|
||||
clone.name = 'curly';
|
||||
ok(clone.name == 'curly' && moe.name == 'moe', 'clones can change shallow attributes without affecting the original');
|
||||
ok(clone.name === 'curly' && moe.name === 'moe', 'clones can change shallow attributes without affecting the original');
|
||||
|
||||
clone.lucky.push(101);
|
||||
equal(_.last(moe.lucky), 101, 'changes to deep attributes are shared with the original');
|
||||
@@ -199,6 +227,7 @@
|
||||
|
||||
// Comparisons involving `NaN`.
|
||||
ok(_.isEqual(NaN, NaN), '`NaN` is equal to `NaN`');
|
||||
ok(_.isEqual(new Object(NaN), NaN), 'Object(`NaN`) is equal to `NaN`');
|
||||
ok(!_.isEqual(61, NaN), 'A number primitive is not equal to `NaN`');
|
||||
ok(!_.isEqual(new Number(79), NaN), 'A number object is not equal to `NaN`');
|
||||
ok(!_.isEqual(Infinity, NaN), '`Infinity` is not equal to `NaN`');
|
||||
@@ -254,7 +283,7 @@
|
||||
|
||||
// Arrays with primitive and object values.
|
||||
ok(_.isEqual([1, 'Larry', true], [1, 'Larry', true]), 'Arrays containing identical primitives are equal');
|
||||
ok(_.isEqual([(/Moe/g), new Date(2009, 9, 25)], [(/Moe/g), new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal');
|
||||
ok(_.isEqual([/Moe/g, new Date(2009, 9, 25)], [/Moe/g, new Date(2009, 9, 25)]), 'Arrays containing equivalent elements are equal');
|
||||
|
||||
// Multi-dimensional arrays.
|
||||
var a = [new Number(47), false, 'Larry', /Moe/, new Date(2009, 11, 13), ['running', 'biking', new String('programming')], {a: 47}];
|
||||
@@ -277,6 +306,10 @@
|
||||
ok(_.isEqual(Array(3), Array(3)), 'Sparse arrays of identical lengths are equal');
|
||||
ok(!_.isEqual(Array(3), Array(6)), 'Sparse arrays of different lengths are not equal when both are empty');
|
||||
|
||||
var sparse = [];
|
||||
sparse[1] = 5;
|
||||
ok(_.isEqual(sparse, [undefined, 5]), 'Handles sparse arrays as dense');
|
||||
|
||||
// Simple objects.
|
||||
ok(_.isEqual({a: 'Curly', b: 1, c: true}, {a: 'Curly', b: 1, c: true}), 'Objects containing identical primitives are equal');
|
||||
ok(_.isEqual({a: /Curly/g, b: new Date(2009, 11, 13)}, {a: /Curly/g, b: new Date(2009, 11, 13)}), 'Objects containing equivalent members are equal');
|
||||
@@ -393,14 +426,14 @@
|
||||
if (Object.create) {
|
||||
a = Object.create(null, {x: {value: 1, enumerable: true}});
|
||||
b = {x: 1};
|
||||
ok(_.isEqual(a, b));
|
||||
ok(_.isEqual(a, b), 'Handles objects without a constructor (e.g. from Object.create');
|
||||
}
|
||||
|
||||
function Foo() { this.a = 1; }
|
||||
Foo.prototype.constructor = null;
|
||||
|
||||
var other = { 'a': 1 };
|
||||
strictEqual(_.isEqual(new Foo, other), false);
|
||||
var other = {a: 1};
|
||||
strictEqual(_.isEqual(new Foo, other), false, 'Objects from different constructors are not equal');
|
||||
});
|
||||
|
||||
test('isEmpty', function() {
|
||||
@@ -425,25 +458,25 @@
|
||||
|
||||
// Setup remote variables for iFrame tests.
|
||||
var iframe = document.createElement('iframe');
|
||||
iframe.frameBorder = iframe.height = iframe.width = 0
|
||||
iframe.frameBorder = iframe.height = iframe.width = 0;
|
||||
document.body.appendChild(iframe);
|
||||
var iDoc = (iDoc = iframe.contentDocument || iframe.contentWindow).document || iDoc;
|
||||
iDoc.write(
|
||||
'<script>\
|
||||
parent.iElement = document.createElement("div");\
|
||||
parent.iArguments = (function(){ return arguments; })(1, 2, 3);\
|
||||
parent.iArray = [1, 2, 3];\
|
||||
parent.iString = new String("hello");\
|
||||
parent.iNumber = new Number(100);\
|
||||
parent.iFunction = (function(){});\
|
||||
parent.iDate = new Date();\
|
||||
parent.iRegExp = /hi/;\
|
||||
parent.iNaN = NaN;\
|
||||
parent.iNull = null;\
|
||||
parent.iBoolean = new Boolean(false);\
|
||||
parent.iUndefined = undefined;\
|
||||
parent.iObject = {};\
|
||||
</script>'
|
||||
'<script>' +
|
||||
' parent.iElement = document.createElement("div");' +
|
||||
' parent.iArguments = (function(){ return arguments; })(1, 2, 3);' +
|
||||
' parent.iArray = [1, 2, 3];' +
|
||||
' parent.iString = new String("hello");' +
|
||||
' parent.iNumber = new Number(100);' +
|
||||
' parent.iFunction = (function(){});' +
|
||||
' parent.iDate = new Date();' +
|
||||
' parent.iRegExp = /hi/;' +
|
||||
' parent.iNaN = NaN;' +
|
||||
' parent.iNull = null;' +
|
||||
' parent.iBoolean = new Boolean(false);' +
|
||||
' parent.iUndefined = undefined;' +
|
||||
' parent.iObject = {};' +
|
||||
'</script>'
|
||||
);
|
||||
iDoc.close();
|
||||
|
||||
@@ -454,7 +487,7 @@
|
||||
});
|
||||
|
||||
test('isArguments', function() {
|
||||
var args = (function(){ return arguments; })(1, 2, 3);
|
||||
var args = (function(){ return arguments; }(1, 2, 3));
|
||||
ok(!_.isArguments('string'), 'a string is not an arguments object');
|
||||
ok(!_.isArguments(_.isArguments), 'a function is not an arguments object');
|
||||
ok(_.isArguments(args), 'but the arguments object is an arguments object');
|
||||
@@ -607,32 +640,79 @@
|
||||
max().
|
||||
tap(interceptor).
|
||||
value();
|
||||
ok(returned == 6 && intercepted == 6, 'can use tapped objects in a chain');
|
||||
equal(returned, 6, 'can use tapped objects in a chain');
|
||||
equal(intercepted, returned, 'can use tapped objects in a chain');
|
||||
});
|
||||
|
||||
test("has", function () {
|
||||
var obj = {foo: "bar", func: function () {} };
|
||||
ok(_.has(obj, "foo"), "has() checks that the object has a property.");
|
||||
ok(_.has(obj, "baz") == false, "has() returns false if the object doesn't have the property.");
|
||||
ok(_.has(obj, "func"), "has() works for functions too.");
|
||||
test('has', function () {
|
||||
var obj = {foo: 'bar', func: function(){}};
|
||||
ok(_.has(obj, 'foo'), 'has() checks that the object has a property.');
|
||||
ok(!_.has(obj, 'baz'), "has() returns false if the object doesn't have the property.");
|
||||
ok(_.has(obj, 'func'), 'has() works for functions too.');
|
||||
obj.hasOwnProperty = null;
|
||||
ok(_.has(obj, "foo"), "has() works even when the hasOwnProperty method is deleted.");
|
||||
ok(_.has(obj, 'foo'), 'has() works even when the hasOwnProperty method is deleted.');
|
||||
var child = {};
|
||||
child.prototype = obj;
|
||||
ok(_.has(child, "foo") == false, "has() does not check the prototype chain for a property.");
|
||||
ok(!_.has(child, 'foo'), 'has() does not check the prototype chain for a property.');
|
||||
strictEqual(_.has(null, 'foo'), false, 'has() returns false for null');
|
||||
strictEqual(_.has(undefined, 'foo'), false, 'has() returns false for undefined');
|
||||
});
|
||||
|
||||
test("matches", function() {
|
||||
test('matches', function() {
|
||||
var moe = {name: 'Moe Howard', hair: true};
|
||||
var curly = {name: 'Curly Howard', hair: false};
|
||||
var stooges = [moe, curly];
|
||||
ok(_.find(stooges, _.matches({hair: false})) === curly, "returns a predicate that can be used by finding functions.");
|
||||
ok(_.find(stooges, _.matches(moe)) === moe, "can be used to locate an object exists in a collection.");
|
||||
|
||||
equal(_.matches({hair: true})(moe), true, 'Returns a boolean');
|
||||
equal(_.matches({hair: true})(curly), false, 'Returns a boolean');
|
||||
|
||||
equal(_.matches({__x__: undefined})(5), false, 'can match undefined props on primitives');
|
||||
equal(_.matches({__x__: undefined})({__x__: undefined}), true, 'can match undefined props');
|
||||
|
||||
equal(_.matches({})(null), true, 'Empty spec called with null object returns true');
|
||||
equal(_.matches({a: 1})(null), false, 'Non-empty spec called with null object returns false');
|
||||
|
||||
ok(_.find(stooges, _.matches({hair: false})) === curly, 'returns a predicate that can be used by finding functions.');
|
||||
ok(_.find(stooges, _.matches(moe)) === moe, 'can be used to locate an object exists in a collection.');
|
||||
deepEqual(_.where([null, undefined], {a: 1}), [], 'Do not throw on null values.');
|
||||
|
||||
deepEqual(_.where([null, undefined], null), [null, undefined], 'null matches null');
|
||||
deepEqual(_.where([null, undefined], {}), [null, undefined], 'null matches {}');
|
||||
deepEqual(_.where([{b: 1}], {a: undefined}), [], 'handles undefined values (1683)');
|
||||
|
||||
_.each([true, 5, NaN, null, undefined], function(item) {
|
||||
deepEqual(_.where([{a: 1}], item), [{a: 1}], 'treats primitives as empty');
|
||||
});
|
||||
|
||||
})();
|
||||
function Prototest() {}
|
||||
Prototest.prototype.x = 1;
|
||||
var specObj = new Prototest;
|
||||
var protospec = _.matches(specObj);
|
||||
equal(protospec({x: 2}), true, 'spec is restricted to own properties');
|
||||
|
||||
specObj.y = 5;
|
||||
protospec = _.matches(specObj);
|
||||
equal(protospec({x: 1, y: 5}), true);
|
||||
equal(protospec({x: 1, y: 4}), false);
|
||||
|
||||
ok(_.matches({x: 1, y: 5})(specObj), 'inherited and own properties are checked on the test object');
|
||||
|
||||
Prototest.x = 5;
|
||||
ok(_.matches(Prototest)({x: 5, y: 1}), 'spec can be a function');
|
||||
|
||||
// #1729
|
||||
var o = {'b': 1};
|
||||
var m = _.matches(o);
|
||||
|
||||
equal(m({'b': 1}), true);
|
||||
o.b = 2;
|
||||
o.a = 1;
|
||||
equal(m({'b': 1}), true, 'changing spec object doesnt change matches result');
|
||||
|
||||
|
||||
//null edge cases
|
||||
var oCon = _.matches({'constructor': Object});
|
||||
deepEqual(_.map([null, undefined, 5, {}], oCon), [false, false, false, true], 'doesnt fasley match constructor on undefined/null');
|
||||
});
|
||||
|
||||
}());
|
||||
|
||||
91
vendor/underscore/test/utility.js
vendored
91
vendor/underscore/test/utility.js
vendored
@@ -60,7 +60,7 @@
|
||||
|
||||
test('uniqueId', function() {
|
||||
var ids = [], i = 0;
|
||||
while(i++ < 100) ids.push(_.uniqueId());
|
||||
while (i++ < 100) ids.push(_.uniqueId());
|
||||
equal(_.uniq(ids).length, ids.length, 'can generate a globally-unique stream of ids');
|
||||
});
|
||||
|
||||
@@ -91,19 +91,49 @@
|
||||
});
|
||||
|
||||
test('_.escape', function() {
|
||||
equal(_.escape('Curly & Moe'), 'Curly & Moe');
|
||||
equal(_.escape('<a href="http://moe.com">Curly & Moe\'s</a>'), '<a href="http://moe.com">Curly & Moe's</a>');
|
||||
equal(_.escape('Curly & Moe'), 'Curly &amp; Moe');
|
||||
equal(_.escape(null), '');
|
||||
});
|
||||
|
||||
test('_.unescape', function() {
|
||||
var string = 'Curly & Moe';
|
||||
equal(_.unescape('Curly & Moe'), string);
|
||||
equal(_.unescape('<a href="http://moe.com">Curly & Moe's</a>'), '<a href="http://moe.com">Curly & Moe\'s</a>');
|
||||
equal(_.unescape('Curly &amp; Moe'), 'Curly & Moe');
|
||||
equal(_.unescape(null), '');
|
||||
equal(_.unescape(_.escape(string)), string);
|
||||
equal(_.unescape(string), string, 'don\'t unescape unnecessarily');
|
||||
});
|
||||
|
||||
// Don't care what they escape them to just that they're escaped and can be unescaped
|
||||
test('_.escape & unescape', function() {
|
||||
// test & (&) seperately obviously
|
||||
var escapeCharacters = ['<', '>', '"', '\'', '`'];
|
||||
|
||||
_.each(escapeCharacters, function(escapeChar) {
|
||||
var str = 'a ' + escapeChar + ' string escaped';
|
||||
var escaped = _.escape(str);
|
||||
notEqual(str, escaped, escapeChar + ' is escaped');
|
||||
equal(str, _.unescape(escaped), escapeChar + ' can be unescaped');
|
||||
|
||||
str = 'a ' + escapeChar + escapeChar + escapeChar + 'some more string' + escapeChar;
|
||||
escaped = _.escape(str);
|
||||
|
||||
equal(escaped.indexOf(escapeChar), -1, 'can escape multiple occurances of ' + escapeChar);
|
||||
equal(_.unescape(escaped), str, 'multiple occurrences of ' + escapeChar + ' can be unescaped');
|
||||
});
|
||||
|
||||
// handles multiple escape characters at once
|
||||
var joiner = ' other stuff ';
|
||||
var allEscaped = escapeCharacters.join(joiner);
|
||||
allEscaped += allEscaped;
|
||||
ok(_.every(escapeCharacters, function(escapeChar) {
|
||||
return allEscaped.indexOf(escapeChar) !== -1;
|
||||
}), 'handles multiple characters');
|
||||
ok(allEscaped.indexOf(joiner) >= 0, 'can escape multiple escape characters at the same time');
|
||||
|
||||
// test & -> &
|
||||
var str = 'some string & another string & yet another';
|
||||
var escaped = _.escape(str);
|
||||
|
||||
ok(escaped.indexOf('&') !== -1, 'handles & aka &');
|
||||
equal(_.unescape(str), str, 'can unescape &');
|
||||
});
|
||||
|
||||
test('template', function() {
|
||||
@@ -120,9 +150,9 @@
|
||||
var escapeTemplate = _.template('<%= a ? "checked=\\"checked\\"" : "" %>');
|
||||
equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.');
|
||||
|
||||
var fancyTemplate = _.template('<ul><% \
|
||||
for (var key in people) { \
|
||||
%><li><%= people[key] %></li><% } %></ul>');
|
||||
var fancyTemplate = _.template('<ul><% ' +
|
||||
' for (var key in people) { ' +
|
||||
'%><li><%= people[key] %></li><% } %></ul>');
|
||||
result = fancyTemplate({people : {moe : 'Moe', larry : 'Larry', curly : 'Curly'}});
|
||||
equal(result, '<ul><li>Moe</li><li>Larry</li><li>Curly</li></ul>', 'can run arbitrary javascript in templates');
|
||||
|
||||
@@ -148,16 +178,16 @@
|
||||
var quoteTemplate = _.template("It's its, not it's");
|
||||
equal(quoteTemplate({}), "It's its, not it's");
|
||||
|
||||
var quoteInStatementAndBody = _.template("<%\
|
||||
if(foo == 'bar'){ \
|
||||
%>Statement quotes and 'quotes'.<% } %>");
|
||||
var quoteInStatementAndBody = _.template('<% ' +
|
||||
" if(foo == 'bar'){ " +
|
||||
"%>Statement quotes and 'quotes'.<% } %>");
|
||||
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
|
||||
|
||||
var withNewlinesAndTabs = _.template('This\n\t\tis: <%= x %>.\n\tok.\nend.');
|
||||
equal(withNewlinesAndTabs({x: 'that'}), 'This\n\t\tis: that.\n\tok.\nend.');
|
||||
|
||||
var template = _.template('<i><%- value %></i>');
|
||||
var result = template({value: '<script>'});
|
||||
result = template({value: '<script>'});
|
||||
equal(result, '<i><script></i>');
|
||||
|
||||
var stooge = {
|
||||
@@ -166,12 +196,12 @@
|
||||
};
|
||||
equal(stooge.template(), "I'm Moe");
|
||||
|
||||
template = _.template('\n \
|
||||
<%\n \
|
||||
// a comment\n \
|
||||
if (data) { data += 12345; }; %>\n \
|
||||
<li><%= data %></li>\n \
|
||||
');
|
||||
template = _.template('\n ' +
|
||||
' <%\n ' +
|
||||
' // a comment\n ' +
|
||||
' if (data) { data += 12345; }; %>\n ' +
|
||||
' <li><%= data %></li>\n '
|
||||
);
|
||||
equal(template({data : 12345}).replace(/\s/g, ''), '<li>24690</li>');
|
||||
|
||||
_.templateSettings = {
|
||||
@@ -186,7 +216,7 @@
|
||||
var customQuote = _.template("It's its, not it's");
|
||||
equal(customQuote({}), "It's its, not it's");
|
||||
|
||||
var quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
|
||||
quoteInStatementAndBody = _.template("{{ if(foo == 'bar'){ }}Statement quotes and 'quotes'.{{ } }}");
|
||||
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
|
||||
|
||||
_.templateSettings = {
|
||||
@@ -201,7 +231,7 @@
|
||||
var customWithSpecialCharsQuote = _.template("It's its, not it's");
|
||||
equal(customWithSpecialCharsQuote({}), "It's its, not it's");
|
||||
|
||||
var quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
|
||||
quoteInStatementAndBody = _.template("<? if(foo == 'bar'){ ?>Statement quotes and 'quotes'.<? } ?>");
|
||||
equal(quoteInStatementAndBody({foo: 'bar'}), "Statement quotes and 'quotes'.");
|
||||
|
||||
_.templateSettings = {
|
||||
@@ -241,7 +271,8 @@
|
||||
test('_.templateSettings.variable', function() {
|
||||
var s = '<%=data.x%>';
|
||||
var data = {x: 'x'};
|
||||
strictEqual(_.template(s, data, {variable: 'data'}), 'x');
|
||||
var tmp = _.template(s, {variable: 'data'});
|
||||
strictEqual(tmp(data), 'x');
|
||||
_.templateSettings.variable = 'data';
|
||||
strictEqual(_.template(s)(data), 'x');
|
||||
});
|
||||
@@ -262,22 +293,22 @@
|
||||
strictEqual(templateEscaped({x: undefined}), '');
|
||||
|
||||
var templateWithProperty = _.template('<%=x.foo%>');
|
||||
strictEqual(templateWithProperty({x: {} }), '');
|
||||
strictEqual(templateWithProperty({x: {} }), '');
|
||||
strictEqual(templateWithProperty({x: {}}), '');
|
||||
strictEqual(templateWithProperty({x: {}}), '');
|
||||
|
||||
var templateWithPropertyEscaped = _.template('<%-x.foo%>');
|
||||
strictEqual(templateWithPropertyEscaped({x: {} }), '');
|
||||
strictEqual(templateWithPropertyEscaped({x: {} }), '');
|
||||
strictEqual(templateWithPropertyEscaped({x: {}}), '');
|
||||
strictEqual(templateWithPropertyEscaped({x: {}}), '');
|
||||
});
|
||||
|
||||
test('interpolate evaluates code only once.', 2, function() {
|
||||
var count = 0;
|
||||
var template = _.template('<%= f() %>');
|
||||
template({f: function(){ ok(!(count++)); }});
|
||||
template({f: function(){ ok(!count++); }});
|
||||
|
||||
var countEscaped = 0;
|
||||
var templateEscaped = _.template('<%- f() %>');
|
||||
templateEscaped({f: function(){ ok(!(countEscaped++)); }});
|
||||
templateEscaped({f: function(){ ok(!countEscaped++); }});
|
||||
});
|
||||
|
||||
test('#746 - _.template settings are not modified.', 1, function() {
|
||||
@@ -291,4 +322,4 @@
|
||||
strictEqual(template(), '<<\nx\n>>');
|
||||
});
|
||||
|
||||
})();
|
||||
}());
|
||||
|
||||
4
vendor/underscore/underscore-min.js
vendored
4
vendor/underscore/underscore-min.js
vendored
File diff suppressed because one or more lines are too long
663
vendor/underscore/underscore.js
vendored
663
vendor/underscore/underscore.js
vendored
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user