server/php/basic/public_html/static/lib/backbone/backbone.js
changeset 498 08d121184a38
parent 442 adb907bba956
equal deleted inserted replaced
497:3ba7920facf4 498:08d121184a38
     1 //     Backbone.js 1.1.2
     1 //     Backbone.js 1.2.3
     2 
     2 
     3 //     (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     3 //     (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     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(root, factory) {
     8 (function(factory) {
       
     9 
       
    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.
       
    12   var root = (typeof self == 'object' && self.self == self && self) ||
       
    13             (typeof global == 'object' && global.global == global && global);
     9 
    14 
    10   // Set up Backbone appropriately for the environment. Start with AMD.
    15   // Set up Backbone appropriately for the environment. Start with AMD.
    11   if (typeof define === 'function' && define.amd) {
    16   if (typeof define === 'function' && define.amd) {
    12     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    17     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    13       // 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
    15       root.Backbone = factory(root, exports, _, $);
    20       root.Backbone = factory(root, exports, _, $);
    16     });
    21     });
    17 
    22 
    18   // 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.
    19   } else if (typeof exports !== 'undefined') {
    24   } else if (typeof exports !== 'undefined') {
    20     var _ = require('underscore');
    25     var _ = require('underscore'), $;
    21     factory(root, exports, _);
    26     try { $ = require('jquery'); } catch(e) {}
       
    27     factory(root, exports, _, $);
    22 
    28 
    23   // Finally, as a browser global.
    29   // Finally, as a browser global.
    24   } else {
    30   } else {
    25     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.$));
    26   }
    32   }
    27 
    33 
    28 }(this, function(root, Backbone, _, $) {
    34 }(function(root, Backbone, _, $) {
    29 
    35 
    30   // Initial Setup
    36   // Initial Setup
    31   // -------------
    37   // -------------
    32 
    38 
    33   // 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
    34   // restored later on, if `noConflict` is used.
    40   // restored later on, if `noConflict` is used.
    35   var previousBackbone = root.Backbone;
    41   var previousBackbone = root.Backbone;
    36 
    42 
    37   // Create local references to array methods we'll want to use later.
    43   // Create a local reference to a common array method we'll want to use later.
    38   var array = [];
    44   var slice = Array.prototype.slice;
    39   var push = array.push;
       
    40   var slice = array.slice;
       
    41   var splice = array.splice;
       
    42 
    45 
    43   // Current version of the library. Keep in sync with `package.json`.
    46   // Current version of the library. Keep in sync with `package.json`.
    44   Backbone.VERSION = '1.1.2';
    47   Backbone.VERSION = '1.2.3';
    45 
    48 
    46   // 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
    47   // the `$` variable.
    50   // the `$` variable.
    48   Backbone.$ = $;
    51   Backbone.$ = $;
    49 
    52 
    58   // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
    61   // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
    59   // set a `X-Http-Method-Override` header.
    62   // set a `X-Http-Method-Override` header.
    60   Backbone.emulateHTTP = false;
    63   Backbone.emulateHTTP = false;
    61 
    64 
    62   // 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
    63   // `application/json` requests ... will encode the body as
    66   // `application/json` requests ... this will encode the body as
    64   // `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
    65   // form param named `model`.
    68   // form param named `model`.
    66   Backbone.emulateJSON = false;
    69   Backbone.emulateJSON = false;
    67 
    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 
    68   // Backbone.Events
   119   // Backbone.Events
    69   // ---------------
   120   // ---------------
    70 
   121 
    71   // A module that can be mixed in to *any object* in order to provide it with
   122   // A module that can be mixed in to *any object* in order to provide it with
    72   // custom events. You may bind with `on` or remove with `off` callback
   123   // a custom event channel. You may bind a callback to an event with `on` or
    73   // functions to an event; `trigger`-ing an event fires all callbacks in
   124   // remove with `off`; `trigger`-ing an event fires all callbacks in
    74   // succession.
   125   // succession.
    75   //
   126   //
    76   //     var object = {};
   127   //     var object = {};
    77   //     _.extend(object, Backbone.Events);
   128   //     _.extend(object, Backbone.Events);
    78   //     object.on('expand', function(){ alert('expanded'); });
   129   //     object.on('expand', function(){ alert('expanded'); });
    79   //     object.trigger('expand');
   130   //     object.trigger('expand');
    80   //
   131   //
    81   var Events = Backbone.Events = {
   132   var Events = Backbone.Events = {};
    82 
   133 
    83     // Bind an event to a `callback` function. Passing `"all"` will bind
   134   // Regular expression used to split event strings.
    84     // the callback to all events fired.
   135   var eventSplitter = /\s+/;
    85     on: function(name, callback, context) {
   136 
    86       if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
   137   // Iterates over the standard `event, callback` (as well as the fancy multiple
    87       this._events || (this._events = {});
   138   // space-separated events `"change blur", callback` and jQuery-style event
    88       var events = this._events[name] || (this._events[name] = []);
   139   // maps `{event: callback}`).
    89       events.push({callback: callback, context: context, ctx: context || this});
   140   var eventsApi = function(iteratee, events, name, callback, opts) {
    90       return this;
   141     var i = 0, names;
    91     },
   142     if (name && typeof name === 'object') {
    92 
   143       // Handle event maps.
    93     // Bind an event to only be triggered a single time. After the first time
   144       if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
    94     // the callback is invoked, it will be removed.
   145       for (names = _.keys(name); i < names.length ; i++) {
    95     once: function(name, callback, context) {
   146         events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
    96       if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
   147       }
    97       var self = this;
   148     } else if (name && eventSplitter.test(name)) {
    98       var once = _.once(function() {
   149       // Handle space separated event names by delegating them individually.
    99         self.off(name, once);
   150       for (names = name.split(eventSplitter); i < names.length; i++) {
       
   151         events = iteratee(events, names[i], callback, opts);
       
   152       }
       
   153     } else {
       
   154       // Finally, standard events.
       
   155       events = iteratee(events, name, callback, opts);
       
   156     }
       
   157     return events;
       
   158   };
       
   159 
       
   160   // Bind an event to a `callback` function. Passing `"all"` will bind
       
   161   // the callback to all events fired.
       
   162   Events.on = function(name, callback, context) {
       
   163     return internalOn(this, name, callback, context);
       
   164   };
       
   165 
       
   166   // Guard the `listening` argument from the public API.
       
   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     });
       
   173 
       
   174     if (listening) {
       
   175       var listeners = obj._listeners || (obj._listeners = {});
       
   176       listeners[listening.id] = listening;
       
   177     }
       
   178 
       
   179     return obj;
       
   180   };
       
   181 
       
   182   // 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
       
   184   // for easier unbinding later.
       
   185   Events.listenTo =  function(obj, name, callback) {
       
   186     if (!obj) return this;
       
   187     var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
       
   188     var listeningTo = this._listeningTo || (this._listeningTo = {});
       
   189     var listening = listeningTo[id];
       
   190 
       
   191     // This object is not listening to any other events on `obj` yet.
       
   192     // Setup the necessary references to track the listening callbacks.
       
   193     if (!listening) {
       
   194       var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
       
   195       listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
       
   196     }
       
   197 
       
   198     // Bind callbacks on obj, and keep track of them on listening.
       
   199     internalOn(obj, name, callback, this, listening);
       
   200     return this;
       
   201   };
       
   202 
       
   203   // The reducing API that adds a callback to the `events` object.
       
   204   var onApi = function(events, name, callback, options) {
       
   205     if (callback) {
       
   206       var handlers = events[name] || (events[name] = []);
       
   207       var context = options.context, ctx = options.ctx, listening = options.listening;
       
   208       if (listening) listening.count++;
       
   209 
       
   210       handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
       
   211     }
       
   212     return events;
       
   213   };
       
   214 
       
   215   // Remove one or many callbacks. If `context` is null, removes all
       
   216   // callbacks with that function. If `callback` is null, removes all
       
   217   // callbacks for the event. If `name` is null, removes all bound
       
   218   // callbacks for all events.
       
   219   Events.off =  function(name, callback, context) {
       
   220     if (!this._events) return this;
       
   221     this._events = eventsApi(offApi, this._events, name, callback, {
       
   222         context: context,
       
   223         listeners: this._listeners
       
   224     });
       
   225     return this;
       
   226   };
       
   227 
       
   228   // Tell this object to stop listening to either specific events ... or
       
   229   // to every object it's currently listening to.
       
   230   Events.stopListening =  function(obj, name, callback) {
       
   231     var listeningTo = this._listeningTo;
       
   232     if (!listeningTo) return this;
       
   233 
       
   234     var ids = obj ? [obj._listenId] : _.keys(listeningTo);
       
   235 
       
   236     for (var i = 0; i < ids.length; i++) {
       
   237       var listening = listeningTo[ids[i]];
       
   238 
       
   239       // If listening doesn't exist, this object is not currently
       
   240       // listening to obj. Break out early.
       
   241       if (!listening) break;
       
   242 
       
   243       listening.obj.off(name, callback, this);
       
   244     }
       
   245     if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
       
   246 
       
   247     return this;
       
   248   };
       
   249 
       
   250   // The reducing API that removes a callback from the `events` object.
       
   251   var offApi = function(events, name, callback, options) {
       
   252     if (!events) return;
       
   253 
       
   254     var i = 0, listening;
       
   255     var context = options.context, listeners = options.listeners;
       
   256 
       
   257     // Delete all events listeners and "drop" events.
       
   258     if (!name && !callback && !context) {
       
   259       var ids = _.keys(listeners);
       
   260       for (; i < ids.length; i++) {
       
   261         listening = listeners[ids[i]];
       
   262         delete listeners[listening.id];
       
   263         delete listening.listeningTo[listening.objId];
       
   264       }
       
   265       return;
       
   266     }
       
   267 
       
   268     var names = name ? [name] : _.keys(events);
       
   269     for (; i < names.length; i++) {
       
   270       name = names[i];
       
   271       var handlers = events[name];
       
   272 
       
   273       // Bail out if there are no events stored.
       
   274       if (!handlers) break;
       
   275 
       
   276       // Replace events if there are any remaining.  Otherwise, clean up.
       
   277       var remaining = [];
       
   278       for (var j = 0; j < handlers.length; j++) {
       
   279         var handler = handlers[j];
       
   280         if (
       
   281           callback && callback !== handler.callback &&
       
   282             callback !== handler.callback._callback ||
       
   283               context && context !== handler.context
       
   284         ) {
       
   285           remaining.push(handler);
       
   286         } else {
       
   287           listening = handler.listening;
       
   288           if (listening && --listening.count === 0) {
       
   289             delete listeners[listening.id];
       
   290             delete listening.listeningTo[listening.objId];
       
   291           }
       
   292         }
       
   293       }
       
   294 
       
   295       // Update tail event if the list has any events.  Otherwise, clean up.
       
   296       if (remaining.length) {
       
   297         events[name] = remaining;
       
   298       } else {
       
   299         delete events[name];
       
   300       }
       
   301     }
       
   302     if (_.size(events)) return events;
       
   303   };
       
   304 
       
   305   // 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
       
   307   // 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.
       
   309   Events.once =  function(name, callback, context) {
       
   310     // Map the event into a `{event: once}` object.
       
   311     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
       
   312     return this.on(events, void 0, context);
       
   313   };
       
   314 
       
   315   // Inversion-of-control versions of `once`.
       
   316   Events.listenToOnce =  function(obj, name, callback) {
       
   317     // Map the event into a `{event: once}` object.
       
   318     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
       
   319     return this.listenTo(obj, events);
       
   320   };
       
   321 
       
   322   // Reduces the event callbacks into a map of `{event: onceWrapper}`.
       
   323   // `offer` unbinds the `onceWrapper` after it has been called.
       
   324   var onceMap = function(map, name, callback, offer) {
       
   325     if (callback) {
       
   326       var once = map[name] = _.once(function() {
       
   327         offer(name, once);
   100         callback.apply(this, arguments);
   328         callback.apply(this, arguments);
   101       });
   329       });
   102       once._callback = callback;
   330       once._callback = callback;
   103       return this.on(name, once, context);
   331     }
   104     },
   332     return map;
   105 
   333   };
   106     // Remove one or many callbacks. If `context` is null, removes all
   334 
   107     // callbacks with that function. If `callback` is null, removes all
   335   // Trigger one or many events, firing all bound callbacks. Callbacks are
   108     // callbacks for the event. If `name` is null, removes all bound
   336   // passed the same arguments as `trigger` is, apart from the event name
   109     // callbacks for all events.
   337   // (unless you're listening on `"all"`, which will cause your callback to
   110     off: function(name, callback, context) {
   338   // receive the true name of the event as the first argument).
   111       var retain, ev, events, names, i, l, j, k;
   339   Events.trigger =  function(name) {
   112       if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
   340     if (!this._events) return this;
   113       if (!name && !callback && !context) {
   341 
   114         this._events = void 0;
   342     var length = Math.max(0, arguments.length - 1);
   115         return this;
   343     var args = Array(length);
   116       }
   344     for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
   117       names = name ? [name] : _.keys(this._events);
   345 
   118       for (i = 0, l = names.length; i < l; i++) {
   346     eventsApi(triggerApi, this._events, name, void 0, args);
   119         name = names[i];
   347     return this;
   120         if (events = this._events[name]) {
   348   };
   121           this._events[name] = retain = [];
   349 
   122           if (callback || context) {
   350   // Handles triggering the appropriate event callbacks.
   123             for (j = 0, k = events.length; j < k; j++) {
   351   var triggerApi = function(objEvents, name, cb, args) {
   124               ev = events[j];
   352     if (objEvents) {
   125               if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
   353       var events = objEvents[name];
   126                   (context && context !== ev.context)) {
   354       var allEvents = objEvents.all;
   127                 retain.push(ev);
   355       if (events && allEvents) allEvents = allEvents.slice();
   128               }
       
   129             }
       
   130           }
       
   131           if (!retain.length) delete this._events[name];
       
   132         }
       
   133       }
       
   134 
       
   135       return this;
       
   136     },
       
   137 
       
   138     // Trigger one or many events, firing all bound callbacks. Callbacks are
       
   139     // passed the same arguments as `trigger` is, apart from the event name
       
   140     // (unless you're listening on `"all"`, which will cause your callback to
       
   141     // receive the true name of the event as the first argument).
       
   142     trigger: function(name) {
       
   143       if (!this._events) return this;
       
   144       var args = slice.call(arguments, 1);
       
   145       if (!eventsApi(this, 'trigger', name, args)) return this;
       
   146       var events = this._events[name];
       
   147       var allEvents = this._events.all;
       
   148       if (events) triggerEvents(events, args);
   356       if (events) triggerEvents(events, args);
   149       if (allEvents) triggerEvents(allEvents, arguments);
   357       if (allEvents) triggerEvents(allEvents, [name].concat(args));
   150       return this;
   358     }
   151     },
   359     return objEvents;
   152 
       
   153     // Tell this object to stop listening to either specific events ... or
       
   154     // to every object it's currently listening to.
       
   155     stopListening: function(obj, name, callback) {
       
   156       var listeningTo = this._listeningTo;
       
   157       if (!listeningTo) return this;
       
   158       var remove = !name && !callback;
       
   159       if (!callback && typeof name === 'object') callback = this;
       
   160       if (obj) (listeningTo = {})[obj._listenId] = obj;
       
   161       for (var id in listeningTo) {
       
   162         obj = listeningTo[id];
       
   163         obj.off(name, callback, this);
       
   164         if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
       
   165       }
       
   166       return this;
       
   167     }
       
   168 
       
   169   };
       
   170 
       
   171   // Regular expression used to split event strings.
       
   172   var eventSplitter = /\s+/;
       
   173 
       
   174   // Implement fancy features of the Events API such as multiple event
       
   175   // names `"change blur"` and jQuery-style event maps `{change: action}`
       
   176   // in terms of the existing API.
       
   177   var eventsApi = function(obj, action, name, rest) {
       
   178     if (!name) return true;
       
   179 
       
   180     // Handle event maps.
       
   181     if (typeof name === 'object') {
       
   182       for (var key in name) {
       
   183         obj[action].apply(obj, [key, name[key]].concat(rest));
       
   184       }
       
   185       return false;
       
   186     }
       
   187 
       
   188     // Handle space separated event names.
       
   189     if (eventSplitter.test(name)) {
       
   190       var names = name.split(eventSplitter);
       
   191       for (var i = 0, l = names.length; i < l; i++) {
       
   192         obj[action].apply(obj, [names[i]].concat(rest));
       
   193       }
       
   194       return false;
       
   195     }
       
   196 
       
   197     return true;
       
   198   };
   360   };
   199 
   361 
   200   // A difficult-to-believe, but optimized internal dispatch function for
   362   // A difficult-to-believe, but optimized internal dispatch function for
   201   // triggering events. Tries to keep the usual cases speedy (most internal
   363   // triggering events. Tries to keep the usual cases speedy (most internal
   202   // Backbone events have 3 arguments).
   364   // Backbone events have 3 arguments).
   209       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   371       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   210       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   372       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   211     }
   373     }
   212   };
   374   };
   213 
   375 
   214   var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
       
   215 
       
   216   // Inversion-of-control versions of `on` and `once`. Tell *this* object to
       
   217   // listen to an event in another object ... keeping track of what it's
       
   218   // listening to.
       
   219   _.each(listenMethods, function(implementation, method) {
       
   220     Events[method] = function(obj, name, callback) {
       
   221       var listeningTo = this._listeningTo || (this._listeningTo = {});
       
   222       var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
       
   223       listeningTo[id] = obj;
       
   224       if (!callback && typeof name === 'object') callback = this;
       
   225       obj[implementation](name, callback, this);
       
   226       return this;
       
   227     };
       
   228   });
       
   229 
       
   230   // Aliases for backwards compatibility.
   376   // Aliases for backwards compatibility.
   231   Events.bind   = Events.on;
   377   Events.bind   = Events.on;
   232   Events.unbind = Events.off;
   378   Events.unbind = Events.off;
   233 
   379 
   234   // Allow the `Backbone` object to serve as a global event bus, for folks who
   380   // Allow the `Backbone` object to serve as a global event bus, for folks who
   246   // Create a new model with the specified attributes. A client id (`cid`)
   392   // Create a new model with the specified attributes. A client id (`cid`)
   247   // is automatically generated and assigned for you.
   393   // is automatically generated and assigned for you.
   248   var Model = Backbone.Model = function(attributes, options) {
   394   var Model = Backbone.Model = function(attributes, options) {
   249     var attrs = attributes || {};
   395     var attrs = attributes || {};
   250     options || (options = {});
   396     options || (options = {});
   251     this.cid = _.uniqueId('c');
   397     this.cid = _.uniqueId(this.cidPrefix);
   252     this.attributes = {};
   398     this.attributes = {};
   253     if (options.collection) this.collection = options.collection;
   399     if (options.collection) this.collection = options.collection;
   254     if (options.parse) attrs = this.parse(attrs, options) || {};
   400     if (options.parse) attrs = this.parse(attrs, options) || {};
   255     attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
   401     attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
   256     this.set(attrs, options);
   402     this.set(attrs, options);
   269 
   415 
   270     // The default name for the JSON `id` attribute is `"id"`. MongoDB and
   416     // The default name for the JSON `id` attribute is `"id"`. MongoDB and
   271     // CouchDB users may want to set this to `"_id"`.
   417     // CouchDB users may want to set this to `"_id"`.
   272     idAttribute: 'id',
   418     idAttribute: 'id',
   273 
   419 
       
   420     // 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.
       
   422     cidPrefix: 'c',
       
   423 
   274     // Initialize is an empty function by default. Override it with your own
   424     // Initialize is an empty function by default. Override it with your own
   275     // initialization logic.
   425     // initialization logic.
   276     initialize: function(){},
   426     initialize: function(){},
   277 
   427 
   278     // Return a copy of the model's `attributes` object.
   428     // Return a copy of the model's `attributes` object.
   300     // or undefined.
   450     // or undefined.
   301     has: function(attr) {
   451     has: function(attr) {
   302       return this.get(attr) != null;
   452       return this.get(attr) != null;
   303     },
   453     },
   304 
   454 
       
   455     // Special-cased proxy to underscore's `_.matches` method.
       
   456     matches: function(attrs) {
       
   457       return !!_.iteratee(attrs, this)(this.attributes);
       
   458     },
       
   459 
   305     // Set a hash of model attributes on the object, firing `"change"`. This is
   460     // Set a hash of model attributes on the object, firing `"change"`. This is
   306     // the core primitive operation of a model, updating the data and notifying
   461     // the core primitive operation of a model, updating the data and notifying
   307     // anyone who needs to know about the change in state. The heart of the beast.
   462     // anyone who needs to know about the change in state. The heart of the beast.
   308     set: function(key, val, options) {
   463     set: function(key, val, options) {
   309       var attr, attrs, unset, changes, silent, changing, prev, current;
       
   310       if (key == null) return this;
   464       if (key == null) return this;
   311 
   465 
   312       // Handle both `"key", value` and `{key: value}` -style arguments.
   466       // Handle both `"key", value` and `{key: value}` -style arguments.
       
   467       var attrs;
   313       if (typeof key === 'object') {
   468       if (typeof key === 'object') {
   314         attrs = key;
   469         attrs = key;
   315         options = val;
   470         options = val;
   316       } else {
   471       } else {
   317         (attrs = {})[key] = val;
   472         (attrs = {})[key] = val;
   321 
   476 
   322       // Run validation.
   477       // Run validation.
   323       if (!this._validate(attrs, options)) return false;
   478       if (!this._validate(attrs, options)) return false;
   324 
   479 
   325       // Extract attributes and options.
   480       // Extract attributes and options.
   326       unset           = options.unset;
   481       var unset      = options.unset;
   327       silent          = options.silent;
   482       var silent     = options.silent;
   328       changes         = [];
   483       var changes    = [];
   329       changing        = this._changing;
   484       var changing   = this._changing;
   330       this._changing  = true;
   485       this._changing = true;
   331 
   486 
   332       if (!changing) {
   487       if (!changing) {
   333         this._previousAttributes = _.clone(this.attributes);
   488         this._previousAttributes = _.clone(this.attributes);
   334         this.changed = {};
   489         this.changed = {};
   335       }
   490       }
   336       current = this.attributes, prev = this._previousAttributes;
   491 
   337 
   492       var current = this.attributes;
   338       // Check for changes of `id`.
   493       var changed = this.changed;
   339       if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
   494       var prev    = this._previousAttributes;
   340 
   495 
   341       // For each `set` attribute, update or delete the current value.
   496       // For each `set` attribute, update or delete the current value.
   342       for (attr in attrs) {
   497       for (var attr in attrs) {
   343         val = attrs[attr];
   498         val = attrs[attr];
   344         if (!_.isEqual(current[attr], val)) changes.push(attr);
   499         if (!_.isEqual(current[attr], val)) changes.push(attr);
   345         if (!_.isEqual(prev[attr], val)) {
   500         if (!_.isEqual(prev[attr], val)) {
   346           this.changed[attr] = val;
   501           changed[attr] = val;
   347         } else {
   502         } else {
   348           delete this.changed[attr];
   503           delete changed[attr];
   349         }
   504         }
   350         unset ? delete current[attr] : current[attr] = val;
   505         unset ? delete current[attr] : current[attr] = val;
   351       }
   506       }
       
   507 
       
   508       // Update the `id`.
       
   509       this.id = this.get(this.idAttribute);
   352 
   510 
   353       // Trigger all relevant attribute changes.
   511       // Trigger all relevant attribute changes.
   354       if (!silent) {
   512       if (!silent) {
   355         if (changes.length) this._pending = options;
   513         if (changes.length) this._pending = options;
   356         for (var i = 0, l = changes.length; i < l; i++) {
   514         for (var i = 0; i < changes.length; i++) {
   357           this.trigger('change:' + changes[i], this, current[changes[i]], options);
   515           this.trigger('change:' + changes[i], this, current[changes[i]], options);
   358         }
   516         }
   359       }
   517       }
   360 
   518 
   361       // You might be wondering why there's a `while` loop here. Changes can
   519       // You might be wondering why there's a `while` loop here. Changes can
   399     // persisted to the server. Unset attributes will be set to undefined.
   557     // persisted to the server. Unset attributes will be set to undefined.
   400     // You can also pass an attributes object to diff against the model,
   558     // You can also pass an attributes object to diff against the model,
   401     // determining if there *would be* a change.
   559     // determining if there *would be* a change.
   402     changedAttributes: function(diff) {
   560     changedAttributes: function(diff) {
   403       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   561       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   404       var val, changed = false;
       
   405       var old = this._changing ? this._previousAttributes : this.attributes;
   562       var old = this._changing ? this._previousAttributes : this.attributes;
       
   563       var changed = {};
   406       for (var attr in diff) {
   564       for (var attr in diff) {
   407         if (_.isEqual(old[attr], (val = diff[attr]))) continue;
   565         var val = diff[attr];
   408         (changed || (changed = {}))[attr] = val;
   566         if (_.isEqual(old[attr], val)) continue;
   409       }
   567         changed[attr] = val;
   410       return changed;
   568       }
       
   569       return _.size(changed) ? changed : false;
   411     },
   570     },
   412 
   571 
   413     // Get the previous value of an attribute, recorded at the time the last
   572     // Get the previous value of an attribute, recorded at the time the last
   414     // `"change"` event was fired.
   573     // `"change"` event was fired.
   415     previous: function(attr) {
   574     previous: function(attr) {
   421     // `"change"` event.
   580     // `"change"` event.
   422     previousAttributes: function() {
   581     previousAttributes: function() {
   423       return _.clone(this._previousAttributes);
   582       return _.clone(this._previousAttributes);
   424     },
   583     },
   425 
   584 
   426     // Fetch the model from the server. If the server's representation of the
   585     // Fetch the model from the server, merging the response with the model's
   427     // model differs from its current attributes, they will be overridden,
   586     // local attributes. Any changed attributes will trigger a "change" event.
   428     // triggering a `"change"` event.
       
   429     fetch: function(options) {
   587     fetch: function(options) {
   430       options = options ? _.clone(options) : {};
   588       options = _.extend({parse: true}, options);
   431       if (options.parse === void 0) options.parse = true;
       
   432       var model = this;
   589       var model = this;
   433       var success = options.success;
   590       var success = options.success;
   434       options.success = function(resp) {
   591       options.success = function(resp) {
   435         if (!model.set(model.parse(resp, options), options)) return false;
   592         var serverAttrs = options.parse ? model.parse(resp, options) : resp;
   436         if (success) success(model, resp, options);
   593         if (!model.set(serverAttrs, options)) return false;
       
   594         if (success) success.call(options.context, model, resp, options);
   437         model.trigger('sync', model, resp, options);
   595         model.trigger('sync', model, resp, options);
   438       };
   596       };
   439       wrapError(this, options);
   597       wrapError(this, options);
   440       return this.sync('read', this, options);
   598       return this.sync('read', this, options);
   441     },
   599     },
   442 
   600 
   443     // Set a hash of model attributes, and sync the model to the server.
   601     // Set a hash of model attributes, and sync the model to the server.
   444     // If the server returns an attributes hash that differs, the model's
   602     // If the server returns an attributes hash that differs, the model's
   445     // state will be `set` again.
   603     // state will be `set` again.
   446     save: function(key, val, options) {
   604     save: function(key, val, options) {
   447       var attrs, method, xhr, attributes = this.attributes;
       
   448 
       
   449       // Handle both `"key", value` and `{key: value}` -style arguments.
   605       // Handle both `"key", value` and `{key: value}` -style arguments.
       
   606       var attrs;
   450       if (key == null || typeof key === 'object') {
   607       if (key == null || typeof key === 'object') {
   451         attrs = key;
   608         attrs = key;
   452         options = val;
   609         options = val;
   453       } else {
   610       } else {
   454         (attrs = {})[key] = val;
   611         (attrs = {})[key] = val;
   455       }
   612       }
   456 
   613 
   457       options = _.extend({validate: true}, options);
   614       options = _.extend({validate: true, parse: true}, options);
       
   615       var wait = options.wait;
   458 
   616 
   459       // If we're not waiting and attributes exist, save acts as
   617       // If we're not waiting and attributes exist, save acts as
   460       // `set(attr).save(null, opts)` with validation. Otherwise, check if
   618       // `set(attr).save(null, opts)` with validation. Otherwise, check if
   461       // the model will be valid when the attributes, if any, are set.
   619       // the model will be valid when the attributes, if any, are set.
   462       if (attrs && !options.wait) {
   620       if (attrs && !wait) {
   463         if (!this.set(attrs, options)) return false;
   621         if (!this.set(attrs, options)) return false;
   464       } else {
   622       } else {
   465         if (!this._validate(attrs, options)) return false;
   623         if (!this._validate(attrs, options)) return false;
   466       }
   624       }
   467 
   625 
   468       // Set temporary attributes if `{wait: true}`.
       
   469       if (attrs && options.wait) {
       
   470         this.attributes = _.extend({}, attributes, attrs);
       
   471       }
       
   472 
       
   473       // After a successful server-side save, the client is (optionally)
   626       // After a successful server-side save, the client is (optionally)
   474       // updated with the server-side state.
   627       // updated with the server-side state.
   475       if (options.parse === void 0) options.parse = true;
       
   476       var model = this;
   628       var model = this;
   477       var success = options.success;
   629       var success = options.success;
       
   630       var attributes = this.attributes;
   478       options.success = function(resp) {
   631       options.success = function(resp) {
   479         // Ensure attributes are restored during synchronous saves.
   632         // Ensure attributes are restored during synchronous saves.
   480         model.attributes = attributes;
   633         model.attributes = attributes;
   481         var serverAttrs = model.parse(resp, options);
   634         var serverAttrs = options.parse ? model.parse(resp, options) : resp;
   482         if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
   635         if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
   483         if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
   636         if (serverAttrs && !model.set(serverAttrs, options)) return false;
   484           return false;
   637         if (success) success.call(options.context, model, resp, options);
   485         }
       
   486         if (success) success(model, resp, options);
       
   487         model.trigger('sync', model, resp, options);
   638         model.trigger('sync', model, resp, options);
   488       };
   639       };
   489       wrapError(this, options);
   640       wrapError(this, options);
   490 
   641 
   491       method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
   642       // Set temporary attributes if `{wait: true}` to properly find new ids.
   492       if (method === 'patch') options.attrs = attrs;
   643       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
   493       xhr = this.sync(method, this, options);
   644 
       
   645       var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
       
   646       if (method === 'patch' && !options.attrs) options.attrs = attrs;
       
   647       var xhr = this.sync(method, this, options);
   494 
   648 
   495       // Restore attributes.
   649       // Restore attributes.
   496       if (attrs && options.wait) this.attributes = attributes;
   650       this.attributes = attributes;
   497 
   651 
   498       return xhr;
   652       return xhr;
   499     },
   653     },
   500 
   654 
   501     // Destroy this model on the server if it was already persisted.
   655     // Destroy this model on the server if it was already persisted.
   503     // If `wait: true` is passed, waits for the server to respond before removal.
   657     // If `wait: true` is passed, waits for the server to respond before removal.
   504     destroy: function(options) {
   658     destroy: function(options) {
   505       options = options ? _.clone(options) : {};
   659       options = options ? _.clone(options) : {};
   506       var model = this;
   660       var model = this;
   507       var success = options.success;
   661       var success = options.success;
       
   662       var wait = options.wait;
   508 
   663 
   509       var destroy = function() {
   664       var destroy = function() {
       
   665         model.stopListening();
   510         model.trigger('destroy', model, model.collection, options);
   666         model.trigger('destroy', model, model.collection, options);
   511       };
   667       };
   512 
   668 
   513       options.success = function(resp) {
   669       options.success = function(resp) {
   514         if (options.wait || model.isNew()) destroy();
   670         if (wait) destroy();
   515         if (success) success(model, resp, options);
   671         if (success) success.call(options.context, model, resp, options);
   516         if (!model.isNew()) model.trigger('sync', model, resp, options);
   672         if (!model.isNew()) model.trigger('sync', model, resp, options);
   517       };
   673       };
   518 
   674 
       
   675       var xhr = false;
   519       if (this.isNew()) {
   676       if (this.isNew()) {
   520         options.success();
   677         _.defer(options.success);
   521         return false;
   678       } else {
   522       }
   679         wrapError(this, options);
   523       wrapError(this, options);
   680         xhr = this.sync('delete', this, options);
   524 
   681       }
   525       var xhr = this.sync('delete', this, options);
   682       if (!wait) destroy();
   526       if (!options.wait) destroy();
       
   527       return xhr;
   683       return xhr;
   528     },
   684     },
   529 
   685 
   530     // Default URL for the model's representation on the server -- if you're
   686     // Default URL for the model's representation on the server -- if you're
   531     // using Backbone's restful methods, override this to change the endpoint
   687     // using Backbone's restful methods, override this to change the endpoint
   534       var base =
   690       var base =
   535         _.result(this, 'urlRoot') ||
   691         _.result(this, 'urlRoot') ||
   536         _.result(this.collection, 'url') ||
   692         _.result(this.collection, 'url') ||
   537         urlError();
   693         urlError();
   538       if (this.isNew()) return base;
   694       if (this.isNew()) return base;
   539       return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
   695       var id = this.get(this.idAttribute);
       
   696       return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
   540     },
   697     },
   541 
   698 
   542     // **parse** converts a response into the hash of attributes to be `set` on
   699     // **parse** converts a response into the hash of attributes to be `set` on
   543     // the model. The default implementation is just to pass the response along.
   700     // the model. The default implementation is just to pass the response along.
   544     parse: function(resp, options) {
   701     parse: function(resp, options) {
   555       return !this.has(this.idAttribute);
   712       return !this.has(this.idAttribute);
   556     },
   713     },
   557 
   714 
   558     // Check if the model is currently in a valid state.
   715     // Check if the model is currently in a valid state.
   559     isValid: function(options) {
   716     isValid: function(options) {
   560       return this._validate({}, _.extend(options || {}, { validate: true }));
   717       return this._validate({}, _.defaults({validate: true}, options));
   561     },
   718     },
   562 
   719 
   563     // Run validation against the next complete set of model attributes,
   720     // Run validation against the next complete set of model attributes,
   564     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
   721     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
   565     _validate: function(attrs, options) {
   722     _validate: function(attrs, options) {
   571       return false;
   728       return false;
   572     }
   729     }
   573 
   730 
   574   });
   731   });
   575 
   732 
   576   // Underscore methods that we want to implement on the Model.
   733   // Underscore methods that we want to implement on the Model, mapped to the
   577   var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
   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 };
   578 
   737 
   579   // Mix in each Underscore method as a proxy to `Model#attributes`.
   738   // Mix in each Underscore method as a proxy to `Model#attributes`.
   580   _.each(modelMethods, function(method) {
   739   addUnderscoreMethods(Model, modelMethods, 'attributes');
   581     Model.prototype[method] = function() {
       
   582       var args = slice.call(arguments);
       
   583       args.unshift(this.attributes);
       
   584       return _[method].apply(_, args);
       
   585     };
       
   586   });
       
   587 
   740 
   588   // Backbone.Collection
   741   // Backbone.Collection
   589   // -------------------
   742   // -------------------
   590 
   743 
   591   // If models tend to represent a single row of data, a Backbone Collection is
   744   // If models tend to represent a single row of data, a Backbone Collection is
   592   // more analagous to a table full of data ... or a small slice or page of that
   745   // more analogous to a table full of data ... or a small slice or page of that
   593   // table, or a collection of rows that belong together for a particular reason
   746   // table, or a collection of rows that belong together for a particular reason
   594   // -- all of the messages in this particular folder, all of the documents
   747   // -- all of the messages in this particular folder, all of the documents
   595   // belonging to this particular author, and so on. Collections maintain
   748   // belonging to this particular author, and so on. Collections maintain
   596   // indexes of their models, both in order, and for lookup by `id`.
   749   // indexes of their models, both in order, and for lookup by `id`.
   597 
   750 
   609 
   762 
   610   // Default options for `Collection#set`.
   763   // Default options for `Collection#set`.
   611   var setOptions = {add: true, remove: true, merge: true};
   764   var setOptions = {add: true, remove: true, merge: true};
   612   var addOptions = {add: true, remove: false};
   765   var addOptions = {add: true, remove: false};
   613 
   766 
       
   767   // Splices `insert` into `array` at index `at`.
       
   768   var splice = function(array, insert, at) {
       
   769     at = Math.min(Math.max(at, 0), array.length);
       
   770     var tail = Array(array.length - at);
       
   771     var length = insert.length;
       
   772     for (var i = 0; i < tail.length; i++) tail[i] = array[i + at];
       
   773     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];
       
   775   };
       
   776 
   614   // Define the Collection's inheritable methods.
   777   // Define the Collection's inheritable methods.
   615   _.extend(Collection.prototype, Events, {
   778   _.extend(Collection.prototype, Events, {
   616 
   779 
   617     // The default model for a collection is just a **Backbone.Model**.
   780     // The default model for a collection is just a **Backbone.Model**.
   618     // This should be overridden in most cases.
   781     // This should be overridden in most cases.
   623     initialize: function(){},
   786     initialize: function(){},
   624 
   787 
   625     // The JSON representation of a Collection is an array of the
   788     // The JSON representation of a Collection is an array of the
   626     // models' attributes.
   789     // models' attributes.
   627     toJSON: function(options) {
   790     toJSON: function(options) {
   628       return this.map(function(model){ return model.toJSON(options); });
   791       return this.map(function(model) { return model.toJSON(options); });
   629     },
   792     },
   630 
   793 
   631     // Proxy `Backbone.sync` by default.
   794     // Proxy `Backbone.sync` by default.
   632     sync: function() {
   795     sync: function() {
   633       return Backbone.sync.apply(this, arguments);
   796       return Backbone.sync.apply(this, arguments);
   634     },
   797     },
   635 
   798 
   636     // Add a model, or list of models to the set.
   799     // Add a model, or list of models to the set. `models` may be Backbone
       
   800     // Models or raw JavaScript objects to be converted to Models, or any
       
   801     // combination of the two.
   637     add: function(models, options) {
   802     add: function(models, options) {
   638       return this.set(models, _.extend({merge: false}, options, addOptions));
   803       return this.set(models, _.extend({merge: false}, options, addOptions));
   639     },
   804     },
   640 
   805 
   641     // Remove a model, or a list of models from the set.
   806     // Remove a model, or a list of models from the set.
   642     remove: function(models, options) {
   807     remove: function(models, options) {
       
   808       options = _.extend({}, options);
   643       var singular = !_.isArray(models);
   809       var singular = !_.isArray(models);
   644       models = singular ? [models] : _.clone(models);
   810       models = singular ? [models] : _.clone(models);
   645       options || (options = {});
   811       var removed = this._removeModels(models, options);
   646       var i, l, index, model;
   812       if (!options.silent && removed) this.trigger('update', this, options);
   647       for (i = 0, l = models.length; i < l; i++) {
   813       return singular ? removed[0] : removed;
   648         model = models[i] = this.get(models[i]);
       
   649         if (!model) continue;
       
   650         delete this._byId[model.id];
       
   651         delete this._byId[model.cid];
       
   652         index = this.indexOf(model);
       
   653         this.models.splice(index, 1);
       
   654         this.length--;
       
   655         if (!options.silent) {
       
   656           options.index = index;
       
   657           model.trigger('remove', model, this, options);
       
   658         }
       
   659         this._removeReference(model, options);
       
   660       }
       
   661       return singular ? models[0] : models;
       
   662     },
   814     },
   663 
   815 
   664     // Update a collection by `set`-ing a new list of models, adding new ones,
   816     // Update a collection by `set`-ing a new list of models, adding new ones,
   665     // removing models that are no longer present, and merging models that
   817     // removing models that are no longer present, and merging models that
   666     // already exist in the collection, as necessary. Similar to **Model#set**,
   818     // already exist in the collection, as necessary. Similar to **Model#set**,
   667     // the core operation for updating the data contained by the collection.
   819     // the core operation for updating the data contained by the collection.
   668     set: function(models, options) {
   820     set: function(models, options) {
       
   821       if (models == null) return;
       
   822 
   669       options = _.defaults({}, options, setOptions);
   823       options = _.defaults({}, options, setOptions);
   670       if (options.parse) models = this.parse(models, options);
   824       if (options.parse && !this._isModel(models)) models = this.parse(models, options);
       
   825 
   671       var singular = !_.isArray(models);
   826       var singular = !_.isArray(models);
   672       models = singular ? (models ? [models] : []) : _.clone(models);
   827       models = singular ? [models] : models.slice();
   673       var i, l, id, model, attrs, existing, sort;
   828 
   674       var at = options.at;
   829       var at = options.at;
   675       var targetModel = this.model;
   830       if (at != null) at = +at;
       
   831       if (at < 0) at += this.length + 1;
       
   832 
       
   833       var set = [];
       
   834       var toAdd = [];
       
   835       var toRemove = [];
       
   836       var modelMap = {};
       
   837 
       
   838       var add = options.add;
       
   839       var merge = options.merge;
       
   840       var remove = options.remove;
       
   841 
       
   842       var sort = false;
   676       var sortable = this.comparator && (at == null) && options.sort !== false;
   843       var sortable = this.comparator && (at == null) && options.sort !== false;
   677       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
   844       var sortAttr = _.isString(this.comparator) ? this.comparator : null;
   678       var toAdd = [], toRemove = [], modelMap = {};
       
   679       var add = options.add, merge = options.merge, remove = options.remove;
       
   680       var order = !sortable && add && remove ? [] : false;
       
   681 
   845 
   682       // Turn bare objects into model references, and prevent invalid models
   846       // Turn bare objects into model references, and prevent invalid models
   683       // from being added.
   847       // from being added.
   684       for (i = 0, l = models.length; i < l; i++) {
   848       var model;
   685         attrs = models[i] || {};
   849       for (var i = 0; i < models.length; i++) {
   686         if (attrs instanceof Model) {
   850         model = models[i];
   687           id = model = attrs;
       
   688         } else {
       
   689           id = attrs[targetModel.prototype.idAttribute || 'id'];
       
   690         }
       
   691 
   851 
   692         // If a duplicate is found, prevent it from being added and
   852         // If a duplicate is found, prevent it from being added and
   693         // optionally merge it into the existing model.
   853         // optionally merge it into the existing model.
   694         if (existing = this.get(id)) {
   854         var existing = this.get(model);
   695           if (remove) modelMap[existing.cid] = true;
   855         if (existing) {
   696           if (merge) {
   856           if (merge && model !== existing) {
   697             attrs = attrs === model ? model.attributes : attrs;
   857             var attrs = this._isModel(model) ? model.attributes : model;
   698             if (options.parse) attrs = existing.parse(attrs, options);
   858             if (options.parse) attrs = existing.parse(attrs, options);
   699             existing.set(attrs, options);
   859             existing.set(attrs, options);
   700             if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
   860             if (sortable && !sort) sort = existing.hasChanged(sortAttr);
       
   861           }
       
   862           if (!modelMap[existing.cid]) {
       
   863             modelMap[existing.cid] = true;
       
   864             set.push(existing);
   701           }
   865           }
   702           models[i] = existing;
   866           models[i] = existing;
   703 
   867 
   704         // If this is a new, valid model, push it to the `toAdd` list.
   868         // If this is a new, valid model, push it to the `toAdd` list.
   705         } else if (add) {
   869         } else if (add) {
   706           model = models[i] = this._prepareModel(attrs, options);
   870           model = models[i] = this._prepareModel(model, options);
   707           if (!model) continue;
   871           if (model) {
   708           toAdd.push(model);
   872             toAdd.push(model);
   709           this._addReference(model, options);
   873             this._addReference(model, options);
   710         }
   874             modelMap[model.cid] = true;
   711 
   875             set.push(model);
   712         // Do not add multiple models with the same `id`.
       
   713         model = existing || model;
       
   714         if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
       
   715         modelMap[model.id] = true;
       
   716       }
       
   717 
       
   718       // Remove nonexistent models if appropriate.
       
   719       if (remove) {
       
   720         for (i = 0, l = this.length; i < l; ++i) {
       
   721           if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
       
   722         }
       
   723         if (toRemove.length) this.remove(toRemove, options);
       
   724       }
       
   725 
       
   726       // See if sorting is needed, update `length` and splice in new models.
       
   727       if (toAdd.length || (order && order.length)) {
       
   728         if (sortable) sort = true;
       
   729         this.length += toAdd.length;
       
   730         if (at != null) {
       
   731           for (i = 0, l = toAdd.length; i < l; i++) {
       
   732             this.models.splice(at + i, 0, toAdd[i]);
       
   733           }
       
   734         } else {
       
   735           if (order) this.models.length = 0;
       
   736           var orderedModels = order || toAdd;
       
   737           for (i = 0, l = orderedModels.length; i < l; i++) {
       
   738             this.models.push(orderedModels[i]);
       
   739           }
   876           }
   740         }
   877         }
   741       }
   878       }
   742 
   879 
       
   880       // Remove stale models.
       
   881       if (remove) {
       
   882         for (i = 0; i < this.length; i++) {
       
   883           model = this.models[i];
       
   884           if (!modelMap[model.cid]) toRemove.push(model);
       
   885         }
       
   886         if (toRemove.length) this._removeModels(toRemove, options);
       
   887       }
       
   888 
       
   889       // See if sorting is needed, update `length` and splice in new models.
       
   890       var orderChanged = false;
       
   891       var replace = !sortable && add && remove;
       
   892       if (set.length && replace) {
       
   893         orderChanged = this.length != set.length || _.some(this.models, function(model, index) {
       
   894           return model !== set[index];
       
   895         });
       
   896         this.models.length = 0;
       
   897         splice(this.models, set, 0);
       
   898         this.length = this.models.length;
       
   899       } else if (toAdd.length) {
       
   900         if (sortable) sort = true;
       
   901         splice(this.models, toAdd, at == null ? this.length : at);
       
   902         this.length = this.models.length;
       
   903       }
       
   904 
   743       // Silently sort the collection if appropriate.
   905       // Silently sort the collection if appropriate.
   744       if (sort) this.sort({silent: true});
   906       if (sort) this.sort({silent: true});
   745 
   907 
   746       // Unless silenced, it's time to fire all appropriate add/sort events.
   908       // Unless silenced, it's time to fire all appropriate add/sort events.
   747       if (!options.silent) {
   909       if (!options.silent) {
   748         for (i = 0, l = toAdd.length; i < l; i++) {
   910         for (i = 0; i < toAdd.length; i++) {
   749           (model = toAdd[i]).trigger('add', model, this, options);
   911           if (at != null) options.index = at + i;
       
   912           model = toAdd[i];
       
   913           model.trigger('add', model, this, options);
   750         }
   914         }
   751         if (sort || (order && order.length)) this.trigger('sort', this, options);
   915         if (sort || orderChanged) this.trigger('sort', this, options);
       
   916         if (toAdd.length || toRemove.length) this.trigger('update', this, options);
   752       }
   917       }
   753 
   918 
   754       // Return the added (or merged) model (or models).
   919       // Return the added (or merged) model (or models).
   755       return singular ? models[0] : models;
   920       return singular ? models[0] : models;
   756     },
   921     },
   758     // When you have more items than you want to add or remove individually,
   923     // When you have more items than you want to add or remove individually,
   759     // you can reset the entire set with a new list of models, without firing
   924     // you can reset the entire set with a new list of models, without firing
   760     // any granular `add` or `remove` events. Fires `reset` when finished.
   925     // any granular `add` or `remove` events. Fires `reset` when finished.
   761     // Useful for bulk operations and optimizations.
   926     // Useful for bulk operations and optimizations.
   762     reset: function(models, options) {
   927     reset: function(models, options) {
   763       options || (options = {});
   928       options = options ? _.clone(options) : {};
   764       for (var i = 0, l = this.models.length; i < l; i++) {
   929       for (var i = 0; i < this.models.length; i++) {
   765         this._removeReference(this.models[i], options);
   930         this._removeReference(this.models[i], options);
   766       }
   931       }
   767       options.previousModels = this.models;
   932       options.previousModels = this.models;
   768       this._reset();
   933       this._reset();
   769       models = this.add(models, _.extend({silent: true}, options));
   934       models = this.add(models, _.extend({silent: true}, options));
   777     },
   942     },
   778 
   943 
   779     // Remove a model from the end of the collection.
   944     // Remove a model from the end of the collection.
   780     pop: function(options) {
   945     pop: function(options) {
   781       var model = this.at(this.length - 1);
   946       var model = this.at(this.length - 1);
   782       this.remove(model, options);
   947       return this.remove(model, options);
   783       return model;
       
   784     },
   948     },
   785 
   949 
   786     // Add a model to the beginning of the collection.
   950     // Add a model to the beginning of the collection.
   787     unshift: function(model, options) {
   951     unshift: function(model, options) {
   788       return this.add(model, _.extend({at: 0}, options));
   952       return this.add(model, _.extend({at: 0}, options));
   789     },
   953     },
   790 
   954 
   791     // Remove a model from the beginning of the collection.
   955     // Remove a model from the beginning of the collection.
   792     shift: function(options) {
   956     shift: function(options) {
   793       var model = this.at(0);
   957       var model = this.at(0);
   794       this.remove(model, options);
   958       return this.remove(model, options);
   795       return model;
       
   796     },
   959     },
   797 
   960 
   798     // Slice out a sub-array of models from the collection.
   961     // Slice out a sub-array of models from the collection.
   799     slice: function() {
   962     slice: function() {
   800       return slice.apply(this.models, arguments);
   963       return slice.apply(this.models, arguments);
   801     },
   964     },
   802 
   965 
   803     // Get a model from the set by id.
   966     // Get a model from the set by id.
   804     get: function(obj) {
   967     get: function(obj) {
   805       if (obj == null) return void 0;
   968       if (obj == null) return void 0;
   806       return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
   969       var id = this.modelId(this._isModel(obj) ? obj.attributes : obj);
       
   970       return this._byId[obj] || this._byId[id] || this._byId[obj.cid];
   807     },
   971     },
   808 
   972 
   809     // Get the model at the given index.
   973     // Get the model at the given index.
   810     at: function(index) {
   974     at: function(index) {
       
   975       if (index < 0) index += this.length;
   811       return this.models[index];
   976       return this.models[index];
   812     },
   977     },
   813 
   978 
   814     // Return models with matching attributes. Useful for simple cases of
   979     // Return models with matching attributes. Useful for simple cases of
   815     // `filter`.
   980     // `filter`.
   816     where: function(attrs, first) {
   981     where: function(attrs, first) {
   817       if (_.isEmpty(attrs)) return first ? void 0 : [];
   982       return this[first ? 'find' : 'filter'](attrs);
   818       return this[first ? 'find' : 'filter'](function(model) {
       
   819         for (var key in attrs) {
       
   820           if (attrs[key] !== model.get(key)) return false;
       
   821         }
       
   822         return true;
       
   823       });
       
   824     },
   983     },
   825 
   984 
   826     // Return the first model with matching attributes. Useful for simple cases
   985     // Return the first model with matching attributes. Useful for simple cases
   827     // of `find`.
   986     // of `find`.
   828     findWhere: function(attrs) {
   987     findWhere: function(attrs) {
   831 
   990 
   832     // Force the collection to re-sort itself. You don't need to call this under
   991     // Force the collection to re-sort itself. You don't need to call this under
   833     // normal circumstances, as the set will maintain sort order as each item
   992     // normal circumstances, as the set will maintain sort order as each item
   834     // is added.
   993     // is added.
   835     sort: function(options) {
   994     sort: function(options) {
   836       if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
   995       var comparator = this.comparator;
       
   996       if (!comparator) throw new Error('Cannot sort a set without a comparator');
   837       options || (options = {});
   997       options || (options = {});
   838 
   998 
       
   999       var length = comparator.length;
       
  1000       if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
       
  1001 
   839       // Run sort based on type of `comparator`.
  1002       // Run sort based on type of `comparator`.
   840       if (_.isString(this.comparator) || this.comparator.length === 1) {
  1003       if (length === 1 || _.isString(comparator)) {
   841         this.models = this.sortBy(this.comparator, this);
  1004         this.models = this.sortBy(comparator);
   842       } else {
  1005       } else {
   843         this.models.sort(_.bind(this.comparator, this));
  1006         this.models.sort(comparator);
   844       }
  1007       }
   845 
       
   846       if (!options.silent) this.trigger('sort', this, options);
  1008       if (!options.silent) this.trigger('sort', this, options);
   847       return this;
  1009       return this;
   848     },
  1010     },
   849 
  1011 
   850     // Pluck an attribute from each model in the collection.
  1012     // Pluck an attribute from each model in the collection.
   854 
  1016 
   855     // Fetch the default set of models for this collection, resetting the
  1017     // Fetch the default set of models for this collection, resetting the
   856     // collection when they arrive. If `reset: true` is passed, the response
  1018     // collection when they arrive. If `reset: true` is passed, the response
   857     // data will be passed through the `reset` method instead of `set`.
  1019     // data will be passed through the `reset` method instead of `set`.
   858     fetch: function(options) {
  1020     fetch: function(options) {
   859       options = options ? _.clone(options) : {};
  1021       options = _.extend({parse: true}, options);
   860       if (options.parse === void 0) options.parse = true;
       
   861       var success = options.success;
  1022       var success = options.success;
   862       var collection = this;
  1023       var collection = this;
   863       options.success = function(resp) {
  1024       options.success = function(resp) {
   864         var method = options.reset ? 'reset' : 'set';
  1025         var method = options.reset ? 'reset' : 'set';
   865         collection[method](resp, options);
  1026         collection[method](resp, options);
   866         if (success) success(collection, resp, options);
  1027         if (success) success.call(options.context, collection, resp, options);
   867         collection.trigger('sync', collection, resp, options);
  1028         collection.trigger('sync', collection, resp, options);
   868       };
  1029       };
   869       wrapError(this, options);
  1030       wrapError(this, options);
   870       return this.sync('read', this, options);
  1031       return this.sync('read', this, options);
   871     },
  1032     },
   873     // Create a new instance of a model in this collection. Add the model to the
  1034     // Create a new instance of a model in this collection. Add the model to the
   874     // collection immediately, unless `wait: true` is passed, in which case we
  1035     // collection immediately, unless `wait: true` is passed, in which case we
   875     // wait for the server to agree.
  1036     // wait for the server to agree.
   876     create: function(model, options) {
  1037     create: function(model, options) {
   877       options = options ? _.clone(options) : {};
  1038       options = options ? _.clone(options) : {};
   878       if (!(model = this._prepareModel(model, options))) return false;
  1039       var wait = options.wait;
   879       if (!options.wait) this.add(model, options);
  1040       model = this._prepareModel(model, options);
       
  1041       if (!model) return false;
       
  1042       if (!wait) this.add(model, options);
   880       var collection = this;
  1043       var collection = this;
   881       var success = options.success;
  1044       var success = options.success;
   882       options.success = function(model, resp) {
  1045       options.success = function(model, resp, callbackOpts) {
   883         if (options.wait) collection.add(model, options);
  1046         if (wait) collection.add(model, callbackOpts);
   884         if (success) success(model, resp, options);
  1047         if (success) success.call(callbackOpts.context, model, resp, callbackOpts);
   885       };
  1048       };
   886       model.save(null, options);
  1049       model.save(null, options);
   887       return model;
  1050       return model;
   888     },
  1051     },
   889 
  1052 
   893       return resp;
  1056       return resp;
   894     },
  1057     },
   895 
  1058 
   896     // Create a new collection with an identical list of models as this one.
  1059     // Create a new collection with an identical list of models as this one.
   897     clone: function() {
  1060     clone: function() {
   898       return new this.constructor(this.models);
  1061       return new this.constructor(this.models, {
       
  1062         model: this.model,
       
  1063         comparator: this.comparator
       
  1064       });
       
  1065     },
       
  1066 
       
  1067     // Define how to uniquely identify models in the collection.
       
  1068     modelId: function (attrs) {
       
  1069       return attrs[this.model.prototype.idAttribute || 'id'];
   899     },
  1070     },
   900 
  1071 
   901     // Private method to reset all internal state. Called when the collection
  1072     // Private method to reset all internal state. Called when the collection
   902     // is first initialized or reset.
  1073     // is first initialized or reset.
   903     _reset: function() {
  1074     _reset: function() {
   907     },
  1078     },
   908 
  1079 
   909     // Prepare a hash of attributes (or other model) to be added to this
  1080     // Prepare a hash of attributes (or other model) to be added to this
   910     // collection.
  1081     // collection.
   911     _prepareModel: function(attrs, options) {
  1082     _prepareModel: function(attrs, options) {
   912       if (attrs instanceof Model) return attrs;
  1083       if (this._isModel(attrs)) {
       
  1084         if (!attrs.collection) attrs.collection = this;
       
  1085         return attrs;
       
  1086       }
   913       options = options ? _.clone(options) : {};
  1087       options = options ? _.clone(options) : {};
   914       options.collection = this;
  1088       options.collection = this;
   915       var model = new this.model(attrs, options);
  1089       var model = new this.model(attrs, options);
   916       if (!model.validationError) return model;
  1090       if (!model.validationError) return model;
   917       this.trigger('invalid', this, model.validationError, options);
  1091       this.trigger('invalid', this, model.validationError, options);
   918       return false;
  1092       return false;
   919     },
  1093     },
   920 
  1094 
       
  1095     // Internal method called by both remove and set.
       
  1096     _removeModels: function(models, options) {
       
  1097       var removed = [];
       
  1098       for (var i = 0; i < models.length; i++) {
       
  1099         var model = this.get(models[i]);
       
  1100         if (!model) continue;
       
  1101 
       
  1102         var index = this.indexOf(model);
       
  1103         this.models.splice(index, 1);
       
  1104         this.length--;
       
  1105 
       
  1106         if (!options.silent) {
       
  1107           options.index = index;
       
  1108           model.trigger('remove', model, this, options);
       
  1109         }
       
  1110 
       
  1111         removed.push(model);
       
  1112         this._removeReference(model, options);
       
  1113       }
       
  1114       return removed.length ? removed : false;
       
  1115     },
       
  1116 
       
  1117     // Method for checking whether an object should be considered a model for
       
  1118     // the purposes of adding to the collection.
       
  1119     _isModel: function (model) {
       
  1120       return model instanceof Model;
       
  1121     },
       
  1122 
   921     // Internal method to create a model's ties to a collection.
  1123     // Internal method to create a model's ties to a collection.
   922     _addReference: function(model, options) {
  1124     _addReference: function(model, options) {
   923       this._byId[model.cid] = model;
  1125       this._byId[model.cid] = model;
   924       if (model.id != null) this._byId[model.id] = model;
  1126       var id = this.modelId(model.attributes);
   925       if (!model.collection) model.collection = this;
  1127       if (id != null) this._byId[id] = model;
   926       model.on('all', this._onModelEvent, this);
  1128       model.on('all', this._onModelEvent, this);
   927     },
  1129     },
   928 
  1130 
   929     // Internal method to sever a model's ties to a collection.
  1131     // Internal method to sever a model's ties to a collection.
   930     _removeReference: function(model, options) {
  1132     _removeReference: function(model, options) {
       
  1133       delete this._byId[model.cid];
       
  1134       var id = this.modelId(model.attributes);
       
  1135       if (id != null) delete this._byId[id];
   931       if (this === model.collection) delete model.collection;
  1136       if (this === model.collection) delete model.collection;
   932       model.off('all', this._onModelEvent, this);
  1137       model.off('all', this._onModelEvent, this);
   933     },
  1138     },
   934 
  1139 
   935     // Internal method called every time a model in the set fires an event.
  1140     // Internal method called every time a model in the set fires an event.
   937     // events simply proxy through. "add" and "remove" events that originate
  1142     // events simply proxy through. "add" and "remove" events that originate
   938     // in other collections are ignored.
  1143     // in other collections are ignored.
   939     _onModelEvent: function(event, model, collection, options) {
  1144     _onModelEvent: function(event, model, collection, options) {
   940       if ((event === 'add' || event === 'remove') && collection !== this) return;
  1145       if ((event === 'add' || event === 'remove') && collection !== this) return;
   941       if (event === 'destroy') this.remove(model, options);
  1146       if (event === 'destroy') this.remove(model, options);
   942       if (model && event === 'change:' + model.idAttribute) {
  1147       if (event === 'change') {
   943         delete this._byId[model.previous(model.idAttribute)];
  1148         var prevId = this.modelId(model.previousAttributes());
   944         if (model.id != null) this._byId[model.id] = model;
  1149         var id = this.modelId(model.attributes);
       
  1150         if (prevId !== id) {
       
  1151           if (prevId != null) delete this._byId[prevId];
       
  1152           if (id != null) this._byId[id] = model;
       
  1153         }
   945       }
  1154       }
   946       this.trigger.apply(this, arguments);
  1155       this.trigger.apply(this, arguments);
   947     }
  1156     }
   948 
  1157 
   949   });
  1158   });
   950 
  1159 
   951   // Underscore methods that we want to implement on the Collection.
  1160   // Underscore methods that we want to implement on the Collection.
   952   // 90% of the core usefulness of Backbone Collections is actually implemented
  1161   // 90% of the core usefulness of Backbone Collections is actually implemented
   953   // right here:
  1162   // right here:
   954   var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
  1163   var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
   955     'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
  1164       foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
   956     'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
  1165       select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
   957     'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
  1166       contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
   958     'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
  1167       head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
   959     'lastIndexOf', 'isEmpty', 'chain', 'sample'];
  1168       without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
       
  1169       isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
       
  1170       sortBy: 3, indexBy: 3};
   960 
  1171 
   961   // Mix in each Underscore method as a proxy to `Collection#models`.
  1172   // Mix in each Underscore method as a proxy to `Collection#models`.
   962   _.each(methods, function(method) {
  1173   addUnderscoreMethods(Collection, collectionMethods, 'models');
   963     Collection.prototype[method] = function() {
       
   964       var args = slice.call(arguments);
       
   965       args.unshift(this.models);
       
   966       return _[method].apply(_, args);
       
   967     };
       
   968   });
       
   969 
       
   970   // Underscore methods that take a property name as an argument.
       
   971   var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
       
   972 
       
   973   // Use attributes instead of properties.
       
   974   _.each(attributeMethods, function(method) {
       
   975     Collection.prototype[method] = function(value, context) {
       
   976       var iterator = _.isFunction(value) ? value : function(model) {
       
   977         return model.get(value);
       
   978       };
       
   979       return _[method](this.models, iterator, context);
       
   980     };
       
   981   });
       
   982 
  1174 
   983   // Backbone.View
  1175   // Backbone.View
   984   // -------------
  1176   // -------------
   985 
  1177 
   986   // Backbone Views are almost more convention than they are actual code. A View
  1178   // Backbone Views are almost more convention than they are actual code. A View
   993 
  1185 
   994   // Creating a Backbone.View creates its initial element outside of the DOM,
  1186   // Creating a Backbone.View creates its initial element outside of the DOM,
   995   // if an existing element is not provided...
  1187   // if an existing element is not provided...
   996   var View = Backbone.View = function(options) {
  1188   var View = Backbone.View = function(options) {
   997     this.cid = _.uniqueId('view');
  1189     this.cid = _.uniqueId('view');
   998     options || (options = {});
       
   999     _.extend(this, _.pick(options, viewOptions));
  1190     _.extend(this, _.pick(options, viewOptions));
  1000     this._ensureElement();
  1191     this._ensureElement();
  1001     this.initialize.apply(this, arguments);
  1192     this.initialize.apply(this, arguments);
  1002     this.delegateEvents();
       
  1003   };
  1193   };
  1004 
  1194 
  1005   // Cached regex to split keys for `delegate`.
  1195   // Cached regex to split keys for `delegate`.
  1006   var delegateEventSplitter = /^(\S+)\s*(.*)$/;
  1196   var delegateEventSplitter = /^(\S+)\s*(.*)$/;
  1007 
  1197 
  1008   // List of view options to be merged as properties.
  1198   // List of view options to be set as properties.
  1009   var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
  1199   var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
  1010 
  1200 
  1011   // Set up all inheritable **Backbone.View** properties and methods.
  1201   // Set up all inheritable **Backbone.View** properties and methods.
  1012   _.extend(View.prototype, Events, {
  1202   _.extend(View.prototype, Events, {
  1013 
  1203 
  1032     },
  1222     },
  1033 
  1223 
  1034     // Remove this view by taking the element out of the DOM, and removing any
  1224     // Remove this view by taking the element out of the DOM, and removing any
  1035     // applicable Backbone.Events listeners.
  1225     // applicable Backbone.Events listeners.
  1036     remove: function() {
  1226     remove: function() {
  1037       this.$el.remove();
  1227       this._removeElement();
  1038       this.stopListening();
  1228       this.stopListening();
  1039       return this;
  1229       return this;
  1040     },
  1230     },
  1041 
  1231 
  1042     // Change the view's element (`this.el` property), including event
  1232     // Remove this view's element from the document and all event listeners
  1043     // re-delegation.
  1233     // attached to it. Exposed for subclasses using an alternative DOM
  1044     setElement: function(element, delegate) {
  1234     // manipulation API.
  1045       if (this.$el) this.undelegateEvents();
  1235     _removeElement: function() {
  1046       this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
  1236       this.$el.remove();
       
  1237     },
       
  1238 
       
  1239     // Change the view's element (`this.el` property) and re-delegate the
       
  1240     // view's events on the new element.
       
  1241     setElement: function(element) {
       
  1242       this.undelegateEvents();
       
  1243       this._setElement(element);
       
  1244       this.delegateEvents();
       
  1245       return this;
       
  1246     },
       
  1247 
       
  1248     // Creates the `this.el` and `this.$el` references for this view using the
       
  1249     // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
       
  1250     // context or an element. Subclasses can override this to utilize an
       
  1251     // alternative DOM manipulation API and are only required to set the
       
  1252     // `this.el` property.
       
  1253     _setElement: function(el) {
       
  1254       this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
  1047       this.el = this.$el[0];
  1255       this.el = this.$el[0];
  1048       if (delegate !== false) this.delegateEvents();
       
  1049       return this;
       
  1050     },
  1256     },
  1051 
  1257 
  1052     // Set callbacks, where `this.events` is a hash of
  1258     // Set callbacks, where `this.events` is a hash of
  1053     //
  1259     //
  1054     // *{"event selector": "callback"}*
  1260     // *{"event selector": "callback"}*
  1060     //     }
  1266     //     }
  1061     //
  1267     //
  1062     // pairs. Callbacks will be bound to the view, with `this` set properly.
  1268     // pairs. Callbacks will be bound to the view, with `this` set properly.
  1063     // Uses event delegation for efficiency.
  1269     // Uses event delegation for efficiency.
  1064     // Omitting the selector binds the event to `this.el`.
  1270     // Omitting the selector binds the event to `this.el`.
  1065     // This only works for delegate-able events: not `focus`, `blur`, and
       
  1066     // not `change`, `submit`, and `reset` in Internet Explorer.
       
  1067     delegateEvents: function(events) {
  1271     delegateEvents: function(events) {
  1068       if (!(events || (events = _.result(this, 'events')))) return this;
  1272       events || (events = _.result(this, 'events'));
       
  1273       if (!events) return this;
  1069       this.undelegateEvents();
  1274       this.undelegateEvents();
  1070       for (var key in events) {
  1275       for (var key in events) {
  1071         var method = events[key];
  1276         var method = events[key];
  1072         if (!_.isFunction(method)) method = this[events[key]];
  1277         if (!_.isFunction(method)) method = this[method];
  1073         if (!method) continue;
  1278         if (!method) continue;
  1074 
       
  1075         var match = key.match(delegateEventSplitter);
  1279         var match = key.match(delegateEventSplitter);
  1076         var eventName = match[1], selector = match[2];
  1280         this.delegate(match[1], match[2], _.bind(method, this));
  1077         method = _.bind(method, this);
       
  1078         eventName += '.delegateEvents' + this.cid;
       
  1079         if (selector === '') {
       
  1080           this.$el.on(eventName, method);
       
  1081         } else {
       
  1082           this.$el.on(eventName, selector, method);
       
  1083         }
       
  1084       }
  1281       }
  1085       return this;
  1282       return this;
  1086     },
  1283     },
  1087 
  1284 
  1088     // Clears all callbacks previously bound to the view with `delegateEvents`.
  1285     // Add a single event listener to the view's element (or a child element
       
  1286     // using `selector`). This only works for delegate-able events: not `focus`,
       
  1287     // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
       
  1288     delegate: function(eventName, selector, listener) {
       
  1289       this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
       
  1290       return this;
       
  1291     },
       
  1292 
       
  1293     // Clears all callbacks previously bound to the view by `delegateEvents`.
  1089     // You usually don't need to use this, but may wish to if you have multiple
  1294     // You usually don't need to use this, but may wish to if you have multiple
  1090     // Backbone views attached to the same DOM element.
  1295     // Backbone views attached to the same DOM element.
  1091     undelegateEvents: function() {
  1296     undelegateEvents: function() {
  1092       this.$el.off('.delegateEvents' + this.cid);
  1297       if (this.$el) this.$el.off('.delegateEvents' + this.cid);
  1093       return this;
  1298       return this;
       
  1299     },
       
  1300 
       
  1301     // A finer-grained `undelegateEvents` for removing a single delegated event.
       
  1302     // `selector` and `listener` are both optional.
       
  1303     undelegate: function(eventName, selector, listener) {
       
  1304       this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
       
  1305       return this;
       
  1306     },
       
  1307 
       
  1308     // Produces a DOM element to be assigned to your view. Exposed for
       
  1309     // subclasses using an alternative DOM manipulation API.
       
  1310     _createElement: function(tagName) {
       
  1311       return document.createElement(tagName);
  1094     },
  1312     },
  1095 
  1313 
  1096     // Ensure that the View has a DOM element to render into.
  1314     // Ensure that the View has a DOM element to render into.
  1097     // If `this.el` is a string, pass it through `$()`, take the first
  1315     // If `this.el` is a string, pass it through `$()`, take the first
  1098     // matching element, and re-assign it to `el`. Otherwise, create
  1316     // matching element, and re-assign it to `el`. Otherwise, create
  1100     _ensureElement: function() {
  1318     _ensureElement: function() {
  1101       if (!this.el) {
  1319       if (!this.el) {
  1102         var attrs = _.extend({}, _.result(this, 'attributes'));
  1320         var attrs = _.extend({}, _.result(this, 'attributes'));
  1103         if (this.id) attrs.id = _.result(this, 'id');
  1321         if (this.id) attrs.id = _.result(this, 'id');
  1104         if (this.className) attrs['class'] = _.result(this, 'className');
  1322         if (this.className) attrs['class'] = _.result(this, 'className');
  1105         var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
  1323         this.setElement(this._createElement(_.result(this, 'tagName')));
  1106         this.setElement($el, false);
  1324         this._setAttributes(attrs);
  1107       } else {
  1325       } else {
  1108         this.setElement(_.result(this, 'el'), false);
  1326         this.setElement(_.result(this, 'el'));
  1109       }
  1327       }
       
  1328     },
       
  1329 
       
  1330     // Set attributes from a hash on this view's element.  Exposed for
       
  1331     // subclasses using an alternative DOM manipulation API.
       
  1332     _setAttributes: function(attributes) {
       
  1333       this.$el.attr(attributes);
  1110     }
  1334     }
  1111 
  1335 
  1112   });
  1336   });
  1113 
  1337 
  1114   // Backbone.sync
  1338   // Backbone.sync
  1173     // Don't process data on a non-GET request.
  1397     // Don't process data on a non-GET request.
  1174     if (params.type !== 'GET' && !options.emulateJSON) {
  1398     if (params.type !== 'GET' && !options.emulateJSON) {
  1175       params.processData = false;
  1399       params.processData = false;
  1176     }
  1400     }
  1177 
  1401 
  1178     // If we're sending a `PATCH` request, and we're in an old Internet Explorer
  1402     // Pass along `textStatus` and `errorThrown` from jQuery.
  1179     // that still has ActiveX enabled by default, override jQuery to use that
  1403     var error = options.error;
  1180     // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
  1404     options.error = function(xhr, textStatus, errorThrown) {
  1181     if (params.type === 'PATCH' && noXhrPatch) {
  1405       options.textStatus = textStatus;
  1182       params.xhr = function() {
  1406       options.errorThrown = errorThrown;
  1183         return new ActiveXObject("Microsoft.XMLHTTP");
  1407       if (error) error.call(options.context, xhr, textStatus, errorThrown);
  1184       };
  1408     };
  1185     }
       
  1186 
  1409 
  1187     // Make the request, allowing the user to override any Ajax options.
  1410     // Make the request, allowing the user to override any Ajax options.
  1188     var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
  1411     var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
  1189     model.trigger('request', model, xhr, options);
  1412     model.trigger('request', model, xhr, options);
  1190     return xhr;
  1413     return xhr;
  1191   };
  1414   };
  1192 
       
  1193   var noXhrPatch =
       
  1194     typeof window !== 'undefined' && !!window.ActiveXObject &&
       
  1195       !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
       
  1196 
  1415 
  1197   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1416   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1198   var methodMap = {
  1417   var methodMap = {
  1199     'create': 'POST',
  1418     'create': 'POST',
  1200     'update': 'PUT',
  1419     'update': 'PUT',
  1249       }
  1468       }
  1250       if (!callback) callback = this[name];
  1469       if (!callback) callback = this[name];
  1251       var router = this;
  1470       var router = this;
  1252       Backbone.history.route(route, function(fragment) {
  1471       Backbone.history.route(route, function(fragment) {
  1253         var args = router._extractParameters(route, fragment);
  1472         var args = router._extractParameters(route, fragment);
  1254         router.execute(callback, args);
  1473         if (router.execute(callback, args, name) !== false) {
  1255         router.trigger.apply(router, ['route:' + name].concat(args));
  1474           router.trigger.apply(router, ['route:' + name].concat(args));
  1256         router.trigger('route', name, args);
  1475           router.trigger('route', name, args);
  1257         Backbone.history.trigger('route', router, name, args);
  1476           Backbone.history.trigger('route', router, name, args);
       
  1477         }
  1258       });
  1478       });
  1259       return this;
  1479       return this;
  1260     },
  1480     },
  1261 
  1481 
  1262     // Execute a route handler with the provided parameters.  This is an
  1482     // Execute a route handler with the provided parameters.  This is an
  1263     // excellent place to do pre-route setup or post-route cleanup.
  1483     // excellent place to do pre-route setup or post-route cleanup.
  1264     execute: function(callback, args) {
  1484     execute: function(callback, args, name) {
  1265       if (callback) callback.apply(this, args);
  1485       if (callback) callback.apply(this, args);
  1266     },
  1486     },
  1267 
  1487 
  1268     // Simple proxy to `Backbone.history` to save a fragment into the history.
  1488     // Simple proxy to `Backbone.history` to save a fragment into the history.
  1269     navigate: function(fragment, options) {
  1489     navigate: function(fragment, options) {
  1317   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1537   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1318   // and URL fragments. If the browser supports neither (old IE, natch),
  1538   // and URL fragments. If the browser supports neither (old IE, natch),
  1319   // falls back to polling.
  1539   // falls back to polling.
  1320   var History = Backbone.History = function() {
  1540   var History = Backbone.History = function() {
  1321     this.handlers = [];
  1541     this.handlers = [];
  1322     _.bindAll(this, 'checkUrl');
  1542     this.checkUrl = _.bind(this.checkUrl, this);
  1323 
  1543 
  1324     // Ensure that `History` can be used outside of the browser.
  1544     // Ensure that `History` can be used outside of the browser.
  1325     if (typeof window !== 'undefined') {
  1545     if (typeof window !== 'undefined') {
  1326       this.location = window.location;
  1546       this.location = window.location;
  1327       this.history = window.history;
  1547       this.history = window.history;
  1332   var routeStripper = /^[#\/]|\s+$/g;
  1552   var routeStripper = /^[#\/]|\s+$/g;
  1333 
  1553 
  1334   // Cached regex for stripping leading and trailing slashes.
  1554   // Cached regex for stripping leading and trailing slashes.
  1335   var rootStripper = /^\/+|\/+$/g;
  1555   var rootStripper = /^\/+|\/+$/g;
  1336 
  1556 
  1337   // Cached regex for detecting MSIE.
       
  1338   var isExplorer = /msie [\w.]+/;
       
  1339 
       
  1340   // Cached regex for removing a trailing slash.
       
  1341   var trailingSlash = /\/$/;
       
  1342 
       
  1343   // Cached regex for stripping urls of hash.
  1557   // Cached regex for stripping urls of hash.
  1344   var pathStripper = /#.*$/;
  1558   var pathStripper = /#.*$/;
  1345 
  1559 
  1346   // Has the history handling already been started?
  1560   // Has the history handling already been started?
  1347   History.started = false;
  1561   History.started = false;
  1353     // twenty times a second.
  1567     // twenty times a second.
  1354     interval: 50,
  1568     interval: 50,
  1355 
  1569 
  1356     // Are we at the app root?
  1570     // Are we at the app root?
  1357     atRoot: function() {
  1571     atRoot: function() {
  1358       return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
  1572       var path = this.location.pathname.replace(/[^\/]$/, '$&/');
       
  1573       return path === this.root && !this.getSearch();
       
  1574     },
       
  1575 
       
  1576     // Does the pathname match the root?
       
  1577     matchRoot: function() {
       
  1578       var path = this.decodeFragment(this.location.pathname);
       
  1579       var root = path.slice(0, this.root.length - 1) + '/';
       
  1580       return root === this.root;
       
  1581     },
       
  1582 
       
  1583     // 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
       
  1585     // of an encoded parameter.
       
  1586     decodeFragment: function(fragment) {
       
  1587       return decodeURI(fragment.replace(/%25/g, '%2525'));
       
  1588     },
       
  1589 
       
  1590     // In IE6, the hash fragment and search params are incorrect if the
       
  1591     // fragment contains `?`.
       
  1592     getSearch: function() {
       
  1593       var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
       
  1594       return match ? match[0] : '';
  1359     },
  1595     },
  1360 
  1596 
  1361     // Gets the true hash value. Cannot use location.hash directly due to bug
  1597     // Gets the true hash value. Cannot use location.hash directly due to bug
  1362     // in Firefox where location.hash will always be decoded.
  1598     // in Firefox where location.hash will always be decoded.
  1363     getHash: function(window) {
  1599     getHash: function(window) {
  1364       var match = (window || this).location.href.match(/#(.*)$/);
  1600       var match = (window || this).location.href.match(/#(.*)$/);
  1365       return match ? match[1] : '';
  1601       return match ? match[1] : '';
  1366     },
  1602     },
  1367 
  1603 
  1368     // Get the cross-browser normalized URL fragment, either from the URL,
  1604     // Get the pathname and search params, without the root.
  1369     // the hash, or the override.
  1605     getPath: function() {
  1370     getFragment: function(fragment, forcePushState) {
  1606       var path = this.decodeFragment(
       
  1607         this.location.pathname + this.getSearch()
       
  1608       ).slice(this.root.length - 1);
       
  1609       return path.charAt(0) === '/' ? path.slice(1) : path;
       
  1610     },
       
  1611 
       
  1612     // Get the cross-browser normalized URL fragment from the path or hash.
       
  1613     getFragment: function(fragment) {
  1371       if (fragment == null) {
  1614       if (fragment == null) {
  1372         if (this._hasPushState || !this._wantsHashChange || forcePushState) {
  1615         if (this._usePushState || !this._wantsHashChange) {
  1373           fragment = decodeURI(this.location.pathname + this.location.search);
  1616           fragment = this.getPath();
  1374           var root = this.root.replace(trailingSlash, '');
       
  1375           if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
       
  1376         } else {
  1617         } else {
  1377           fragment = this.getHash();
  1618           fragment = this.getHash();
  1378         }
  1619         }
  1379       }
  1620       }
  1380       return fragment.replace(routeStripper, '');
  1621       return fragment.replace(routeStripper, '');
  1381     },
  1622     },
  1382 
  1623 
  1383     // Start the hash change handling, returning `true` if the current URL matches
  1624     // Start the hash change handling, returning `true` if the current URL matches
  1384     // an existing route, and `false` otherwise.
  1625     // an existing route, and `false` otherwise.
  1385     start: function(options) {
  1626     start: function(options) {
  1386       if (History.started) throw new Error("Backbone.history has already been started");
  1627       if (History.started) throw new Error('Backbone.history has already been started');
  1387       History.started = true;
  1628       History.started = true;
  1388 
  1629 
  1389       // Figure out the initial configuration. Do we need an iframe?
  1630       // Figure out the initial configuration. Do we need an iframe?
  1390       // Is pushState desired ... is it available?
  1631       // Is pushState desired ... is it available?
  1391       this.options          = _.extend({root: '/'}, this.options, options);
  1632       this.options          = _.extend({root: '/'}, this.options, options);
  1392       this.root             = this.options.root;
  1633       this.root             = this.options.root;
  1393       this._wantsHashChange = this.options.hashChange !== false;
  1634       this._wantsHashChange = this.options.hashChange !== false;
       
  1635       this._hasHashChange   = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
       
  1636       this._useHashChange   = this._wantsHashChange && this._hasHashChange;
  1394       this._wantsPushState  = !!this.options.pushState;
  1637       this._wantsPushState  = !!this.options.pushState;
  1395       this._hasPushState    = !!(this.options.pushState && this.history && this.history.pushState);
  1638       this._hasPushState    = !!(this.history && this.history.pushState);
  1396       var fragment          = this.getFragment();
  1639       this._usePushState    = this._wantsPushState && this._hasPushState;
  1397       var docMode           = document.documentMode;
  1640       this.fragment         = this.getFragment();
  1398       var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
       
  1399 
  1641 
  1400       // Normalize root to always include a leading and trailing slash.
  1642       // Normalize root to always include a leading and trailing slash.
  1401       this.root = ('/' + this.root + '/').replace(rootStripper, '/');
  1643       this.root = ('/' + this.root + '/').replace(rootStripper, '/');
  1402 
       
  1403       if (oldIE && this._wantsHashChange) {
       
  1404         var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
       
  1405         this.iframe = frame.hide().appendTo('body')[0].contentWindow;
       
  1406         this.navigate(fragment);
       
  1407       }
       
  1408 
       
  1409       // Depending on whether we're using pushState or hashes, and whether
       
  1410       // 'onhashchange' is supported, determine how we check the URL state.
       
  1411       if (this._hasPushState) {
       
  1412         Backbone.$(window).on('popstate', this.checkUrl);
       
  1413       } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
       
  1414         Backbone.$(window).on('hashchange', this.checkUrl);
       
  1415       } else if (this._wantsHashChange) {
       
  1416         this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
       
  1417       }
       
  1418 
       
  1419       // Determine if we need to change the base url, for a pushState link
       
  1420       // opened by a non-pushState browser.
       
  1421       this.fragment = fragment;
       
  1422       var loc = this.location;
       
  1423 
  1644 
  1424       // Transition from hashChange to pushState or vice versa if both are
  1645       // Transition from hashChange to pushState or vice versa if both are
  1425       // requested.
  1646       // requested.
  1426       if (this._wantsHashChange && this._wantsPushState) {
  1647       if (this._wantsHashChange && this._wantsPushState) {
  1427 
  1648 
  1428         // If we've started off with a route from a `pushState`-enabled
  1649         // If we've started off with a route from a `pushState`-enabled
  1429         // browser, but we're currently in a browser that doesn't support it...
  1650         // browser, but we're currently in a browser that doesn't support it...
  1430         if (!this._hasPushState && !this.atRoot()) {
  1651         if (!this._hasPushState && !this.atRoot()) {
  1431           this.fragment = this.getFragment(null, true);
  1652           var root = this.root.slice(0, -1) || '/';
  1432           this.location.replace(this.root + '#' + this.fragment);
  1653           this.location.replace(root + '#' + this.getPath());
  1433           // Return immediately as browser will do redirect to new url
  1654           // Return immediately as browser will do redirect to new url
  1434           return true;
  1655           return true;
  1435 
  1656 
  1436         // Or if we've started out with a hash-based route, but we're currently
  1657         // Or if we've started out with a hash-based route, but we're currently
  1437         // in a browser where it could be `pushState`-based instead...
  1658         // in a browser where it could be `pushState`-based instead...
  1438         } else if (this._hasPushState && this.atRoot() && loc.hash) {
  1659         } else if (this._hasPushState && this.atRoot()) {
  1439           this.fragment = this.getHash().replace(routeStripper, '');
  1660           this.navigate(this.getHash(), {replace: true});
  1440           this.history.replaceState({}, document.title, this.root + this.fragment);
       
  1441         }
  1661         }
  1442 
  1662 
       
  1663       }
       
  1664 
       
  1665       // Proxy an iframe to handle location events if the browser doesn't
       
  1666       // support the `hashchange` event, HTML5 history, or the user wants
       
  1667       // `hashChange` but not `pushState`.
       
  1668       if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
       
  1669         this.iframe = document.createElement('iframe');
       
  1670         this.iframe.src = 'javascript:0';
       
  1671         this.iframe.style.display = 'none';
       
  1672         this.iframe.tabIndex = -1;
       
  1673         var body = document.body;
       
  1674         // Using `appendChild` will throw on IE < 9 if the document is not ready.
       
  1675         var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
       
  1676         iWindow.document.open();
       
  1677         iWindow.document.close();
       
  1678         iWindow.location.hash = '#' + this.fragment;
       
  1679       }
       
  1680 
       
  1681       // Add a cross-platform `addEventListener` shim for older browsers.
       
  1682       var addEventListener = window.addEventListener || function (eventName, listener) {
       
  1683         return attachEvent('on' + eventName, listener);
       
  1684       };
       
  1685 
       
  1686       // Depending on whether we're using pushState or hashes, and whether
       
  1687       // 'onhashchange' is supported, determine how we check the URL state.
       
  1688       if (this._usePushState) {
       
  1689         addEventListener('popstate', this.checkUrl, false);
       
  1690       } else if (this._useHashChange && !this.iframe) {
       
  1691         addEventListener('hashchange', this.checkUrl, false);
       
  1692       } else if (this._wantsHashChange) {
       
  1693         this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
  1443       }
  1694       }
  1444 
  1695 
  1445       if (!this.options.silent) return this.loadUrl();
  1696       if (!this.options.silent) return this.loadUrl();
  1446     },
  1697     },
  1447 
  1698 
  1448     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
  1699     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
  1449     // but possibly useful for unit testing Routers.
  1700     // but possibly useful for unit testing Routers.
  1450     stop: function() {
  1701     stop: function() {
  1451       Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
  1702       // Add a cross-platform `removeEventListener` shim for older browsers.
       
  1703       var removeEventListener = window.removeEventListener || function (eventName, listener) {
       
  1704         return detachEvent('on' + eventName, listener);
       
  1705       };
       
  1706 
       
  1707       // Remove window listeners.
       
  1708       if (this._usePushState) {
       
  1709         removeEventListener('popstate', this.checkUrl, false);
       
  1710       } else if (this._useHashChange && !this.iframe) {
       
  1711         removeEventListener('hashchange', this.checkUrl, false);
       
  1712       }
       
  1713 
       
  1714       // Clean up the iframe if necessary.
       
  1715       if (this.iframe) {
       
  1716         document.body.removeChild(this.iframe);
       
  1717         this.iframe = null;
       
  1718       }
       
  1719 
       
  1720       // Some environments will throw when clearing an undefined interval.
  1452       if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
  1721       if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
  1453       History.started = false;
  1722       History.started = false;
  1454     },
  1723     },
  1455 
  1724 
  1456     // Add a route to be tested when the fragment changes. Routes added later
  1725     // Add a route to be tested when the fragment changes. Routes added later
  1461 
  1730 
  1462     // Checks the current URL to see if it has changed, and if it has,
  1731     // Checks the current URL to see if it has changed, and if it has,
  1463     // calls `loadUrl`, normalizing across the hidden iframe.
  1732     // calls `loadUrl`, normalizing across the hidden iframe.
  1464     checkUrl: function(e) {
  1733     checkUrl: function(e) {
  1465       var current = this.getFragment();
  1734       var current = this.getFragment();
       
  1735 
       
  1736       // If the user pressed the back button, the iframe's hash will have
       
  1737       // changed and we should use that for comparison.
  1466       if (current === this.fragment && this.iframe) {
  1738       if (current === this.fragment && this.iframe) {
  1467         current = this.getFragment(this.getHash(this.iframe));
  1739         current = this.getHash(this.iframe.contentWindow);
  1468       }
  1740       }
       
  1741 
  1469       if (current === this.fragment) return false;
  1742       if (current === this.fragment) return false;
  1470       if (this.iframe) this.navigate(current);
  1743       if (this.iframe) this.navigate(current);
  1471       this.loadUrl();
  1744       this.loadUrl();
  1472     },
  1745     },
  1473 
  1746 
  1474     // Attempt to load the current URL fragment. If a route succeeds with a
  1747     // Attempt to load the current URL fragment. If a route succeeds with a
  1475     // match, returns `true`. If no defined routes matches the fragment,
  1748     // match, returns `true`. If no defined routes matches the fragment,
  1476     // returns `false`.
  1749     // returns `false`.
  1477     loadUrl: function(fragment) {
  1750     loadUrl: function(fragment) {
       
  1751       // If the root doesn't match, no routes can match either.
       
  1752       if (!this.matchRoot()) return false;
  1478       fragment = this.fragment = this.getFragment(fragment);
  1753       fragment = this.fragment = this.getFragment(fragment);
  1479       return _.any(this.handlers, function(handler) {
  1754       return _.some(this.handlers, function(handler) {
  1480         if (handler.route.test(fragment)) {
  1755         if (handler.route.test(fragment)) {
  1481           handler.callback(fragment);
  1756           handler.callback(fragment);
  1482           return true;
  1757           return true;
  1483         }
  1758         }
  1484       });
  1759       });
  1493     // you wish to modify the current URL without adding an entry to the history.
  1768     // you wish to modify the current URL without adding an entry to the history.
  1494     navigate: function(fragment, options) {
  1769     navigate: function(fragment, options) {
  1495       if (!History.started) return false;
  1770       if (!History.started) return false;
  1496       if (!options || options === true) options = {trigger: !!options};
  1771       if (!options || options === true) options = {trigger: !!options};
  1497 
  1772 
  1498       var url = this.root + (fragment = this.getFragment(fragment || ''));
  1773       // Normalize the fragment.
  1499 
  1774       fragment = this.getFragment(fragment || '');
  1500       // Strip the hash for matching.
  1775 
  1501       fragment = fragment.replace(pathStripper, '');
  1776       // Don't include a trailing slash on the root.
       
  1777       var root = this.root;
       
  1778       if (fragment === '' || fragment.charAt(0) === '?') {
       
  1779         root = root.slice(0, -1) || '/';
       
  1780       }
       
  1781       var url = root + fragment;
       
  1782 
       
  1783       // Strip the hash and decode for matching.
       
  1784       fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
  1502 
  1785 
  1503       if (this.fragment === fragment) return;
  1786       if (this.fragment === fragment) return;
  1504       this.fragment = fragment;
  1787       this.fragment = fragment;
  1505 
  1788 
  1506       // Don't include a trailing slash on the root.
       
  1507       if (fragment === '' && url !== '/') url = url.slice(0, -1);
       
  1508 
       
  1509       // If pushState is available, we use it to set the fragment as a real URL.
  1789       // If pushState is available, we use it to set the fragment as a real URL.
  1510       if (this._hasPushState) {
  1790       if (this._usePushState) {
  1511         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1791         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1512 
  1792 
  1513       // If hash changes haven't been explicitly disabled, update the hash
  1793       // If hash changes haven't been explicitly disabled, update the hash
  1514       // fragment to store history.
  1794       // fragment to store history.
  1515       } else if (this._wantsHashChange) {
  1795       } else if (this._wantsHashChange) {
  1516         this._updateHash(this.location, fragment, options.replace);
  1796         this._updateHash(this.location, fragment, options.replace);
  1517         if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
  1797         if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) {
       
  1798           var iWindow = this.iframe.contentWindow;
       
  1799 
  1518           // Opening and closing the iframe tricks IE7 and earlier to push a
  1800           // Opening and closing the iframe tricks IE7 and earlier to push a
  1519           // history entry on hash-tag change.  When replace is true, we don't
  1801           // history entry on hash-tag change.  When replace is true, we don't
  1520           // want this.
  1802           // want this.
  1521           if(!options.replace) this.iframe.document.open().close();
  1803           if (!options.replace) {
  1522           this._updateHash(this.iframe.location, fragment, options.replace);
  1804             iWindow.document.open();
       
  1805             iWindow.document.close();
       
  1806           }
       
  1807 
       
  1808           this._updateHash(iWindow.location, fragment, options.replace);
  1523         }
  1809         }
  1524 
  1810 
  1525       // If you've told us that you explicitly don't want fallback hashchange-
  1811       // If you've told us that you explicitly don't want fallback hashchange-
  1526       // based history, then `navigate` becomes a page refresh.
  1812       // based history, then `navigate` becomes a page refresh.
  1527       } else {
  1813       } else {
  1548   Backbone.history = new History;
  1834   Backbone.history = new History;
  1549 
  1835 
  1550   // Helpers
  1836   // Helpers
  1551   // -------
  1837   // -------
  1552 
  1838 
  1553   // Helper function to correctly set up the prototype chain, for subclasses.
  1839   // Helper function to correctly set up the prototype chain for subclasses.
  1554   // Similar to `goog.inherits`, but uses a hash of prototype properties and
  1840   // Similar to `goog.inherits`, but uses a hash of prototype properties and
  1555   // class properties to be extended.
  1841   // class properties to be extended.
  1556   var extend = function(protoProps, staticProps) {
  1842   var extend = function(protoProps, staticProps) {
  1557     var parent = this;
  1843     var parent = this;
  1558     var child;
  1844     var child;
  1559 
  1845 
  1560     // The constructor function for the new subclass is either defined by you
  1846     // The constructor function for the new subclass is either defined by you
  1561     // (the "constructor" property in your `extend` definition), or defaulted
  1847     // (the "constructor" property in your `extend` definition), or defaulted
  1562     // by us to simply call the parent's constructor.
  1848     // by us to simply call the parent constructor.
  1563     if (protoProps && _.has(protoProps, 'constructor')) {
  1849     if (protoProps && _.has(protoProps, 'constructor')) {
  1564       child = protoProps.constructor;
  1850       child = protoProps.constructor;
  1565     } else {
  1851     } else {
  1566       child = function(){ return parent.apply(this, arguments); };
  1852       child = function(){ return parent.apply(this, arguments); };
  1567     }
  1853     }
  1568 
  1854 
  1569     // Add static properties to the constructor function, if supplied.
  1855     // Add static properties to the constructor function, if supplied.
  1570     _.extend(child, parent, staticProps);
  1856     _.extend(child, parent, staticProps);
  1571 
  1857 
  1572     // Set the prototype chain to inherit from `parent`, without calling
  1858     // Set the prototype chain to inherit from `parent`, without calling
  1573     // `parent`'s constructor function.
  1859     // `parent` constructor function.
  1574     var Surrogate = function(){ this.constructor = child; };
  1860     var Surrogate = function(){ this.constructor = child; };
  1575     Surrogate.prototype = parent.prototype;
  1861     Surrogate.prototype = parent.prototype;
  1576     child.prototype = new Surrogate;
  1862     child.prototype = new Surrogate;
  1577 
  1863 
  1578     // Add prototype properties (instance properties) to the subclass,
  1864     // Add prototype properties (instance properties) to the subclass,
  1596 
  1882 
  1597   // Wrap an optional error callback with a fallback error event.
  1883   // Wrap an optional error callback with a fallback error event.
  1598   var wrapError = function(model, options) {
  1884   var wrapError = function(model, options) {
  1599     var error = options.error;
  1885     var error = options.error;
  1600     options.error = function(resp) {
  1886     options.error = function(resp) {
  1601       if (error) error(model, resp, options);
  1887       if (error) error.call(options.context, model, resp, options);
  1602       model.trigger('error', model, resp, options);
  1888       model.trigger('error', model, resp, options);
  1603     };
  1889     };
  1604   };
  1890   };
  1605 
  1891 
  1606   return Backbone;
  1892   return Backbone;