From da3bf989ee1c7845705b0b1c5f319cbb5b5c77a6 Mon Sep 17 00:00:00 2001 From: Karel Ledru-Mathe Date: Thu, 7 Jun 2012 09:49:39 -0400 Subject: [PATCH] Update Backbone back to the non-AMD version and use RequireJS shim --- js/vendor/backbone-0.9.2.js | 254 ++++++++++++++++++++---------------- 1 file changed, 138 insertions(+), 116 deletions(-) diff --git a/js/vendor/backbone-0.9.2.js b/js/vendor/backbone-0.9.2.js index b1fc4bc..d06caf1 100644 --- a/js/vendor/backbone-0.9.2.js +++ b/js/vendor/backbone-0.9.2.js @@ -5,52 +5,46 @@ // For all details and documentation: // http://backbonejs.org -(function(root, factory) { - // Set up Backbone appropriately for the environment. - if (typeof exports !== 'undefined') { - // Node/CommonJS, no need for jQuery in that case. - factory(root, exports, require('underscore')); - } else if (typeof define === 'function' && define.amd) { - // AMD - define(['underscore', 'jquery', 'exports'], function(_, $, exports) { - // Export global even in AMD case in case this script is loaded with - // others that may still expect a global Backbone. - root.Backbone = factory(root, exports, _, $); - }); - } else { - // Browser globals - root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender)); - } -}(this, function(root, Backbone, _, $) { +(function(){ // Initial Setup // ------------- + // Save a reference to the global object (`window` in the browser, `global` + // on the server). + var root = this; + // Save the previous value of the `Backbone` variable, so that it can be // restored later on, if `noConflict` is used. var previousBackbone = root.Backbone; - // Create a local reference to slice/splice. - var slice = Array.prototype.slice; + // Create a local reference to splice. var splice = Array.prototype.splice; + // The top-level namespace. All public Backbone classes and modules will + // be attached to this. Exported for both CommonJS and the browser. + var Backbone; + if (typeof exports !== 'undefined') { + Backbone = exports; + } else { + Backbone = root.Backbone = {}; + } + // Current version of the library. Keep in sync with `package.json`. Backbone.VERSION = '0.9.2'; - // Set the JavaScript library that will be used for DOM manipulation and - // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery, - // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an - // alternate JavaScript library (or a mock library for testing your views - // outside of a browser). - Backbone.setDomLibrary = function(lib) { - $ = lib; - }; + // Require Underscore, if we're on the server, and it's not already present. + var _ = root._; + if (!_ && (typeof require !== 'undefined')) _ = require('underscore'); + + // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable. + Backbone.$ = root.jQuery || root.Zepto || root.ender; // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable // to its previous owner. Returns a reference to this Backbone object. Backbone.noConflict = function() { root.Backbone = previousBackbone; - return Backbone; + return this; }; // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option @@ -72,7 +66,7 @@ // A module that can be mixed in to *any object* in order to provide it with // custom events. You may bind with `on` or remove with `off` callback functions - // to an event; trigger`-ing an event fires all callbacks in succession. + // to an event; `trigger`-ing an event fires all callbacks in succession. // // var object = {}; // _.extend(object, Backbone.Events); @@ -84,22 +78,15 @@ // Bind one or more space separated events, `events`, to a `callback` // function. Passing `"all"` will bind the callback to all events fired. on: function(events, callback, context) { - - var calls, event, node, tail, list; + var calls, event, list; if (!callback) return this; + events = events.split(eventSplitter); calls = this._callbacks || (this._callbacks = {}); - // Create an immutable callback list, allowing traversal during - // modification. The tail is an empty object that will always be used - // as the next node. while (event = events.shift()) { - list = calls[event]; - node = list ? list.tail : {}; - node.next = tail = {}; - node.context = context; - node.callback = callback; - calls[event] = {tail: tail, next: list ? list.next : node}; + list = calls[event] || (calls[event] = []); + list.push(callback, context); } return this; @@ -109,29 +96,27 @@ // with that function. If `callback` is null, removes all callbacks for the // event. If `events` is null, removes all bound callbacks for all events. off: function(events, callback, context) { - var event, calls, node, tail, cb, ctx; + var event, calls, list, i; // No events, or removing *all* events. - if (!(calls = this._callbacks)) return; + if (!(calls = this._callbacks)) return this; if (!(events || callback || context)) { delete this._callbacks; return this; } - // Loop through the listed events and contexts, splicing them out of the - // linked list of callbacks if appropriate. events = events ? events.split(eventSplitter) : _.keys(calls); + + // Loop through the callback list, splicing where appropriate. while (event = events.shift()) { - node = calls[event]; - delete calls[event]; - if (!node || !(callback || context)) continue; - // Create a new list, omitting the indicated callbacks. - tail = node.tail; - while ((node = node.next) !== tail) { - cb = node.callback; - ctx = node.context; - if ((callback && cb !== callback) || (context && ctx !== context)) { - this.on(event, cb, ctx); + if (!(list = calls[event]) || !(callback || context)) { + delete calls[event]; + continue; + } + + for (i = list.length - 2; i >= 0; i -= 2) { + if (!(callback && list[i] !== callback || context && list[i + 1] !== context)) { + list.splice(i, 2); } } } @@ -144,26 +129,34 @@ // (unless you're listening on `"all"`, which will cause your callback to // receive the true name of the event as the first argument). trigger: function(events) { - var event, node, calls, tail, args, all, rest; + var event, calls, list, i, length, args, all, rest; if (!(calls = this._callbacks)) return this; - all = calls.all; + + rest = []; events = events.split(eventSplitter); - rest = slice.call(arguments, 1); + for (i = 1, length = arguments.length; i < length; i++) { + rest[i - 1] = arguments[i]; + } - // For each event, walk through the linked list of callbacks twice, - // first to trigger the event, then to trigger any `"all"` callbacks. + // For each event, walk through the list of callbacks twice, first to + // trigger the event, then to trigger any `"all"` callbacks. while (event = events.shift()) { - if (node = calls[event]) { - tail = node.tail; - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, rest); + // Copy callback lists to prevent modification. + if (all = calls.all) all = all.slice(); + if (list = calls[event]) list = list.slice(); + + // Execute event callbacks. + if (list) { + for (i = 0, length = list.length; i < length; i += 2) { + list[i].apply(list[i + 1] || this, rest); } } - if (node = all) { - tail = node.tail; + + // Execute "all" callbacks. + if (all) { args = [event].concat(rest); - while ((node = node.next) !== tail) { - node.callback.apply(node.context || this, args); + for (i = 0, length = all.length; i < length; i += 2) { + all[i].apply(all[i + 1] || this, args); } } } @@ -314,14 +307,14 @@ // Remove an attribute from the model, firing `"change"` unless you choose // to silence it. `unset` is a noop if the attribute doesn't exist. unset: function(attr, options) { - (options || (options = {})).unset = true; + options = _.extend({}, options, {unset: true}); return this.set(attr, null, options); }, // Clear all attributes on the model, firing `"change"` unless you choose // to silence it. clear: function(options) { - (options || (options = {})).unset = true; + options = _.extend({}, options, {unset: true}); return this.set(_.clone(this.attributes), options); }, @@ -334,7 +327,7 @@ var success = options.success; options.success = function(resp, status, xhr) { if (!model.set(model.parse(resp, xhr), options)) return false; - if (success) success(model, resp); + if (success) success(model, resp, options); }; options.error = Backbone.wrapError(options.error, model, options); return (this.sync || Backbone.sync).call(this, 'read', this, options); @@ -380,7 +373,7 @@ } if (!model.set(serverAttrs, options)) return false; if (success) { - success(model, resp); + success(model, resp, options); } else { model.trigger('sync', model, resp, options); } @@ -390,7 +383,7 @@ options.error = Backbone.wrapError(options.error, model, options); var method = this.isNew() ? 'create' : 'update'; var xhr = (this.sync || Backbone.sync).call(this, method, this, options); - if (options.wait) this.set(current, silentOptions); + if (options.wait) this.clear(silentOptions).set(current, silentOptions); return xhr; }, @@ -414,7 +407,7 @@ options.success = function(resp) { if (options.wait) triggerDestroy(); if (success) { - success(model, resp); + success(model, resp, options); } else { model.trigger('sync', model, resp, options); } @@ -489,7 +482,7 @@ // 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 (!arguments.length) return !_.isEmpty(this.changed); + if (attr == null) return !_.isEmpty(this.changed); return _.has(this.changed, attr); }, @@ -512,7 +505,7 @@ // Get the previous value of an attribute, recorded at the time the last // `"change"` event was fired. previous: function(attr) { - if (!arguments.length || !this._previousAttributes) return null; + if (attr == null || !this._previousAttributes) return null; return this._previousAttributes[attr]; }, @@ -525,7 +518,7 @@ // Check if the model is currently in a valid state. It's only possible to // get into an *invalid* state if you're using silent changes. isValid: function() { - return !this.validate(this.attributes); + return !this.validate || !this.validate(this.attributes); }, // Run validation against the next complete set of model attributes, @@ -555,7 +548,7 @@ var Collection = Backbone.Collection = function(models, options) { options || (options = {}); if (options.model) this.model = options.model; - if (options.comparator) this.comparator = options.comparator; + if (options.comparator !== undefined) this.comparator = options.comparator; this._reset(); this.initialize.apply(this, arguments); if (models) this.reset(models, {silent: true, parse: options.parse}); @@ -603,7 +596,7 @@ // Remove duplicates. i = dups.length; while (i--) { - models.splice(dups[i], 1); + dups[i] = models.splice(dups[i], 1)[0]; } // Listen to added models' events, and index models for lookup by @@ -619,13 +612,24 @@ this.length += length; index = options.at != null ? options.at : this.models.length; splice.apply(this.models, [index, 0].concat(models)); - if (this.comparator) this.sort({silent: true}); + if (this.comparator && options.at == null) this.sort({silent: true}); + + // 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 (options.silent) return this; for (i = 0, length = this.models.length; i < length; i++) { if (!cids[(model = this.models[i]).cid]) continue; options.index = i; model.trigger('add', model, this, options); } + return this; }, @@ -680,6 +684,11 @@ return model; }, + // Slice out a sub-array of models from the collection. + slice: function(begin, end) { + return this.models.slice(begin, end); + }, + // Get a model from the set by id. get: function(id) { if (id == null) return void 0; @@ -753,7 +762,7 @@ var success = options.success; options.success = function(resp, status, xhr) { collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options); - if (success) success(collection, resp); + if (success) success(collection, resp, options); }; options.error = Backbone.wrapError(options.error, collection, options); return (this.sync || Backbone.sync).call(this, 'read', this, options); @@ -772,7 +781,7 @@ options.success = function(nextModel, resp, xhr) { if (options.wait) coll.add(nextModel, options); if (success) { - success(nextModel, resp); + success(nextModel, resp, options); } else { nextModel.trigger('sync', model, resp, options); } @@ -787,10 +796,15 @@ return resp; }, + // Create a new collection with an identical list of models as this one. + clone: function() { + return new this.constructor(this.models); + }, + // Proxy to _'s chain. Can't be proxied the same way the rest of the // underscore methods are proxied because it relies on the underscore // constructor. - chain: function () { + chain: function() { return _(this.models).chain(); }, @@ -803,16 +817,15 @@ }, // Prepare a model or hash of attributes to be added to this collection. - _prepareModel: function(model, options) { - options || (options = {}); - if (!(model instanceof Model)) { - var attrs = model; - options.collection = this; - model = new this.model(attrs, options); - if (!model._validate(model.attributes, options)) model = false; - } else if (!model.collection) { - model.collection = this; + _prepareModel: function(attrs, options) { + if (attrs instanceof Model) { + if (!attrs.collection) attrs.collection = this; + return attrs; } + options || (options = {}); + options.collection = this; + var model = new this.model(attrs, options); + if (!model._validate(model.attributes, options)) return false; return model; }, @@ -835,7 +848,7 @@ } if (model && event === 'change:' + model.idAttribute) { delete this._byId[model.previous(model.idAttribute)]; - this._byId[model.id] = model; + if (model.id != null) this._byId[model.id] = model; } this.trigger.apply(this, arguments); } @@ -974,10 +987,8 @@ // the hash, or the override. getFragment: function(fragment, forcePushState) { if (fragment == null) { - if (this._hasPushState || forcePushState) { + if (this._hasPushState || !this._wantsHashChange || forcePushState) { fragment = window.location.pathname; - var search = window.location.search; - if (search) fragment += search; } else { fragment = this.getHash(); } @@ -1002,17 +1013,17 @@ var docMode = document.documentMode; var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7)); - if (oldIE) { - this.iframe = $('