web/res/js/backbone.js
changeset 1512 487ca37bb0c7
parent 1304 10974bff4dae
equal deleted inserted replaced
1511:25731d245eda 1512:487ca37bb0c7
     1 //     Backbone.js 1.2.3
     1 //     Backbone.js 1.4.0
     2 
     2 
     3 //     (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     3 //     (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
     4 //     Backbone may be freely distributed under the MIT license.
     4 //     Backbone may be freely distributed under the MIT license.
     5 //     For all details and documentation:
     5 //     For all details and documentation:
     6 //     http://backbonejs.org
     6 //     http://backbonejs.org
     7 
     7 
     8 (function(factory) {
     8 (function(factory) {
     9 
     9 
    10   // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
    10   // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
    11   // We use `self` instead of `window` for `WebWorker` support.
    11   // We use `self` instead of `window` for `WebWorker` support.
    12   var root = (typeof self == 'object' && self.self == self && self) ||
    12   var root = typeof self == 'object' && self.self === self && self ||
    13             (typeof global == 'object' && global.global == global && global);
    13             typeof global == 'object' && global.global === global && global;
    14 
    14 
    15   // Set up Backbone appropriately for the environment. Start with AMD.
    15   // Set up Backbone appropriately for the environment. Start with AMD.
    16   if (typeof define === 'function' && define.amd) {
    16   if (typeof define === 'function' && define.amd) {
    17     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    17     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    18       // Export global even in AMD case in case this script is loaded with
    18       // Export global even in AMD case in case this script is loaded with
    21     });
    21     });
    22 
    22 
    23   // Next for Node.js or CommonJS. jQuery may not be needed as a module.
    23   // Next for Node.js or CommonJS. jQuery may not be needed as a module.
    24   } else if (typeof exports !== 'undefined') {
    24   } else if (typeof exports !== 'undefined') {
    25     var _ = require('underscore'), $;
    25     var _ = require('underscore'), $;
    26     try { $ = require('jquery'); } catch(e) {}
    26     try { $ = require('jquery'); } catch (e) {}
    27     factory(root, exports, _, $);
    27     factory(root, exports, _, $);
    28 
    28 
    29   // Finally, as a browser global.
    29   // Finally, as a browser global.
    30   } else {
    30   } else {
    31     root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
    31     root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$);
    32   }
    32   }
    33 
    33 
    34 }(function(root, Backbone, _, $) {
    34 })(function(root, Backbone, _, $) {
    35 
    35 
    36   // Initial Setup
    36   // Initial Setup
    37   // -------------
    37   // -------------
    38 
    38 
    39   // Save the previous value of the `Backbone` variable, so that it can be
    39   // Save the previous value of the `Backbone` variable, so that it can be
    42 
    42 
    43   // Create a local reference to a common array method we'll want to use later.
    43   // Create a local reference to a common array method we'll want to use later.
    44   var slice = Array.prototype.slice;
    44   var slice = Array.prototype.slice;
    45 
    45 
    46   // Current version of the library. Keep in sync with `package.json`.
    46   // Current version of the library. Keep in sync with `package.json`.
    47   Backbone.VERSION = '1.2.3';
    47   Backbone.VERSION = '1.4.0';
    48 
    48 
    49   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
    49   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
    50   // the `$` variable.
    50   // the `$` variable.
    51   Backbone.$ = $;
    51   Backbone.$ = $;
    52 
    52 
    65   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
    65   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
    66   // `application/json` requests ... this will encode the body as
    66   // `application/json` requests ... this will encode the body as
    67   // `application/x-www-form-urlencoded` instead and will send the model in a
    67   // `application/x-www-form-urlencoded` instead and will send the model in a
    68   // form param named `model`.
    68   // form param named `model`.
    69   Backbone.emulateJSON = false;
    69   Backbone.emulateJSON = false;
    70 
       
    71   // Proxy Backbone class methods to Underscore functions, wrapping the model's
       
    72   // `attributes` object or collection's `models` array behind the scenes.
       
    73   //
       
    74   // collection.filter(function(model) { return model.get('age') > 10 });
       
    75   // collection.each(this.addView);
       
    76   //
       
    77   // `Function#apply` can be slow so we use the method's arg count, if we know it.
       
    78   var addMethod = function(length, method, attribute) {
       
    79     switch (length) {
       
    80       case 1: return function() {
       
    81         return _[method](this[attribute]);
       
    82       };
       
    83       case 2: return function(value) {
       
    84         return _[method](this[attribute], value);
       
    85       };
       
    86       case 3: return function(iteratee, context) {
       
    87         return _[method](this[attribute], cb(iteratee, this), context);
       
    88       };
       
    89       case 4: return function(iteratee, defaultVal, context) {
       
    90         return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
       
    91       };
       
    92       default: return function() {
       
    93         var args = slice.call(arguments);
       
    94         args.unshift(this[attribute]);
       
    95         return _[method].apply(_, args);
       
    96       };
       
    97     }
       
    98   };
       
    99   var addUnderscoreMethods = function(Class, methods, attribute) {
       
   100     _.each(methods, function(length, method) {
       
   101       if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
       
   102     });
       
   103   };
       
   104 
       
   105   // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
       
   106   var cb = function(iteratee, instance) {
       
   107     if (_.isFunction(iteratee)) return iteratee;
       
   108     if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
       
   109     if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
       
   110     return iteratee;
       
   111   };
       
   112   var modelMatcher = function(attrs) {
       
   113     var matcher = _.matches(attrs);
       
   114     return function(model) {
       
   115       return matcher(model.attributes);
       
   116     };
       
   117   };
       
   118 
    70 
   119   // Backbone.Events
    71   // Backbone.Events
   120   // ---------------
    72   // ---------------
   121 
    73 
   122   // A module that can be mixed in to *any object* in order to provide it with
    74   // A module that can be mixed in to *any object* in order to provide it with
   132   var Events = Backbone.Events = {};
    84   var Events = Backbone.Events = {};
   133 
    85 
   134   // Regular expression used to split event strings.
    86   // Regular expression used to split event strings.
   135   var eventSplitter = /\s+/;
    87   var eventSplitter = /\s+/;
   136 
    88 
       
    89   // A private global variable to share between listeners and listenees.
       
    90   var _listening;
       
    91 
   137   // Iterates over the standard `event, callback` (as well as the fancy multiple
    92   // Iterates over the standard `event, callback` (as well as the fancy multiple
   138   // space-separated events `"change blur", callback` and jQuery-style event
    93   // space-separated events `"change blur", callback` and jQuery-style event
   139   // maps `{event: callback}`).
    94   // maps `{event: callback}`).
   140   var eventsApi = function(iteratee, events, name, callback, opts) {
    95   var eventsApi = function(iteratee, events, name, callback, opts) {
   141     var i = 0, names;
    96     var i = 0, names;
   144       if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
    99       if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
   145       for (names = _.keys(name); i < names.length ; i++) {
   100       for (names = _.keys(name); i < names.length ; i++) {
   146         events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
   101         events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
   147       }
   102       }
   148     } else if (name && eventSplitter.test(name)) {
   103     } else if (name && eventSplitter.test(name)) {
   149       // Handle space separated event names by delegating them individually.
   104       // Handle space-separated event names by delegating them individually.
   150       for (names = name.split(eventSplitter); i < names.length; i++) {
   105       for (names = name.split(eventSplitter); i < names.length; i++) {
   151         events = iteratee(events, names[i], callback, opts);
   106         events = iteratee(events, names[i], callback, opts);
   152       }
   107       }
   153     } else {
   108     } else {
   154       // Finally, standard events.
   109       // Finally, standard events.
   158   };
   113   };
   159 
   114 
   160   // Bind an event to a `callback` function. Passing `"all"` will bind
   115   // Bind an event to a `callback` function. Passing `"all"` will bind
   161   // the callback to all events fired.
   116   // the callback to all events fired.
   162   Events.on = function(name, callback, context) {
   117   Events.on = function(name, callback, context) {
   163     return internalOn(this, name, callback, context);
   118     this._events = eventsApi(onApi, this._events || {}, name, callback, {
   164   };
   119       context: context,
   165 
   120       ctx: this,
   166   // Guard the `listening` argument from the public API.
   121       listening: _listening
   167   var internalOn = function(obj, name, callback, context, listening) {
       
   168     obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
       
   169         context: context,
       
   170         ctx: obj,
       
   171         listening: listening
       
   172     });
   122     });
   173 
   123 
   174     if (listening) {
   124     if (_listening) {
   175       var listeners = obj._listeners || (obj._listeners = {});
   125       var listeners = this._listeners || (this._listeners = {});
   176       listeners[listening.id] = listening;
   126       listeners[_listening.id] = _listening;
   177     }
   127       // Allow the listening to use a counter, instead of tracking
   178 
   128       // callbacks for library interop
   179     return obj;
   129       _listening.interop = false;
       
   130     }
       
   131 
       
   132     return this;
   180   };
   133   };
   181 
   134 
   182   // Inversion-of-control versions of `on`. Tell *this* object to listen to
   135   // Inversion-of-control versions of `on`. Tell *this* object to listen to
   183   // an event in another object... keeping track of what it's listening to
   136   // an event in another object... keeping track of what it's listening to
   184   // for easier unbinding later.
   137   // for easier unbinding later.
   185   Events.listenTo =  function(obj, name, callback) {
   138   Events.listenTo = function(obj, name, callback) {
   186     if (!obj) return this;
   139     if (!obj) return this;
   187     var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
   140     var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
   188     var listeningTo = this._listeningTo || (this._listeningTo = {});
   141     var listeningTo = this._listeningTo || (this._listeningTo = {});
   189     var listening = listeningTo[id];
   142     var listening = _listening = listeningTo[id];
   190 
   143 
   191     // This object is not listening to any other events on `obj` yet.
   144     // This object is not listening to any other events on `obj` yet.
   192     // Setup the necessary references to track the listening callbacks.
   145     // Setup the necessary references to track the listening callbacks.
   193     if (!listening) {
   146     if (!listening) {
   194       var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
   147       this._listenId || (this._listenId = _.uniqueId('l'));
   195       listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
   148       listening = _listening = listeningTo[id] = new Listening(this, obj);
   196     }
   149     }
   197 
   150 
   198     // Bind callbacks on obj, and keep track of them on listening.
   151     // Bind callbacks on obj.
   199     internalOn(obj, name, callback, this, listening);
   152     var error = tryCatchOn(obj, name, callback, this);
       
   153     _listening = void 0;
       
   154 
       
   155     if (error) throw error;
       
   156     // If the target obj is not Backbone.Events, track events manually.
       
   157     if (listening.interop) listening.on(name, callback);
       
   158 
   200     return this;
   159     return this;
   201   };
   160   };
   202 
   161 
   203   // The reducing API that adds a callback to the `events` object.
   162   // The reducing API that adds a callback to the `events` object.
   204   var onApi = function(events, name, callback, options) {
   163   var onApi = function(events, name, callback, options) {
   205     if (callback) {
   164     if (callback) {
   206       var handlers = events[name] || (events[name] = []);
   165       var handlers = events[name] || (events[name] = []);
   207       var context = options.context, ctx = options.ctx, listening = options.listening;
   166       var context = options.context, ctx = options.ctx, listening = options.listening;
   208       if (listening) listening.count++;
   167       if (listening) listening.count++;
   209 
   168 
   210       handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
   169       handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
   211     }
   170     }
   212     return events;
   171     return events;
       
   172   };
       
   173 
       
   174   // An try-catch guarded #on function, to prevent poisoning the global
       
   175   // `_listening` variable.
       
   176   var tryCatchOn = function(obj, name, callback, context) {
       
   177     try {
       
   178       obj.on(name, callback, context);
       
   179     } catch (e) {
       
   180       return e;
       
   181     }
   213   };
   182   };
   214 
   183 
   215   // Remove one or many callbacks. If `context` is null, removes all
   184   // Remove one or many callbacks. If `context` is null, removes all
   216   // callbacks with that function. If `callback` is null, removes all
   185   // callbacks with that function. If `callback` is null, removes all
   217   // callbacks for the event. If `name` is null, removes all bound
   186   // callbacks for the event. If `name` is null, removes all bound
   218   // callbacks for all events.
   187   // callbacks for all events.
   219   Events.off =  function(name, callback, context) {
   188   Events.off = function(name, callback, context) {
   220     if (!this._events) return this;
   189     if (!this._events) return this;
   221     this._events = eventsApi(offApi, this._events, name, callback, {
   190     this._events = eventsApi(offApi, this._events, name, callback, {
   222         context: context,
   191       context: context,
   223         listeners: this._listeners
   192       listeners: this._listeners
   224     });
   193     });
       
   194 
   225     return this;
   195     return this;
   226   };
   196   };
   227 
   197 
   228   // Tell this object to stop listening to either specific events ... or
   198   // Tell this object to stop listening to either specific events ... or
   229   // to every object it's currently listening to.
   199   // to every object it's currently listening to.
   230   Events.stopListening =  function(obj, name, callback) {
   200   Events.stopListening = function(obj, name, callback) {
   231     var listeningTo = this._listeningTo;
   201     var listeningTo = this._listeningTo;
   232     if (!listeningTo) return this;
   202     if (!listeningTo) return this;
   233 
   203 
   234     var ids = obj ? [obj._listenId] : _.keys(listeningTo);
   204     var ids = obj ? [obj._listenId] : _.keys(listeningTo);
   235 
       
   236     for (var i = 0; i < ids.length; i++) {
   205     for (var i = 0; i < ids.length; i++) {
   237       var listening = listeningTo[ids[i]];
   206       var listening = listeningTo[ids[i]];
   238 
   207 
   239       // If listening doesn't exist, this object is not currently
   208       // If listening doesn't exist, this object is not currently
   240       // listening to obj. Break out early.
   209       // listening to obj. Break out early.
   241       if (!listening) break;
   210       if (!listening) break;
   242 
   211 
   243       listening.obj.off(name, callback, this);
   212       listening.obj.off(name, callback, this);
       
   213       if (listening.interop) listening.off(name, callback);
   244     }
   214     }
   245     if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
   215     if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
   246 
   216 
   247     return this;
   217     return this;
   248   };
   218   };
   249 
   219 
   250   // The reducing API that removes a callback from the `events` object.
   220   // The reducing API that removes a callback from the `events` object.
   251   var offApi = function(events, name, callback, options) {
   221   var offApi = function(events, name, callback, options) {
   252     if (!events) return;
   222     if (!events) return;
   253 
   223 
   254     var i = 0, listening;
       
   255     var context = options.context, listeners = options.listeners;
   224     var context = options.context, listeners = options.listeners;
   256 
   225     var i = 0, names;
   257     // Delete all events listeners and "drop" events.
   226 
   258     if (!name && !callback && !context) {
   227     // Delete all event listeners and "drop" events.
   259       var ids = _.keys(listeners);
   228     if (!name && !context && !callback) {
   260       for (; i < ids.length; i++) {
   229       for (names = _.keys(listeners); i < names.length; i++) {
   261         listening = listeners[ids[i]];
   230         listeners[names[i]].cleanup();
   262         delete listeners[listening.id];
       
   263         delete listening.listeningTo[listening.objId];
       
   264       }
   231       }
   265       return;
   232       return;
   266     }
   233     }
   267 
   234 
   268     var names = name ? [name] : _.keys(events);
   235     names = name ? [name] : _.keys(events);
   269     for (; i < names.length; i++) {
   236     for (; i < names.length; i++) {
   270       name = names[i];
   237       name = names[i];
   271       var handlers = events[name];
   238       var handlers = events[name];
   272 
   239 
   273       // Bail out if there are no events stored.
   240       // Bail out if there are no events stored.
   274       if (!handlers) break;
   241       if (!handlers) break;
   275 
   242 
   276       // Replace events if there are any remaining.  Otherwise, clean up.
   243       // Find any remaining events.
   277       var remaining = [];
   244       var remaining = [];
   278       for (var j = 0; j < handlers.length; j++) {
   245       for (var j = 0; j < handlers.length; j++) {
   279         var handler = handlers[j];
   246         var handler = handlers[j];
   280         if (
   247         if (
   281           callback && callback !== handler.callback &&
   248           callback && callback !== handler.callback &&
   282             callback !== handler.callback._callback ||
   249             callback !== handler.callback._callback ||
   283               context && context !== handler.context
   250               context && context !== handler.context
   284         ) {
   251         ) {
   285           remaining.push(handler);
   252           remaining.push(handler);
   286         } else {
   253         } else {
   287           listening = handler.listening;
   254           var listening = handler.listening;
   288           if (listening && --listening.count === 0) {
   255           if (listening) listening.off(name, callback);
   289             delete listeners[listening.id];
       
   290             delete listening.listeningTo[listening.objId];
       
   291           }
       
   292         }
   256         }
   293       }
   257       }
   294 
   258 
   295       // Update tail event if the list has any events.  Otherwise, clean up.
   259       // Replace events if there are any remaining.  Otherwise, clean up.
   296       if (remaining.length) {
   260       if (remaining.length) {
   297         events[name] = remaining;
   261         events[name] = remaining;
   298       } else {
   262       } else {
   299         delete events[name];
   263         delete events[name];
   300       }
   264       }
   301     }
   265     }
   302     if (_.size(events)) return events;
   266 
       
   267     return events;
   303   };
   268   };
   304 
   269 
   305   // Bind an event to only be triggered a single time. After the first time
   270   // Bind an event to only be triggered a single time. After the first time
   306   // the callback is invoked, its listener will be removed. If multiple events
   271   // the callback is invoked, its listener will be removed. If multiple events
   307   // are passed in using the space-separated syntax, the handler will fire
   272   // are passed in using the space-separated syntax, the handler will fire
   308   // once for each event, not once for a combination of all events.
   273   // once for each event, not once for a combination of all events.
   309   Events.once =  function(name, callback, context) {
   274   Events.once = function(name, callback, context) {
   310     // Map the event into a `{event: once}` object.
   275     // Map the event into a `{event: once}` object.
   311     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
   276     var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
   312     return this.on(events, void 0, context);
   277     if (typeof name === 'string' && context == null) callback = void 0;
       
   278     return this.on(events, callback, context);
   313   };
   279   };
   314 
   280 
   315   // Inversion-of-control versions of `once`.
   281   // Inversion-of-control versions of `once`.
   316   Events.listenToOnce =  function(obj, name, callback) {
   282   Events.listenToOnce = function(obj, name, callback) {
   317     // Map the event into a `{event: once}` object.
   283     // Map the event into a `{event: once}` object.
   318     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
   284     var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
   319     return this.listenTo(obj, events);
   285     return this.listenTo(obj, events);
   320   };
   286   };
   321 
   287 
   322   // Reduces the event callbacks into a map of `{event: onceWrapper}`.
   288   // Reduces the event callbacks into a map of `{event: onceWrapper}`.
   323   // `offer` unbinds the `onceWrapper` after it has been called.
   289   // `offer` unbinds the `onceWrapper` after it has been called.
   334 
   300 
   335   // Trigger one or many events, firing all bound callbacks. Callbacks are
   301   // Trigger one or many events, firing all bound callbacks. Callbacks are
   336   // passed the same arguments as `trigger` is, apart from the event name
   302   // passed the same arguments as `trigger` is, apart from the event name
   337   // (unless you're listening on `"all"`, which will cause your callback to
   303   // (unless you're listening on `"all"`, which will cause your callback to
   338   // receive the true name of the event as the first argument).
   304   // receive the true name of the event as the first argument).
   339   Events.trigger =  function(name) {
   305   Events.trigger = function(name) {
   340     if (!this._events) return this;
   306     if (!this._events) return this;
   341 
   307 
   342     var length = Math.max(0, arguments.length - 1);
   308     var length = Math.max(0, arguments.length - 1);
   343     var args = Array(length);
   309     var args = Array(length);
   344     for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
   310     for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
   346     eventsApi(triggerApi, this._events, name, void 0, args);
   312     eventsApi(triggerApi, this._events, name, void 0, args);
   347     return this;
   313     return this;
   348   };
   314   };
   349 
   315 
   350   // Handles triggering the appropriate event callbacks.
   316   // Handles triggering the appropriate event callbacks.
   351   var triggerApi = function(objEvents, name, cb, args) {
   317   var triggerApi = function(objEvents, name, callback, args) {
   352     if (objEvents) {
   318     if (objEvents) {
   353       var events = objEvents[name];
   319       var events = objEvents[name];
   354       var allEvents = objEvents.all;
   320       var allEvents = objEvents.all;
   355       if (events && allEvents) allEvents = allEvents.slice();
   321       if (events && allEvents) allEvents = allEvents.slice();
   356       if (events) triggerEvents(events, args);
   322       if (events) triggerEvents(events, args);
   371       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   337       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   372       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   338       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   373     }
   339     }
   374   };
   340   };
   375 
   341 
       
   342   // A listening class that tracks and cleans up memory bindings
       
   343   // when all callbacks have been offed.
       
   344   var Listening = function(listener, obj) {
       
   345     this.id = listener._listenId;
       
   346     this.listener = listener;
       
   347     this.obj = obj;
       
   348     this.interop = true;
       
   349     this.count = 0;
       
   350     this._events = void 0;
       
   351   };
       
   352 
       
   353   Listening.prototype.on = Events.on;
       
   354 
       
   355   // Offs a callback (or several).
       
   356   // Uses an optimized counter if the listenee uses Backbone.Events.
       
   357   // Otherwise, falls back to manual tracking to support events
       
   358   // library interop.
       
   359   Listening.prototype.off = function(name, callback) {
       
   360     var cleanup;
       
   361     if (this.interop) {
       
   362       this._events = eventsApi(offApi, this._events, name, callback, {
       
   363         context: void 0,
       
   364         listeners: void 0
       
   365       });
       
   366       cleanup = !this._events;
       
   367     } else {
       
   368       this.count--;
       
   369       cleanup = this.count === 0;
       
   370     }
       
   371     if (cleanup) this.cleanup();
       
   372   };
       
   373 
       
   374   // Cleans up memory bindings between the listener and the listenee.
       
   375   Listening.prototype.cleanup = function() {
       
   376     delete this.listener._listeningTo[this.obj._listenId];
       
   377     if (!this.interop) delete this.obj._listeners[this.id];
       
   378   };
       
   379 
   376   // Aliases for backwards compatibility.
   380   // Aliases for backwards compatibility.
   377   Events.bind   = Events.on;
   381   Events.bind   = Events.on;
   378   Events.unbind = Events.off;
   382   Events.unbind = Events.off;
   379 
   383 
   380   // Allow the `Backbone` object to serve as a global event bus, for folks who
   384   // Allow the `Backbone` object to serve as a global event bus, for folks who
   392   // Create a new model with the specified attributes. A client id (`cid`)
   396   // Create a new model with the specified attributes. A client id (`cid`)
   393   // is automatically generated and assigned for you.
   397   // is automatically generated and assigned for you.
   394   var Model = Backbone.Model = function(attributes, options) {
   398   var Model = Backbone.Model = function(attributes, options) {
   395     var attrs = attributes || {};
   399     var attrs = attributes || {};
   396     options || (options = {});
   400     options || (options = {});
       
   401     this.preinitialize.apply(this, arguments);
   397     this.cid = _.uniqueId(this.cidPrefix);
   402     this.cid = _.uniqueId(this.cidPrefix);
   398     this.attributes = {};
   403     this.attributes = {};
   399     if (options.collection) this.collection = options.collection;
   404     if (options.collection) this.collection = options.collection;
   400     if (options.parse) attrs = this.parse(attrs, options) || {};
   405     if (options.parse) attrs = this.parse(attrs, options) || {};
   401     attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
   406     var defaults = _.result(this, 'defaults');
       
   407     attrs = _.defaults(_.extend({}, defaults, attrs), defaults);
   402     this.set(attrs, options);
   408     this.set(attrs, options);
   403     this.changed = {};
   409     this.changed = {};
   404     this.initialize.apply(this, arguments);
   410     this.initialize.apply(this, arguments);
   405   };
   411   };
   406 
   412 
   418     idAttribute: 'id',
   424     idAttribute: 'id',
   419 
   425 
   420     // The prefix is used to create the client id which is used to identify models locally.
   426     // The prefix is used to create the client id which is used to identify models locally.
   421     // You may want to override this if you're experiencing name clashes with model ids.
   427     // You may want to override this if you're experiencing name clashes with model ids.
   422     cidPrefix: 'c',
   428     cidPrefix: 'c',
       
   429 
       
   430     // preinitialize is an empty function by default. You can override it with a function
       
   431     // or object.  preinitialize will run before any instantiation logic is run in the Model.
       
   432     preinitialize: function(){},
   423 
   433 
   424     // Initialize is an empty function by default. Override it with your own
   434     // Initialize is an empty function by default. Override it with your own
   425     // initialization logic.
   435     // initialization logic.
   426     initialize: function(){},
   436     initialize: function(){},
   427 
   437 
   504         }
   514         }
   505         unset ? delete current[attr] : current[attr] = val;
   515         unset ? delete current[attr] : current[attr] = val;
   506       }
   516       }
   507 
   517 
   508       // Update the `id`.
   518       // Update the `id`.
   509       this.id = this.get(this.idAttribute);
   519       if (this.idAttribute in attrs) this.id = this.get(this.idAttribute);
   510 
   520 
   511       // Trigger all relevant attribute changes.
   521       // Trigger all relevant attribute changes.
   512       if (!silent) {
   522       if (!silent) {
   513         if (changes.length) this._pending = options;
   523         if (changes.length) this._pending = options;
   514         for (var i = 0; i < changes.length; i++) {
   524         for (var i = 0; i < changes.length; i++) {
   559     // determining if there *would be* a change.
   569     // determining if there *would be* a change.
   560     changedAttributes: function(diff) {
   570     changedAttributes: function(diff) {
   561       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   571       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   562       var old = this._changing ? this._previousAttributes : this.attributes;
   572       var old = this._changing ? this._previousAttributes : this.attributes;
   563       var changed = {};
   573       var changed = {};
       
   574       var hasChanged;
   564       for (var attr in diff) {
   575       for (var attr in diff) {
   565         var val = diff[attr];
   576         var val = diff[attr];
   566         if (_.isEqual(old[attr], val)) continue;
   577         if (_.isEqual(old[attr], val)) continue;
   567         changed[attr] = val;
   578         changed[attr] = val;
   568       }
   579         hasChanged = true;
   569       return _.size(changed) ? changed : false;
   580       }
       
   581       return hasChanged ? changed : false;
   570     },
   582     },
   571 
   583 
   572     // Get the previous value of an attribute, recorded at the time the last
   584     // Get the previous value of an attribute, recorded at the time the last
   573     // `"change"` event was fired.
   585     // `"change"` event was fired.
   574     previous: function(attr) {
   586     previous: function(attr) {
   617       // If we're not waiting and attributes exist, save acts as
   629       // If we're not waiting and attributes exist, save acts as
   618       // `set(attr).save(null, opts)` with validation. Otherwise, check if
   630       // `set(attr).save(null, opts)` with validation. Otherwise, check if
   619       // the model will be valid when the attributes, if any, are set.
   631       // the model will be valid when the attributes, if any, are set.
   620       if (attrs && !wait) {
   632       if (attrs && !wait) {
   621         if (!this.set(attrs, options)) return false;
   633         if (!this.set(attrs, options)) return false;
   622       } else {
   634       } else if (!this._validate(attrs, options)) {
   623         if (!this._validate(attrs, options)) return false;
   635         return false;
   624       }
   636       }
   625 
   637 
   626       // After a successful server-side save, the client is (optionally)
   638       // After a successful server-side save, the client is (optionally)
   627       // updated with the server-side state.
   639       // updated with the server-side state.
   628       var model = this;
   640       var model = this;
   640       wrapError(this, options);
   652       wrapError(this, options);
   641 
   653 
   642       // Set temporary attributes if `{wait: true}` to properly find new ids.
   654       // Set temporary attributes if `{wait: true}` to properly find new ids.
   643       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
   655       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
   644 
   656 
   645       var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
   657       var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
   646       if (method === 'patch' && !options.attrs) options.attrs = attrs;
   658       if (method === 'patch' && !options.attrs) options.attrs = attrs;
   647       var xhr = this.sync(method, this, options);
   659       var xhr = this.sync(method, this, options);
   648 
   660 
   649       // Restore attributes.
   661       // Restore attributes.
   650       this.attributes = attributes;
   662       this.attributes = attributes;
   712       return !this.has(this.idAttribute);
   724       return !this.has(this.idAttribute);
   713     },
   725     },
   714 
   726 
   715     // Check if the model is currently in a valid state.
   727     // Check if the model is currently in a valid state.
   716     isValid: function(options) {
   728     isValid: function(options) {
   717       return this._validate({}, _.defaults({validate: true}, options));
   729       return this._validate({}, _.extend({}, options, {validate: true}));
   718     },
   730     },
   719 
   731 
   720     // Run validation against the next complete set of model attributes,
   732     // Run validation against the next complete set of model attributes,
   721     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
   733     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
   722     _validate: function(attrs, options) {
   734     _validate: function(attrs, options) {
   728       return false;
   740       return false;
   729     }
   741     }
   730 
   742 
   731   });
   743   });
   732 
   744 
   733   // Underscore methods that we want to implement on the Model, mapped to the
       
   734   // number of arguments they take.
       
   735   var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
       
   736       omit: 0, chain: 1, isEmpty: 1 };
       
   737 
       
   738   // Mix in each Underscore method as a proxy to `Model#attributes`.
       
   739   addUnderscoreMethods(Model, modelMethods, 'attributes');
       
   740 
       
   741   // Backbone.Collection
   745   // Backbone.Collection
   742   // -------------------
   746   // -------------------
   743 
   747 
   744   // If models tend to represent a single row of data, a Backbone Collection is
   748   // If models tend to represent a single row of data, a Backbone Collection is
   745   // more analogous to a table full of data ... or a small slice or page of that
   749   // more analogous to a table full of data ... or a small slice or page of that
   751   // Create a new **Collection**, perhaps to contain a specific type of `model`.
   755   // Create a new **Collection**, perhaps to contain a specific type of `model`.
   752   // If a `comparator` is specified, the Collection will maintain
   756   // If a `comparator` is specified, the Collection will maintain
   753   // its models in sort order, as they're added and removed.
   757   // its models in sort order, as they're added and removed.
   754   var Collection = Backbone.Collection = function(models, options) {
   758   var Collection = Backbone.Collection = function(models, options) {
   755     options || (options = {});
   759     options || (options = {});
       
   760     this.preinitialize.apply(this, arguments);
   756     if (options.model) this.model = options.model;
   761     if (options.model) this.model = options.model;
   757     if (options.comparator !== void 0) this.comparator = options.comparator;
   762     if (options.comparator !== void 0) this.comparator = options.comparator;
   758     this._reset();
   763     this._reset();
   759     this.initialize.apply(this, arguments);
   764     this.initialize.apply(this, arguments);
   760     if (models) this.reset(models, _.extend({silent: true}, options));
   765     if (models) this.reset(models, _.extend({silent: true}, options));
   767   // Splices `insert` into `array` at index `at`.
   772   // Splices `insert` into `array` at index `at`.
   768   var splice = function(array, insert, at) {
   773   var splice = function(array, insert, at) {
   769     at = Math.min(Math.max(at, 0), array.length);
   774     at = Math.min(Math.max(at, 0), array.length);
   770     var tail = Array(array.length - at);
   775     var tail = Array(array.length - at);
   771     var length = insert.length;
   776     var length = insert.length;
   772     for (var i = 0; i < tail.length; i++) tail[i] = array[i + at];
   777     var i;
       
   778     for (i = 0; i < tail.length; i++) tail[i] = array[i + at];
   773     for (i = 0; i < length; i++) array[i + at] = insert[i];
   779     for (i = 0; i < length; i++) array[i + at] = insert[i];
   774     for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
   780     for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
   775   };
   781   };
   776 
   782 
   777   // Define the Collection's inheritable methods.
   783   // Define the Collection's inheritable methods.
   778   _.extend(Collection.prototype, Events, {
   784   _.extend(Collection.prototype, Events, {
   779 
   785 
   780     // The default model for a collection is just a **Backbone.Model**.
   786     // The default model for a collection is just a **Backbone.Model**.
   781     // This should be overridden in most cases.
   787     // This should be overridden in most cases.
   782     model: Model,
   788     model: Model,
       
   789 
       
   790 
       
   791     // preinitialize is an empty function by default. You can override it with a function
       
   792     // or object.  preinitialize will run before any instantiation logic is run in the Collection.
       
   793     preinitialize: function(){},
   783 
   794 
   784     // Initialize is an empty function by default. Override it with your own
   795     // Initialize is an empty function by default. Override it with your own
   785     // initialization logic.
   796     // initialization logic.
   786     initialize: function(){},
   797     initialize: function(){},
   787 
   798 
   805 
   816 
   806     // Remove a model, or a list of models from the set.
   817     // Remove a model, or a list of models from the set.
   807     remove: function(models, options) {
   818     remove: function(models, options) {
   808       options = _.extend({}, options);
   819       options = _.extend({}, options);
   809       var singular = !_.isArray(models);
   820       var singular = !_.isArray(models);
   810       models = singular ? [models] : _.clone(models);
   821       models = singular ? [models] : models.slice();
   811       var removed = this._removeModels(models, options);
   822       var removed = this._removeModels(models, options);
   812       if (!options.silent && removed) this.trigger('update', this, options);
   823       if (!options.silent && removed.length) {
       
   824         options.changes = {added: [], merged: [], removed: removed};
       
   825         this.trigger('update', this, options);
       
   826       }
   813       return singular ? removed[0] : removed;
   827       return singular ? removed[0] : removed;
   814     },
   828     },
   815 
   829 
   816     // Update a collection by `set`-ing a new list of models, adding new ones,
   830     // Update a collection by `set`-ing a new list of models, adding new ones,
   817     // removing models that are no longer present, and merging models that
   831     // removing models that are no longer present, and merging models that
   818     // already exist in the collection, as necessary. Similar to **Model#set**,
   832     // already exist in the collection, as necessary. Similar to **Model#set**,
   819     // the core operation for updating the data contained by the collection.
   833     // the core operation for updating the data contained by the collection.
   820     set: function(models, options) {
   834     set: function(models, options) {
   821       if (models == null) return;
   835       if (models == null) return;
   822 
   836 
   823       options = _.defaults({}, options, setOptions);
   837       options = _.extend({}, setOptions, options);
   824       if (options.parse && !this._isModel(models)) models = this.parse(models, options);
   838       if (options.parse && !this._isModel(models)) {
       
   839         models = this.parse(models, options) || [];
       
   840       }
   825 
   841 
   826       var singular = !_.isArray(models);
   842       var singular = !_.isArray(models);
   827       models = singular ? [models] : models.slice();
   843       models = singular ? [models] : models.slice();
   828 
   844 
   829       var at = options.at;
   845       var at = options.at;
   830       if (at != null) at = +at;
   846       if (at != null) at = +at;
       
   847       if (at > this.length) at = this.length;
   831       if (at < 0) at += this.length + 1;
   848       if (at < 0) at += this.length + 1;
   832 
   849 
   833       var set = [];
   850       var set = [];
   834       var toAdd = [];
   851       var toAdd = [];
       
   852       var toMerge = [];
   835       var toRemove = [];
   853       var toRemove = [];
   836       var modelMap = {};
   854       var modelMap = {};
   837 
   855 
   838       var add = options.add;
   856       var add = options.add;
   839       var merge = options.merge;
   857       var merge = options.merge;
   840       var remove = options.remove;
   858       var remove = options.remove;
   841 
   859 
   842       var sort = false;
   860       var sort = false;
   843       var sortable = this.comparator && (at == null) && options.sort !== false;
   861       var sortable = this.comparator && at == null && options.sort !== false;
   844       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
   862       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
   845 
   863 
   846       // Turn bare objects into model references, and prevent invalid models
   864       // Turn bare objects into model references, and prevent invalid models
   847       // from being added.
   865       // from being added.
   848       var model;
   866       var model, i;
   849       for (var i = 0; i < models.length; i++) {
   867       for (i = 0; i < models.length; i++) {
   850         model = models[i];
   868         model = models[i];
   851 
   869 
   852         // If a duplicate is found, prevent it from being added and
   870         // If a duplicate is found, prevent it from being added and
   853         // optionally merge it into the existing model.
   871         // optionally merge it into the existing model.
   854         var existing = this.get(model);
   872         var existing = this.get(model);
   855         if (existing) {
   873         if (existing) {
   856           if (merge && model !== existing) {
   874           if (merge && model !== existing) {
   857             var attrs = this._isModel(model) ? model.attributes : model;
   875             var attrs = this._isModel(model) ? model.attributes : model;
   858             if (options.parse) attrs = existing.parse(attrs, options);
   876             if (options.parse) attrs = existing.parse(attrs, options);
   859             existing.set(attrs, options);
   877             existing.set(attrs, options);
       
   878             toMerge.push(existing);
   860             if (sortable && !sort) sort = existing.hasChanged(sortAttr);
   879             if (sortable && !sort) sort = existing.hasChanged(sortAttr);
   861           }
   880           }
   862           if (!modelMap[existing.cid]) {
   881           if (!modelMap[existing.cid]) {
   863             modelMap[existing.cid] = true;
   882             modelMap[existing.cid] = true;
   864             set.push(existing);
   883             set.push(existing);
   888 
   907 
   889       // See if sorting is needed, update `length` and splice in new models.
   908       // See if sorting is needed, update `length` and splice in new models.
   890       var orderChanged = false;
   909       var orderChanged = false;
   891       var replace = !sortable && add && remove;
   910       var replace = !sortable && add && remove;
   892       if (set.length && replace) {
   911       if (set.length && replace) {
   893         orderChanged = this.length != set.length || _.some(this.models, function(model, index) {
   912         orderChanged = this.length !== set.length || _.some(this.models, function(m, index) {
   894           return model !== set[index];
   913           return m !== set[index];
   895         });
   914         });
   896         this.models.length = 0;
   915         this.models.length = 0;
   897         splice(this.models, set, 0);
   916         splice(this.models, set, 0);
   898         this.length = this.models.length;
   917         this.length = this.models.length;
   899       } else if (toAdd.length) {
   918       } else if (toAdd.length) {
   903       }
   922       }
   904 
   923 
   905       // Silently sort the collection if appropriate.
   924       // Silently sort the collection if appropriate.
   906       if (sort) this.sort({silent: true});
   925       if (sort) this.sort({silent: true});
   907 
   926 
   908       // Unless silenced, it's time to fire all appropriate add/sort events.
   927       // Unless silenced, it's time to fire all appropriate add/sort/update events.
   909       if (!options.silent) {
   928       if (!options.silent) {
   910         for (i = 0; i < toAdd.length; i++) {
   929         for (i = 0; i < toAdd.length; i++) {
   911           if (at != null) options.index = at + i;
   930           if (at != null) options.index = at + i;
   912           model = toAdd[i];
   931           model = toAdd[i];
   913           model.trigger('add', model, this, options);
   932           model.trigger('add', model, this, options);
   914         }
   933         }
   915         if (sort || orderChanged) this.trigger('sort', this, options);
   934         if (sort || orderChanged) this.trigger('sort', this, options);
   916         if (toAdd.length || toRemove.length) this.trigger('update', this, options);
   935         if (toAdd.length || toRemove.length || toMerge.length) {
       
   936           options.changes = {
       
   937             added: toAdd,
       
   938             removed: toRemove,
       
   939             merged: toMerge
       
   940           };
       
   941           this.trigger('update', this, options);
       
   942         }
   917       }
   943       }
   918 
   944 
   919       // Return the added (or merged) model (or models).
   945       // Return the added (or merged) model (or models).
   920       return singular ? models[0] : models;
   946       return singular ? models[0] : models;
   921     },
   947     },
   961     // Slice out a sub-array of models from the collection.
   987     // Slice out a sub-array of models from the collection.
   962     slice: function() {
   988     slice: function() {
   963       return slice.apply(this.models, arguments);
   989       return slice.apply(this.models, arguments);
   964     },
   990     },
   965 
   991 
   966     // Get a model from the set by id.
   992     // Get a model from the set by id, cid, model object with id or cid
       
   993     // properties, or an attributes object that is transformed through modelId.
   967     get: function(obj) {
   994     get: function(obj) {
   968       if (obj == null) return void 0;
   995       if (obj == null) return void 0;
   969       var id = this.modelId(this._isModel(obj) ? obj.attributes : obj);
   996       return this._byId[obj] ||
   970       return this._byId[obj] || this._byId[id] || this._byId[obj.cid];
   997         this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] ||
       
   998         obj.cid && this._byId[obj.cid];
       
   999     },
       
  1000 
       
  1001     // Returns `true` if the model is in the collection.
       
  1002     has: function(obj) {
       
  1003       return this.get(obj) != null;
   971     },
  1004     },
   972 
  1005 
   973     // Get the model at the given index.
  1006     // Get the model at the given index.
   974     at: function(index) {
  1007     at: function(index) {
   975       if (index < 0) index += this.length;
  1008       if (index < 0) index += this.length;
   995       var comparator = this.comparator;
  1028       var comparator = this.comparator;
   996       if (!comparator) throw new Error('Cannot sort a set without a comparator');
  1029       if (!comparator) throw new Error('Cannot sort a set without a comparator');
   997       options || (options = {});
  1030       options || (options = {});
   998 
  1031 
   999       var length = comparator.length;
  1032       var length = comparator.length;
  1000       if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
  1033       if (_.isFunction(comparator)) comparator = comparator.bind(this);
  1001 
  1034 
  1002       // Run sort based on type of `comparator`.
  1035       // Run sort based on type of `comparator`.
  1003       if (length === 1 || _.isString(comparator)) {
  1036       if (length === 1 || _.isString(comparator)) {
  1004         this.models = this.sortBy(comparator);
  1037         this.models = this.sortBy(comparator);
  1005       } else {
  1038       } else {
  1009       return this;
  1042       return this;
  1010     },
  1043     },
  1011 
  1044 
  1012     // Pluck an attribute from each model in the collection.
  1045     // Pluck an attribute from each model in the collection.
  1013     pluck: function(attr) {
  1046     pluck: function(attr) {
  1014       return _.invoke(this.models, 'get', attr);
  1047       return this.map(attr + '');
  1015     },
  1048     },
  1016 
  1049 
  1017     // Fetch the default set of models for this collection, resetting the
  1050     // Fetch the default set of models for this collection, resetting the
  1018     // collection when they arrive. If `reset: true` is passed, the response
  1051     // collection when they arrive. If `reset: true` is passed, the response
  1019     // data will be passed through the `reset` method instead of `set`.
  1052     // data will be passed through the `reset` method instead of `set`.
  1040       model = this._prepareModel(model, options);
  1073       model = this._prepareModel(model, options);
  1041       if (!model) return false;
  1074       if (!model) return false;
  1042       if (!wait) this.add(model, options);
  1075       if (!wait) this.add(model, options);
  1043       var collection = this;
  1076       var collection = this;
  1044       var success = options.success;
  1077       var success = options.success;
  1045       options.success = function(model, resp, callbackOpts) {
  1078       options.success = function(m, resp, callbackOpts) {
  1046         if (wait) collection.add(model, callbackOpts);
  1079         if (wait) collection.add(m, callbackOpts);
  1047         if (success) success.call(callbackOpts.context, model, resp, callbackOpts);
  1080         if (success) success.call(callbackOpts.context, m, resp, callbackOpts);
  1048       };
  1081       };
  1049       model.save(null, options);
  1082       model.save(null, options);
  1050       return model;
  1083       return model;
  1051     },
  1084     },
  1052 
  1085 
  1063         comparator: this.comparator
  1096         comparator: this.comparator
  1064       });
  1097       });
  1065     },
  1098     },
  1066 
  1099 
  1067     // Define how to uniquely identify models in the collection.
  1100     // Define how to uniquely identify models in the collection.
  1068     modelId: function (attrs) {
  1101     modelId: function(attrs) {
  1069       return attrs[this.model.prototype.idAttribute || 'id'];
  1102       return attrs[this.model.prototype.idAttribute || 'id'];
       
  1103     },
       
  1104 
       
  1105     // Get an iterator of all models in this collection.
       
  1106     values: function() {
       
  1107       return new CollectionIterator(this, ITERATOR_VALUES);
       
  1108     },
       
  1109 
       
  1110     // Get an iterator of all model IDs in this collection.
       
  1111     keys: function() {
       
  1112       return new CollectionIterator(this, ITERATOR_KEYS);
       
  1113     },
       
  1114 
       
  1115     // Get an iterator of all [ID, model] tuples in this collection.
       
  1116     entries: function() {
       
  1117       return new CollectionIterator(this, ITERATOR_KEYSVALUES);
  1070     },
  1118     },
  1071 
  1119 
  1072     // Private method to reset all internal state. Called when the collection
  1120     // Private method to reset all internal state. Called when the collection
  1073     // is first initialized or reset.
  1121     // is first initialized or reset.
  1074     _reset: function() {
  1122     _reset: function() {
  1101 
  1149 
  1102         var index = this.indexOf(model);
  1150         var index = this.indexOf(model);
  1103         this.models.splice(index, 1);
  1151         this.models.splice(index, 1);
  1104         this.length--;
  1152         this.length--;
  1105 
  1153 
       
  1154         // Remove references before triggering 'remove' event to prevent an
       
  1155         // infinite loop. #3693
       
  1156         delete this._byId[model.cid];
       
  1157         var id = this.modelId(model.attributes);
       
  1158         if (id != null) delete this._byId[id];
       
  1159 
  1106         if (!options.silent) {
  1160         if (!options.silent) {
  1107           options.index = index;
  1161           options.index = index;
  1108           model.trigger('remove', model, this, options);
  1162           model.trigger('remove', model, this, options);
  1109         }
  1163         }
  1110 
  1164 
  1111         removed.push(model);
  1165         removed.push(model);
  1112         this._removeReference(model, options);
  1166         this._removeReference(model, options);
  1113       }
  1167       }
  1114       return removed.length ? removed : false;
  1168       return removed;
  1115     },
  1169     },
  1116 
  1170 
  1117     // Method for checking whether an object should be considered a model for
  1171     // Method for checking whether an object should be considered a model for
  1118     // the purposes of adding to the collection.
  1172     // the purposes of adding to the collection.
  1119     _isModel: function (model) {
  1173     _isModel: function(model) {
  1120       return model instanceof Model;
  1174       return model instanceof Model;
  1121     },
  1175     },
  1122 
  1176 
  1123     // Internal method to create a model's ties to a collection.
  1177     // Internal method to create a model's ties to a collection.
  1124     _addReference: function(model, options) {
  1178     _addReference: function(model, options) {
  1140     // Internal method called every time a model in the set fires an event.
  1194     // Internal method called every time a model in the set fires an event.
  1141     // Sets need to update their indexes when models change ids. All other
  1195     // Sets need to update their indexes when models change ids. All other
  1142     // events simply proxy through. "add" and "remove" events that originate
  1196     // events simply proxy through. "add" and "remove" events that originate
  1143     // in other collections are ignored.
  1197     // in other collections are ignored.
  1144     _onModelEvent: function(event, model, collection, options) {
  1198     _onModelEvent: function(event, model, collection, options) {
  1145       if ((event === 'add' || event === 'remove') && collection !== this) return;
  1199       if (model) {
  1146       if (event === 'destroy') this.remove(model, options);
  1200         if ((event === 'add' || event === 'remove') && collection !== this) return;
  1147       if (event === 'change') {
  1201         if (event === 'destroy') this.remove(model, options);
  1148         var prevId = this.modelId(model.previousAttributes());
  1202         if (event === 'change') {
  1149         var id = this.modelId(model.attributes);
  1203           var prevId = this.modelId(model.previousAttributes());
  1150         if (prevId !== id) {
  1204           var id = this.modelId(model.attributes);
  1151           if (prevId != null) delete this._byId[prevId];
  1205           if (prevId !== id) {
  1152           if (id != null) this._byId[id] = model;
  1206             if (prevId != null) delete this._byId[prevId];
       
  1207             if (id != null) this._byId[id] = model;
       
  1208           }
  1153         }
  1209         }
  1154       }
  1210       }
  1155       this.trigger.apply(this, arguments);
  1211       this.trigger.apply(this, arguments);
  1156     }
  1212     }
  1157 
  1213 
  1158   });
  1214   });
  1159 
  1215 
  1160   // Underscore methods that we want to implement on the Collection.
  1216   // Defining an @@iterator method implements JavaScript's Iterable protocol.
  1161   // 90% of the core usefulness of Backbone Collections is actually implemented
  1217   // In modern ES2015 browsers, this value is found at Symbol.iterator.
  1162   // right here:
  1218   /* global Symbol */
  1163   var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
  1219   var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
  1164       foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
  1220   if ($$iterator) {
  1165       select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
  1221     Collection.prototype[$$iterator] = Collection.prototype.values;
  1166       contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
  1222   }
  1167       head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
  1223 
  1168       without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
  1224   // CollectionIterator
  1169       isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
  1225   // ------------------
  1170       sortBy: 3, indexBy: 3};
  1226 
  1171 
  1227   // A CollectionIterator implements JavaScript's Iterator protocol, allowing the
  1172   // Mix in each Underscore method as a proxy to `Collection#models`.
  1228   // use of `for of` loops in modern browsers and interoperation between
  1173   addUnderscoreMethods(Collection, collectionMethods, 'models');
  1229   // Backbone.Collection and other JavaScript functions and third-party libraries
       
  1230   // which can operate on Iterables.
       
  1231   var CollectionIterator = function(collection, kind) {
       
  1232     this._collection = collection;
       
  1233     this._kind = kind;
       
  1234     this._index = 0;
       
  1235   };
       
  1236 
       
  1237   // This "enum" defines the three possible kinds of values which can be emitted
       
  1238   // by a CollectionIterator that correspond to the values(), keys() and entries()
       
  1239   // methods on Collection, respectively.
       
  1240   var ITERATOR_VALUES = 1;
       
  1241   var ITERATOR_KEYS = 2;
       
  1242   var ITERATOR_KEYSVALUES = 3;
       
  1243 
       
  1244   // All Iterators should themselves be Iterable.
       
  1245   if ($$iterator) {
       
  1246     CollectionIterator.prototype[$$iterator] = function() {
       
  1247       return this;
       
  1248     };
       
  1249   }
       
  1250 
       
  1251   CollectionIterator.prototype.next = function() {
       
  1252     if (this._collection) {
       
  1253 
       
  1254       // Only continue iterating if the iterated collection is long enough.
       
  1255       if (this._index < this._collection.length) {
       
  1256         var model = this._collection.at(this._index);
       
  1257         this._index++;
       
  1258 
       
  1259         // Construct a value depending on what kind of values should be iterated.
       
  1260         var value;
       
  1261         if (this._kind === ITERATOR_VALUES) {
       
  1262           value = model;
       
  1263         } else {
       
  1264           var id = this._collection.modelId(model.attributes);
       
  1265           if (this._kind === ITERATOR_KEYS) {
       
  1266             value = id;
       
  1267           } else { // ITERATOR_KEYSVALUES
       
  1268             value = [id, model];
       
  1269           }
       
  1270         }
       
  1271         return {value: value, done: false};
       
  1272       }
       
  1273 
       
  1274       // Once exhausted, remove the reference to the collection so future
       
  1275       // calls to the next method always return done.
       
  1276       this._collection = void 0;
       
  1277     }
       
  1278 
       
  1279     return {value: void 0, done: true};
       
  1280   };
  1174 
  1281 
  1175   // Backbone.View
  1282   // Backbone.View
  1176   // -------------
  1283   // -------------
  1177 
  1284 
  1178   // Backbone Views are almost more convention than they are actual code. A View
  1285   // Backbone Views are almost more convention than they are actual code. A View
  1185 
  1292 
  1186   // Creating a Backbone.View creates its initial element outside of the DOM,
  1293   // Creating a Backbone.View creates its initial element outside of the DOM,
  1187   // if an existing element is not provided...
  1294   // if an existing element is not provided...
  1188   var View = Backbone.View = function(options) {
  1295   var View = Backbone.View = function(options) {
  1189     this.cid = _.uniqueId('view');
  1296     this.cid = _.uniqueId('view');
       
  1297     this.preinitialize.apply(this, arguments);
  1190     _.extend(this, _.pick(options, viewOptions));
  1298     _.extend(this, _.pick(options, viewOptions));
  1191     this._ensureElement();
  1299     this._ensureElement();
  1192     this.initialize.apply(this, arguments);
  1300     this.initialize.apply(this, arguments);
  1193   };
  1301   };
  1194 
  1302 
  1207     // jQuery delegate for element lookup, scoped to DOM elements within the
  1315     // jQuery delegate for element lookup, scoped to DOM elements within the
  1208     // current view. This should be preferred to global lookups where possible.
  1316     // current view. This should be preferred to global lookups where possible.
  1209     $: function(selector) {
  1317     $: function(selector) {
  1210       return this.$el.find(selector);
  1318       return this.$el.find(selector);
  1211     },
  1319     },
       
  1320 
       
  1321     // preinitialize is an empty function by default. You can override it with a function
       
  1322     // or object.  preinitialize will run before any instantiation logic is run in the View
       
  1323     preinitialize: function(){},
  1212 
  1324 
  1213     // Initialize is an empty function by default. Override it with your own
  1325     // Initialize is an empty function by default. Override it with your own
  1214     // initialization logic.
  1326     // initialization logic.
  1215     initialize: function(){},
  1327     initialize: function(){},
  1216 
  1328 
  1275       for (var key in events) {
  1387       for (var key in events) {
  1276         var method = events[key];
  1388         var method = events[key];
  1277         if (!_.isFunction(method)) method = this[method];
  1389         if (!_.isFunction(method)) method = this[method];
  1278         if (!method) continue;
  1390         if (!method) continue;
  1279         var match = key.match(delegateEventSplitter);
  1391         var match = key.match(delegateEventSplitter);
  1280         this.delegate(match[1], match[2], _.bind(method, this));
  1392         this.delegate(match[1], match[2], method.bind(this));
  1281       }
  1393       }
  1282       return this;
  1394       return this;
  1283     },
  1395     },
  1284 
  1396 
  1285     // Add a single event listener to the view's element (or a child element
  1397     // Add a single event listener to the view's element (or a child element
  1331     // subclasses using an alternative DOM manipulation API.
  1443     // subclasses using an alternative DOM manipulation API.
  1332     _setAttributes: function(attributes) {
  1444     _setAttributes: function(attributes) {
  1333       this.$el.attr(attributes);
  1445       this.$el.attr(attributes);
  1334     }
  1446     }
  1335 
  1447 
       
  1448   });
       
  1449 
       
  1450   // Proxy Backbone class methods to Underscore functions, wrapping the model's
       
  1451   // `attributes` object or collection's `models` array behind the scenes.
       
  1452   //
       
  1453   // collection.filter(function(model) { return model.get('age') > 10 });
       
  1454   // collection.each(this.addView);
       
  1455   //
       
  1456   // `Function#apply` can be slow so we use the method's arg count, if we know it.
       
  1457   var addMethod = function(base, length, method, attribute) {
       
  1458     switch (length) {
       
  1459       case 1: return function() {
       
  1460         return base[method](this[attribute]);
       
  1461       };
       
  1462       case 2: return function(value) {
       
  1463         return base[method](this[attribute], value);
       
  1464       };
       
  1465       case 3: return function(iteratee, context) {
       
  1466         return base[method](this[attribute], cb(iteratee, this), context);
       
  1467       };
       
  1468       case 4: return function(iteratee, defaultVal, context) {
       
  1469         return base[method](this[attribute], cb(iteratee, this), defaultVal, context);
       
  1470       };
       
  1471       default: return function() {
       
  1472         var args = slice.call(arguments);
       
  1473         args.unshift(this[attribute]);
       
  1474         return base[method].apply(base, args);
       
  1475       };
       
  1476     }
       
  1477   };
       
  1478 
       
  1479   var addUnderscoreMethods = function(Class, base, methods, attribute) {
       
  1480     _.each(methods, function(length, method) {
       
  1481       if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute);
       
  1482     });
       
  1483   };
       
  1484 
       
  1485   // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
       
  1486   var cb = function(iteratee, instance) {
       
  1487     if (_.isFunction(iteratee)) return iteratee;
       
  1488     if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
       
  1489     if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
       
  1490     return iteratee;
       
  1491   };
       
  1492   var modelMatcher = function(attrs) {
       
  1493     var matcher = _.matches(attrs);
       
  1494     return function(model) {
       
  1495       return matcher(model.attributes);
       
  1496     };
       
  1497   };
       
  1498 
       
  1499   // Underscore methods that we want to implement on the Collection.
       
  1500   // 90% of the core usefulness of Backbone Collections is actually implemented
       
  1501   // right here:
       
  1502   var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
       
  1503     foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
       
  1504     select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
       
  1505     contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
       
  1506     head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
       
  1507     without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
       
  1508     isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
       
  1509     sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
       
  1510 
       
  1511 
       
  1512   // Underscore methods that we want to implement on the Model, mapped to the
       
  1513   // number of arguments they take.
       
  1514   var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
       
  1515     omit: 0, chain: 1, isEmpty: 1};
       
  1516 
       
  1517   // Mix in each Underscore method as a proxy to `Collection#models`.
       
  1518 
       
  1519   _.each([
       
  1520     [Collection, collectionMethods, 'models'],
       
  1521     [Model, modelMethods, 'attributes']
       
  1522   ], function(config) {
       
  1523     var Base = config[0],
       
  1524         methods = config[1],
       
  1525         attribute = config[2];
       
  1526 
       
  1527     Base.mixin = function(obj) {
       
  1528       var mappings = _.reduce(_.functions(obj), function(memo, name) {
       
  1529         memo[name] = 0;
       
  1530         return memo;
       
  1531       }, {});
       
  1532       addUnderscoreMethods(Base, obj, mappings, attribute);
       
  1533     };
       
  1534 
       
  1535     addUnderscoreMethods(Base, _, methods, attribute);
  1336   });
  1536   });
  1337 
  1537 
  1338   // Backbone.sync
  1538   // Backbone.sync
  1339   // -------------
  1539   // -------------
  1340 
  1540 
  1413     return xhr;
  1613     return xhr;
  1414   };
  1614   };
  1415 
  1615 
  1416   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1616   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1417   var methodMap = {
  1617   var methodMap = {
  1418     'create': 'POST',
  1618     create: 'POST',
  1419     'update': 'PUT',
  1619     update: 'PUT',
  1420     'patch':  'PATCH',
  1620     patch: 'PATCH',
  1421     'delete': 'DELETE',
  1621     delete: 'DELETE',
  1422     'read':   'GET'
  1622     read: 'GET'
  1423   };
  1623   };
  1424 
  1624 
  1425   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
  1625   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
  1426   // Override this if you'd like to use a different library.
  1626   // Override this if you'd like to use a different library.
  1427   Backbone.ajax = function() {
  1627   Backbone.ajax = function() {
  1433 
  1633 
  1434   // Routers map faux-URLs to actions, and fire events when routes are
  1634   // Routers map faux-URLs to actions, and fire events when routes are
  1435   // matched. Creating a new one sets its `routes` hash, if not set statically.
  1635   // matched. Creating a new one sets its `routes` hash, if not set statically.
  1436   var Router = Backbone.Router = function(options) {
  1636   var Router = Backbone.Router = function(options) {
  1437     options || (options = {});
  1637     options || (options = {});
       
  1638     this.preinitialize.apply(this, arguments);
  1438     if (options.routes) this.routes = options.routes;
  1639     if (options.routes) this.routes = options.routes;
  1439     this._bindRoutes();
  1640     this._bindRoutes();
  1440     this.initialize.apply(this, arguments);
  1641     this.initialize.apply(this, arguments);
  1441   };
  1642   };
  1442 
  1643 
  1447   var splatParam    = /\*\w+/g;
  1648   var splatParam    = /\*\w+/g;
  1448   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  1649   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  1449 
  1650 
  1450   // Set up all inheritable **Backbone.Router** properties and methods.
  1651   // Set up all inheritable **Backbone.Router** properties and methods.
  1451   _.extend(Router.prototype, Events, {
  1652   _.extend(Router.prototype, Events, {
       
  1653 
       
  1654     // preinitialize is an empty function by default. You can override it with a function
       
  1655     // or object.  preinitialize will run before any instantiation logic is run in the Router.
       
  1656     preinitialize: function(){},
  1452 
  1657 
  1453     // Initialize is an empty function by default. Override it with your own
  1658     // Initialize is an empty function by default. Override it with your own
  1454     // initialization logic.
  1659     // initialization logic.
  1455     initialize: function(){},
  1660     initialize: function(){},
  1456 
  1661 
  1505 
  1710 
  1506     // Convert a route string into a regular expression, suitable for matching
  1711     // Convert a route string into a regular expression, suitable for matching
  1507     // against the current location hash.
  1712     // against the current location hash.
  1508     _routeToRegExp: function(route) {
  1713     _routeToRegExp: function(route) {
  1509       route = route.replace(escapeRegExp, '\\$&')
  1714       route = route.replace(escapeRegExp, '\\$&')
  1510                    .replace(optionalParam, '(?:$1)?')
  1715         .replace(optionalParam, '(?:$1)?')
  1511                    .replace(namedParam, function(match, optional) {
  1716         .replace(namedParam, function(match, optional) {
  1512                      return optional ? match : '([^/?]+)';
  1717           return optional ? match : '([^/?]+)';
  1513                    })
  1718         })
  1514                    .replace(splatParam, '([^?]*?)');
  1719         .replace(splatParam, '([^?]*?)');
  1515       return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  1720       return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  1516     },
  1721     },
  1517 
  1722 
  1518     // Given a route, and a URL fragment that it matches, return the array of
  1723     // Given a route, and a URL fragment that it matches, return the array of
  1519     // extracted decoded parameters. Empty or unmatched parameters will be
  1724     // extracted decoded parameters. Empty or unmatched parameters will be
  1537   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1742   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1538   // and URL fragments. If the browser supports neither (old IE, natch),
  1743   // and URL fragments. If the browser supports neither (old IE, natch),
  1539   // falls back to polling.
  1744   // falls back to polling.
  1540   var History = Backbone.History = function() {
  1745   var History = Backbone.History = function() {
  1541     this.handlers = [];
  1746     this.handlers = [];
  1542     this.checkUrl = _.bind(this.checkUrl, this);
  1747     this.checkUrl = this.checkUrl.bind(this);
  1543 
  1748 
  1544     // Ensure that `History` can be used outside of the browser.
  1749     // Ensure that `History` can be used outside of the browser.
  1545     if (typeof window !== 'undefined') {
  1750     if (typeof window !== 'undefined') {
  1546       this.location = window.location;
  1751       this.location = window.location;
  1547       this.history = window.history;
  1752       this.history = window.history;
  1574     },
  1779     },
  1575 
  1780 
  1576     // Does the pathname match the root?
  1781     // Does the pathname match the root?
  1577     matchRoot: function() {
  1782     matchRoot: function() {
  1578       var path = this.decodeFragment(this.location.pathname);
  1783       var path = this.decodeFragment(this.location.pathname);
  1579       var root = path.slice(0, this.root.length - 1) + '/';
  1784       var rootPath = path.slice(0, this.root.length - 1) + '/';
  1580       return root === this.root;
  1785       return rootPath === this.root;
  1581     },
  1786     },
  1582 
  1787 
  1583     // Unicode characters in `location.pathname` are percent encoded so they're
  1788     // Unicode characters in `location.pathname` are percent encoded so they're
  1584     // decoded for comparison. `%25` should not be decoded since it may be part
  1789     // decoded for comparison. `%25` should not be decoded since it may be part
  1585     // of an encoded parameter.
  1790     // of an encoded parameter.
  1647       if (this._wantsHashChange && this._wantsPushState) {
  1852       if (this._wantsHashChange && this._wantsPushState) {
  1648 
  1853 
  1649         // If we've started off with a route from a `pushState`-enabled
  1854         // If we've started off with a route from a `pushState`-enabled
  1650         // browser, but we're currently in a browser that doesn't support it...
  1855         // browser, but we're currently in a browser that doesn't support it...
  1651         if (!this._hasPushState && !this.atRoot()) {
  1856         if (!this._hasPushState && !this.atRoot()) {
  1652           var root = this.root.slice(0, -1) || '/';
  1857           var rootPath = this.root.slice(0, -1) || '/';
  1653           this.location.replace(root + '#' + this.getPath());
  1858           this.location.replace(rootPath + '#' + this.getPath());
  1654           // Return immediately as browser will do redirect to new url
  1859           // Return immediately as browser will do redirect to new url
  1655           return true;
  1860           return true;
  1656 
  1861 
  1657         // Or if we've started out with a hash-based route, but we're currently
  1862         // Or if we've started out with a hash-based route, but we're currently
  1658         // in a browser where it could be `pushState`-based instead...
  1863         // in a browser where it could be `pushState`-based instead...
  1677         iWindow.document.close();
  1882         iWindow.document.close();
  1678         iWindow.location.hash = '#' + this.fragment;
  1883         iWindow.location.hash = '#' + this.fragment;
  1679       }
  1884       }
  1680 
  1885 
  1681       // Add a cross-platform `addEventListener` shim for older browsers.
  1886       // Add a cross-platform `addEventListener` shim for older browsers.
  1682       var addEventListener = window.addEventListener || function (eventName, listener) {
  1887       var addEventListener = window.addEventListener || function(eventName, listener) {
  1683         return attachEvent('on' + eventName, listener);
  1888         return attachEvent('on' + eventName, listener);
  1684       };
  1889       };
  1685 
  1890 
  1686       // Depending on whether we're using pushState or hashes, and whether
  1891       // Depending on whether we're using pushState or hashes, and whether
  1687       // 'onhashchange' is supported, determine how we check the URL state.
  1892       // 'onhashchange' is supported, determine how we check the URL state.
  1698 
  1903 
  1699     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
  1904     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
  1700     // but possibly useful for unit testing Routers.
  1905     // but possibly useful for unit testing Routers.
  1701     stop: function() {
  1906     stop: function() {
  1702       // Add a cross-platform `removeEventListener` shim for older browsers.
  1907       // Add a cross-platform `removeEventListener` shim for older browsers.
  1703       var removeEventListener = window.removeEventListener || function (eventName, listener) {
  1908       var removeEventListener = window.removeEventListener || function(eventName, listener) {
  1704         return detachEvent('on' + eventName, listener);
  1909         return detachEvent('on' + eventName, listener);
  1705       };
  1910       };
  1706 
  1911 
  1707       // Remove window listeners.
  1912       // Remove window listeners.
  1708       if (this._usePushState) {
  1913       if (this._usePushState) {
  1772 
  1977 
  1773       // Normalize the fragment.
  1978       // Normalize the fragment.
  1774       fragment = this.getFragment(fragment || '');
  1979       fragment = this.getFragment(fragment || '');
  1775 
  1980 
  1776       // Don't include a trailing slash on the root.
  1981       // Don't include a trailing slash on the root.
  1777       var root = this.root;
  1982       var rootPath = this.root;
  1778       if (fragment === '' || fragment.charAt(0) === '?') {
  1983       if (fragment === '' || fragment.charAt(0) === '?') {
  1779         root = root.slice(0, -1) || '/';
  1984         rootPath = rootPath.slice(0, -1) || '/';
  1780       }
  1985       }
  1781       var url = root + fragment;
  1986       var url = rootPath + fragment;
  1782 
  1987 
  1783       // Strip the hash and decode for matching.
  1988       // Strip the fragment of the query and hash for matching.
  1784       fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
  1989       fragment = fragment.replace(pathStripper, '');
  1785 
  1990 
  1786       if (this.fragment === fragment) return;
  1991       // Decode for matching.
  1787       this.fragment = fragment;
  1992       var decodedFragment = this.decodeFragment(fragment);
       
  1993 
       
  1994       if (this.fragment === decodedFragment) return;
       
  1995       this.fragment = decodedFragment;
  1788 
  1996 
  1789       // If pushState is available, we use it to set the fragment as a real URL.
  1997       // If pushState is available, we use it to set the fragment as a real URL.
  1790       if (this._usePushState) {
  1998       if (this._usePushState) {
  1791         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1999         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1792 
  2000 
  1793       // If hash changes haven't been explicitly disabled, update the hash
  2001       // If hash changes haven't been explicitly disabled, update the hash
  1794       // fragment to store history.
  2002       // fragment to store history.
  1795       } else if (this._wantsHashChange) {
  2003       } else if (this._wantsHashChange) {
  1796         this._updateHash(this.location, fragment, options.replace);
  2004         this._updateHash(this.location, fragment, options.replace);
  1797         if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) {
  2005         if (this.iframe && fragment !== this.getHash(this.iframe.contentWindow)) {
  1798           var iWindow = this.iframe.contentWindow;
  2006           var iWindow = this.iframe.contentWindow;
  1799 
  2007 
  1800           // Opening and closing the iframe tricks IE7 and earlier to push a
  2008           // Opening and closing the iframe tricks IE7 and earlier to push a
  1801           // history entry on hash-tag change.  When replace is true, we don't
  2009           // history entry on hash-tag change.  When replace is true, we don't
  1802           // want this.
  2010           // want this.
  1854 
  2062 
  1855     // Add static properties to the constructor function, if supplied.
  2063     // Add static properties to the constructor function, if supplied.
  1856     _.extend(child, parent, staticProps);
  2064     _.extend(child, parent, staticProps);
  1857 
  2065 
  1858     // Set the prototype chain to inherit from `parent`, without calling
  2066     // Set the prototype chain to inherit from `parent`, without calling
  1859     // `parent` constructor function.
  2067     // `parent`'s constructor function and add the prototype properties.
  1860     var Surrogate = function(){ this.constructor = child; };
  2068     child.prototype = _.create(parent.prototype, protoProps);
  1861     Surrogate.prototype = parent.prototype;
  2069     child.prototype.constructor = child;
  1862     child.prototype = new Surrogate;
       
  1863 
       
  1864     // Add prototype properties (instance properties) to the subclass,
       
  1865     // if supplied.
       
  1866     if (protoProps) _.extend(child.prototype, protoProps);
       
  1867 
  2070 
  1868     // Set a convenience property in case the parent's prototype is needed
  2071     // Set a convenience property in case the parent's prototype is needed
  1869     // later.
  2072     // later.
  1870     child.__super__ = parent.prototype;
  2073     child.__super__ = parent.prototype;
  1871 
  2074 
  1888       model.trigger('error', model, resp, options);
  2091       model.trigger('error', model, resp, options);
  1889     };
  2092     };
  1890   };
  2093   };
  1891 
  2094 
  1892   return Backbone;
  2095   return Backbone;
  1893 
  2096 });
  1894 }));