master
   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}));