xosDeveloper view marionette wip
[plstackapi.git] / planetstack / core / xoslib / static / js / backbone.js
1 //     Backbone.js 1.1.2
2
3 //     (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
4 //     Backbone may be freely distributed under the MIT license.
5 //     For all details and documentation:
6 //     http://backbonejs.org
7
8 (function(root, factory) {
9
10   // Set up Backbone appropriately for the environment. Start with AMD.
11   if (typeof define === 'function' && define.amd) {
12     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
13       // Export global even in AMD case in case this script is loaded with
14       // others that may still expect a global Backbone.
15       root.Backbone = factory(root, exports, _, $);
16     });
17
18   // Next for Node.js or CommonJS. jQuery may not be needed as a module.
19   } else if (typeof exports !== 'undefined') {
20     var _ = require('underscore');
21     factory(root, exports, _);
22
23   // Finally, as a browser global.
24   } else {
25     root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
26   }
27
28 }(this, function(root, Backbone, _, $) {
29
30   // Initial Setup
31   // -------------
32
33   // Save the previous value of the `Backbone` variable, so that it can be
34   // restored later on, if `noConflict` is used.
35   var previousBackbone = root.Backbone;
36
37   // Create local references to array methods we'll want to use later.
38   var array = [];
39   var push = array.push;
40   var slice = array.slice;
41   var splice = array.splice;
42
43   // Current version of the library. Keep in sync with `package.json`.
44   Backbone.VERSION = '1.1.2';
45
46   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
47   // the `$` variable.
48   Backbone.$ = $;
49
50   // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
51   // to its previous owner. Returns a reference to this Backbone object.
52   Backbone.noConflict = function() {
53     root.Backbone = previousBackbone;
54     return this;
55   };
56
57   // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
58   // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
59   // set a `X-Http-Method-Override` header.
60   Backbone.emulateHTTP = false;
61
62   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
63   // `application/json` requests ... will encode the body as
64   // `application/x-www-form-urlencoded` instead and will send the model in a
65   // form param named `model`.
66   Backbone.emulateJSON = false;
67
68   // Backbone.Events
69   // ---------------
70
71   // 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
73   // functions to an event; `trigger`-ing an event fires all callbacks in
74   // succession.
75   //
76   //     var object = {};
77   //     _.extend(object, Backbone.Events);
78   //     object.on('expand', function(){ alert('expanded'); });
79   //     object.trigger('expand');
80   //
81   var Events = Backbone.Events = {
82
83     // Bind an event to a `callback` function. Passing `"all"` will bind
84     // the callback to all events fired.
85     on: function(name, callback, context) {
86       if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
87       this._events || (this._events = {});
88       var events = this._events[name] || (this._events[name] = []);
89       events.push({callback: callback, context: context, ctx: context || this});
90       return this;
91     },
92
93     // Bind an event to only be triggered a single time. After the first time
94     // the callback is invoked, it will be removed.
95     once: function(name, callback, context) {
96       if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
97       var self = this;
98       var once = _.once(function() {
99         self.off(name, once);
100         callback.apply(this, arguments);
101       });
102       once._callback = callback;
103       return this.on(name, once, context);
104     },
105
106     // Remove one or many callbacks. If `context` is null, removes all
107     // callbacks with that function. If `callback` is null, removes all
108     // callbacks for the event. If `name` is null, removes all bound
109     // callbacks for all events.
110     off: function(name, callback, context) {
111       var retain, ev, events, names, i, l, j, k;
112       if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
113       if (!name && !callback && !context) {
114         this._events = void 0;
115         return this;
116       }
117       names = name ? [name] : _.keys(this._events);
118       for (i = 0, l = names.length; i < l; i++) {
119         name = names[i];
120         if (events = this._events[name]) {
121           this._events[name] = retain = [];
122           if (callback || context) {
123             for (j = 0, k = events.length; j < k; j++) {
124               ev = events[j];
125               if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
126                   (context && context !== ev.context)) {
127                 retain.push(ev);
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);
149       if (allEvents) triggerEvents(allEvents, arguments);
150       return this;
151     },
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   };
199
200   // A difficult-to-believe, but optimized internal dispatch function for
201   // triggering events. Tries to keep the usual cases speedy (most internal
202   // Backbone events have 3 arguments).
203   var triggerEvents = function(events, args) {
204     var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
205     switch (args.length) {
206       case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
207       case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
208       case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
209       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;
211     }
212   };
213
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.
231   Events.bind   = Events.on;
232   Events.unbind = Events.off;
233
234   // Allow the `Backbone` object to serve as a global event bus, for folks who
235   // want global "pubsub" in a convenient place.
236   _.extend(Backbone, Events);
237
238   // Backbone.Model
239   // --------------
240
241   // Backbone **Models** are the basic data object in the framework --
242   // frequently representing a row in a table in a database on your server.
243   // A discrete chunk of data and a bunch of useful, related methods for
244   // performing computations and transformations on that data.
245
246   // Create a new model with the specified attributes. A client id (`cid`)
247   // is automatically generated and assigned for you.
248   var Model = Backbone.Model = function(attributes, options) {
249     var attrs = attributes || {};
250     options || (options = {});
251     this.cid = _.uniqueId('c');
252     this.attributes = {};
253     if (options.collection) this.collection = options.collection;
254     if (options.parse) attrs = this.parse(attrs, options) || {};
255     attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
256     this.set(attrs, options);
257     this.changed = {};
258     this.initialize.apply(this, arguments);
259   };
260
261   // Attach all inheritable methods to the Model prototype.
262   _.extend(Model.prototype, Events, {
263
264     // A hash of attributes whose current and previous value differ.
265     changed: null,
266
267     // The value returned during the last failed validation.
268     validationError: null,
269
270     // The default name for the JSON `id` attribute is `"id"`. MongoDB and
271     // CouchDB users may want to set this to `"_id"`.
272     idAttribute: 'id',
273
274     // Initialize is an empty function by default. Override it with your own
275     // initialization logic.
276     initialize: function(){},
277
278     // Return a copy of the model's `attributes` object.
279     toJSON: function(options) {
280       return _.clone(this.attributes);
281     },
282
283     // Proxy `Backbone.sync` by default -- but override this if you need
284     // custom syncing semantics for *this* particular model.
285     sync: function() {
286       return Backbone.sync.apply(this, arguments);
287     },
288
289     // Get the value of an attribute.
290     get: function(attr) {
291       return this.attributes[attr];
292     },
293
294     // Get the HTML-escaped value of an attribute.
295     escape: function(attr) {
296       return _.escape(this.get(attr));
297     },
298
299     // Returns `true` if the attribute contains a value that is not null
300     // or undefined.
301     has: function(attr) {
302       return this.get(attr) != null;
303     },
304
305     // 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
307     // anyone who needs to know about the change in state. The heart of the beast.
308     set: function(key, val, options) {
309       var attr, attrs, unset, changes, silent, changing, prev, current;
310       if (key == null) return this;
311
312       // Handle both `"key", value` and `{key: value}` -style arguments.
313       if (typeof key === 'object') {
314         attrs = key;
315         options = val;
316       } else {
317         (attrs = {})[key] = val;
318       }
319
320       options || (options = {});
321
322       // Run validation.
323       if (!this._validate(attrs, options)) return false;
324
325       // Extract attributes and options.
326       unset           = options.unset;
327       silent          = options.silent;
328       changes         = [];
329       changing        = this._changing;
330       this._changing  = true;
331
332       if (!changing) {
333         this._previousAttributes = _.clone(this.attributes);
334         this.changed = {};
335       }
336       current = this.attributes, prev = this._previousAttributes;
337
338       // Check for changes of `id`.
339       if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
340
341       // For each `set` attribute, update or delete the current value.
342       for (attr in attrs) {
343         val = attrs[attr];
344         if (!_.isEqual(current[attr], val)) changes.push(attr);
345         if (!_.isEqual(prev[attr], val)) {
346           this.changed[attr] = val;
347         } else {
348           delete this.changed[attr];
349         }
350         unset ? delete current[attr] : current[attr] = val;
351       }
352
353       // Trigger all relevant attribute changes.
354       if (!silent) {
355         if (changes.length) this._pending = options;
356         for (var i = 0, l = changes.length; i < l; i++) {
357           this.trigger('change:' + changes[i], this, current[changes[i]], options);
358         }
359       }
360
361       // You might be wondering why there's a `while` loop here. Changes can
362       // be recursively nested within `"change"` events.
363       if (changing) return this;
364       if (!silent) {
365         while (this._pending) {
366           options = this._pending;
367           this._pending = false;
368           this.trigger('change', this, options);
369         }
370       }
371       this._pending = false;
372       this._changing = false;
373       return this;
374     },
375
376     // Remove an attribute from the model, firing `"change"`. `unset` is a noop
377     // if the attribute doesn't exist.
378     unset: function(attr, options) {
379       return this.set(attr, void 0, _.extend({}, options, {unset: true}));
380     },
381
382     // Clear all attributes on the model, firing `"change"`.
383     clear: function(options) {
384       var attrs = {};
385       for (var key in this.attributes) attrs[key] = void 0;
386       return this.set(attrs, _.extend({}, options, {unset: true}));
387     },
388
389     // Determine if the model has changed since the last `"change"` event.
390     // If you specify an attribute name, determine if that attribute has changed.
391     hasChanged: function(attr) {
392       if (attr == null) return !_.isEmpty(this.changed);
393       return _.has(this.changed, attr);
394     },
395
396     // Return an object containing all the attributes that have changed, or
397     // false if there are no changed attributes. Useful for determining what
398     // parts of a view need to be updated and/or what attributes need to be
399     // persisted to the server. Unset attributes will be set to undefined.
400     // You can also pass an attributes object to diff against the model,
401     // determining if there *would be* a change.
402     changedAttributes: function(diff) {
403       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
404       var val, changed = false;
405       var old = this._changing ? this._previousAttributes : this.attributes;
406       for (var attr in diff) {
407         if (_.isEqual(old[attr], (val = diff[attr]))) continue;
408         (changed || (changed = {}))[attr] = val;
409       }
410       return changed;
411     },
412
413     // Get the previous value of an attribute, recorded at the time the last
414     // `"change"` event was fired.
415     previous: function(attr) {
416       if (attr == null || !this._previousAttributes) return null;
417       return this._previousAttributes[attr];
418     },
419
420     // Get all of the attributes of the model at the time of the previous
421     // `"change"` event.
422     previousAttributes: function() {
423       return _.clone(this._previousAttributes);
424     },
425
426     // Fetch the model from the server. If the server's representation of the
427     // model differs from its current attributes, they will be overridden,
428     // triggering a `"change"` event.
429     fetch: function(options) {
430       options = options ? _.clone(options) : {};
431       if (options.parse === void 0) options.parse = true;
432       var model = this;
433       var success = options.success;
434       options.success = function(resp) {
435         if (!model.set(model.parse(resp, options), options)) return false;
436         if (success) success(model, resp, options);
437         model.trigger('sync', model, resp, options);
438       };
439       wrapError(this, options);
440       return this.sync('read', this, options);
441     },
442
443     // 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
445     // state will be `set` again.
446     save: function(key, val, options) {
447       var attrs, method, xhr, attributes = this.attributes;
448
449       // Handle both `"key", value` and `{key: value}` -style arguments.
450       if (key == null || typeof key === 'object') {
451         attrs = key;
452         options = val;
453       } else {
454         (attrs = {})[key] = val;
455       }
456
457       options = _.extend({validate: true}, options);
458
459       // If we're not waiting and attributes exist, save acts as
460       // `set(attr).save(null, opts)` with validation. Otherwise, check if
461       // the model will be valid when the attributes, if any, are set.
462       if (attrs && !options.wait) {
463         if (!this.set(attrs, options)) return false;
464       } else {
465         if (!this._validate(attrs, options)) return false;
466       }
467
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)
474       // updated with the server-side state.
475       if (options.parse === void 0) options.parse = true;
476       var model = this;
477       var success = options.success;
478       options.success = function(resp) {
479         // Ensure attributes are restored during synchronous saves.
480         model.attributes = attributes;
481         var serverAttrs = model.parse(resp, options);
482         if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
483         if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
484           return false;
485         }
486         if (success) success(model, resp, options);
487         model.trigger('sync', model, resp, options);
488       };
489       wrapError(this, options);
490
491       method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
492       if (method === 'patch') options.attrs = attrs;
493       xhr = this.sync(method, this, options);
494
495       // Restore attributes.
496       if (attrs && options.wait) this.attributes = attributes;
497
498       return xhr;
499     },
500
501     // Destroy this model on the server if it was already persisted.
502     // Optimistically removes the model from its collection, if it has one.
503     // If `wait: true` is passed, waits for the server to respond before removal.
504     destroy: function(options) {
505       options = options ? _.clone(options) : {};
506       var model = this;
507       var success = options.success;
508
509       var destroy = function() {
510         model.trigger('destroy', model, model.collection, options);
511       };
512
513       options.success = function(resp) {
514         if (options.wait || model.isNew()) destroy();
515         if (success) success(model, resp, options);
516         if (!model.isNew()) model.trigger('sync', model, resp, options);
517       };
518
519       if (this.isNew()) {
520         options.success();
521         return false;
522       }
523       wrapError(this, options);
524
525       var xhr = this.sync('delete', this, options);
526       if (!options.wait) destroy();
527       return xhr;
528     },
529
530     // 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
532     // that will be called.
533     url: function() {
534       var base =
535         _.result(this, 'urlRoot') ||
536         _.result(this.collection, 'url') ||
537         urlError();
538       if (this.isNew()) return base;
539       return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
540     },
541
542     // **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.
544     parse: function(resp, options) {
545       return resp;
546     },
547
548     // Create a new model with identical attributes to this one.
549     clone: function() {
550       return new this.constructor(this.attributes);
551     },
552
553     // A model is new if it has never been saved to the server, and lacks an id.
554     isNew: function() {
555       return !this.has(this.idAttribute);
556     },
557
558     // Check if the model is currently in a valid state.
559     isValid: function(options) {
560       return this._validate({}, _.extend(options || {}, { validate: true }));
561     },
562
563     // Run validation against the next complete set of model attributes,
564     // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
565     _validate: function(attrs, options) {
566       if (!options.validate || !this.validate) return true;
567       attrs = _.extend({}, this.attributes, attrs);
568       var error = this.validationError = this.validate(attrs, options) || null;
569       if (!error) return true;
570       this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
571       return false;
572     }
573
574   });
575
576   // Underscore methods that we want to implement on the Model.
577   var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
578
579   // Mix in each Underscore method as a proxy to `Model#attributes`.
580   _.each(modelMethods, function(method) {
581     Model.prototype[method] = function() {
582       var args = slice.call(arguments);
583       args.unshift(this.attributes);
584       return _[method].apply(_, args);
585     };
586   });
587
588   // Backbone.Collection
589   // -------------------
590
591   // 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
593   // 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
595   // belonging to this particular author, and so on. Collections maintain
596   // indexes of their models, both in order, and for lookup by `id`.
597
598   // Create a new **Collection**, perhaps to contain a specific type of `model`.
599   // If a `comparator` is specified, the Collection will maintain
600   // its models in sort order, as they're added and removed.
601   var Collection = Backbone.Collection = function(models, options) {
602     options || (options = {});
603     if (options.model) this.model = options.model;
604     if (options.comparator !== void 0) this.comparator = options.comparator;
605     this._reset();
606     this.initialize.apply(this, arguments);
607     if (models) this.reset(models, _.extend({silent: true}, options));
608   };
609
610   // Default options for `Collection#set`.
611   var setOptions = {add: true, remove: true, merge: true};
612   var addOptions = {add: true, remove: false};
613
614   // Define the Collection's inheritable methods.
615   _.extend(Collection.prototype, Events, {
616
617     // The default model for a collection is just a **Backbone.Model**.
618     // This should be overridden in most cases.
619     model: Model,
620
621     // Initialize is an empty function by default. Override it with your own
622     // initialization logic.
623     initialize: function(){},
624
625     // The JSON representation of a Collection is an array of the
626     // models' attributes.
627     toJSON: function(options) {
628       return this.map(function(model){ return model.toJSON(options); });
629     },
630
631     // Proxy `Backbone.sync` by default.
632     sync: function() {
633       return Backbone.sync.apply(this, arguments);
634     },
635
636     // Add a model, or list of models to the set.
637     add: function(models, options) {
638       return this.set(models, _.extend({merge: false}, options, addOptions));
639     },
640
641     // Remove a model, or a list of models from the set.
642     remove: function(models, options) {
643       var singular = !_.isArray(models);
644       models = singular ? [models] : _.clone(models);
645       options || (options = {});
646       var i, l, index, model;
647       for (i = 0, l = models.length; i < l; i++) {
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     },
663
664     // 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
666     // already exist in the collection, as necessary. Similar to **Model#set**,
667     // the core operation for updating the data contained by the collection.
668     set: function(models, options) {
669       options = _.defaults({}, options, setOptions);
670       if (options.parse) models = this.parse(models, options);
671       var singular = !_.isArray(models);
672       models = singular ? (models ? [models] : []) : _.clone(models);
673       var i, l, id, model, attrs, existing, sort;
674       var at = options.at;
675       var targetModel = this.model;
676       var sortable = this.comparator && (at == null) && options.sort !== false;
677       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
682       // Turn bare objects into model references, and prevent invalid models
683       // from being added.
684       for (i = 0, l = models.length; i < l; i++) {
685         attrs = models[i] || {};
686         if (attrs instanceof Model) {
687           id = model = attrs;
688         } else {
689           id = attrs[targetModel.prototype.idAttribute || 'id'];
690         }
691
692         // If a duplicate is found, prevent it from being added and
693         // optionally merge it into the existing model.
694         if (existing = this.get(id)) {
695           if (remove) modelMap[existing.cid] = true;
696           if (merge) {
697             attrs = attrs === model ? model.attributes : attrs;
698             if (options.parse) attrs = existing.parse(attrs, options);
699             existing.set(attrs, options);
700             if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
701           }
702           models[i] = existing;
703
704         // If this is a new, valid model, push it to the `toAdd` list.
705         } else if (add) {
706           model = models[i] = this._prepareModel(attrs, options);
707           if (!model) continue;
708           toAdd.push(model);
709           this._addReference(model, options);
710         }
711
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           }
740         }
741       }
742
743       // Silently sort the collection if appropriate.
744       if (sort) this.sort({silent: true});
745
746       // Unless silenced, it's time to fire all appropriate add/sort events.
747       if (!options.silent) {
748         for (i = 0, l = toAdd.length; i < l; i++) {
749           (model = toAdd[i]).trigger('add', model, this, options);
750         }
751         if (sort || (order && order.length)) this.trigger('sort', this, options);
752       }
753
754       // Return the added (or merged) model (or models).
755       return singular ? models[0] : models;
756     },
757
758     // 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
760     // any granular `add` or `remove` events. Fires `reset` when finished.
761     // Useful for bulk operations and optimizations.
762     reset: function(models, options) {
763       options || (options = {});
764       for (var i = 0, l = this.models.length; i < l; i++) {
765         this._removeReference(this.models[i], options);
766       }
767       options.previousModels = this.models;
768       this._reset();
769       models = this.add(models, _.extend({silent: true}, options));
770       if (!options.silent) this.trigger('reset', this, options);
771       return models;
772     },
773
774     // Add a model to the end of the collection.
775     push: function(model, options) {
776       return this.add(model, _.extend({at: this.length}, options));
777     },
778
779     // Remove a model from the end of the collection.
780     pop: function(options) {
781       var model = this.at(this.length - 1);
782       this.remove(model, options);
783       return model;
784     },
785
786     // Add a model to the beginning of the collection.
787     unshift: function(model, options) {
788       return this.add(model, _.extend({at: 0}, options));
789     },
790
791     // Remove a model from the beginning of the collection.
792     shift: function(options) {
793       var model = this.at(0);
794       this.remove(model, options);
795       return model;
796     },
797
798     // Slice out a sub-array of models from the collection.
799     slice: function() {
800       return slice.apply(this.models, arguments);
801     },
802
803     // Get a model from the set by id.
804     get: function(obj) {
805       if (obj == null) return void 0;
806       return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
807     },
808
809     // Get the model at the given index.
810     at: function(index) {
811       return this.models[index];
812     },
813
814     // Return models with matching attributes. Useful for simple cases of
815     // `filter`.
816     where: function(attrs, first) {
817       if (_.isEmpty(attrs)) return first ? void 0 : [];
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     },
825
826     // Return the first model with matching attributes. Useful for simple cases
827     // of `find`.
828     findWhere: function(attrs) {
829       return this.where(attrs, true);
830     },
831
832     // 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
834     // is added.
835     sort: function(options) {
836       if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
837       options || (options = {});
838
839       // Run sort based on type of `comparator`.
840       if (_.isString(this.comparator) || this.comparator.length === 1) {
841         this.models = this.sortBy(this.comparator, this);
842       } else {
843         this.models.sort(_.bind(this.comparator, this));
844       }
845
846       if (!options.silent) this.trigger('sort', this, options);
847       return this;
848     },
849
850     // Pluck an attribute from each model in the collection.
851     pluck: function(attr) {
852       return _.invoke(this.models, 'get', attr);
853     },
854
855     // Fetch the default set of models for this collection, resetting the
856     // collection when they arrive. If `reset: true` is passed, the response
857     // data will be passed through the `reset` method instead of `set`.
858     fetch: function(options) {
859       options = options ? _.clone(options) : {};
860       if (options.parse === void 0) options.parse = true;
861       var success = options.success;
862       var collection = this;
863       options.success = function(resp) {
864         var method = options.reset ? 'reset' : 'set';
865         collection[method](resp, options);
866         if (success) success(collection, resp, options);
867         collection.trigger('sync', collection, resp, options);
868       };
869       wrapError(this, options);
870       return this.sync('read', this, options);
871     },
872
873     // 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
875     // wait for the server to agree.
876     create: function(model, options) {
877       options = options ? _.clone(options) : {};
878       if (!(model = this._prepareModel(model, options))) return false;
879       if (!options.wait) this.add(model, options);
880       var collection = this;
881       var success = options.success;
882       options.success = function(model, resp) {
883         if (options.wait) collection.add(model, options);
884         if (success) success(model, resp, options);
885       };
886       model.save(null, options);
887       return model;
888     },
889
890     // **parse** converts a response into a list of models to be added to the
891     // collection. The default implementation is just to pass it through.
892     parse: function(resp, options) {
893       return resp;
894     },
895
896     // Create a new collection with an identical list of models as this one.
897     clone: function() {
898       return new this.constructor(this.models);
899     },
900
901     // Private method to reset all internal state. Called when the collection
902     // is first initialized or reset.
903     _reset: function() {
904       this.length = 0;
905       this.models = [];
906       this._byId  = {};
907     },
908
909     // Prepare a hash of attributes (or other model) to be added to this
910     // collection.
911     _prepareModel: function(attrs, options) {
912       if (attrs instanceof Model) return attrs;
913       options = options ? _.clone(options) : {};
914       options.collection = this;
915       var model = new this.model(attrs, options);
916       if (!model.validationError) return model;
917       this.trigger('invalid', this, model.validationError, options);
918       return false;
919     },
920
921     // Internal method to create a model's ties to a collection.
922     _addReference: function(model, options) {
923       this._byId[model.cid] = model;
924       if (model.id != null) this._byId[model.id] = model;
925       if (!model.collection) model.collection = this;
926       model.on('all', this._onModelEvent, this);
927     },
928
929     // Internal method to sever a model's ties to a collection.
930     _removeReference: function(model, options) {
931       if (this === model.collection) delete model.collection;
932       model.off('all', this._onModelEvent, this);
933     },
934
935     // Internal method called every time a model in the set fires an event.
936     // Sets need to update their indexes when models change ids. All other
937     // events simply proxy through. "add" and "remove" events that originate
938     // in other collections are ignored.
939     _onModelEvent: function(event, model, collection, options) {
940       if ((event === 'add' || event === 'remove') && collection !== this) return;
941       if (event === 'destroy') this.remove(model, options);
942       if (model && event === 'change:' + model.idAttribute) {
943         delete this._byId[model.previous(model.idAttribute)];
944         if (model.id != null) this._byId[model.id] = model;
945       }
946       this.trigger.apply(this, arguments);
947     }
948
949   });
950
951   // Underscore methods that we want to implement on the Collection.
952   // 90% of the core usefulness of Backbone Collections is actually implemented
953   // right here:
954   var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
955     'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
956     'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
957     'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
958     'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
959     'lastIndexOf', 'isEmpty', 'chain', 'sample'];
960
961   // Mix in each Underscore method as a proxy to `Collection#models`.
962   _.each(methods, function(method) {
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
983   // Backbone.View
984   // -------------
985
986   // Backbone Views are almost more convention than they are actual code. A View
987   // is simply a JavaScript object that represents a logical chunk of UI in the
988   // DOM. This might be a single item, an entire list, a sidebar or panel, or
989   // even the surrounding frame which wraps your whole app. Defining a chunk of
990   // UI as a **View** allows you to define your DOM events declaratively, without
991   // having to worry about render order ... and makes it easy for the view to
992   // react to specific changes in the state of your models.
993
994   // Creating a Backbone.View creates its initial element outside of the DOM,
995   // if an existing element is not provided...
996   var View = Backbone.View = function(options) {
997     this.cid = _.uniqueId('view');
998     options || (options = {});
999     _.extend(this, _.pick(options, viewOptions));
1000     this._ensureElement();
1001     this.initialize.apply(this, arguments);
1002     this.delegateEvents();
1003   };
1004
1005   // Cached regex to split keys for `delegate`.
1006   var delegateEventSplitter = /^(\S+)\s*(.*)$/;
1007
1008   // List of view options to be merged as properties.
1009   var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
1010
1011   // Set up all inheritable **Backbone.View** properties and methods.
1012   _.extend(View.prototype, Events, {
1013
1014     // The default `tagName` of a View's element is `"div"`.
1015     tagName: 'div',
1016
1017     // jQuery delegate for element lookup, scoped to DOM elements within the
1018     // current view. This should be preferred to global lookups where possible.
1019     $: function(selector) {
1020       return this.$el.find(selector);
1021     },
1022
1023     // Initialize is an empty function by default. Override it with your own
1024     // initialization logic.
1025     initialize: function(){},
1026
1027     // **render** is the core function that your view should override, in order
1028     // to populate its element (`this.el`), with the appropriate HTML. The
1029     // convention is for **render** to always return `this`.
1030     render: function() {
1031       return this;
1032     },
1033
1034     // Remove this view by taking the element out of the DOM, and removing any
1035     // applicable Backbone.Events listeners.
1036     remove: function() {
1037       this.$el.remove();
1038       this.stopListening();
1039       return this;
1040     },
1041
1042     // Change the view's element (`this.el` property), including event
1043     // re-delegation.
1044     setElement: function(element, delegate) {
1045       if (this.$el) this.undelegateEvents();
1046       this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
1047       this.el = this.$el[0];
1048       if (delegate !== false) this.delegateEvents();
1049       return this;
1050     },
1051
1052     // Set callbacks, where `this.events` is a hash of
1053     //
1054     // *{"event selector": "callback"}*
1055     //
1056     //     {
1057     //       'mousedown .title':  'edit',
1058     //       'click .button':     'save',
1059     //       'click .open':       function(e) { ... }
1060     //     }
1061     //
1062     // pairs. Callbacks will be bound to the view, with `this` set properly.
1063     // Uses event delegation for efficiency.
1064     // 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) {
1068       if (!(events || (events = _.result(this, 'events')))) return this;
1069       this.undelegateEvents();
1070       for (var key in events) {
1071         var method = events[key];
1072         if (!_.isFunction(method)) method = this[events[key]];
1073         if (!method) continue;
1074
1075         var match = key.match(delegateEventSplitter);
1076         var eventName = match[1], selector = match[2];
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       }
1085       return this;
1086     },
1087
1088     // Clears all callbacks previously bound to the view with `delegateEvents`.
1089     // 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.
1091     undelegateEvents: function() {
1092       this.$el.off('.delegateEvents' + this.cid);
1093       return this;
1094     },
1095
1096     // Ensure that the View has a DOM element to render into.
1097     // If `this.el` is a string, pass it through `$()`, take the first
1098     // matching element, and re-assign it to `el`. Otherwise, create
1099     // an element from the `id`, `className` and `tagName` properties.
1100     _ensureElement: function() {
1101       if (!this.el) {
1102         var attrs = _.extend({}, _.result(this, 'attributes'));
1103         if (this.id) attrs.id = _.result(this, 'id');
1104         if (this.className) attrs['class'] = _.result(this, 'className');
1105         var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
1106         this.setElement($el, false);
1107       } else {
1108         this.setElement(_.result(this, 'el'), false);
1109       }
1110     }
1111
1112   });
1113
1114   // Backbone.sync
1115   // -------------
1116
1117   // Override this function to change the manner in which Backbone persists
1118   // models to the server. You will be passed the type of request, and the
1119   // model in question. By default, makes a RESTful Ajax request
1120   // to the model's `url()`. Some possible customizations could be:
1121   //
1122   // * Use `setTimeout` to batch rapid-fire updates into a single request.
1123   // * Send up the models as XML instead of JSON.
1124   // * Persist models via WebSockets instead of Ajax.
1125   //
1126   // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
1127   // as `POST`, with a `_method` parameter containing the true HTTP method,
1128   // as well as all requests with the body as `application/x-www-form-urlencoded`
1129   // instead of `application/json` with the model in a param named `model`.
1130   // Useful when interfacing with server-side languages like **PHP** that make
1131   // it difficult to read the body of `PUT` requests.
1132   Backbone.sync = function(method, model, options) {
1133     var type = methodMap[method];
1134
1135     // Default options, unless specified.
1136     _.defaults(options || (options = {}), {
1137       emulateHTTP: Backbone.emulateHTTP,
1138       emulateJSON: Backbone.emulateJSON
1139     });
1140
1141     // Default JSON-request options.
1142     var params = {type: type, dataType: 'json'};
1143
1144     // Ensure that we have a URL.
1145     if (!options.url) {
1146       params.url = _.result(model, 'url') || urlError();
1147     }
1148
1149     // Ensure that we have the appropriate request data.
1150     if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
1151       params.contentType = 'application/json';
1152       params.data = JSON.stringify(options.attrs || model.toJSON(options));
1153     }
1154
1155     // For older servers, emulate JSON by encoding the request into an HTML-form.
1156     if (options.emulateJSON) {
1157       params.contentType = 'application/x-www-form-urlencoded';
1158       params.data = params.data ? {model: params.data} : {};
1159     }
1160
1161     // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
1162     // And an `X-HTTP-Method-Override` header.
1163     if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
1164       params.type = 'POST';
1165       if (options.emulateJSON) params.data._method = type;
1166       var beforeSend = options.beforeSend;
1167       options.beforeSend = function(xhr) {
1168         xhr.setRequestHeader('X-HTTP-Method-Override', type);
1169         if (beforeSend) return beforeSend.apply(this, arguments);
1170       };
1171     }
1172
1173     // Don't process data on a non-GET request.
1174     if (params.type !== 'GET' && !options.emulateJSON) {
1175       params.processData = false;
1176     }
1177
1178     // If we're sending a `PATCH` request, and we're in an old Internet Explorer
1179     // that still has ActiveX enabled by default, override jQuery to use that
1180     // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
1181     if (params.type === 'PATCH' && noXhrPatch) {
1182       params.xhr = function() {
1183         return new ActiveXObject("Microsoft.XMLHTTP");
1184       };
1185     }
1186
1187     // Make the request, allowing the user to override any Ajax options.
1188     var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
1189     model.trigger('request', model, xhr, options);
1190     return xhr;
1191   };
1192
1193   var noXhrPatch =
1194     typeof window !== 'undefined' && !!window.ActiveXObject &&
1195       !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
1196
1197   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
1198   var methodMap = {
1199     'create': 'POST',
1200     'update': 'PUT',
1201     'patch':  'PATCH',
1202     'delete': 'DELETE',
1203     'read':   'GET'
1204   };
1205
1206   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
1207   // Override this if you'd like to use a different library.
1208   Backbone.ajax = function() {
1209     return Backbone.$.ajax.apply(Backbone.$, arguments);
1210   };
1211
1212   // Backbone.Router
1213   // ---------------
1214
1215   // Routers map faux-URLs to actions, and fire events when routes are
1216   // matched. Creating a new one sets its `routes` hash, if not set statically.
1217   var Router = Backbone.Router = function(options) {
1218     options || (options = {});
1219     if (options.routes) this.routes = options.routes;
1220     this._bindRoutes();
1221     this.initialize.apply(this, arguments);
1222   };
1223
1224   // Cached regular expressions for matching named param parts and splatted
1225   // parts of route strings.
1226   var optionalParam = /\((.*?)\)/g;
1227   var namedParam    = /(\(\?)?:\w+/g;
1228   var splatParam    = /\*\w+/g;
1229   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
1230
1231   // Set up all inheritable **Backbone.Router** properties and methods.
1232   _.extend(Router.prototype, Events, {
1233
1234     // Initialize is an empty function by default. Override it with your own
1235     // initialization logic.
1236     initialize: function(){},
1237
1238     // Manually bind a single named route to a callback. For example:
1239     //
1240     //     this.route('search/:query/p:num', 'search', function(query, num) {
1241     //       ...
1242     //     });
1243     //
1244     route: function(route, name, callback) {
1245       if (!_.isRegExp(route)) route = this._routeToRegExp(route);
1246       if (_.isFunction(name)) {
1247         callback = name;
1248         name = '';
1249       }
1250       if (!callback) callback = this[name];
1251       var router = this;
1252       Backbone.history.route(route, function(fragment) {
1253         var args = router._extractParameters(route, fragment);
1254         router.execute(callback, args);
1255         router.trigger.apply(router, ['route:' + name].concat(args));
1256         router.trigger('route', name, args);
1257         Backbone.history.trigger('route', router, name, args);
1258       });
1259       return this;
1260     },
1261
1262     // Execute a route handler with the provided parameters.  This is an
1263     // excellent place to do pre-route setup or post-route cleanup.
1264     execute: function(callback, args) {
1265       if (callback) callback.apply(this, args);
1266     },
1267
1268     // Simple proxy to `Backbone.history` to save a fragment into the history.
1269     navigate: function(fragment, options) {
1270       Backbone.history.navigate(fragment, options);
1271       return this;
1272     },
1273
1274     // Bind all defined routes to `Backbone.history`. We have to reverse the
1275     // order of the routes here to support behavior where the most general
1276     // routes can be defined at the bottom of the route map.
1277     _bindRoutes: function() {
1278       if (!this.routes) return;
1279       this.routes = _.result(this, 'routes');
1280       var route, routes = _.keys(this.routes);
1281       while ((route = routes.pop()) != null) {
1282         this.route(route, this.routes[route]);
1283       }
1284     },
1285
1286     // Convert a route string into a regular expression, suitable for matching
1287     // against the current location hash.
1288     _routeToRegExp: function(route) {
1289       route = route.replace(escapeRegExp, '\\$&')
1290                    .replace(optionalParam, '(?:$1)?')
1291                    .replace(namedParam, function(match, optional) {
1292                      return optional ? match : '([^/?]+)';
1293                    })
1294                    .replace(splatParam, '([^?]*?)');
1295       return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
1296     },
1297
1298     // Given a route, and a URL fragment that it matches, return the array of
1299     // extracted decoded parameters. Empty or unmatched parameters will be
1300     // treated as `null` to normalize cross-browser behavior.
1301     _extractParameters: function(route, fragment) {
1302       var params = route.exec(fragment).slice(1);
1303       return _.map(params, function(param, i) {
1304         // Don't decode the search params.
1305         if (i === params.length - 1) return param || null;
1306         return param ? decodeURIComponent(param) : null;
1307       });
1308     }
1309
1310   });
1311
1312   // Backbone.History
1313   // ----------------
1314
1315   // Handles cross-browser history management, based on either
1316   // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
1317   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
1318   // and URL fragments. If the browser supports neither (old IE, natch),
1319   // falls back to polling.
1320   var History = Backbone.History = function() {
1321     this.handlers = [];
1322     _.bindAll(this, 'checkUrl');
1323
1324     // Ensure that `History` can be used outside of the browser.
1325     if (typeof window !== 'undefined') {
1326       this.location = window.location;
1327       this.history = window.history;
1328     }
1329   };
1330
1331   // Cached regex for stripping a leading hash/slash and trailing space.
1332   var routeStripper = /^[#\/]|\s+$/g;
1333
1334   // Cached regex for stripping leading and trailing slashes.
1335   var rootStripper = /^\/+|\/+$/g;
1336
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.
1344   var pathStripper = /#.*$/;
1345
1346   // Has the history handling already been started?
1347   History.started = false;
1348
1349   // Set up all inheritable **Backbone.History** properties and methods.
1350   _.extend(History.prototype, Events, {
1351
1352     // The default interval to poll for hash changes, if necessary, is
1353     // twenty times a second.
1354     interval: 50,
1355
1356     // Are we at the app root?
1357     atRoot: function() {
1358       return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
1359     },
1360
1361     // Gets the true hash value. Cannot use location.hash directly due to bug
1362     // in Firefox where location.hash will always be decoded.
1363     getHash: function(window) {
1364       var match = (window || this).location.href.match(/#(.*)$/);
1365       return match ? match[1] : '';
1366     },
1367
1368     // Get the cross-browser normalized URL fragment, either from the URL,
1369     // the hash, or the override.
1370     getFragment: function(fragment, forcePushState) {
1371       if (fragment == null) {
1372         if (this._hasPushState || !this._wantsHashChange || forcePushState) {
1373           fragment = decodeURI(this.location.pathname + this.location.search);
1374           var root = this.root.replace(trailingSlash, '');
1375           if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
1376         } else {
1377           fragment = this.getHash();
1378         }
1379       }
1380       return fragment.replace(routeStripper, '');
1381     },
1382
1383     // Start the hash change handling, returning `true` if the current URL matches
1384     // an existing route, and `false` otherwise.
1385     start: function(options) {
1386       if (History.started) throw new Error("Backbone.history has already been started");
1387       History.started = true;
1388
1389       // Figure out the initial configuration. Do we need an iframe?
1390       // Is pushState desired ... is it available?
1391       this.options          = _.extend({root: '/'}, this.options, options);
1392       this.root             = this.options.root;
1393       this._wantsHashChange = this.options.hashChange !== false;
1394       this._wantsPushState  = !!this.options.pushState;
1395       this._hasPushState    = !!(this.options.pushState && this.history && this.history.pushState);
1396       var fragment          = this.getFragment();
1397       var docMode           = document.documentMode;
1398       var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
1399
1400       // Normalize root to always include a leading and trailing slash.
1401       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
1424       // Transition from hashChange to pushState or vice versa if both are
1425       // requested.
1426       if (this._wantsHashChange && this._wantsPushState) {
1427
1428         // 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...
1430         if (!this._hasPushState && !this.atRoot()) {
1431           this.fragment = this.getFragment(null, true);
1432           this.location.replace(this.root + '#' + this.fragment);
1433           // Return immediately as browser will do redirect to new url
1434           return true;
1435
1436         // 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...
1438         } else if (this._hasPushState && this.atRoot() && loc.hash) {
1439           this.fragment = this.getHash().replace(routeStripper, '');
1440           this.history.replaceState({}, document.title, this.root + this.fragment);
1441         }
1442
1443       }
1444
1445       if (!this.options.silent) return this.loadUrl();
1446     },
1447
1448     // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
1449     // but possibly useful for unit testing Routers.
1450     stop: function() {
1451       Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
1452       if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
1453       History.started = false;
1454     },
1455
1456     // Add a route to be tested when the fragment changes. Routes added later
1457     // may override previous routes.
1458     route: function(route, callback) {
1459       this.handlers.unshift({route: route, callback: callback});
1460     },
1461
1462     // Checks the current URL to see if it has changed, and if it has,
1463     // calls `loadUrl`, normalizing across the hidden iframe.
1464     checkUrl: function(e) {
1465       var current = this.getFragment();
1466       if (current === this.fragment && this.iframe) {
1467         current = this.getFragment(this.getHash(this.iframe));
1468       }
1469       if (current === this.fragment) return false;
1470       if (this.iframe) this.navigate(current);
1471       this.loadUrl();
1472     },
1473
1474     // Attempt to load the current URL fragment. If a route succeeds with a
1475     // match, returns `true`. If no defined routes matches the fragment,
1476     // returns `false`.
1477     loadUrl: function(fragment) {
1478       fragment = this.fragment = this.getFragment(fragment);
1479       return _.any(this.handlers, function(handler) {
1480         if (handler.route.test(fragment)) {
1481           handler.callback(fragment);
1482           return true;
1483         }
1484       });
1485     },
1486
1487     // Save a fragment into the hash history, or replace the URL state if the
1488     // 'replace' option is passed. You are responsible for properly URL-encoding
1489     // the fragment in advance.
1490     //
1491     // The options object can contain `trigger: true` if you wish to have the
1492     // route callback be fired (not usually desirable), or `replace: true`, if
1493     // you wish to modify the current URL without adding an entry to the history.
1494     navigate: function(fragment, options) {
1495       if (!History.started) return false;
1496       if (!options || options === true) options = {trigger: !!options};
1497
1498       var url = this.root + (fragment = this.getFragment(fragment || ''));
1499
1500       // Strip the hash for matching.
1501       fragment = fragment.replace(pathStripper, '');
1502
1503       if (this.fragment === fragment) return;
1504       this.fragment = fragment;
1505
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.
1510       if (this._hasPushState) {
1511         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
1512
1513       // If hash changes haven't been explicitly disabled, update the hash
1514       // fragment to store history.
1515       } else if (this._wantsHashChange) {
1516         this._updateHash(this.location, fragment, options.replace);
1517         if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
1518           // 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
1520           // want this.
1521           if(!options.replace) this.iframe.document.open().close();
1522           this._updateHash(this.iframe.location, fragment, options.replace);
1523         }
1524
1525       // If you've told us that you explicitly don't want fallback hashchange-
1526       // based history, then `navigate` becomes a page refresh.
1527       } else {
1528         return this.location.assign(url);
1529       }
1530       if (options.trigger) return this.loadUrl(fragment);
1531     },
1532
1533     // Update the hash location, either replacing the current entry, or adding
1534     // a new one to the browser history.
1535     _updateHash: function(location, fragment, replace) {
1536       if (replace) {
1537         var href = location.href.replace(/(javascript:|#).*$/, '');
1538         location.replace(href + '#' + fragment);
1539       } else {
1540         // Some browsers require that `hash` contains a leading #.
1541         location.hash = '#' + fragment;
1542       }
1543     }
1544
1545   });
1546
1547   // Create the default Backbone.history.
1548   Backbone.history = new History;
1549
1550   // Helpers
1551   // -------
1552
1553   // Helper function to correctly set up the prototype chain, for subclasses.
1554   // Similar to `goog.inherits`, but uses a hash of prototype properties and
1555   // class properties to be extended.
1556   var extend = function(protoProps, staticProps) {
1557     var parent = this;
1558     var child;
1559
1560     // The constructor function for the new subclass is either defined by you
1561     // (the "constructor" property in your `extend` definition), or defaulted
1562     // by us to simply call the parent's constructor.
1563     if (protoProps && _.has(protoProps, 'constructor')) {
1564       child = protoProps.constructor;
1565     } else {
1566       child = function(){ return parent.apply(this, arguments); };
1567     }
1568
1569     // Add static properties to the constructor function, if supplied.
1570     _.extend(child, parent, staticProps);
1571
1572     // Set the prototype chain to inherit from `parent`, without calling
1573     // `parent`'s constructor function.
1574     var Surrogate = function(){ this.constructor = child; };
1575     Surrogate.prototype = parent.prototype;
1576     child.prototype = new Surrogate;
1577
1578     // Add prototype properties (instance properties) to the subclass,
1579     // if supplied.
1580     if (protoProps) _.extend(child.prototype, protoProps);
1581
1582     // Set a convenience property in case the parent's prototype is needed
1583     // later.
1584     child.__super__ = parent.prototype;
1585
1586     return child;
1587   };
1588
1589   // Set up inheritance for the model, collection, router, view and history.
1590   Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
1591
1592   // Throw an error when a URL is needed, and none is supplied.
1593   var urlError = function() {
1594     throw new Error('A "url" property or function must be specified');
1595   };
1596
1597   // Wrap an optional error callback with a fallback error event.
1598   var wrapError = function(model, options) {
1599     var error = options.error;
1600     options.error = function(resp) {
1601       if (error) error(model, resp, options);
1602       model.trigger('error', model, resp, options);
1603     };
1604   };
1605
1606   return Backbone;
1607
1608 }));