From d43ede3a118e93e44461457f75dfed4dcd2318c4 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Thu, 19 Jul 2012 01:32:47 -0400 Subject: [PATCH] Update vendor/underscore and backbone. Former-commit-id: 614926653b0b1669f8a5533666adb0aecbd46c03 --- vendor/backbone/backbone.js | 47 +++++++++++---------------- vendor/underscore/test/collections.js | 12 +++++++ vendor/underscore/underscore.js | 40 ++++++++++++++++++----- 3 files changed, 62 insertions(+), 37 deletions(-) diff --git a/vendor/backbone/backbone.js b/vendor/backbone/backbone.js index 15a74d10e..a307e09ff 100644 --- a/vendor/backbone/backbone.js +++ b/vendor/backbone/backbone.js @@ -295,7 +295,7 @@ // If the new and previous value differ, record the change. If not, // then remove changes for this attribute. - if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) { + if (!_.isEqual(prev[attr], val) || (_.has(now, attr) !== _.has(prev, attr))) { this.changed[attr] = val; if (!options.silent) this._pending[attr] = true; } else { @@ -432,7 +432,7 @@ url: function() { var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError(); if (this.isNew()) return base; - return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id); + return base + (base.charAt(base.length - 1) === '/' ? '' : '/') + encodeURIComponent(this.id); }, // **parse** converts a response into the hash of attributes to be `set` on @@ -555,7 +555,7 @@ var Collection = Backbone.Collection = function(models, options) { options || (options = {}); if (options.model) this.model = options.model; - if (options.comparator !== undefined) this.comparator = options.comparator; + if (options.comparator !== void 0) this.comparator = options.comparator; this._reset(); this.initialize.apply(this, arguments); if (models) this.reset(models, {silent: true, parse: options.parse}); @@ -628,9 +628,7 @@ // Merge in duplicate models. if (options.merge) { for (i = 0, length = dups.length; i < length; i++) { - if (model = this._byId[dups[i].id]) { - model.set(dups[i], options); - } + if (model = this._byId[dups[i].id]) model.set(dups[i], options); } } @@ -737,7 +735,7 @@ options || (options = {}); if (!this.comparator) throw new Error('Cannot sort a set without a comparator'); var boundComparator = _.bind(this.comparator, this); - if (this.comparator.length == 1) { + if (this.comparator.length === 1) { this.models = this.sortBy(boundComparator); } else { this.models.sort(boundComparator); @@ -771,7 +769,7 @@ // models to the collection instead of resetting. fetch: function(options) { options = options ? _.clone(options) : {}; - if (options.parse === undefined) options.parse = true; + if (options.parse === void 0) options.parse = true; var collection = this; var success = options.success; options.success = function(resp, status, xhr) { @@ -842,9 +840,7 @@ // Internal method to remove a model's ties to a collection. _removeReference: function(model) { - if (this == model.collection) { - delete model.collection; - } + if (this === model.collection) delete model.collection; model.off('all', this._onModelEvent, this); }, @@ -853,10 +849,8 @@ // events simply proxy through. "add" and "remove" events that originate // in other collections are ignored. _onModelEvent: function(event, model, collection, options) { - if ((event == 'add' || event == 'remove') && collection != this) return; - if (event == 'destroy') { - this.remove(model, options); - } + if ((event === 'add' || event === 'remove') && collection !== this) return; + if (event === 'destroy') this.remove(model, options); if (model && event === 'change:' + model.idAttribute) { delete this._byId[model.previous(model.idAttribute)]; if (model.id != null) this._byId[model.id] = model; @@ -1048,7 +1042,7 @@ // opened by a non-pushState browser. this.fragment = fragment; var loc = this.location; - var atRoot = (loc.pathname == this.options.root) && !loc.search; + var atRoot = (loc.pathname === this.options.root) && !loc.search; // 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... @@ -1065,9 +1059,7 @@ this.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment); } - if (!this.options.silent) { - return this.loadUrl(); - } + if (!this.options.silent) return this.loadUrl(); }, // Disable Backbone.history, perhaps temporarily. Not useful in a real app, @@ -1088,10 +1080,10 @@ // calls `loadUrl`, normalizing across the hidden iframe. checkUrl: function(e) { var current = this.getFragment(); - if (current == this.fragment && this.iframe) { + if (current === this.fragment && this.iframe) { current = this.getFragment(this.getHash(this.iframe)); } - if (current == this.fragment) return false; + if (current === this.fragment) return false; if (this.iframe) this.navigate(current); this.loadUrl() || this.loadUrl(this.getHash()); }, @@ -1121,9 +1113,9 @@ if (!History.started) return false; if (!options || options === true) options = {trigger: options}; var frag = (fragment || '').replace(routeStripper, ''); - if (this.fragment == frag) return; + if (this.fragment === frag) return; this.fragment = frag; - var url = (frag.indexOf(this.options.root) != 0 ? this.options.root : '') + frag; + var url = (frag.indexOf(this.options.root) !== 0 ? this.options.root : '') + frag; // If pushState is available, we use it to set the fragment as a real URL. if (this._hasPushState) { @@ -1133,7 +1125,7 @@ // fragment to store history. } else if (this._wantsHashChange) { this._updateHash(this.location, frag, options.replace); - if (this.iframe && (frag != this.getFragment(this.getHash(this.iframe)))) { + if (this.iframe && (frag !== 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. @@ -1158,6 +1150,7 @@ location.hash = fragment; } } + }); // Backbone.View @@ -1303,9 +1296,7 @@ // The self-propagating extend function that Backbone classes use. var extend = function(protoProps, classProps) { - var child = inherits(this, protoProps, classProps); - child.extend = this.extend; - return child; + return inherits(this, protoProps, classProps); }; // Set up inheritance for the model, collection, and view. @@ -1352,7 +1343,7 @@ } // Ensure that we have the appropriate request data. - if (!options.data && model && (method == 'create' || method == 'update')) { + if (!options.data && model && (method === 'create' || method === 'update')) { params.contentType = 'application/json'; params.data = JSON.stringify(model); } diff --git a/vendor/underscore/test/collections.js b/vendor/underscore/test/collections.js index 4d9fdaa5d..711053a90 100644 --- a/vendor/underscore/test/collections.js +++ b/vendor/underscore/test/collections.js @@ -253,6 +253,18 @@ $(document).ready(function() { equal(grouped['5'].join(' '), 'three seven eight'); }); + test('collections: 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 list = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]; + var grouped = _.countBy(list, 'length'); + equal(grouped['3'], 4); + equal(grouped['4'], 3); + equal(grouped['5'], 3); + }); + test('collections: sortedIndex', function() { var numbers = [10, 20, 30, 40, 50], num = 35; var indexForNum = _.sortedIndex(numbers, num); diff --git a/vendor/underscore/underscore.js b/vendor/underscore/underscore.js index a23ef7872..88141d168 100644 --- a/vendor/underscore/underscore.js +++ b/vendor/underscore/underscore.js @@ -267,7 +267,7 @@ // Sort the object's values by a criterion produced by an iterator. _.sortBy = function(obj, val, context) { - var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; + var iterator = lookupIterator(obj, val); return _.pluck(_.map(obj, function(value, index, list) { return { value : value, @@ -281,16 +281,38 @@ }), 'value'); }; + // An internal function to generate lookup iterators. + var lookupIterator = function(obj, val) { + return _.isFunction(val) ? val : function(obj) { return obj[val]; }; + }; + + // An internal function used for aggregate "group by" operations. + var group = function(obj, val, behavior) { + var result = {}; + var iterator = lookupIterator(obj, val); + each(obj, function(value, index) { + var key = iterator(value, index); + behavior(result, key, value); + }); + return result; + }; + // Groups the object's values by a criterion. Pass either a string attribute // to group by, or a function that returns the criterion. _.groupBy = function(obj, val) { - var result = {}; - var iterator = _.isFunction(val) ? val : function(obj) { return obj[val]; }; - each(obj, function(value, index) { - var key = iterator(value, index); + return group(obj, val, function(result, key, value) { (result[key] || (result[key] = [])).push(value); }); - return result; + }; + + // Counts instances of an object that group by a certain criterion. Pass + // either a string attribute to count by, or a function that returns the + // criterion. + _.countBy = function(obj, val) { + return group(obj, val, function(result, key, value) { + result[key] || (result[key] = 0); + result[key]++; + }); }; // Use a comparator function to figure out the smallest index at which @@ -709,7 +731,7 @@ }; // Internal recursive comparison function for `isEqual`. - function eq(a, b, stack) { + var eq = function(a, b, stack) { // Identical objects are equal. `0 === -0`, but they aren't identical. // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal. if (a === b) return a !== 0 || 1 / a == 1 / b; @@ -794,7 +816,7 @@ // Remove the first object from the stack of traversed objects. stack.pop(); return result; - } + }; // Perform a deep comparison to check if two objects are equal. _.isEqual = function(a, b) { @@ -832,7 +854,7 @@ return toString.call(obj) == '[object ' + name + ']'; }; }); - + // Define a fallback version of the method in browsers (ahem, IE), where // there isn't any inspectable "Arguments" type. if (!_.isArguments(arguments)) {