PageRenderTime 379ms CodeModel.GetById 218ms app.highlight 96ms RepoModel.GetById 46ms app.codeStats 1ms

/public/javascripts/backbone.js

https://bitbucket.org/nguyenkha/quadratic-equation
JavaScript | 1293 lines | 786 code | 165 blank | 342 comment | 286 complexity | 442af55cd29865bbb98e3be170ac1949 MD5 | raw file
   1//     Backbone.js 0.9.1
   2
   3//     (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
   4//     Backbone may be freely distributed under the MIT license.
   5//     For all details and documentation:
   6//     http://backbonejs.org
   7
   8define(function(require, exports, module) {
   9
  10  // Initial Setup
  11  // -------------
  12
  13  // Save a reference to the global object (`window` in the browser, `global`
  14  // on the server).
  15  var root = this;
  16
  17  // Save the previous value of the `Backbone` variable, so that it can be
  18  // restored later on, if `noConflict` is used.
  19  var previousBackbone = root.Backbone;
  20
  21  // Create a local reference to slice/splice.
  22  var slice = Array.prototype.slice;
  23  var splice = Array.prototype.splice;
  24
  25  // The top-level namespace. All public Backbone classes and modules will
  26  // be attached to this. Exported for both CommonJS and the browser.
  27  var Backbone;
  28  if (typeof exports !== 'undefined') {
  29    Backbone = exports;
  30  } else {
  31    Backbone = root.Backbone = {};
  32  }
  33
  34  // Current version of the library. Keep in sync with `package.json`.
  35  Backbone.VERSION = '0.9.1';
  36
  37  // Require Underscore, if we're on the server, and it's not already present.
  38  var _ = root._;
  39  if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
  40
  41  // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
  42  var $ = root.jQuery || root.Zepto || root.ender;
  43
  44  // Sure jQuery has loaded
  45  if (!$) $ = require('jquery');
  46
  47  // Set the JavaScript library that will be used for DOM manipulation and
  48  // Ajax calls (a.k.a. the `$` variable). By default Backbone will use: jQuery,
  49  // Zepto, or Ender; but the `setDomLibrary()` method lets you inject an
  50  // alternate JavaScript library (or a mock library for testing your views
  51  // outside of a browser).
  52  Backbone.setDomLibrary = function(lib) {
  53    $ = lib;
  54  };
  55
  56  // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
  57  // to its previous owner. Returns a reference to this Backbone object.
  58  Backbone.noConflict = function() {
  59    root.Backbone = previousBackbone;
  60    return this;
  61  };
  62
  63  // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
  64  // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
  65  // set a `X-Http-Method-Override` header.
  66  Backbone.emulateHTTP = false;
  67
  68  // Turn on `emulateJSON` to support legacy servers that can't deal with direct
  69  // `application/json` requests ... will encode the body as
  70  // `application/x-www-form-urlencoded` instead and will send the model in a
  71  // form param named `model`.
  72  Backbone.emulateJSON = false;
  73
  74  // Backbone.Events
  75  // -----------------
  76
  77  // A module that can be mixed in to *any object* in order to provide it with
  78  // custom events. You may bind with `on` or remove with `off` callback functions
  79  // to an event; trigger`-ing an event fires all callbacks in succession.
  80  //
  81  //     var object = {};
  82  //     _.extend(object, Backbone.Events);
  83  //     object.on('expand', function(){ alert('expanded'); });
  84  //     object.trigger('expand');
  85  //
  86  Backbone.Events = {
  87
  88    // Bind an event, specified by a string name, `ev`, to a `callback`
  89    // function. Passing `"all"` will bind the callback to all events fired.
  90    on: function(events, callback, context) {
  91      var ev;
  92      events = events.split(/\s+/);
  93      var calls = this._callbacks || (this._callbacks = {});
  94      while (ev = events.shift()) {
  95        // Create an immutable callback list, allowing traversal during
  96        // modification.  The tail is an empty object that will always be used
  97        // as the next node.
  98        var list  = calls[ev] || (calls[ev] = {});
  99        var tail = list.tail || (list.tail = list.next = {});
 100        tail.callback = callback;
 101        tail.context = context;
 102        list.tail = tail.next = {};
 103      }
 104      return this;
 105    },
 106
 107    // Remove one or many callbacks. If `context` is null, removes all callbacks
 108    // with that function. If `callback` is null, removes all callbacks for the
 109    // event. If `ev` is null, removes all bound callbacks for all events.
 110    off: function(events, callback, context) {
 111      var ev, calls, node;
 112      if (!events) {
 113        delete this._callbacks;
 114      } else if (calls = this._callbacks) {
 115        events = events.split(/\s+/);
 116        while (ev = events.shift()) {
 117          node = calls[ev];
 118          delete calls[ev];
 119          if (!callback || !node) continue;
 120          // Create a new list, omitting the indicated event/context pairs.
 121          while ((node = node.next) && node.next) {
 122            if (node.callback === callback &&
 123              (!context || node.context === context)) continue;
 124            this.on(ev, node.callback, node.context);
 125          }
 126        }
 127      }
 128      return this;
 129    },
 130
 131    // Trigger an event, firing all bound callbacks. Callbacks are passed the
 132    // same arguments as `trigger` is, apart from the event name.
 133    // Listening for `"all"` passes the true event name as the first argument.
 134    trigger: function(events) {
 135      var event, node, calls, tail, args, all, rest;
 136      if (!(calls = this._callbacks)) return this;
 137      all = calls['all'];
 138      (events = events.split(/\s+/)).push(null);
 139      // Save references to the current heads & tails.
 140      while (event = events.shift()) {
 141        if (all) events.push({next: all.next, tail: all.tail, event: event});
 142        if (!(node = calls[event])) continue;
 143        events.push({next: node.next, tail: node.tail});
 144      }
 145      // Traverse each list, stopping when the saved tail is reached.
 146      rest = slice.call(arguments, 1);
 147      while (node = events.pop()) {
 148        tail = node.tail;
 149        args = node.event ? [node.event].concat(rest) : rest;
 150        while ((node = node.next) !== tail) {
 151          node.callback.apply(node.context || this, args);
 152        }
 153      }
 154      return this;
 155    }
 156
 157  };
 158
 159  // Aliases for backwards compatibility.
 160  Backbone.Events.bind   = Backbone.Events.on;
 161  Backbone.Events.unbind = Backbone.Events.off;
 162
 163  // Backbone.Model
 164  // --------------
 165
 166  // Create a new model, with defined attributes. A client id (`cid`)
 167  // is automatically generated and assigned for you.
 168  Backbone.Model = function(attributes, options) {
 169    var defaults;
 170    attributes || (attributes = {});
 171    if (options && options.parse) attributes = this.parse(attributes);
 172    if (defaults = getValue(this, 'defaults')) {
 173      attributes = _.extend({}, defaults, attributes);
 174    }
 175    if (options && options.collection) this.collection = options.collection;
 176    this.attributes = {};
 177    this._escapedAttributes = {};
 178    this.cid = _.uniqueId('c');
 179    if (!this.set(attributes, {silent: true})) {
 180      throw new Error("Can't create an invalid model");
 181    }
 182    delete this._changed;
 183    this._previousAttributes = _.clone(this.attributes);
 184    this.initialize.apply(this, arguments);
 185  };
 186
 187  // Attach all inheritable methods to the Model prototype.
 188  _.extend(Backbone.Model.prototype, Backbone.Events, {
 189
 190    // The default name for the JSON `id` attribute is `"id"`. MongoDB and
 191    // CouchDB users may want to set this to `"_id"`.
 192    idAttribute: 'id',
 193
 194    // Initialize is an empty function by default. Override it with your own
 195    // initialization logic.
 196    initialize: function(){},
 197
 198    // Return a copy of the model's `attributes` object.
 199    toJSON: function() {
 200      return _.clone(this.attributes);
 201    },
 202
 203    // Get the value of an attribute.
 204    get: function(attr) {
 205      return this.attributes[attr];
 206    },
 207
 208    // Get the HTML-escaped value of an attribute.
 209    escape: function(attr) {
 210      var html;
 211      if (html = this._escapedAttributes[attr]) return html;
 212      var val = this.attributes[attr];
 213      return this._escapedAttributes[attr] = _.escape(val == null ? '' : '' + val);
 214    },
 215
 216    // Returns `true` if the attribute contains a value that is not null
 217    // or undefined.
 218    has: function(attr) {
 219      return this.attributes[attr] != null;
 220    },
 221
 222    // Set a hash of model attributes on the object, firing `"change"` unless
 223    // you choose to silence it.
 224    set: function(key, value, options) {
 225      var attrs, attr, val;
 226      if (_.isObject(key) || key == null) {
 227        attrs = key;
 228        options = value;
 229      } else {
 230        attrs = {};
 231        attrs[key] = value;
 232      }
 233
 234      // Extract attributes and options.
 235      options || (options = {});
 236      if (!attrs) return this;
 237      if (attrs instanceof Backbone.Model) attrs = attrs.attributes;
 238      if (options.unset) for (attr in attrs) attrs[attr] = void 0;
 239
 240      // Run validation.
 241      if (!this._validate(attrs, options)) return false;
 242
 243      // Check for changes of `id`.
 244      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
 245
 246      var now = this.attributes;
 247      var escaped = this._escapedAttributes;
 248      var prev = this._previousAttributes || {};
 249      var alreadySetting = this._setting;
 250      this._changed || (this._changed = {});
 251      this._setting = true;
 252
 253      // Update attributes.
 254      for (attr in attrs) {
 255        val = attrs[attr];
 256        if (!_.isEqual(now[attr], val)) delete escaped[attr];
 257        options.unset ? delete now[attr] : now[attr] = val;
 258        if (this._changing && !_.isEqual(this._changed[attr], val)) {
 259          this.trigger('change:' + attr, this, val, options);
 260          this._moreChanges = true;
 261        }
 262        delete this._changed[attr];
 263        if (!_.isEqual(prev[attr], val) || (_.has(now, attr) != _.has(prev, attr))) {
 264          this._changed[attr] = val;
 265        }
 266      }
 267
 268      // Fire the `"change"` events, if the model has been changed.
 269      if (!alreadySetting) {
 270        if (!options.silent && this.hasChanged()) this.change(options);
 271        this._setting = false;
 272      }
 273      return this;
 274    },
 275
 276    // Remove an attribute from the model, firing `"change"` unless you choose
 277    // to silence it. `unset` is a noop if the attribute doesn't exist.
 278    unset: function(attr, options) {
 279      (options || (options = {})).unset = true;
 280      return this.set(attr, null, options);
 281    },
 282
 283    // Clear all attributes on the model, firing `"change"` unless you choose
 284    // to silence it.
 285    clear: function(options) {
 286      (options || (options = {})).unset = true;
 287      return this.set(_.clone(this.attributes), options);
 288    },
 289
 290    // Fetch the model from the server. If the server's representation of the
 291    // model differs from its current attributes, they will be overriden,
 292    // triggering a `"change"` event.
 293    fetch: function(options) {
 294      options = options ? _.clone(options) : {};
 295      var model = this;
 296      var success = options.success;
 297      options.success = function(resp, status, xhr) {
 298        if (!model.set(model.parse(resp, xhr), options)) return false;
 299        if (success) success(model, resp);
 300      };
 301      options.error = Backbone.wrapError(options.error, model, options);
 302      return (this.sync || Backbone.sync).call(this, 'read', this, options);
 303    },
 304
 305    // Set a hash of model attributes, and sync the model to the server.
 306    // If the server returns an attributes hash that differs, the model's
 307    // state will be `set` again.
 308    save: function(key, value, options) {
 309      var attrs, current;
 310      if (_.isObject(key) || key == null) {
 311        attrs = key;
 312        options = value;
 313      } else {
 314        attrs = {};
 315        attrs[key] = value;
 316      }
 317
 318      options = options ? _.clone(options) : {};
 319      if (options.wait) current = _.clone(this.attributes);
 320      var silentOptions = _.extend({}, options, {silent: true});
 321      if (attrs && !this.set(attrs, options.wait ? silentOptions : options)) {
 322        return false;
 323      }
 324      var model = this;
 325      var success = options.success;
 326      options.success = function(resp, status, xhr) {
 327        var serverAttrs = model.parse(resp, xhr);
 328        if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
 329        if (!model.set(serverAttrs, options)) return false;
 330        if (success) {
 331          success(model, resp);
 332        } else {
 333          model.trigger('sync', model, resp, options);
 334        }
 335      };
 336      options.error = Backbone.wrapError(options.error, model, options);
 337      var method = this.isNew() ? 'create' : 'update';
 338      var xhr = (this.sync || Backbone.sync).call(this, method, this, options);
 339      if (options.wait) this.set(current, silentOptions);
 340      return xhr;
 341    },
 342
 343    // Destroy this model on the server if it was already persisted.
 344    // Optimistically removes the model from its collection, if it has one.
 345    // If `wait: true` is passed, waits for the server to respond before removal.
 346    destroy: function(options) {
 347      options = options ? _.clone(options) : {};
 348      var model = this;
 349      var success = options.success;
 350
 351      var triggerDestroy = function() {
 352        model.trigger('destroy', model, model.collection, options);
 353      };
 354
 355      if (this.isNew()) return triggerDestroy();
 356      options.success = function(resp) {
 357        if (options.wait) triggerDestroy();
 358        if (success) {
 359          success(model, resp);
 360        } else {
 361          model.trigger('sync', model, resp, options);
 362        }
 363      };
 364      options.error = Backbone.wrapError(options.error, model, options);
 365      var xhr = (this.sync || Backbone.sync).call(this, 'delete', this, options);
 366      if (!options.wait) triggerDestroy();
 367      return xhr;
 368    },
 369
 370    // Default URL for the model's representation on the server -- if you're
 371    // using Backbone's restful methods, override this to change the endpoint
 372    // that will be called.
 373    url: function() {
 374      var base = getValue(this.collection, 'url') || getValue(this, 'urlRoot') || urlError();
 375      if (this.isNew()) return base;
 376      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
 377    },
 378
 379    // **parse** converts a response into the hash of attributes to be `set` on
 380    // the model. The default implementation is just to pass the response along.
 381    parse: function(resp, xhr) {
 382      return resp;
 383    },
 384
 385    // Create a new model with identical attributes to this one.
 386    clone: function() {
 387      return new this.constructor(this.attributes);
 388    },
 389
 390    // A model is new if it has never been saved to the server, and lacks an id.
 391    isNew: function() {
 392      return this.id == null;
 393    },
 394
 395    // Call this method to manually fire a `"change"` event for this model and
 396    // a `"change:attribute"` event for each changed attribute.
 397    // Calling this will cause all objects observing the model to update.
 398    change: function(options) {
 399      if (this._changing || !this.hasChanged()) return this;
 400      this._changing = true;
 401      this._moreChanges = true;
 402      for (var attr in this._changed) {
 403        this.trigger('change:' + attr, this, this._changed[attr], options);
 404      }
 405      while (this._moreChanges) {
 406        this._moreChanges = false;
 407        this.trigger('change', this, options);
 408      }
 409      this._previousAttributes = _.clone(this.attributes);
 410      delete this._changed;
 411      this._changing = false;
 412      return this;
 413    },
 414
 415    // Determine if the model has changed since the last `"change"` event.
 416    // If you specify an attribute name, determine if that attribute has changed.
 417    hasChanged: function(attr) {
 418      if (!arguments.length) return !_.isEmpty(this._changed);
 419      return this._changed && _.has(this._changed, attr);
 420    },
 421
 422    // Return an object containing all the attributes that have changed, or
 423    // false if there are no changed attributes. Useful for determining what
 424    // parts of a view need to be updated and/or what attributes need to be
 425    // persisted to the server. Unset attributes will be set to undefined.
 426    // You can also pass an attributes object to diff against the model,
 427    // determining if there *would be* a change.
 428    changedAttributes: function(diff) {
 429      if (!diff) return this.hasChanged() ? _.clone(this._changed) : false;
 430      var val, changed = false, old = this._previousAttributes;
 431      for (var attr in diff) {
 432        if (_.isEqual(old[attr], (val = diff[attr]))) continue;
 433        (changed || (changed = {}))[attr] = val;
 434      }
 435      return changed;
 436    },
 437
 438    // Get the previous value of an attribute, recorded at the time the last
 439    // `"change"` event was fired.
 440    previous: function(attr) {
 441      if (!arguments.length || !this._previousAttributes) return null;
 442      return this._previousAttributes[attr];
 443    },
 444
 445    // Get all of the attributes of the model at the time of the previous
 446    // `"change"` event.
 447    previousAttributes: function() {
 448      return _.clone(this._previousAttributes);
 449    },
 450
 451    // Check if the model is currently in a valid state. It's only possible to
 452    // get into an *invalid* state if you're using silent changes.
 453    isValid: function() {
 454      return !this.validate(this.attributes);
 455    },
 456
 457    // Run validation against a set of incoming attributes, returning `true`
 458    // if all is well. If a specific `error` callback has been passed,
 459    // call that instead of firing the general `"error"` event.
 460    _validate: function(attrs, options) {
 461      if (options.silent || !this.validate) return true;
 462      attrs = _.extend({}, this.attributes, attrs);
 463      var error = this.validate(attrs, options);
 464      if (!error) return true;
 465      if (options && options.error) {
 466        options.error(this, error, options);
 467      } else {
 468        this.trigger('error', this, error, options);
 469      }
 470      return false;
 471    }
 472
 473  });
 474
 475  // Backbone.Collection
 476  // -------------------
 477
 478  // Provides a standard collection class for our sets of models, ordered
 479  // or unordered. If a `comparator` is specified, the Collection will maintain
 480  // its models in sort order, as they're added and removed.
 481  Backbone.Collection = function(models, options) {
 482    options || (options = {});
 483    if (options.comparator) this.comparator = options.comparator;
 484    this._reset();
 485    this.initialize.apply(this, arguments);
 486    if (models) this.reset(models, {silent: true, parse: options.parse});
 487  };
 488
 489  // Define the Collection's inheritable methods.
 490  _.extend(Backbone.Collection.prototype, Backbone.Events, {
 491
 492    // The default model for a collection is just a **Backbone.Model**.
 493    // This should be overridden in most cases.
 494    model: Backbone.Model,
 495
 496    // Initialize is an empty function by default. Override it with your own
 497    // initialization logic.
 498    initialize: function(){},
 499
 500    // The JSON representation of a Collection is an array of the
 501    // models' attributes.
 502    toJSON: function() {
 503      return this.map(function(model){ return model.toJSON(); });
 504    },
 505
 506    // Add a model, or list of models to the set. Pass **silent** to avoid
 507    // firing the `add` event for every new model.
 508    add: function(models, options) {
 509      var i, index, length, model, cid, id, cids = {}, ids = {};
 510      options || (options = {});
 511      models = _.isArray(models) ? models.slice() : [models];
 512
 513      // Begin by turning bare objects into model references, and preventing
 514      // invalid models or duplicate models from being added.
 515      for (i = 0, length = models.length; i < length; i++) {
 516        if (!(model = models[i] = this._prepareModel(models[i], options))) {
 517          throw new Error("Can't add an invalid model to a collection");
 518        }
 519        if (cids[cid = model.cid] || this._byCid[cid] ||
 520          (((id = model.id) != null) && (ids[id] || this._byId[id]))) {
 521          throw new Error("Can't add the same model to a collection twice");
 522        }
 523        cids[cid] = ids[id] = model;
 524      }
 525
 526      // Listen to added models' events, and index models for lookup by
 527      // `id` and by `cid`.
 528      for (i = 0; i < length; i++) {
 529        (model = models[i]).on('all', this._onModelEvent, this);
 530        this._byCid[model.cid] = model;
 531        if (model.id != null) this._byId[model.id] = model;
 532      }
 533
 534      // Insert models into the collection, re-sorting if needed, and triggering
 535      // `add` events unless silenced.
 536      this.length += length;
 537      index = options.at != null ? options.at : this.models.length;
 538      splice.apply(this.models, [index, 0].concat(models));
 539      if (this.comparator) this.sort({silent: true});
 540      if (options.silent) return this;
 541      for (i = 0, length = this.models.length; i < length; i++) {
 542        if (!cids[(model = this.models[i]).cid]) continue;
 543        options.index = i;
 544        model.trigger('add', model, this, options);
 545      }
 546      return this;
 547    },
 548
 549    // Remove a model, or a list of models from the set. Pass silent to avoid
 550    // firing the `remove` event for every model removed.
 551    remove: function(models, options) {
 552      var i, l, index, model;
 553      options || (options = {});
 554      models = _.isArray(models) ? models.slice() : [models];
 555      for (i = 0, l = models.length; i < l; i++) {
 556        model = this.getByCid(models[i]) || this.get(models[i]);
 557        if (!model) continue;
 558        delete this._byId[model.id];
 559        delete this._byCid[model.cid];
 560        index = this.indexOf(model);
 561        this.models.splice(index, 1);
 562        this.length--;
 563        if (!options.silent) {
 564          options.index = index;
 565          model.trigger('remove', model, this, options);
 566        }
 567        this._removeReference(model);
 568      }
 569      return this;
 570    },
 571
 572    // Get a model from the set by id.
 573    get: function(id) {
 574      if (id == null) return null;
 575      return this._byId[id.id != null ? id.id : id];
 576    },
 577
 578    // Get a model from the set by client id.
 579    getByCid: function(cid) {
 580      return cid && this._byCid[cid.cid || cid];
 581    },
 582
 583    // Get the model at the given index.
 584    at: function(index) {
 585      return this.models[index];
 586    },
 587
 588    // Force the collection to re-sort itself. You don't need to call this under
 589    // normal circumstances, as the set will maintain sort order as each item
 590    // is added.
 591    sort: function(options) {
 592      options || (options = {});
 593      if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
 594      var boundComparator = _.bind(this.comparator, this);
 595      if (this.comparator.length == 1) {
 596        this.models = this.sortBy(boundComparator);
 597      } else {
 598        this.models.sort(boundComparator);
 599      }
 600      if (!options.silent) this.trigger('reset', this, options);
 601      return this;
 602    },
 603
 604    // Pluck an attribute from each model in the collection.
 605    pluck: function(attr) {
 606      return _.map(this.models, function(model){ return model.get(attr); });
 607    },
 608
 609    // When you have more items than you want to add or remove individually,
 610    // you can reset the entire set with a new list of models, without firing
 611    // any `add` or `remove` events. Fires `reset` when finished.
 612    reset: function(models, options) {
 613      models  || (models = []);
 614      options || (options = {});
 615      for (var i = 0, l = this.models.length; i < l; i++) {
 616        this._removeReference(this.models[i]);
 617      }
 618      this._reset();
 619      this.add(models, {silent: true, parse: options.parse});
 620      if (!options.silent) this.trigger('reset', this, options);
 621      return this;
 622    },
 623
 624    // Fetch the default set of models for this collection, resetting the
 625    // collection when they arrive. If `add: true` is passed, appends the
 626    // models to the collection instead of resetting.
 627    fetch: function(options) {
 628      options = options ? _.clone(options) : {};
 629      if (options.parse === undefined) options.parse = true;
 630      var collection = this;
 631      var success = options.success;
 632      options.success = function(resp, status, xhr) {
 633        collection[options.add ? 'add' : 'reset'](collection.parse(resp, xhr), options);
 634        if (success) success(collection, resp);
 635      };
 636      options.error = Backbone.wrapError(options.error, collection, options);
 637      return (this.sync || Backbone.sync).call(this, 'read', this, options);
 638    },
 639
 640    // Create a new instance of a model in this collection. Add the model to the
 641    // collection immediately, unless `wait: true` is passed, in which case we
 642    // wait for the server to agree.
 643    create: function(model, options) {
 644      var coll = this;
 645      options = options ? _.clone(options) : {};
 646      model = this._prepareModel(model, options);
 647      if (!model) return false;
 648      if (!options.wait) coll.add(model, options);
 649      var success = options.success;
 650      options.success = function(nextModel, resp, xhr) {
 651        if (options.wait) coll.add(nextModel, options);
 652        if (success) {
 653          success(nextModel, resp);
 654        } else {
 655          nextModel.trigger('sync', model, resp, options);
 656        }
 657      };
 658      model.save(null, options);
 659      return model;
 660    },
 661
 662    // **parse** converts a response into a list of models to be added to the
 663    // collection. The default implementation is just to pass it through.
 664    parse: function(resp, xhr) {
 665      return resp;
 666    },
 667
 668    // Proxy to _'s chain. Can't be proxied the same way the rest of the
 669    // underscore methods are proxied because it relies on the underscore
 670    // constructor.
 671    chain: function () {
 672      return _(this.models).chain();
 673    },
 674
 675    // Reset all internal state. Called when the collection is reset.
 676    _reset: function(options) {
 677      this.length = 0;
 678      this.models = [];
 679      this._byId  = {};
 680      this._byCid = {};
 681    },
 682
 683    // Prepare a model or hash of attributes to be added to this collection.
 684    _prepareModel: function(model, options) {
 685      if (!(model instanceof Backbone.Model)) {
 686        var attrs = model;
 687        options.collection = this;
 688        model = new this.model(attrs, options);
 689        if (!model._validate(model.attributes, options)) model = false;
 690      } else if (!model.collection) {
 691        model.collection = this;
 692      }
 693      return model;
 694    },
 695
 696    // Internal method to remove a model's ties to a collection.
 697    _removeReference: function(model) {
 698      if (this == model.collection) {
 699        delete model.collection;
 700      }
 701      model.off('all', this._onModelEvent, this);
 702    },
 703
 704    // Internal method called every time a model in the set fires an event.
 705    // Sets need to update their indexes when models change ids. All other
 706    // events simply proxy through. "add" and "remove" events that originate
 707    // in other collections are ignored.
 708    _onModelEvent: function(ev, model, collection, options) {
 709      if ((ev == 'add' || ev == 'remove') && collection != this) return;
 710      if (ev == 'destroy') {
 711        this.remove(model, options);
 712      }
 713      if (model && ev === 'change:' + model.idAttribute) {
 714        delete this._byId[model.previous(model.idAttribute)];
 715        this._byId[model.id] = model;
 716      }
 717      this.trigger.apply(this, arguments);
 718    }
 719
 720  });
 721
 722  // Underscore methods that we want to implement on the Collection.
 723  var methods = ['forEach', 'each', 'map', 'reduce', 'reduceRight', 'find',
 724    'detect', 'filter', 'select', 'reject', 'every', 'all', 'some', 'any',
 725    'include', 'contains', 'invoke', 'max', 'min', 'sortBy', 'sortedIndex',
 726    'toArray', 'size', 'first', 'initial', 'rest', 'last', 'without', 'indexOf',
 727    'shuffle', 'lastIndexOf', 'isEmpty', 'groupBy'];
 728
 729  // Mix in each Underscore method as a proxy to `Collection#models`.
 730  _.each(methods, function(method) {
 731    Backbone.Collection.prototype[method] = function() {
 732      return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
 733    };
 734  });
 735
 736  // Backbone.Router
 737  // -------------------
 738
 739  // Routers map faux-URLs to actions, and fire events when routes are
 740  // matched. Creating a new one sets its `routes` hash, if not set statically.
 741  Backbone.Router = function(options) {
 742    options || (options = {});
 743    if (options.routes) this.routes = options.routes;
 744    this._bindRoutes();
 745    this.initialize.apply(this, arguments);
 746  };
 747
 748  // Cached regular expressions for matching named param parts and splatted
 749  // parts of route strings.
 750  var namedParam    = /:\w+/g;
 751  var splatParam    = /\*\w+/g;
 752  var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
 753
 754  // Set up all inheritable **Backbone.Router** properties and methods.
 755  _.extend(Backbone.Router.prototype, Backbone.Events, {
 756
 757    // Initialize is an empty function by default. Override it with your own
 758    // initialization logic.
 759    initialize: function(){},
 760
 761    // Manually bind a single named route to a callback. For example:
 762    //
 763    //     this.route('search/:query/p:num', 'search', function(query, num) {
 764    //       ...
 765    //     });
 766    //
 767    route: function(route, name, callback) {
 768      Backbone.history || (Backbone.history = new Backbone.History);
 769      if (!_.isRegExp(route)) route = this._routeToRegExp(route);
 770      if (!callback) callback = this[name];
 771      Backbone.history.route(route, _.bind(function(fragment) {
 772        var args = this._extractParameters(route, fragment);
 773        callback && callback.apply(this, args);
 774        this.trigger.apply(this, ['route:' + name].concat(args));
 775        Backbone.history.trigger('route', this, name, args);
 776      }, this));
 777      return this;
 778    },
 779
 780    // Simple proxy to `Backbone.history` to save a fragment into the history.
 781    navigate: function(fragment, options) {
 782      Backbone.history.navigate(fragment, options);
 783    },
 784
 785    // Bind all defined routes to `Backbone.history`. We have to reverse the
 786    // order of the routes here to support behavior where the most general
 787    // routes can be defined at the bottom of the route map.
 788    _bindRoutes: function() {
 789      if (!this.routes) return;
 790      var routes = [];
 791      for (var route in this.routes) {
 792        routes.unshift([route, this.routes[route]]);
 793      }
 794      for (var i = 0, l = routes.length; i < l; i++) {
 795        this.route(routes[i][0], routes[i][1], this[routes[i][1]]);
 796      }
 797    },
 798
 799    // Convert a route string into a regular expression, suitable for matching
 800    // against the current location hash.
 801    _routeToRegExp: function(route) {
 802      route = route.replace(escapeRegExp, '\\$&')
 803                   .replace(namedParam, '([^\/]+)')
 804                   .replace(splatParam, '(.*?)');
 805      return new RegExp('^' + route + '$');
 806    },
 807
 808    // Given a route, and a URL fragment that it matches, return the array of
 809    // extracted parameters.
 810    _extractParameters: function(route, fragment) {
 811      return route.exec(fragment).slice(1);
 812    }
 813
 814  });
 815
 816  // Backbone.History
 817  // ----------------
 818
 819  // Handles cross-browser history management, based on URL fragments. If the
 820  // browser does not support `onhashchange`, falls back to polling.
 821  Backbone.History = function() {
 822    this.handlers = [];
 823    _.bindAll(this, 'checkUrl');
 824  };
 825
 826  // Cached regex for cleaning leading hashes and slashes .
 827  var routeStripper = /^[#\/]/;
 828
 829  // Cached regex for detecting MSIE.
 830  var isExplorer = /msie [\w.]+/;
 831
 832  // Has the history handling already been started?
 833  var historyStarted = false;
 834
 835  // Set up all inheritable **Backbone.History** properties and methods.
 836  _.extend(Backbone.History.prototype, Backbone.Events, {
 837
 838    // The default interval to poll for hash changes, if necessary, is
 839    // twenty times a second.
 840    interval: 50,
 841
 842    // Get the cross-browser normalized URL fragment, either from the URL,
 843    // the hash, or the override.
 844    getFragment: function(fragment, forcePushState) {
 845      if (fragment == null) {
 846        if (this._hasPushState || forcePushState) {
 847          fragment = window.location.pathname;
 848          var search = window.location.search;
 849          if (search) fragment += search;
 850        } else {
 851          fragment = window.location.hash;
 852        }
 853      }
 854      fragment = decodeURIComponent(fragment);
 855      if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);
 856      return fragment.replace(routeStripper, '');
 857    },
 858
 859    // Start the hash change handling, returning `true` if the current URL matches
 860    // an existing route, and `false` otherwise.
 861    start: function(options) {
 862
 863      // Figure out the initial configuration. Do we need an iframe?
 864      // Is pushState desired ... is it available?
 865      if (historyStarted) throw new Error("Backbone.history has already been started");
 866      this.options          = _.extend({}, {root: '/'}, this.options, options);
 867      this._wantsHashChange = this.options.hashChange !== false;
 868      this._wantsPushState  = !!this.options.pushState;
 869      this._hasPushState    = !!(this.options.pushState && window.history && window.history.pushState);
 870      var fragment          = this.getFragment();
 871      var docMode           = document.documentMode;
 872      var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
 873      if (oldIE) {
 874        this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
 875        this.navigate(fragment);
 876      }
 877
 878      // Depending on whether we're using pushState or hashes, and whether
 879      // 'onhashchange' is supported, determine how we check the URL state.
 880      if (this._hasPushState) {
 881        $(window).bind('popstate', this.checkUrl);
 882      } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
 883        $(window).bind('hashchange', this.checkUrl);
 884      } else if (this._wantsHashChange) {
 885        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
 886      }
 887
 888      // Determine if we need to change the base url, for a pushState link
 889      // opened by a non-pushState browser.
 890      this.fragment = fragment;
 891      historyStarted = true;
 892      var loc = window.location;
 893      var atRoot  = loc.pathname == this.options.root;
 894
 895      // If we've started off with a route from a `pushState`-enabled browser,
 896      // but we're currently in a browser that doesn't support it...
 897      if (this._wantsHashChange && this._wantsPushState && !this._hasPushState && !atRoot) {
 898        this.fragment = this.getFragment(null, true);
 899        window.location.replace(this.options.root + '#' + this.fragment);
 900        // Return immediately as browser will do redirect to new url
 901        return true;
 902
 903      // Or if we've started out with a hash-based route, but we're currently
 904      // in a browser where it could be `pushState`-based instead...
 905      } else if (this._wantsPushState && this._hasPushState && atRoot && loc.hash) {
 906        this.fragment = loc.hash.replace(routeStripper, '');
 907        window.history.replaceState({}, document.title, loc.protocol + '//' + loc.host + this.options.root + this.fragment);
 908      }
 909
 910      if (!this.options.silent) {
 911        return this.loadUrl();
 912      }
 913    },
 914
 915    // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
 916    // but possibly useful for unit testing Routers.
 917    stop: function() {
 918      $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);
 919      clearInterval(this._checkUrlInterval);
 920      historyStarted = false;
 921    },
 922
 923    // Add a route to be tested when the fragment changes. Routes added later
 924    // may override previous routes.
 925    route: function(route, callback) {
 926      this.handlers.unshift({route: route, callback: callback});
 927    },
 928
 929    // Checks the current URL to see if it has changed, and if it has,
 930    // calls `loadUrl`, normalizing across the hidden iframe.
 931    checkUrl: function(e) {
 932      var current = this.getFragment();
 933      if (current == this.fragment && this.iframe) current = this.getFragment(this.iframe.location.hash);
 934      if (current == this.fragment || current == decodeURIComponent(this.fragment)) return false;
 935      if (this.iframe) this.navigate(current);
 936      this.loadUrl() || this.loadUrl(window.location.hash);
 937    },
 938
 939    // Attempt to load the current URL fragment. If a route succeeds with a
 940    // match, returns `true`. If no defined routes matches the fragment,
 941    // returns `false`.
 942    loadUrl: function(fragmentOverride) {
 943      var fragment = this.fragment = this.getFragment(fragmentOverride);
 944      var matched = _.any(this.handlers, function(handler) {
 945        if (handler.route.test(fragment)) {
 946          handler.callback(fragment);
 947          return true;
 948        }
 949      });
 950      return matched;
 951    },
 952
 953    // Save a fragment into the hash history, or replace the URL state if the
 954    // 'replace' option is passed. You are responsible for properly URL-encoding
 955    // the fragment in advance.
 956    //
 957    // The options object can contain `trigger: true` if you wish to have the
 958    // route callback be fired (not usually desirable), or `replace: true`, if
 959    // you which to modify the current URL without adding an entry to the history.
 960    navigate: function(fragment, options) {
 961      if (!historyStarted) return false;
 962      if (!options || options === true) options = {trigger: options};
 963      var frag = (fragment || '').replace(routeStripper, '');
 964      if (this.fragment == frag || this.fragment == decodeURIComponent(frag)) return;
 965
 966      // If pushState is available, we use it to set the fragment as a real URL.
 967      if (this._hasPushState) {
 968        if (frag.indexOf(this.options.root) != 0) frag = this.options.root + frag;
 969        this.fragment = frag;
 970        window.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, frag);
 971
 972      // If hash changes haven't been explicitly disabled, update the hash
 973      // fragment to store history.
 974      } else if (this._wantsHashChange) {
 975        this.fragment = frag;
 976        this._updateHash(window.location, frag, options.replace);
 977        if (this.iframe && (frag != this.getFragment(this.iframe.location.hash))) {
 978          // Opening and closing the iframe tricks IE7 and earlier to push a history entry on hash-tag change.
 979          // When replace is true, we don't want this.
 980          if(!options.replace) this.iframe.document.open().close();
 981          this._updateHash(this.iframe.location, frag, options.replace);
 982        }
 983
 984      // If you've told us that you explicitly don't want fallback hashchange-
 985      // based history, then `navigate` becomes a page refresh.
 986      } else {
 987        window.location.assign(this.options.root + fragment);
 988      }
 989      if (options.trigger) this.loadUrl(fragment);
 990    },
 991
 992    // Update the hash location, either replacing the current entry, or adding
 993    // a new one to the browser history.
 994    _updateHash: function(location, fragment, replace) {
 995      if (replace) {
 996        location.replace(location.toString().replace(/(javascript:|#).*$/, '') + '#' + fragment);
 997      } else {
 998        location.hash = fragment;
 999      }
1000    }
1001  });
1002
1003  // Backbone.View
1004  // -------------
1005
1006  // Creating a Backbone.View creates its initial element outside of the DOM,
1007  // if an existing element is not provided...
1008  Backbone.View = function(options) {
1009    this.cid = _.uniqueId('view');
1010    this._configure(options || {});
1011    this._ensureElement();
1012    this.initialize.apply(this, arguments);
1013    this.delegateEvents();
1014  };
1015
1016  // Cached regex to split keys for `delegate`.
1017  var eventSplitter = /^(\S+)\s*(.*)$/;
1018
1019  // List of view options to be merged as properties.
1020  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName'];
1021
1022  // Set up all inheritable **Backbone.View** properties and methods.
1023  _.extend(Backbone.View.prototype, Backbone.Events, {
1024
1025    // The default `tagName` of a View's element is `"div"`.
1026    tagName: 'div',
1027
1028    // jQuery delegate for element lookup, scoped to DOM elements within the
1029    // current view. This should be prefered to global lookups where possible.
1030    $: function(selector) {
1031      return this.$el.find(selector);
1032    },
1033
1034    // Initialize is an empty function by default. Override it with your own
1035    // initialization logic.
1036    initialize: function(){},
1037
1038    // **render** is the core function that your view should override, in order
1039    // to populate its element (`this.el`), with the appropriate HTML. The
1040    // convention is for **render** to always return `this`.
1041    render: function() {
1042      return this;
1043    },
1044
1045    // Remove this view from the DOM. Note that the view isn't present in the
1046    // DOM by default, so calling this method may be a no-op.
1047    remove: function() {
1048      this.$el.remove();
1049      return this;
1050    },
1051
1052    // For small amounts of DOM Elements, where a full-blown template isn't
1053    // needed, use **make** to manufacture elements, one at a time.
1054    //
1055    //     var el = this.make('li', {'class': 'row'}, this.model.escape('title'));
1056    //
1057    make: function(tagName, attributes, content) {
1058      var el = document.createElement(tagName);
1059      if (attributes) $(el).attr(attributes);
1060      if (content) $(el).html(content);
1061      return el;
1062    },
1063
1064    // Change the view's element (`this.el` property), including event
1065    // re-delegation.
1066    setElement: function(element, delegate) {
1067      this.$el = $(element);
1068      this.el = this.$el[0];
1069      if (delegate !== false) this.delegateEvents();
1070      return this;
1071    },
1072
1073    // Set callbacks, where `this.events` is a hash of
1074    //
1075    // *{"event selector": "callback"}*
1076    //
1077    //     {
1078    //       'mousedown .title':  'edit',
1079    //       'click .button':     'save'
1080    //       'click .open':       function(e) { ... }
1081    //     }
1082    //
1083    // pairs. Callbacks will be bound to the view, with `this` set properly.
1084    // Uses event delegation for efficiency.
1085    // Omitting the selector binds the event to `this.el`.
1086    // This only works for delegate-able events: not `focus`, `blur`, and
1087    // not `change`, `submit`, and `reset` in Internet Explorer.
1088    delegateEvents: function(events) {
1089      if (!(events || (events = getValue(this, 'events')))) return;
1090      this.undelegateEvents();
1091      for (var key in events) {
1092        var method = events[key];
1093        if (!_.isFunction(method)) method = this[events[key]];
1094        if (!method) throw new Error('Event "' + events[key] + '" does not exist');
1095        var match = key.match(eventSplitter);
1096        var eventName = match[1], selector = match[2];
1097        method = _.bind(method, this);
1098        eventName += '.delegateEvents' + this.cid;
1099        if (selector === '') {
1100          this.$el.bind(eventName, method);
1101        } else {
1102          this.$el.delegate(selector, eventName, method);
1103        }
1104      }
1105    },
1106
1107    // Clears all callbacks previously bound to the view with `delegateEvents`.
1108    // You usually don't need to use this, but may wish to if you have multiple
1109    // Backbone views attached to the same DOM element.
1110    undelegateEvents: function() {
1111      this.$el.unbind('.delegateEvents' + this.cid);
1112    },
1113
1114    // Performs the initial configuration of a View with a set of options.
1115    // Keys with special meaning *(model, collection, id, className)*, are
1116    // attached directly to the view.
1117    _configure: function(options) {
1118      if (this.options) options = _.extend({}, this.options, options);
1119      for (var i = 0, l = viewOptions.length; i < l; i++) {
1120        var attr = viewOptions[i];
1121        if (options[attr]) this[attr] = options[attr];
1122      }
1123      this.options = options;
1124    },
1125
1126    // Ensure that the View has a DOM element to render into.
1127    // If `this.el` is a string, pass it through `$()`, take the first
1128    // matching element, and re-assign it to `el`. Otherwise, create
1129    // an element from the `id`, `className` and `tagName` properties.
1130    _ensureElement: function() {
1131      if (!this.el) {
1132        var attrs = getValue(this, 'attributes') || {};
1133        if (this.id) attrs.id = this.id;
1134        if (this.className) attrs['class'] = this.className;
1135        this.setElement(this.make(this.tagName, attrs), false);
1136      } else {
1137        this.setElement(this.el, false);
1138      }
1139    }
1140
1141  });
1142
1143  // The self-propagating extend function that Backbone classes use.
1144  var extend = function (protoProps, classProps) {
1145    var child = inherits(this, protoProps, classProps);
1146    child.extend = this.extend;
1147    return child;
1148  };
1149
1150  // Set up inheritance for the model, collection, and view.
1151  Backbone.Model.extend = Backbone.Collection.extend =
1152    Backbone.Router.extend = Backbone.View.extend = extend;
1153
1154  // Backbone.sync
1155  // -------------
1156
1157  // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
1158  var methodMap = {
1159    'create': 'POST',
1160    'update': 'PUT',
1161    'delete': 'DELETE',
1162    'read':   'GET'
1163  };
1164
1165  // Override this function to change the manner in which Backbone persists
1166  // models to the server. You will be passed the type of request, and the
1167  // model in question. By default, makes a RESTful Ajax request
1168  // to the model's `url()`. Some possible customizations could be:
1169  //
1170  // * Use `setTimeout` to batch rapid-fire updates into a single request.
1171  // * Send up the models as XML instead of JSON.
1172  // * Persist models via WebSockets instead of Ajax.
1173  //
1174  // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
1175  // as `POST`, with a `_method` parameter containing the true HTTP method,
1176  // as well as all requests with the body as `application/x-www-form-urlencoded`
1177  // instead of `application/json` with the model in a param named `model`.
1178  // Useful when interfacing with server-side languages like **PHP** that make
1179  // it difficult to read the body of `PUT` requests.
1180  Backbone.sync = function(method, model, options) {
1181    var type = methodMap[method];
1182
1183    // Default JSON-request options.
1184    var params = {type: type, dataType: 'json'};
1185
1186    // Ensure that we have a URL.
1187    if (!options.url) {
1188      params.url = getValue(model, 'url') || urlError();
1189    }
1190
1191    // Ensure that we have the appropriate request data.
1192    if (!options.data && model && (method == 'create' || method == 'update')) {
1193      params.contentType = 'application/json';
1194      params.data = JSON.stringify(model.toJSON());
1195    }
1196
1197    // For older servers, emulate JSON by encoding the request into an HTML-form.
1198    if (Backbone.emulateJSON) {
1199      params.contentType = 'application/x-www-form-urlencoded';
1200      params.data = params.data ? {model: params.data} : {};
1201    }
1202
1203    // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
1204    // And an `X-HTTP-Method-Override` header.
1205    if (Backbone.emulateHTTP) {
1206      if (type === 'PUT' || type === 'DELETE') {
1207        if (Backbone.emulateJSON) params.data._method = type;
1208        params.type = 'POST';
1209        params.beforeSend = function(xhr) {
1210          xhr.setRequestHeader('X-HTTP-Method-Override', type);
1211        };
1212      }
1213    }
1214
1215    // Don't process data on a non-GET request.
1216    if (params.type !== 'GET' && !Backbone.emulateJSON) {
1217      params.processData = false;
1218    }
1219
1220    // Make the request, allowing the user to override any Ajax options.
1221    return $.ajax(_.extend(params, options));
1222  };
1223
1224  // Wrap an optional error callback with a fallback error event.
1225  Backbone.wrapError = function(onError, originalModel, options) {
1226    return function(model, resp) {
1227      resp = model === originalModel ? resp : model;
1228      if (onError) {
1229        onError(originalModel, resp, options);
1230      } else {
1231        originalModel.trigger('error', originalModel, resp, options);
1232      }
1233    };
1234  };
1235
1236  // Helpers
1237  // -------
1238
1239  // Shared empty constructor function to aid in prototype-chain creation.
1240  var ctor = function(){};
1241
1242  // Helper function to correctly set up the prototype chain, for subclasses.
1243  // Similar to `goog.inherits`, but uses a hash of prototype properties and
1244  // class properties to be extended.
1245  var inherits = function(parent, protoProps, staticProps) {
1246    var child;
1247
1248    // The constructor function for the new subclass is either defined by you
1249    // (the "constructor" property in your `extend` definition), or defaulted
1250    // by us to simply call the parent's constructor.
1251    if (protoProps && protoProps.hasOwnProperty('constructor')) {
1252      child = protoProps.constructor;
1253    } else {
1254      child = function(){ parent.apply(this, arguments); };
1255    }
1256
1257    // Inherit class (static) properties from parent.
1258    _.extend(child, parent);
1259
1260    // Set the prototype chain to inherit from `parent`, without calling
1261    // `parent`'s constructor function.
1262    ctor.prototype = parent.prototype;
1263    child.prototype = new ctor();
1264
1265    // Add prototype properties (instance properties) to the subclass,
1266    // if supplied.
1267    if (protoProps) _.extend(child.prototype, protoProps);
1268
1269    // Add static properties to the constructor function, if supplied.
1270    if (staticProps) _.extend(child, staticProps);
1271
1272    // Correctly set child's `prototype.constructor`.
1273    child.prototype.constructor = child;
1274
1275    // Set a convenience property in case the parent's prototype is needed later.
1276    child.__super__ = parent.prototype;
1277
1278    return child;
1279  };
1280
1281  // Helper function to get a value from a Backbone object as a property
1282  // or as a function.
1283  var getValue = function(object, prop) {
1284    if (!(object && object[prop])) return null;
1285    return _.isFunction(object[prop]) ? object[prop]() : object[prop];
1286  };
1287
1288  // Throw an error when a URL is needed, and none is supplied.
1289  var urlError = function() {
1290    throw new Error('A "url" property or function must be specified');
1291  };
1292
1293});