From 87f880ca526901bef1f301416d0f38f45cfa988a Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Fri, 28 Dec 2012 19:47:44 -0600 Subject: [PATCH] Update vendors. Former-commit-id: baf89d2c3bd7077462995bffa7f8bff1e1cf28f9 --- vendor/backbone/backbone.js | 409 ++++++++++++-------------- vendor/backbone/test/collection.js | 79 ++++- vendor/backbone/test/events.js | 28 +- vendor/backbone/test/model.js | 294 ++++++++++++------ vendor/backbone/test/router.js | 22 +- vendor/backbone/test/view.js | 16 - vendor/underscore/test/arrays.js | 2 +- vendor/underscore/test/collections.js | 2 +- vendor/underscore/test/objects.js | 4 +- vendor/underscore/test/utility.js | 20 +- vendor/underscore/underscore-min.js | 2 +- vendor/underscore/underscore.js | 2 +- 12 files changed, 499 insertions(+), 381 deletions(-) diff --git a/vendor/backbone/backbone.js b/vendor/backbone/backbone.js index f4cfabd26..dd262c835 100644 --- a/vendor/backbone/backbone.js +++ b/vendor/backbone/backbone.js @@ -160,7 +160,8 @@ if (callback || context) { for (j = 0, k = list.length; j < k; j++) { ev = list[j]; - if ((callback && callback !== (ev.callback._callback || ev.callback)) || + if ((callback && callback !== ev.callback && + callback !== ev.callback._callback) || (context && context !== ev.context)) { events.push(ev); } @@ -190,27 +191,25 @@ // An inversion-of-control version of `on`. Tell *this* object to listen to // an event in another object ... keeping track of what it's listening to. - listenTo: function(object, events, callback, context) { - context = context || this; + listenTo: function(object, events, callback) { var listeners = this._listeners || (this._listeners = {}); var id = object._listenerId || (object._listenerId = _.uniqueId('l')); listeners[id] = object; - object.on(events, callback || context, context); + object.on(events, callback || this, this); return this; }, // Tell this object to stop listening to either specific events ... or // to every object it's currently listening to. - stopListening: function(object, events, callback, context) { - context = context || this; + stopListening: function(object, events, callback) { var listeners = this._listeners; if (!listeners) return; if (object) { - object.off(events, callback, context); + object.off(events, callback, this); if (!events && !callback) delete listeners[object._listenerId]; } else { for (var id in listeners) { - listeners[id].off(null, null, context); + listeners[id].off(null, null, this); } this._listeners = {}; } @@ -235,15 +234,14 @@ var defaults; var attrs = attributes || {}; this.cid = _.uniqueId('c'); - this.changed = {}; this.attributes = {}; - this._changes = []; if (options && options.collection) this.collection = options.collection; - if (options && options.parse) attrs = this.parse(attrs, options); - if (defaults = _.result(this, 'defaults')) _.defaults(attrs, defaults); - this.set(attrs, {silent: true}); - this._currentAttributes = _.clone(this.attributes); - this._previousAttributes = _.clone(this.attributes); + if (options && options.parse) attrs = this.parse(attrs, options) || {}; + if (defaults = _.result(this, 'defaults')) { + attrs = _.defaults({}, attrs, defaults); + } + this.set(attrs, options); + this.changed = {}; this.initialize.apply(this, arguments); }; @@ -287,47 +285,72 @@ return this.get(attr) != null; }, + // ---------------------------------------------------------------------- + // Set a hash of model attributes on the object, firing `"change"` unless // you choose to silence it. set: function(key, val, options) { - var attr, attrs; + var attr, attrs, unset, changes, silent, changing, prev, current; if (key == null) return this; // Handle both `"key", value` and `{key: value}` -style arguments. - if (_.isObject(key)) { + if (typeof key === 'object') { attrs = key; options = val; } else { (attrs = {})[key] = val; } - // Extract attributes and options. - var silent = options && options.silent; - var unset = options && options.unset; + options || (options = {}); // Run validation. if (!this._validate(attrs, options)) return false; + // Extract attributes and options. + unset = options.unset; + silent = options.silent; + changes = []; + changing = this._changing; + this._changing = true; + + if (!changing) { + this._previousAttributes = _.clone(this.attributes); + this.changed = {}; + } + current = this.attributes, prev = this._previousAttributes; + // Check for changes of `id`. if (this.idAttribute in attrs) this.id = attrs[this.idAttribute]; - var now = this.attributes; - - // For each `set` attribute... + // For each `set` attribute, update or delete the current value. for (attr in attrs) { val = attrs[attr]; - - // Update or delete the current value, and track the change. - unset ? delete now[attr] : now[attr] = val; - this._changes.push(attr, val); + if (!_.isEqual(current[attr], val)) changes.push(attr); + if (!_.isEqual(prev[attr], val)) { + this.changed[attr] = val; + } else { + delete this.changed[attr]; + } + unset ? delete current[attr] : current[attr] = val; } - // Signal that the model's state has potentially changed, and we need - // to recompute the actual changes. - this._hasComputed = false; + // Trigger all relevant attribute changes. + if (!silent) { + if (changes.length) this._pending = true; + for (var i = 0, l = changes.length; i < l; i++) { + this.trigger('change:' + changes[i], this, current[changes[i]], options); + } + } - // Fire the `"change"` events. - if (!silent) this.change(options); + if (changing) return this; + if (!silent) { + while (this._pending) { + this._pending = false; + this.trigger('change', this, options); + } + } + this._pending = false; + this._changing = false; return this; }, @@ -345,15 +368,53 @@ return this.set(attrs, _.extend({}, options, {unset: true})); }, + // Determine if the model has changed since the last `"change"` event. + // If you specify an attribute name, determine if that attribute has changed. + hasChanged: function(attr) { + if (attr == null) return !_.isEmpty(this.changed); + return _.has(this.changed, attr); + }, + + // Return an object containing all the attributes that have changed, or + // false if there are no changed attributes. Useful for determining what + // parts of a view need to be updated and/or what attributes need to be + // persisted to the server. Unset attributes will be set to undefined. + // You can also pass an attributes object to diff against the model, + // determining if there *would be* a change. + changedAttributes: function(diff) { + if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; + var val, changed = false; + var old = this._changing ? this._previousAttributes : this.attributes; + for (var attr in diff) { + if (_.isEqual(old[attr], (val = diff[attr]))) continue; + (changed || (changed = {}))[attr] = val; + } + return changed; + }, + + // Get the previous value of an attribute, recorded at the time the last + // `"change"` event was fired. + previous: function(attr) { + if (attr == null || !this._previousAttributes) return null; + return this._previousAttributes[attr]; + }, + + // Get all of the attributes of the model at the time of the previous + // `"change"` event. + previousAttributes: function() { + return _.clone(this._previousAttributes); + }, + + // --------------------------------------------------------------------- + // Fetch the model from the server. If the server's representation of the // model differs from its current attributes, they will be overriden, // triggering a `"change"` event. fetch: function(options) { options = options ? _.clone(options) : {}; if (options.parse === void 0) options.parse = true; - var model = this; var success = options.success; - options.success = function(resp, status, xhr) { + options.success = function(model, resp, options) { if (!model.set(model.parse(resp, options), options)) return false; if (success) success(model, resp, options); }; @@ -364,55 +425,50 @@ // If the server returns an attributes hash that differs, the model's // state will be `set` again. save: function(key, val, options) { - var attrs, current, done; + var attrs, model, success, method, xhr, attributes = this.attributes; // Handle both `"key", value` and `{key: value}` -style arguments. - if (key == null || _.isObject(key)) { + if (key == null || typeof key === 'object') { attrs = key; options = val; - } else if (key != null) { + } else { (attrs = {})[key] = val; } - options = options ? _.clone(options) : {}; - // If we're "wait"-ing to set changed attributes, validate early. - if (options.wait) { - if (attrs && !this._validate(attrs, options)) return false; - current = _.clone(this.attributes); - } + // If we're not waiting and attributes exist, save acts as `set(attr).save(null, opts)`. + if (attrs && (!options || !options.wait) && !this.set(attrs, options)) return false; - // Regular saves `set` attributes before persisting to the server. - var silentOptions = _.extend({}, options, {silent: true}); - if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) { - return false; - } + options = _.extend({validate: true}, options); // Do not persist invalid models. - if (!attrs && !this._validate(null, options)) return false; + if (!this._validate(attrs, options)) return false; + + // Set temporary attributes if `{wait: true}`. + if (attrs && options.wait) { + this.attributes = _.extend({}, attributes, attrs); + } // After a successful server-side save, the client is (optionally) // updated with the server-side state. - var model = this; - var success = options.success; - options.success = function(resp, status, xhr) { - done = true; + success = options.success; + options.success = function(model, resp, options) { + // Ensure attributes are restored during synchronous saves. + model.attributes = attributes; var serverAttrs = model.parse(resp, options); if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs); - if (!model.set(serverAttrs, options)) return false; + if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) { + return false; + } if (success) success(model, resp, options); }; // Finish configuring and sending the Ajax request. - var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); + method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); if (method == 'patch') options.attrs = attrs; - var xhr = this.sync(method, this, options); + xhr = this.sync(method, this, options); - // When using `wait`, reset attributes to original values unless - // `success` has been called already. - if (!done && options.wait) { - this.clear(silentOptions); - this.set(current, silentOptions); - } + // Restore attributes. + if (attrs && options.wait) this.attributes = attributes; return xhr; }, @@ -429,13 +485,13 @@ model.trigger('destroy', model, model.collection, options); }; - options.success = function(resp) { + options.success = function(model, resp, options) { if (options.wait || model.isNew()) destroy(); if (success) success(model, resp, options); }; if (this.isNew()) { - options.success(); + options.success(this, null, options); return false; } @@ -469,115 +525,20 @@ return this.id == null; }, - // Call this method to manually fire a `"change"` event for this model and - // a `"change:attribute"` event for each changed attribute. - // Calling this will cause all objects observing the model to update. - change: function(options) { - var changing = this._changing; - this._changing = true; - - // Generate the changes to be triggered on the model. - var triggers = this._computeChanges(true); - - this._pending = !!triggers.length; - - for (var i = triggers.length - 2; i >= 0; i -= 2) { - this.trigger('change:' + triggers[i], this, triggers[i + 1], options); - } - - if (changing) return this; - - // Trigger a `change` while there have been changes. - while (this._pending) { - this._pending = false; - this.trigger('change', this, options); - this._previousAttributes = _.clone(this.attributes); - } - - this._changing = false; - return this; - }, - - // Determine if the model has changed since the last `"change"` event. - // If you specify an attribute name, determine if that attribute has changed. - hasChanged: function(attr) { - if (!this._hasComputed) this._computeChanges(); - if (attr == null) return !_.isEmpty(this.changed); - return _.has(this.changed, attr); - }, - - // Return an object containing all the attributes that have changed, or - // false if there are no changed attributes. Useful for determining what - // parts of a view need to be updated and/or what attributes need to be - // persisted to the server. Unset attributes will be set to undefined. - // You can also pass an attributes object to diff against the model, - // determining if there *would be* a change. - changedAttributes: function(diff) { - if (!diff) return this.hasChanged() ? _.clone(this.changed) : false; - var val, changed = false, old = this._previousAttributes; - for (var attr in diff) { - if (_.isEqual(old[attr], (val = diff[attr]))) continue; - (changed || (changed = {}))[attr] = val; - } - return changed; - }, - - // Looking at the built up list of `set` attribute changes, compute how - // many of the attributes have actually changed. If `loud`, return a - // boiled-down list of only the real changes. - _computeChanges: function(loud) { - this.changed = {}; - var already = {}; - var triggers = []; - var current = this._currentAttributes; - var changes = this._changes; - - // Loop through the current queue of potential model changes. - for (var i = changes.length - 2; i >= 0; i -= 2) { - var key = changes[i], val = changes[i + 1]; - if (already[key]) continue; - already[key] = true; - - // Check if the attribute has been modified since the last change, - // and update `this.changed` accordingly. If we're inside of a `change` - // call, also add a trigger to the list. - if (!_.isEqual(current[key], val)) { - this.changed[key] = val; - if (!loud) continue; - triggers.push(key, val); - current[key] = val; - } - } - if (loud) this._changes = []; - - // Signals `this.changed` is current to prevent duplicate calls from `this.hasChanged`. - this._hasComputed = true; - return triggers; - }, - - // Get the previous value of an attribute, recorded at the time the last - // `"change"` event was fired. - previous: function(attr) { - if (attr == null || !this._previousAttributes) return null; - return this._previousAttributes[attr]; - }, - - // Get all of the attributes of the model at the time of the previous - // `"change"` event. - previousAttributes: function() { - return _.clone(this._previousAttributes); + // Check if the model is currently in a valid state. + isValid: function(options) { + return !this.validate || !this.validate(this.attributes, options); }, // Run validation against the next complete set of model attributes, // returning `true` if all is well. Otherwise, fire a general // `"error"` event and call the error callback, if specified. _validate: function(attrs, options) { - if (!this.validate) return true; + if (!options.validate || !this.validate) return true; attrs = _.extend({}, this.attributes, attrs); - var error = this.validate(attrs, options); + var error = this.validationError = this.validate(attrs, options) || null; if (!error) return true; - if (options && options.error) options.error(this, error, options); - this.trigger('error', this, error, options); + this.trigger('invalid', this, error, options || {}); return false; } @@ -620,20 +581,22 @@ return Backbone.sync.apply(this, arguments); }, - // Add a model, or list of models to the set. Pass **silent** to avoid - // firing the `add` event for every new model. + // Add a model, or list of models to the set. add: function(models, options) { - var i, args, length, model, existing, needsSort; - var at = options && options.at; - var sort = ((options && options.sort) == null ? true : options.sort); models = _.isArray(models) ? models.slice() : [models]; + options || (options = {}); + var i, l, model, attrs, existing, sort, doSort, sortAttr, at, add; + add = []; + at = options.at; + sort = this.comparator && (at == null) && (options.sort == null || options.sort); + sortAttr = _.isString(this.comparator) ? this.comparator : null; // Turn bare objects into model references, and prevent invalid models // from being added. - for (i = models.length - 1; i >= 0; i--) { - if(!(model = this._prepareModel(models[i], options))) { - this.trigger("error", this, models[i], options); - models.splice(i, 1); + for (i = 0, l = models.length; i < l; i++) { + attrs = models[i]; + if(!(model = this._prepareModel(attrs, options))) { + this.trigger('invalid', this, attrs, options); continue; } models[i] = model; @@ -641,14 +604,16 @@ // If a duplicate is found, prevent it from being added and // optionally merge it into the existing model. if (existing = this.get(model)) { - if (options && options.merge) { - existing.set(model.attributes, options); - needsSort = sort; + if (options.merge) { + existing.set(attrs === model ? model.attributes : attrs, options); + if (sort && !doSort && existing.hasChanged(sortAttr)) doSort = true; } - models.splice(i, 1); continue; } + // This is a new model, push it to the `add` list. + add.push(model); + // Listen to added models' events, and index models for lookup by // `id` and by `cid`. model.on('all', this._onModelEvent, this); @@ -657,31 +622,37 @@ } // See if sorting is needed, update `length` and splice in new models. - if (models.length) needsSort = sort; - this.length += models.length; - args = [at != null ? at : this.models.length, 0]; - push.apply(args, models); - splice.apply(this.models, args); + if (add.length) { + if (sort) doSort = true; + this.length += add.length; + if (at != null) { + splice.apply(this.models, [at, 0].concat(add)); + } else { + push.apply(this.models, add); + } + } - // Sort the collection if appropriate. - if (needsSort && this.comparator && at == null) this.sort({silent: true}); + // Silently sort the collection if appropriate. + if (doSort) this.sort({silent: true}); - if (options && options.silent) return this; + if (options.silent) return this; // Trigger `add` events. - while (model = models.shift()) { - model.trigger('add', model, this, options); + for (i = 0, l = add.length; i < l; i++) { + (model = add[i]).trigger('add', model, this, options); } + // Trigger `sort` if the collection was sorted. + if (doSort) this.trigger('sort', this, options); + return this; }, - // Remove a model, or a list of models from the set. Pass silent to avoid - // firing the `remove` event for every model removed. + // Remove a model, or a list of models from the set. remove: function(models, options) { - var i, l, index, model; - options || (options = {}); models = _.isArray(models) ? models.slice() : [models]; + options || (options = {}); + var i, l, index, model; for (i = 0, l = models.length; i < l; i++) { model = this.get(models[i]); if (!model) continue; @@ -762,14 +733,16 @@ if (!this.comparator) { throw new Error('Cannot sort a set without a comparator'); } + options || (options = {}); + // Run sort based on type of `comparator`. if (_.isString(this.comparator) || this.comparator.length === 1) { this.models = this.sortBy(this.comparator, this); } else { this.models.sort(_.bind(this.comparator, this)); } - if (!options || !options.silent) this.trigger('sort', this, options); + if (!options.silent) this.trigger('sort', this, options); return this; }, @@ -781,10 +754,10 @@ // Smartly update a collection with a change set of models, adding, // removing, and merging as necessary. update: function(models, options) { - var model, i, l, existing; - var add = [], remove = [], modelMap = {}; options = _.extend({add: true, merge: true, remove: true}, options); if (options.parse) models = this.parse(models, options); + var model, i, l, existing; + var add = [], remove = [], modelMap = {}; // Allow a single model (or no argument) to be passed. if (!_.isArray(models)) models = models ? [models] : []; @@ -836,9 +809,8 @@ fetch: function(options) { options = options ? _.clone(options) : {}; if (options.parse === void 0) options.parse = true; - var collection = this; var success = options.success; - options.success = function(resp, status, xhr) { + options.success = function(collection, resp, options) { var method = options.update ? 'update' : 'reset'; collection[method](resp, options); if (success) success(collection, resp, options); @@ -850,9 +822,9 @@ // collection immediately, unless `wait: true` is passed, in which case we // wait for the server to agree. create: function(model, options) { - var collection = this; options = options ? _.clone(options) : {}; model = this._prepareModel(model, options); + var collection = this; if (!model) return false; if (!options.wait) collection.add(model, options); var success = options.success; @@ -929,7 +901,7 @@ 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke', 'max', 'min', 'sortedIndex', 'toArray', 'size', 'first', 'head', 'take', - 'initial', 'rest', 'tail', 'last', 'without', 'indexOf', 'shuffle', + 'initial', 'rest', 'tail', 'drop', 'last', 'without', 'indexOf', 'shuffle', 'lastIndexOf', 'isEmpty']; // Mix in each Underscore method as a proxy to `Collection#models`. @@ -969,7 +941,7 @@ // Cached regular expressions for matching named param parts and splatted // parts of route strings. var optionalParam = /\((.*?)\)/g; - var namedParam = /:\w+/g; + var namedParam = /(\(\?)?:\w+/g; var splatParam = /\*\w+/g; var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; @@ -1020,7 +992,9 @@ _routeToRegExp: function(route) { route = route.replace(escapeRegExp, '\\$&') .replace(optionalParam, '(?:$1)?') - .replace(namedParam, '([^\/]+)') + .replace(namedParam, function(match, optional){ + return optional ? match : '([^\/]+)'; + }) .replace(splatParam, '(.*?)'); return new RegExp('^' + route + '$'); }, @@ -1121,9 +1095,9 @@ // Depending on whether we're using pushState or hashes, and whether // 'onhashchange' is supported, determine how we check the URL state. if (this._hasPushState) { - Backbone.$(window).bind('popstate', this.checkUrl); + Backbone.$(window).on('popstate', this.checkUrl); } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) { - Backbone.$(window).bind('hashchange', this.checkUrl); + Backbone.$(window).on('hashchange', this.checkUrl); } else if (this._wantsHashChange) { this._checkUrlInterval = setInterval(this.checkUrl, this.interval); } @@ -1155,7 +1129,7 @@ // Disable Backbone.history, perhaps temporarily. Not useful in a real app, // but possibly useful for unit testing Routers. stop: function() { - Backbone.$(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl); + Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl); clearInterval(this._checkUrlInterval); History.started = false; }, @@ -1298,18 +1272,6 @@ return this; }, - // For small amounts of DOM Elements, where a full-blown template isn't - // needed, use **make** to manufacture elements, one at a time. - // - // var el = this.make('li', {'class': 'row'}, this.model.escape('title')); - // - make: function(tagName, attributes, content) { - var el = document.createElement(tagName); - if (attributes) Backbone.$(el).attr(attributes); - if (content != null) Backbone.$(el).html(content); - return el; - }, - // Change the view's element (`this.el` property), including event // re-delegation. setElement: function(element, delegate) { @@ -1347,9 +1309,9 @@ method = _.bind(method, this); eventName += '.delegateEvents' + this.cid; if (selector === '') { - this.$el.bind(eventName, method); + this.$el.on(eventName, method); } else { - this.$el.delegate(selector, eventName, method); + this.$el.on(eventName, selector, method); } } }, @@ -1358,7 +1320,7 @@ // 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() { - this.$el.unbind('.delegateEvents' + this.cid); + this.$el.off('.delegateEvents' + this.cid); }, // Performs the initial configuration of a View with a set of options. @@ -1379,7 +1341,8 @@ 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.make(_.result(this, 'tagName'), attrs), false); + var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs); + this.setElement($el, false); } else { this.setElement(_.result(this, 'el'), false); } @@ -1461,13 +1424,13 @@ } var success = options.success; - options.success = function(resp, status, xhr) { - if (success) success(resp, status, xhr); + options.success = function(resp) { + if (success) success(model, resp, options); model.trigger('sync', model, resp, options); }; var error = options.error; - options.error = function(xhr, status, thrown) { + options.error = function(xhr) { if (error) error(model, xhr, options); model.trigger('error', model, xhr, options); }; diff --git a/vendor/backbone/test/collection.js b/vendor/backbone/test/collection.js index 28b857a44..0bca07d94 100644 --- a/vendor/backbone/test/collection.js +++ b/vendor/backbone/test/collection.js @@ -82,7 +82,7 @@ $(document).ready(function() { equal(col.get(101), model); var Col2 = Backbone.Collection.extend({ model: MongoModel }); - col2 = new Col2(); + var col2 = new Col2(); col2.push(model); equal(col2.get({_id: 101}), model); equal(col2.get(model.clone()), model); @@ -362,7 +362,9 @@ $(document).ready(function() { test("model destroy removes from all collections", 3, function() { var e = new Backbone.Model({id: 5, title: 'Othello'}); - e.sync = function(method, model, options) { options.success({}); }; + e.sync = function(method, model, options) { + options.success(model, [], options); + }; var colE = new Backbone.Collection([e]); var colF = new Backbone.Collection([e]); e.destroy(); @@ -417,7 +419,7 @@ $(document).ready(function() { equal(model.collection, collection); }); - test("create enforces validation", 1, function() { + test("create with validate:true enforces validation", 1, function() { var ValidatingModel = Backbone.Model.extend({ validate: function(attrs) { return "fail"; @@ -427,10 +429,10 @@ $(document).ready(function() { model: ValidatingModel }); var col = new ValidatingCollection(); - equal(col.create({"foo":"bar"}), false); + equal(col.create({"foo":"bar"}, {validate:true}), false); }); - test("a failing create runs the error callback", 1, function() { + test("a failing create returns model with errors", function() { var ValidatingModel = Backbone.Model.extend({ validate: function(attrs) { return "fail"; @@ -439,11 +441,10 @@ $(document).ready(function() { var ValidatingCollection = Backbone.Collection.extend({ model: ValidatingModel }); - var flag = false; - var callback = function(model, error) { flag = true; }; var col = new ValidatingCollection(); - col.create({"foo":"bar"}, { error: callback }); - equal(flag, true); + var m = col.create({"foo":"bar"}); + equal(m.validationError, 'fail'); + equal(col.length, 1); }); test("initialize", 1, function() { @@ -567,7 +568,7 @@ $(document).ready(function() { equal(col.length, 0); }); - test("#861, adding models to a collection which do not pass validation", function() { + test("#861, adding models to a collection which do not pass validation, with validate:true", function() { var Model = Backbone.Model.extend({ validate: function(attrs) { if (attrs.id == 3) return "id can't be 3"; @@ -581,18 +582,18 @@ $(document).ready(function() { var collection = new Collection; collection.on("error", function() { ok(true); }); - collection.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}]); + collection.add([{id: 1}, {id: 2}, {id: 3}, {id: 4}, {id: 5}, {id: 6}], {validate:true}); deepEqual(collection.pluck('id'), [1, 2, 4, 5, 6]); }); - test("Invalid models are discarded.", 5, function() { + test("Invalid models are discarded with validate:true.", 5, function() { var collection = new Backbone.Collection; collection.on('test', function() { ok(true); }); collection.model = Backbone.Model.extend({ validate: function(attrs){ if (!attrs.valid) return 'invalid'; } }); var model = new collection.model({id: 1, valid: true}); - collection.add([model, {id: 2}]); + collection.add([model, {id: 2}], {validate:true}); model.trigger('test'); ok(collection.get(model.cid)); ok(collection.get(1)); @@ -656,7 +657,7 @@ $(document).ready(function() { } }; col.sync = m.sync = function( method, collection, options ){ - options.success(); + options.success(collection, [], options); }; col.fetch(opts); col.create(m, opts); @@ -674,7 +675,9 @@ $(document).ready(function() { test("#1447 - create with wait adds model.", 1, function() { var collection = new Backbone.Collection; var model = new Backbone.Model; - model.sync = function(method, model, options){ options.success(); }; + model.sync = function(method, model, options){ + options.success(model, [], options); + }; collection.on('add', function(){ ok(true); }); collection.create(model, {wait: true}); }); @@ -880,6 +883,24 @@ $(document).ready(function() { equal(c.length, 2); }); + test("update + merge with default values defined", function() { + var Model = Backbone.Model.extend({ + defaults: { + key: 'value' + } + }); + var m = new Model({id: 1}); + var col = new Backbone.Collection([m], {model: Model}); + equal(col.first().get('key'), 'value'); + + col.update({id: 1, key: 'other'}); + equal(col.first().get('key'), 'other'); + + col.update({id: 1, other: 'value'}); + equal(col.first().get('key'), 'other'); + equal(col.length, 1); + }); + test("#1894 - Push should not trigger a sort", 0, function() { var Collection = Backbone.Collection.extend({ comparator: 'id', @@ -937,4 +958,32 @@ $(document).ready(function() { Backbone.ajax = ajax; }); + test("`add` only `sort`s when necessary", 2, function () { + var collection = new (Backbone.Collection.extend({ + comparator: 'a' + }))([{id: 1}, {id: 2}, {id: 3}]); + collection.on('sort', function () { ok(true); }); + collection.add({id: 4}); // do sort, new model + collection.add({id: 1, a: 1}, {merge: true}); // do sort, comparator change + collection.add({id: 1, b: 1}, {merge: true}); // don't sort, no comparator change + collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no comparator change + collection.add(collection.models); // don't sort, nothing new + collection.add(collection.models, {merge: true}); // don't sort + }); + + test("`add` only `sort`s when necessary with comparator function", 3, function () { + var collection = new (Backbone.Collection.extend({ + comparator: function(a, b) { + a.get('a') > b.get('a') ? 1 : (a.get('a') < b.get('a') ? -1 : 0); + } + }))([{id: 1}, {id: 2}, {id: 3}]); + collection.on('sort', function () { ok(true); }); + collection.add({id: 4}); // do sort, new model + collection.add({id: 1, a: 1}, {merge: true}); // do sort, model change + collection.add({id: 1, b: 1}, {merge: true}); // do sort, model change + collection.add({id: 1, a: 1}, {merge: true}); // don't sort, no model change + collection.add(collection.models); // don't sort, nothing new + collection.add(collection.models, {merge: true}); // don't sort + }); + }); diff --git a/vendor/backbone/test/events.js b/vendor/backbone/test/events.js index 946c25bb0..cf6c8f53a 100644 --- a/vendor/backbone/test/events.js +++ b/vendor/backbone/test/events.js @@ -86,30 +86,6 @@ $(document).ready(function() { b.trigger('change'); }); - test("listenTo with context", 1, function() { - var a = _.extend({}, Backbone.Events); - var ctx = {}; - a.listenTo(a, 'foo', function(){ equal(this, ctx); }, ctx); - a.trigger('foo'); - }); - - test("stopListening with context", 2, function() { - var a = _.extend({}, Backbone.Events); - var ctx = {}; - var calledWithContext = false; - var calledWithoutContext = false; - - a.listenTo(a, 'foo', function(){ calledWithContext = true; }, ctx); - a.listenTo(a, 'foo', function(){ calledWithoutContext = true; }); - - a.stopListening(a, 'foo', null, ctx); - - a.trigger('foo'); - - equal(false, calledWithContext); - equal(true, calledWithoutContext); - }); - test("trigger all for each event", 3, function() { var a, b, obj = { counter: 0 }; _.extend(obj, Backbone.Events); @@ -384,4 +360,8 @@ $(document).ready(function() { Backbone.trigger('all'); }); + test("once without a callback is a noop", 0, function() { + _.extend({}, Backbone.Events).once('event').trigger('event'); + }); + }); diff --git a/vendor/backbone/test/model.js b/vendor/backbone/test/model.js index c09729801..8b8fe7109 100644 --- a/vendor/backbone/test/model.js +++ b/vendor/backbone/test/model.js @@ -202,9 +202,9 @@ $(document).ready(function() { ok(changeCount == 1, "Change count should NOT have incremented."); a.validate = function(attrs) { - equal(attrs.foo, void 0, "don't ignore values when unsetting"); + equal(attrs.foo, void 0, "validate:true passed while unsetting"); }; - a.unset('foo'); + a.unset('foo', {validate: true}); equal(a.get('foo'), void 0, "Foo should have changed"); delete a.validate; ok(changeCount == 2, "Change count should have incremented for unset."); @@ -213,6 +213,24 @@ $(document).ready(function() { equal(a.id, undefined, "Unsetting the id should remove the id property."); }); + test("#2030 - set with failed validate, followed by another set triggers change", function () { + var attr = 0, main = 0, error = 0; + var Model = Backbone.Model.extend({ + validate: function (attr) { + if (attr.x > 1) { + error++; + return "this is an error"; + } + } + }); + var model = new Model({x:0}); + model.on('change:x', function () { attr++; }); + model.on('change', function () { main++; }); + model.set({x:2}, {validate:true}); + model.set({x:1}, {validate:true}); + deepEqual([attr, main, error], [1, 1, 1]); + }); + test("set triggers changes in the correct order", function() { var value = null; var model = new Backbone.Model; @@ -223,15 +241,16 @@ $(document).ready(function() { equal(value, 'last'); }); - test("set falsy values in the correct order", 1, function() { + test("set falsy values in the correct order", 2, function() { var model = new Backbone.Model({result: 'result'}); model.on('change', function() { - equal(model.changed.result, false); + equal(model.changed.result, void 0); + equal(model.previous('result'), false); }); model.set({result: void 0}, {silent: true}); model.set({result: null}, {silent: true}); model.set({result: false}, {silent: true}); - model.change(); + model.set({result: void 0}); }); test("multiple unsets", 1, function() { @@ -245,14 +264,12 @@ $(document).ready(function() { equal(i, 2, 'Unset does not fire an event for missing attributes.'); }); - test("unset and changedAttributes", 2, function() { + test("unset and changedAttributes", 1, function() { var model = new Backbone.Model({a: 1}); - model.unset('a', {silent: true}); - var changedAttributes = model.changedAttributes(); - ok('a' in changedAttributes, 'changedAttributes should contain unset properties'); - - changedAttributes = model.changedAttributes(); - ok('a' in changedAttributes, 'changedAttributes should contain unset properties when running changedAttributes again after an unset.'); + model.on('change', function() { + ok('a' in model.changedAttributes(), 'changedAttributes should contain unset properties'); + }); + model.unset('a'); }); test("using a non-default id attribute.", 5, function() { @@ -272,6 +289,21 @@ $(document).ready(function() { equal(model.get('name'), ''); }); + test("setting an object", 1, function() { + var model = new Backbone.Model({ + custom: { foo: 1 } + }); + model.on('change', function() { + ok(1); + }); + model.set({ + custom: { foo: 1 } // no change should be fired + }); + model.set({ + custom: { foo: 2 } // change event should be fired + }); + }); + test("clear", 3, function() { var changed; var model = new Backbone.Model({id: 1, name : "Model"}); @@ -308,9 +340,9 @@ $(document).ready(function() { equal(model.get('two'), 4); }); - test("change, hasChanged, changedAttributes, previous, previousAttributes", 12, function() { - var model = new Backbone.Model({name : "Tim", age : 10}); - equal(model.changedAttributes(), false); + test("change, hasChanged, changedAttributes, previous, previousAttributes", 9, function() { + var model = new Backbone.Model({name: "Tim", age: 10}); + deepEqual(model.changedAttributes(), false); model.on('change', function() { ok(model.hasChanged('name'), 'name changed'); ok(!model.hasChanged('age'), 'age did not'); @@ -320,17 +352,13 @@ $(document).ready(function() { }); equal(model.hasChanged(), false); equal(model.hasChanged(undefined), false); - model.set({name : 'Rob'}, {silent : true}); - equal(model.hasChanged(), true); - equal(model.hasChanged(undefined), true); - equal(model.hasChanged('name'), true); - model.change(); + model.set({name : 'Rob'}); equal(model.get('name'), 'Rob'); }); test("changedAttributes", 3, function() { var model = new Backbone.Model({a: 'a', b: 'b'}); - equal(model.changedAttributes(), false); + deepEqual(model.changedAttributes(), false); equal(model.changedAttributes({a: 'a'}), false); equal(model.changedAttributes({a: 'b'}).a, 'b'); }); @@ -341,8 +369,7 @@ $(document).ready(function() { model.on('change', function(model, options) { value = options.prefix + model.get('name'); }); - model.set({name: 'Bob'}, {silent: true}); - model.change({prefix: 'Mr. '}); + model.set({name: 'Bob'}, {prefix: 'Mr. '}); equal(value, 'Mr. Bob'); model.set({name: 'Sue'}, {prefix: 'Ms. '}); equal(value, 'Ms. Sue'); @@ -368,19 +395,21 @@ $(document).ready(function() { model.set({lastName: 'Hicks'}); }); - test("validate after save", 1, function() { + test("validate after save", 2, function() { var lastError, model = new Backbone.Model(); model.validate = function(attrs) { if (attrs.admin) return "Can't change admin status."; }; model.sync = function(method, model, options) { - options.success.call(this, {admin: true}); + options.success.call(this, this, {admin: true}, options); }; - model.save(null, {error: function(model, error) { + model.on('invalid', function(model, error) { lastError = error; - }}); + }); + model.save(null); equal(lastError, "Can't change admin status."); + equal(model.validationError, "Can't change admin status."); }); test("save", 2, function() { @@ -406,13 +435,24 @@ $(document).ready(function() { test("save in positional style", 1, function() { var model = new Backbone.Model(); model.sync = function(method, model, options) { - options.success(); + options.success(model, {}, options); }; model.save('title', 'Twelfth Night'); equal(model.get('title'), 'Twelfth Night'); }); - + test("save with non-object success response", 2, function () { + var model = new Backbone.Model(); + model.sync = function(method, model, options) { + options.success(model, '', options); + options.success(model, null, options); + }; + model.save({testing:'empty'}, { + success: function (model) { + deepEqual(model.attributes, {testing:'empty'}); + } + }); + }); test("fetch", 2, function() { doc.fetch(); @@ -442,14 +482,16 @@ $(document).ready(function() { model.validate = function(attrs) { if (attrs.admin != this.get('admin')) return "Can't change admin status."; }; - model.on('error', function(model, error) { + model.on('invalid', function(model, error) { lastError = error; }); var result = model.set({a: 100}); equal(result, model); equal(model.get('a'), 100); equal(lastError, undefined); - result = model.set({a: 200, admin: false}); + result = model.set({admin: true}); + equal(model.get('admin'), true); + result = model.set({a: 200, admin: false}, {validate:true}); equal(lastError, "Can't change admin status."); equal(result, false); equal(model.get('a'), 100); @@ -467,10 +509,10 @@ $(document).ready(function() { model.set({name: "Two"}); equal(model.get('name'), 'Two'); equal(error, undefined); - model.unset('name'); + model.unset('name', {validate: true}); equal(error, true); equal(model.get('name'), 'Two'); - model.clear(); + model.clear({validate:true}); equal(model.get('name'), 'Two'); delete model.validate; model.clear(); @@ -483,21 +525,18 @@ $(document).ready(function() { model.validate = function(attrs) { if (attrs.admin) return "Can't change admin status."; }; - var callback = function(model, error) { - lastError = error; - }; - model.on('error', function(model, error) { + model.on('invalid', function(model, error) { boundError = true; }); - var result = model.set({a: 100}, {error: callback}); + var result = model.set({a: 100}, {validate:true}); equal(result, model); equal(model.get('a'), 100); - equal(lastError, undefined); + equal(model.validationError, null); equal(boundError, undefined); - result = model.set({a: 200, admin: true}, {error: callback}); + result = model.set({a: 200, admin: true}, {validate:true}); equal(result, false); equal(model.get('a'), 100); - equal(lastError, "Can't change admin status."); + equal(model.validationError, "Can't change admin status."); equal(boundError, true); }); @@ -592,6 +631,13 @@ $(document).ready(function() { ok(model.get('x') === a); }); + test("set same value does not trigger change", 0, function() { + var model = new Backbone.Model({x: 1}); + model.on('change change:x', function() { ok(false); }); + model.set({x: 1}); + model.set({x: 1}); + }); + test("unset does not fire a change for undefined attributes", 0, function() { var model = new Backbone.Model({x: undefined}); model.on('change:x', function(){ ok(false); }); @@ -603,19 +649,29 @@ $(document).ready(function() { ok('x' in model.attributes); }); - test("change fires change:attr", 1, function() { + test("hasChanged works outside of change events, and true within", 6, function() { var model = new Backbone.Model({x: 1}); - model.set({x: 2}, {silent: true}); - model.on('change:x', function(){ ok(true); }); - model.change(); - }); - - test("hasChanged is false after original values are set", 2, function() { - var model = new Backbone.Model({x: 1}); - model.on('change:x', function(){ ok(false); }); + model.on('change:x', function() { + ok(model.hasChanged('x')); + equal(model.get('x'), 1); + }); model.set({x: 2}, {silent: true}); ok(model.hasChanged()); - model.set({x: 1}, {silent: true}); + equal(model.hasChanged('x'), true); + model.set({x: 1}); + ok(model.hasChanged()); + equal(model.hasChanged('x'), true); + }); + + test("hasChanged gets cleared on the following set", 4, function() { + var model = new Backbone.Model; + model.set({x: 1}); + ok(model.hasChanged()); + model.set({x: 1}); + ok(!model.hasChanged()); + model.set({x: 2}); + ok(model.hasChanged()); + model.set({}); ok(!model.hasChanged()); }); @@ -664,7 +720,7 @@ $(document).ready(function() { test("#1030 - `save` with `wait` results in correct attributes if success is called during sync", 2, function() { var model = new Backbone.Model({x: 1, y: 2}); model.sync = function(method, model, options) { - options.success(); + options.success(model, {}, options); }; model.on("change:x", function() { ok(true); }); model.save({x: 3}, {wait: true}); @@ -691,25 +747,19 @@ $(document).ready(function() { model.set({x: true}); deepEqual(events, ['change:y', 'change:x', 'change']); events = []; - model.change(); - deepEqual(events, ['change:z', 'change']); + model.set({z: true}); + deepEqual(events, []); }); test("nested `change` only fires once", 1, function() { var model = new Backbone.Model(); model.on('change', function() { ok(true); - model.change(); + model.set({x: true}); }); model.set({x: true}); }); - test("no `'change'` event if no changes", 0, function() { - var model = new Backbone.Model(); - model.on('change', function() { ok(false); }); - model.change(); - }); - test("nested `set` during `'change'`", 6, function() { var count = 0; var model = new Backbone.Model(); @@ -721,13 +771,13 @@ $(document).ready(function() { model.set({y: true}); break; case 1: - deepEqual(this.changedAttributes(), {y: true}); - equal(model.previous('x'), true); + deepEqual(this.changedAttributes(), {x: true, y: true}); + equal(model.previous('x'), undefined); model.set({z: true}); break; case 2: - deepEqual(this.changedAttributes(), {z: true}); - equal(model.previous('y'), true); + deepEqual(this.changedAttributes(), {x: true, y: true, z: true}); + equal(model.previous('y'), undefined); break; default: ok(false); @@ -736,30 +786,34 @@ $(document).ready(function() { model.set({x: true}); }); - test("nested `'change'` with silent", 3, function() { + test("nested `change` with silent", 3, function() { var count = 0; var model = new Backbone.Model(); - model.on('change:y', function() { ok(true); }); + model.on('change:y', function() { ok(false); }); model.on('change', function() { switch(count++) { case 0: deepEqual(this.changedAttributes(), {x: true}); model.set({y: true}, {silent: true}); + model.set({z: true}); break; case 1: - deepEqual(this.changedAttributes(), {y: true, z: true}); + deepEqual(this.changedAttributes(), {x: true, y: true, z: true}); + break; + case 2: + deepEqual(this.changedAttributes(), {z: false}); break; default: ok(false); } }); model.set({x: true}); - model.set({z: true}); + model.set({z: false}); }); - test("nested `'change:attr'` with silent", 1, function() { + test("nested `change:attr` with silent", 0, function() { var model = new Backbone.Model(); - model.on('change:y', function(){ ok(true); }); + model.on('change:y', function(){ ok(false); }); model.on('change', function() { model.set({y: true}, {silent: true}); model.set({z: true}); @@ -777,21 +831,25 @@ $(document).ready(function() { equal(val, 2); }); model.set({x: true}); - model.change(); }); - test("multiple nested changes with silent", 2, function() { + test("multiple nested changes with silent", 1, function() { var changes = []; var model = new Backbone.Model(); model.on('change:b', function(model, val) { changes.push(val); }); model.on('change', function() { model.set({b: 1}); - model.set({b: 2}, {silent: true}); }); model.set({b: 0}); deepEqual(changes, [0, 1]); - model.change(); - deepEqual(changes, [0, 1, 2, 1]); + }); + + test("basic silent change semantics", 1, function() { + var model = new Backbone.Model; + model.set({x: 1}); + model.on('change', function(){ ok(true); }); + model.set({x: 2}, {silent: true}); + model.set({x: 1}); }); test("nested set multiple times", 1, function() { @@ -828,7 +886,7 @@ $(document).ready(function() { } }; model.sync = function(method, model, options) { - options.success(); + options.success(model, {}, options); }; model.save({id: 1}, opts); model.fetch(opts); @@ -866,7 +924,7 @@ $(document).ready(function() { validate: function(){ return 'invalid'; } }); var model = new Model({id: 1}); - model.on('error', function(){ ok(true); }); + model.on('invalid', function(){ ok(true); }); model.save(); }); @@ -885,7 +943,7 @@ $(document).ready(function() { var Model = Backbone.Model.extend({ sync: function(method, model, options) { setTimeout(function(){ - options.success(); + options.success(model, {}, options); start(); }, 0); } @@ -895,9 +953,9 @@ $(document).ready(function() { .save(null, {wait: true}); }); - test("#1664 - Changing from one value, silently to another, back to original does not trigger change.", 0, function() { + test("#1664 - Changing from one value, silently to another, back to original triggers a change.", 1, function() { var model = new Backbone.Model({x:1}); - model.on('change:x', function() { ok(false); }); + model.on('change:x', function() { ok(true); }); model.set({x:2},{silent:true}); model.set({x:3},{silent:true}); model.set({x:1}); @@ -910,15 +968,11 @@ $(document).ready(function() { model.set({a:'c'}, {silent:true}); model.set({b:2}, {silent:true}); model.unset('c', {silent:true}); - model.set({a:'a'}, {silent:true}); - model.set({b:1}, {silent:true}); - model.set({c:'item'}, {silent:true}); }); model.on('change:a change:b change:c', function(model, val) { changes.push(val); }); model.set({a:'a', b:1, c:'item'}); deepEqual(changes, ['a',1,'item']); - model.change(); - deepEqual(changes, ['a',1,'item']); + deepEqual(model.attributes, {a: 'c', b: 2}); }); test("#1791 - `attributes` is available for `parse`", function() { @@ -929,7 +983,7 @@ $(document).ready(function() { expect(0); }); - test("silent changes in last `change` event back to original does not trigger change", 2, function() { + test("silent changes in last `change` event back to original triggers change", 2, function() { var changes = []; var model = new Backbone.Model(); model.on('change:a change:b change:c', function(model, val) { changes.push(val); }); @@ -938,9 +992,8 @@ $(document).ready(function() { }); model.set({a:'a'}); deepEqual(changes, ['a']); - model.set({a:'a'}, {silent:true}); - model.change(); - deepEqual(changes, ['a']); + model.set({a:'a'}); + deepEqual(changes, ['a', 'a']); }); test("#1943 change calculations should use _.isEqual", function() { @@ -949,4 +1002,65 @@ $(document).ready(function() { equal(model.changedAttributes(), false); }); + test("#1964 - final `change` event is always fired, regardless of interim changes", 1, function () { + var model = new Backbone.Model(); + model.on('change:property', function() { + model.set('property', 'bar'); + }); + model.on('change', function() { + ok(true); + }); + model.set('property', 'foo'); + }); + + test("isValid", function() { + var model = new Backbone.Model({valid: true}); + model.validate = function(attrs) { + if (!attrs.valid) return "invalid"; + }; + equal(model.isValid(), true); + equal(model.set({valid: false}, {validate:true}), false); + equal(model.isValid(), true); + model.set({valid:false}); + equal(model.isValid(), false); + ok(!model.set('valid', false, {validate: true})); + }); + + test("#1179 - isValid returns true in the absence of validate.", 1, function() { + var model = new Backbone.Model(); + model.validate = null; + ok(model.isValid()); + }); + + test("#1961 - Creating a model with {validate:true} will call validate and use the error callback", function () { + var Model = Backbone.Model.extend({ + validate: function (attrs) { + if (attrs.id === 1) return "This shouldn't happen"; + } + }); + var model = new Model({id: 1}, {validate: true}); + equal(model.validationError, "This shouldn't happen"); + }); + + test("toJSON receives attrs during save(..., {wait: true})", 1, function() { + var Model = Backbone.Model.extend({ + url: '/test', + toJSON: function() { + strictEqual(this.attributes.x, 1); + return _.clone(this.attributes); + } + }); + var model = new Model; + model.save({x: 1}, {wait: true}); + }); + + test("#2034 - nested set with silent only triggers one change", 1, function() { + var model = new Backbone.Model(); + model.on('change', function() { + model.set({b: true}, {silent: true}); + ok(true); + }); + model.set({a: true}); + }); + }); diff --git a/vendor/backbone/test/router.js b/vendor/backbone/test/router.js index 50be7effb..b1c6a71a8 100644 --- a/vendor/backbone/test/router.js +++ b/vendor/backbone/test/router.js @@ -70,6 +70,7 @@ $(document).ready(function() { "contacts/new": "newContact", "contacts/:id": "loadContact", "optional(/:item)": "optionalItem", + "named/optional/(y:z)": "namedOptional", "splat/*args/end": "splat", "*first/complex-:part/*rest": "complex", ":entity?*args": "query", @@ -110,23 +111,27 @@ $(document).ready(function() { this.arg = arg != void 0 ? arg : null; }, - splat : function(args) { + splat: function(args) { this.args = args; }, - complex : function(first, part, rest) { + complex: function(first, part, rest) { this.first = first; this.part = part; this.rest = rest; }, - query : function(entity, args) { + query: function(entity, args) { this.entity = entity; this.queryArgs = args; }, - anything : function(whatever) { + anything: function(whatever) { this.anything = whatever; + }, + + namedOptional: function(z) { + this.z = z; } }); @@ -502,4 +507,13 @@ $(document).ready(function() { strictEqual(history.getFragment('/fragment '), 'fragment'); }); + test("#1980 - Optional parameters.", 2, function() { + location.replace('http://example.com#named/optional/y'); + Backbone.history.checkUrl(); + strictEqual(router.z, undefined); + location.replace('http://example.com#named/optional/y123'); + Backbone.history.checkUrl(); + strictEqual(router.z, '123'); + }); + }); diff --git a/vendor/backbone/test/view.js b/vendor/backbone/test/view.js index 444d90b2c..7496c9da2 100644 --- a/vendor/backbone/test/view.js +++ b/vendor/backbone/test/view.js @@ -29,22 +29,6 @@ $(document).ready(function() { strictEqual(view.$('a b').html(), 'test'); }); - test("make", 3, function() { - var div = view.make('div', {id: 'test-div'}, "one two three"); - - equal(div.tagName.toLowerCase(), 'div'); - equal(div.id, 'test-div'); - equal($(div).text(), 'one two three'); - }); - - test("make can take falsy values for content", 2, function() { - var div = view.make('div', {id: 'test-div'}, 0); - equal($(div).text(), '0'); - - div = view.make('div', {id: 'test-div'}, ''); - equal($(div).text(), ''); - }); - test("initialize", 1, function() { var View = Backbone.View.extend({ initialize: function() { diff --git a/vendor/underscore/test/arrays.js b/vendor/underscore/test/arrays.js index 7145099b4..9b7bb0de3 100644 --- a/vendor/underscore/test/arrays.js +++ b/vendor/underscore/test/arrays.js @@ -182,7 +182,7 @@ $(document).ready(function() { equal(_.indexOf(null, 2), -1, 'handles nulls properly'); numbers = [1, 2, 3, 1, 2, 3, 1, 2, 3]; - index = _.lastIndexOf(numbers, 2, 2); + var index = _.lastIndexOf(numbers, 2, 2); equal(index, 1, 'supports the fromIndex argument'); }); diff --git a/vendor/underscore/test/collections.js b/vendor/underscore/test/collections.js index 2214ed79c..6e000993d 100644 --- a/vendor/underscore/test/collections.js +++ b/vendor/underscore/test/collections.js @@ -22,7 +22,7 @@ $(document).ready(function() { equal(answers.join(", "), 'one, two, three', 'iterating over objects works, and ignores the object prototype.'); delete obj.constructor.prototype.four; - answer = null; + var answer = null; _.each([1, 2, 3], function(num, index, arr){ if (_.include(arr, num)) answer = true; }); ok(answer, 'can reference the original collection from inside the iterator'); diff --git a/vendor/underscore/test/objects.js b/vendor/underscore/test/objects.js index 939bffddf..73bdf6b4e 100644 --- a/vendor/underscore/test/objects.js +++ b/vendor/underscore/test/objects.js @@ -555,7 +555,7 @@ $(document).ready(function() { value(); ok(returned == 6 && intercepted == 6, '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."); @@ -563,7 +563,7 @@ $(document).ready(function() { 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."); - child = {}; + var child = {}; child.prototype = obj; ok (_.has(child, "foo") == false, "has() does not check the prototype chain for a property.") }); diff --git a/vendor/underscore/test/utility.js b/vendor/underscore/test/utility.js index 4da269ddb..0bca8c892 100644 --- a/vendor/underscore/test/utility.js +++ b/vendor/underscore/test/utility.js @@ -25,6 +25,20 @@ $(document).ready(function() { equal(_.identity(moe), moe, 'moe is the same as his identity'); }); + test("random", function() { + var array = _.range(1000); + var min = Math.pow(2, 31); + var max = Math.pow(2, 62); + + ok(_.every(array, function() { + return _.random(min, max) >= min; + }), "should produce a random number greater than or equal to the minimum number"); + + ok(_.some(array, function() { + return _.random(Number.MAX_VALUE) > 0; + }), "should produce a random number when passed `Number.MAX_VALUE`"); + }); + test("uniqueId", function() { var ids = [], i = 0; while(i++ < 100) ids.push(_.uniqueId()); @@ -82,7 +96,7 @@ $(document).ready(function() { equal(escapeTemplate({a: true}), 'checked="checked"', 'can handle slash escapes in interpolations.'); var fancyTemplate = _.template(""); result = fancyTemplate({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equal(result, "", 'can run arbitrary javascript in templates'); @@ -137,7 +151,7 @@ $(document).ready(function() { interpolate : /\{\{=([\s\S]+?)\}\}/g }; - var custom = _.template(""); + var custom = _.template(""); result = custom({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equal(result, "", 'can run arbitrary javascript in templates'); @@ -152,7 +166,7 @@ $(document).ready(function() { interpolate : /<\?=([\s\S]+?)\?>/g }; - var customWithSpecialChars = _.template(""); + var customWithSpecialChars = _.template(""); result = customWithSpecialChars({people : {moe : "Moe", larry : "Larry", curly : "Curly"}}); equal(result, "", 'can run arbitrary javascript in templates'); diff --git a/vendor/underscore/underscore-min.js b/vendor/underscore/underscore-min.js index a3bb5f354..307393e73 100644 --- a/vendor/underscore/underscore-min.js +++ b/vendor/underscore/underscore-min.js @@ -2,4 +2,4 @@ // http://underscorejs.org // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. // Underscore may be freely distributed under the MIT license. -(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,v=e.reduce,h=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.3";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduce===v)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduceRight===h)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2);return w.map(n,function(n){return(w.isFunction(t)?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t){return w.isEmpty(t)?[]:w.filter(n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var F=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=F(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i};var I=function(){};w.bind=function(n,t){var r,e;if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));if(!w.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));I.prototype=n.prototype;var u=new I;I.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=S(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&S(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return S(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+(0|Math.random()*(t-n+1))};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};T.unescape=w.invert(T.escape);var M={escape:RegExp("["+w.keys(T.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(T.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(M[n],function(t){return T[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var z=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); \ No newline at end of file +(function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,_=Object.keys,j=i.bind,w=function(n){return n instanceof w?n:this instanceof w?(this._wrapped=n,void 0):new w(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=w),exports._=w):n._=w,w.VERSION="1.4.3";var A=w.each=w.forEach=function(n,t,e){if(null!=n)if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a in n)if(w.has(n,a)&&t.call(e,n[a],a,n)===r)return};w.map=w.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e[e.length]=t.call(r,n,u,i)}),e)};var O="Reduce of empty array with no initial value";w.reduce=w.foldl=w.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=w.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},w.reduceRight=w.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=w.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=w.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},w.find=w.detect=function(n,t,r){var e;return E(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},w.filter=w.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&(e[e.length]=n)}),e)},w.reject=function(n,t,r){return w.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},w.every=w.all=function(n,t,e){t||(t=w.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var E=w.some=w.any=function(n,t,e){t||(t=w.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};w.contains=w.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:E(n,function(n){return n===t})},w.invoke=function(n,t){var r=o.call(arguments,2);return w.map(n,function(n){return(w.isFunction(t)?t:n[t]).apply(n,r)})},w.pluck=function(n,t){return w.map(n,function(n){return n[t]})},w.where=function(n,t){return w.isEmpty(t)?[]:w.filter(n,function(n){for(var r in t)if(t[r]!==n[r])return!1;return!0})},w.max=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.max.apply(Math,n);if(!t&&w.isEmpty(n))return-1/0;var e={computed:-1/0,value:-1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;a>=e.computed&&(e={value:n,computed:a})}),e.value},w.min=function(n,t,r){if(!t&&w.isArray(n)&&n[0]===+n[0]&&65535>n.length)return Math.min.apply(Math,n);if(!t&&w.isEmpty(n))return 1/0;var e={computed:1/0,value:1/0};return A(n,function(n,u,i){var a=t?t.call(r,n,u,i):n;e.computed>a&&(e={value:n,computed:a})}),e.value},w.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=w.random(r++),e[r-1]=e[t],e[t]=n}),e};var F=function(n){return w.isFunction(n)?n:function(t){return t[n]}};w.sortBy=function(n,t,r){var e=F(t);return w.pluck(w.map(n,function(n,t,u){return{value:n,index:t,criteria:e.call(r,n,t,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.indexi;){var o=i+a>>>1;u>r.call(e,n[o])?i=o+1:a=o}return i},w.toArray=function(n){return n?w.isArray(n)?o.call(n):n.length===+n.length?w.map(n,w.identity):w.values(n):[]},w.size=function(n){return null==n?0:n.length===+n.length?n.length:w.keys(n).length},w.first=w.head=w.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:o.call(n,0,t)},w.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},w.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},w.rest=w.tail=w.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},w.compact=function(n){return w.filter(n,w.identity)};var R=function(n,t,r){return A(n,function(n){w.isArray(n)?t?a.apply(r,n):R(n,t,r):r.push(n)}),r};w.flatten=function(n,t){return R(n,t,[])},w.without=function(n){return w.difference(n,o.call(arguments,1))},w.uniq=w.unique=function(n,t,r,e){w.isFunction(t)&&(e=r,r=t,t=!1);var u=r?w.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:w.contains(a,r))||(a.push(r),i.push(n[e]))}),i},w.union=function(){return w.uniq(c.apply(e,arguments))},w.intersection=function(n){var t=o.call(arguments,1);return w.filter(w.uniq(n),function(n){return w.every(t,function(t){return w.indexOf(t,n)>=0})})},w.difference=function(n){var t=c.apply(e,o.call(arguments,1));return w.filter(n,function(n){return!w.contains(t,n)})},w.zip=function(){for(var n=o.call(arguments),t=w.max(w.pluck(n,"length")),r=Array(t),e=0;t>e;e++)r[e]=w.pluck(n,""+e);return r},w.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},w.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=w.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},w.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},w.range=function(n,t,r){1>=arguments.length&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=Array(e);e>u;)i[u++]=n,n+=r;return i};var I=function(){};w.bind=function(n,t){var r,e;if(n.bind===j&&j)return j.apply(n,o.call(arguments,1));if(!w.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));I.prototype=n.prototype;var u=new I;I.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},w.bindAll=function(n){var t=o.call(arguments,1);return 0===t.length&&(t=w.functions(n)),A(t,function(t){n[t]=w.bind(n[t],n)}),n},w.memoize=function(n,t){var r={};return t||(t=w.identity),function(){var e=t.apply(this,arguments);return w.has(r,e)?r[e]:r[e]=n.apply(this,arguments)}},w.delay=function(n,t){var r=o.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},w.defer=function(n){return w.delay.apply(w,[n,1].concat(o.call(arguments,1)))},w.throttle=function(n,t){var r,e,u,i,a=0,o=function(){a=new Date,u=null,i=n.apply(r,e)};return function(){var c=new Date,l=t-(c-a);return r=this,e=arguments,0>=l?(clearTimeout(u),u=null,a=c,i=n.apply(r,e)):u||(u=setTimeout(o,l)),i}},w.debounce=function(n,t,r){var e,u;return function(){var i=this,a=arguments,o=function(){e=null,r||(u=n.apply(i,a))},c=r&&!e;return clearTimeout(e),e=setTimeout(o,t),c&&(u=n.apply(i,a)),u}},w.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},w.wrap=function(n,t){return function(){var r=[n];return a.apply(r,arguments),t.apply(this,r)}},w.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},w.after=function(n,t){return 0>=n?t():function(){return 1>--n?t.apply(this,arguments):void 0}},w.keys=_||function(n){if(n!==Object(n))throw new TypeError("Invalid object");var t=[];for(var r in n)w.has(n,r)&&(t[t.length]=r);return t},w.values=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push(n[r]);return t},w.pairs=function(n){var t=[];for(var r in n)w.has(n,r)&&t.push([r,n[r]]);return t},w.invert=function(n){var t={};for(var r in n)w.has(n,r)&&(t[n[r]]=r);return t},w.functions=w.methods=function(n){var t=[];for(var r in n)w.isFunction(n[r])&&t.push(r);return t.sort()},w.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},w.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},w.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)w.contains(r,u)||(t[u]=n[u]);return t},w.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)null==n[r]&&(n[r]=t[r])}),n},w.clone=function(n){return w.isObject(n)?w.isArray(n)?n.slice():w.extend({},n):n},w.tap=function(n,t){return t(n),n};var M=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof w&&(n=n._wrapped),t instanceof w&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==t+"";case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;r.push(n),e.push(t);var a=0,o=!0;if("[object Array]"==u){if(a=n.length,o=a==t.length)for(;a--&&(o=M(n[a],t[a],r,e)););}else{var c=n.constructor,f=t.constructor;if(c!==f&&!(w.isFunction(c)&&c instanceof c&&w.isFunction(f)&&f instanceof f))return!1;for(var s in n)if(w.has(n,s)&&(a++,!(o=w.has(t,s)&&M(n[s],t[s],r,e))))break;if(o){for(s in t)if(w.has(t,s)&&!a--)break;o=!a}}return r.pop(),e.pop(),o};w.isEqual=function(n,t){return M(n,t,[],[])},w.isEmpty=function(n){if(null==n)return!0;if(w.isArray(n)||w.isString(n))return 0===n.length;for(var t in n)if(w.has(n,t))return!1;return!0},w.isElement=function(n){return!(!n||1!==n.nodeType)},w.isArray=x||function(n){return"[object Array]"==l.call(n)},w.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){w["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),w.isArguments(arguments)||(w.isArguments=function(n){return!(!n||!w.has(n,"callee"))}),"function"!=typeof/./&&(w.isFunction=function(n){return"function"==typeof n}),w.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},w.isNaN=function(n){return w.isNumber(n)&&n!=+n},w.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},w.isNull=function(n){return null===n},w.isUndefined=function(n){return n===void 0},w.has=function(n,t){return f.call(n,t)},w.noConflict=function(){return n._=t,this},w.identity=function(n){return n},w.times=function(n,t,r){for(var e=Array(n),u=0;n>u;u++)e[u]=t.call(r,u);return e},w.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))};var S={escape:{"&":"&","<":"<",">":">",'"':""","'":"'","/":"/"}};S.unescape=w.invert(S.escape);var T={escape:RegExp("["+w.keys(S.escape).join("")+"]","g"),unescape:RegExp("("+w.keys(S.unescape).join("|")+")","g")};w.each(["escape","unescape"],function(n){w[n]=function(t){return null==t?"":(""+t).replace(T[n],function(t){return S[n][t]})}}),w.result=function(n,t){if(null==n)return null;var r=n[t];return w.isFunction(r)?r.call(n):r},w.mixin=function(n){A(w.functions(n),function(t){var r=w[t]=n[t];w.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(w,n))}})};var N=0;w.uniqueId=function(n){var t=++N+"";return n?n+t:t},w.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;w.template=function(n,t,r){var e;r=w.defaults({},r,w.templateSettings);var u=RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,w);var c=function(n){return e.call(this,n,w)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},w.chain=function(n){return w(n).chain()};var z=function(n){return this._chain?w(n).chain():n};w.mixin(w),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];w.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];w.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),w.extend(w.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}})}).call(this); \ No newline at end of file diff --git a/vendor/underscore/underscore.js b/vendor/underscore/underscore.js index b943608c9..e228c95c4 100644 --- a/vendor/underscore/underscore.js +++ b/vendor/underscore/underscore.js @@ -1019,7 +1019,7 @@ max = min; min = 0; } - return min + (0 | Math.random() * (max - min + 1)); + return min + Math.floor(Math.random() * (max - min + 1)); }; // List of HTML entities for escaping.