From 465576b5cb69aee9fd94407aefa18b29aa5ff319 Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 10 Feb 2013 23:51:46 -0800 Subject: [PATCH] Update vendors. Former-commit-id: 4ffc3f5b267f8fdf1ac074f9f9ab44b0a7c4c3dd --- build/minify.js | 2 +- vendor/backbone/backbone.js | 102 ++++++++++++++++------------- vendor/backbone/test/collection.js | 83 ++++++++++++++++------- vendor/backbone/test/events.js | 5 ++ vendor/backbone/test/model.js | 30 ++++++--- 5 files changed, 143 insertions(+), 79 deletions(-) diff --git a/build/minify.js b/build/minify.js index f37aead75..0cf343881 100755 --- a/build/minify.js +++ b/build/minify.js @@ -18,7 +18,7 @@ var closureId = 'c92fd8f7f2b2096eb910c99d10ede0ec25d3c3cc'; /** The Git object ID of `uglifyjs.tar.gz` */ - var uglifyId = 'db14216975ad703f8b903a9c4bbcfe9ecb824c61'; + var uglifyId = '7948672b0f85eeaba258c7298b2422fc5ea594b2'; /** The path of the directory that is the base of the repository */ var basePath = fs.realpathSync(path.join(__dirname, '..')); diff --git a/vendor/backbone/backbone.js b/vendor/backbone/backbone.js index aee5cb789..0a1669d11 100644 --- a/vendor/backbone/backbone.js +++ b/vendor/backbone/backbone.js @@ -193,15 +193,12 @@ stopListening: function(obj, name, callback) { var listeners = this._listeners; if (!listeners) return this; - if (obj) { - obj.off(name, typeof name === 'object' ? this : callback, this); - if (!name && !callback) delete listeners[obj._listenerId]; - } else { - if (typeof name === 'object') callback = this; - for (var id in listeners) { - listeners[id].off(name, callback, this); - } - this._listeners = {}; + var deleteListener = !name && !callback; + if (typeof name === 'object') callback = this; + if (obj) (listeners = {})[obj._listenerId] = obj; + for (var id in listeners) { + listeners[id].off(name, callback, this); + if (deleteListener) delete this._listeners[id]; } return this; } @@ -216,7 +213,8 @@ var listeners = this._listeners || (this._listeners = {}); var id = obj._listenerId || (obj._listenerId = _.uniqueId('l')); listeners[id] = obj; - obj[implementation](name, typeof name === 'object' ? this : callback, this); + if (typeof name === 'object') callback = this; + obj[implementation](name, callback, this); return this; }; }); @@ -420,11 +418,14 @@ 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(model, resp, options) { + options.success = function(resp) { if (!model.set(model.parse(resp, options), options)) return false; if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); }; + wrapError(this, options); return this.sync('read', this, options); }, @@ -432,7 +433,7 @@ // If the server returns an attributes hash that differs, the model's // state will be `set` again. save: function(key, val, options) { - var attrs, success, method, xhr, attributes = this.attributes; + var attrs, method, xhr, attributes = this.attributes; // Handle both `"key", value` and `{key: value}` -style arguments. if (key == null || typeof key === 'object') { @@ -458,8 +459,9 @@ // After a successful server-side save, the client is (optionally) // updated with the server-side state. if (options.parse === void 0) options.parse = true; - success = options.success; - options.success = function(model, resp, options) { + var model = this; + var success = options.success; + options.success = function(resp) { // Ensure attributes are restored during synchronous saves. model.attributes = attributes; var serverAttrs = model.parse(resp, options); @@ -468,9 +470,10 @@ return false; } if (success) success(model, resp, options); + model.trigger('sync', model, resp, options); }; + wrapError(this, options); - // Finish configuring and sending the Ajax request. method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update'); if (method === 'patch') options.attrs = attrs; xhr = this.sync(method, this, options); @@ -493,15 +496,17 @@ model.trigger('destroy', model, model.collection, options); }; - options.success = function(model, resp, options) { + options.success = function(resp) { if (options.wait || model.isNew()) destroy(); if (success) success(model, resp, options); + if (!model.isNew()) model.trigger('sync', model, resp, options); }; if (this.isNew()) { - options.success(this, null, options); + options.success(); return false; } + wrapError(this, options); var xhr = this.sync('delete', this, options); if (!options.wait) destroy(); @@ -602,10 +607,7 @@ // Turn bare objects into model references, and prevent invalid models // from being added. for (i = 0, l = models.length; i < l; i++) { - if (!(model = this._prepareModel(attrs = models[i], options))) { - this.trigger('invalid', this, attrs, options); - continue; - } + if (!(model = this._prepareModel(attrs = models[i], options))) continue; // If a duplicate is found, prevent it from being added and // optionally merge it into the existing model. @@ -712,8 +714,7 @@ // Get a model from the set by id. get: function(obj) { if (obj == null) return void 0; - this._idAttr || (this._idAttr = this.model.prototype.idAttribute); - return this._byId[obj.id || obj.cid || obj[this._idAttr] || obj]; + return this._byId[obj.id || obj.cid || obj]; }, // Get the model at the given index. @@ -721,10 +722,11 @@ return this.models[index]; }, - // Return models with matching attributes. Useful for simple cases of `filter`. - where: function(attrs) { - if (_.isEmpty(attrs)) return []; - return this.filter(function(model) { + // Return models with matching attributes. Useful for simple cases of + // `filter`. + where: function(attrs, first) { + if (_.isEmpty(attrs)) return first ? void 0 : []; + return this[first ? 'find' : 'filter'](function(model) { for (var key in attrs) { if (attrs[key] !== model.get(key)) return false; } @@ -732,6 +734,12 @@ }); }, + // Return the first model with matching attributes. Useful for simple cases + // of `find`. + findWhere: function(attrs) { + return this.where(attrs, true); + }, + // Force the collection to re-sort itself. You don't need to call this under // normal circumstances, as the set will maintain sort order as each item // is added. @@ -762,7 +770,7 @@ update: function(models, options) { options = _.extend({add: true, merge: true, remove: true}, options); if (options.parse) models = this.parse(models, options); - var model, i, l, existing; + var attrs, model, i, l, existing; var add = [], remove = [], modelMap = {}; // Allow a single model (or no argument) to be passed. @@ -773,11 +781,12 @@ // Determine which models to add and merge, and which to remove. for (i = 0, l = models.length; i < l; i++) { - model = models[i]; + if (!((attrs = models[i]) instanceof Model)) attrs = _.clone(attrs); + if (!(model = this._prepareModel(attrs, options))) continue; existing = this.get(model); if (options.remove && existing) modelMap[existing.cid] = true; if ((options.add && !existing) || (options.merge && existing)) { - add.push(model); + add.push(models[i]); } } if (options.remove) { @@ -816,11 +825,14 @@ options = options ? _.clone(options) : {}; if (options.parse === void 0) options.parse = true; var success = options.success; - options.success = function(collection, resp, options) { + var collection = this; + options.success = function(resp) { var method = options.update ? 'update' : 'reset'; collection[method](resp, options); if (success) success(collection, resp, options); + collection.trigger('sync', collection, resp, options); }; + wrapError(this, options); return this.sync('read', this, options); }, @@ -833,7 +845,7 @@ if (!options.wait) this.add(model, options); var collection = this; var success = options.success; - options.success = function(model, resp, options) { + options.success = function(resp) { if (options.wait) collection.add(model, options); if (success) success(model, resp, options); }; @@ -868,7 +880,10 @@ options || (options = {}); options.collection = this; var model = new this.model(attrs, options); - if (!model._validate(attrs, options)) return false; + if (!model._validate(attrs, options)) { + this.trigger('invalid', this, attrs, options); + return false; + } return model; }, @@ -1432,18 +1447,6 @@ params.processData = false; } - var success = options.success; - options.success = function(resp) { - if (success) success(model, resp, options); - model.trigger('sync', model, resp, options); - }; - - var error = options.error; - options.error = function(xhr) { - if (error) error(model, xhr, options); - model.trigger('error', model, xhr, options); - }; - // Make the request, allowing the user to override any Ajax options. var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); model.trigger('request', model, xhr, options); @@ -1502,4 +1505,13 @@ throw new Error('A "url" property or function must be specified'); }; + // Wrap an optional error callback with a fallback error event. + var wrapError = function (model, options) { + var error = options.error; + options.error = function(resp) { + if (error) error(model, resp, options); + model.trigger('error', model, resp, options); + }; + }; + }).call(this); diff --git a/vendor/backbone/test/collection.js b/vendor/backbone/test/collection.js index baf3f54a3..c67600b6d 100644 --- a/vendor/backbone/test/collection.js +++ b/vendor/backbone/test/collection.js @@ -70,22 +70,20 @@ $(document).ready(function() { equal(col.get(col.first().cid), col.first()); }); - test("get with non-default ids", 4, function() { + test("get with non-default ids", 5, function() { var col = new Backbone.Collection(); - var MongoModel = Backbone.Model.extend({ - idAttribute: '_id' - }); + var MongoModel = Backbone.Model.extend({idAttribute: '_id'}); var model = new MongoModel({_id: 100}); - col.push(model); + col.add(model); equal(col.get(100), model); - model.set({_id: 101}); - equal(col.get(101), model); + equal(col.get(model.cid), model); + equal(col.get(model), model); + equal(col.get(101), void 0); - var Col2 = Backbone.Collection.extend({ model: MongoModel }); - var col2 = new Col2(); - col2.push(model); - equal(col2.get({_id: 101}), model); - equal(col2.get(model.clone()), model); + var col2 = new Backbone.Collection(); + col2.model = MongoModel; + col2.add(model.attributes); + equal(col2.get(model.clone()), col2.first()); }); test("update index when id changes", 3, function() { @@ -362,9 +360,7 @@ $(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(model, [], options); - }; + e.sync = function(method, model, options) { options.success(); }; var colE = new Backbone.Collection([e]); var colF = new Backbone.Collection([e]); e.destroy(); @@ -396,6 +392,15 @@ $(document).ready(function() { equal(this.syncArgs.options.parse, false); }); + test("fetch with an error response triggers an error event", 1, function () { + var collection = new Backbone.Collection(); + collection.on('error', function () { + ok(true); + }); + collection.sync = function (method, model, options) { options.error(); }; + collection.fetch(); + }); + test("ensure fetch only parses once", 1, function() { var collection = new Backbone.Collection; var counter = 0; @@ -405,7 +410,7 @@ $(document).ready(function() { }; collection.url = '/test'; collection.fetch(); - this.syncArgs.options.success([]); + this.syncArgs.options.success(); equal(counter, 1); }); @@ -461,9 +466,10 @@ $(document).ready(function() { equal(JSON.stringify(col), '[{"id":3,"label":"a"},{"id":2,"label":"b"},{"id":1,"label":"c"},{"id":0,"label":"d"}]'); }); - test("where", 6, function() { + test("where and findWhere", 8, function() { + var model = new Backbone.Model({a: 1}); var coll = new Backbone.Collection([ - {a: 1}, + model, {a: 1}, {a: 1, b: 2}, {a: 2, b: 2}, @@ -475,6 +481,8 @@ $(document).ready(function() { equal(coll.where({b: 1}).length, 0); equal(coll.where({b: 2}).length, 2); equal(coll.where({a: 1, b: 2}).length, 1); + equal(coll.findWhere({a: 1}), model); + equal(coll.findWhere({a: 4}), void 0); }); test("Underscore methods", 13, function() { @@ -484,10 +492,10 @@ $(document).ready(function() { equal(col.indexOf(b), 1); equal(col.size(), 4); equal(col.rest().length, 3); - ok(!_.include(col.rest()), a); - ok(!_.include(col.rest()), d); + ok(!_.include(col.rest(), a)); + ok(_.include(col.rest(), d)); ok(!col.isEmpty()); - ok(!_.include(col.without(d)), d); + ok(!_.include(col.without(d), d)); equal(col.max(function(model){ return model.id; }).id, 3); equal(col.min(function(model){ return model.id; }).id, 0); deepEqual(col.chain() @@ -702,9 +710,7 @@ $(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, [], options); - }; + model.sync = function(method, model, options){ options.success(); }; collection.on('add', function(){ ok(true); }); collection.create(model, {wait: true}); }); @@ -928,6 +934,35 @@ $(document).ready(function() { equal(col.length, 1); }); + test("`update` and model level `parse`", function() { + var Model = Backbone.Model.extend({ + parse: function (res) { return res.model; } + }); + var Collection = Backbone.Collection.extend({ + model: Model, + parse: function (res) { return res.models; } + }); + var model = new Model({id: 1}); + var collection = new Collection(model); + collection.update({models: [ + {model: {id: 1}}, + {model: {id: 2}} + ]}, {parse: true}); + equal(collection.first(), model); + }); + + test("`update` data is only parsed once", function() { + var collection = new Backbone.Collection(); + collection.model = Backbone.Model.extend({ + parse: function (data) { + equal(data.parsed, void 0); + data.parsed = true; + return data; + } + }); + collection.update({}, {parse: true}); + }); + test("#1894 - Push should not trigger a sort", 0, function() { var Collection = Backbone.Collection.extend({ comparator: 'id', diff --git a/vendor/backbone/test/events.js b/vendor/backbone/test/events.js index f7ba753f0..920943ad8 100644 --- a/vendor/backbone/test/events.js +++ b/vendor/backbone/test/events.js @@ -99,6 +99,11 @@ $(document).ready(function() { a.listenTo(b, 'event2', cb); a.stopListening(null, {event: cb}); b.trigger('event event2'); + b.off(); + a.listenTo(b, 'event event2', cb); + a.stopListening(null, 'event'); + a.stopListening(); + b.trigger('event2'); }); test("listenToOnce and stopListening", 1, function() { diff --git a/vendor/backbone/test/model.js b/vendor/backbone/test/model.js index 7bbb8337c..088032d7f 100644 --- a/vendor/backbone/test/model.js +++ b/vendor/backbone/test/model.js @@ -401,7 +401,7 @@ $(document).ready(function() { if (attrs.admin) return "Can't change admin status."; }; model.sync = function(method, model, options) { - options.success.call(this, this, {admin: true}, options); + options.success.call(this, {admin: true}); }; model.on('invalid', function(model, error) { lastError = error; @@ -418,6 +418,19 @@ $(document).ready(function() { ok(_.isEqual(this.syncArgs.model, doc)); }); + test("save, fetch, destroy triggers error event when an error occurs", 3, function () { + var model = new Backbone.Model(); + model.on('error', function () { + ok(true); + }); + model.sync = function (method, model, options) { + options.error(); + }; + model.save({data: 2, id: 1}); + model.fetch(); + model.destroy(); + }); + test("save with PATCH", function() { doc.clear().set({id: 1, a: 1, b: 2, c: 3, d: 4}); doc.save(); @@ -435,7 +448,7 @@ $(document).ready(function() { test("save in positional style", 1, function() { var model = new Backbone.Model(); model.sync = function(method, model, options) { - options.success(model, {}, options); + options.success(); }; model.save('title', 'Twelfth Night'); equal(model.get('title'), 'Twelfth Night'); @@ -444,8 +457,8 @@ $(document).ready(function() { 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); + options.success('', options); + options.success(null, options); }; model.save({testing:'empty'}, { success: function (model) { @@ -720,7 +733,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(model, {}, options); + options.success(); }; model.on("change:x", function() { ok(true); }); model.save({x: 3}, {wait: true}); @@ -893,7 +906,7 @@ $(document).ready(function() { } }; model.sync = function(method, model, options) { - options.success(model, {}, options); + options.success(); }; model.save({id: 1}, opts); model.fetch(opts); @@ -902,9 +915,8 @@ $(document).ready(function() { test("#1412 - Trigger 'sync' event.", 3, function() { var model = new Backbone.Model({id: 1}); - model.url = '/test'; + model.sync = function (method, model, options) { options.success(); }; model.on('sync', function(){ ok(true); }); - Backbone.ajax = function(settings){ settings.success(); }; model.fetch(); model.save(); model.destroy(); @@ -950,7 +962,7 @@ $(document).ready(function() { var Model = Backbone.Model.extend({ sync: function(method, model, options) { setTimeout(function(){ - options.success(model, {}, options); + options.success(); start(); }, 0); }