Tag: JavaScript框

  • WordPress 源代码——主干(backbone-1.0.js)

    WordPress 源代码——主干(backbone-1.0.js)

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