wp/wp-includes/js/backbone.js
changeset 16 a86126ab1dd4
parent 9 177826044cd9
child 19 3d72ae0968f4
equal deleted inserted replaced
15:3d4e9c994f10 16:a86126ab1dd4
     1 //     Backbone.js 1.3.3
     1 //     Backbone.js 1.4.0
     2 
     2 
     3 //     (c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     3 //     (c) 2010-2019 Jeremy Ashkenas and DocumentCloud
     4 //     Backbone may be freely distributed under the MIT license.
     4 //     Backbone may be freely distributed under the MIT license.
     5 //     For all details and documentation:
     5 //     For all details and documentation:
     6 //     http://backbonejs.org
     6 //     http://backbonejs.org
     7 
     7 
     8 (function(factory) {
     8 (function(factory) {
     9 
     9 
    10   // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
    10   // Establish the root object, `window` (`self`) in the browser, or `global` on the server.
    11   // We use `self` instead of `window` for `WebWorker` support.
    11   // We use `self` instead of `window` for `WebWorker` support.
    12   var root = (typeof self == 'object' && self.self === self && self) ||
    12   var root = typeof self == 'object' && self.self === self && self ||
    13             (typeof global == 'object' && global.global === global && global);
    13             typeof global == 'object' && global.global === global && global;
    14 
    14 
    15   // Set up Backbone appropriately for the environment. Start with AMD.
    15   // Set up Backbone appropriately for the environment. Start with AMD.
    16   if (typeof define === 'function' && define.amd) {
    16   if (typeof define === 'function' && define.amd) {
    17     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    17     define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
    18       // Export global even in AMD case in case this script is loaded with
    18       // Export global even in AMD case in case this script is loaded with
    26     try { $ = require('jquery'); } catch (e) {}
    26     try { $ = require('jquery'); } catch (e) {}
    27     factory(root, exports, _, $);
    27     factory(root, exports, _, $);
    28 
    28 
    29   // Finally, as a browser global.
    29   // Finally, as a browser global.
    30   } else {
    30   } else {
    31     root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
    31     root.Backbone = factory(root, {}, root._, root.jQuery || root.Zepto || root.ender || root.$);
    32   }
    32   }
    33 
    33 
    34 })(function(root, Backbone, _, $) {
    34 })(function(root, Backbone, _, $) {
    35 
    35 
    36   // Initial Setup
    36   // Initial Setup
    42 
    42 
    43   // Create a local reference to a common array method we'll want to use later.
    43   // Create a local reference to a common array method we'll want to use later.
    44   var slice = Array.prototype.slice;
    44   var slice = Array.prototype.slice;
    45 
    45 
    46   // Current version of the library. Keep in sync with `package.json`.
    46   // Current version of the library. Keep in sync with `package.json`.
    47   Backbone.VERSION = '1.3.3';
    47   Backbone.VERSION = '1.4.0';
    48 
    48 
    49   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
    49   // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
    50   // the `$` variable.
    50   // the `$` variable.
    51   Backbone.$ = $;
    51   Backbone.$ = $;
    52 
    52 
    65   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
    65   // Turn on `emulateJSON` to support legacy servers that can't deal with direct
    66   // `application/json` requests ... this will encode the body as
    66   // `application/json` requests ... this will encode the body as
    67   // `application/x-www-form-urlencoded` instead and will send the model in a
    67   // `application/x-www-form-urlencoded` instead and will send the model in a
    68   // form param named `model`.
    68   // form param named `model`.
    69   Backbone.emulateJSON = false;
    69   Backbone.emulateJSON = false;
    70 
       
    71   // Proxy Backbone class methods to Underscore functions, wrapping the model's
       
    72   // `attributes` object or collection's `models` array behind the scenes.
       
    73   //
       
    74   // collection.filter(function(model) { return model.get('age') > 10 });
       
    75   // collection.each(this.addView);
       
    76   //
       
    77   // `Function#apply` can be slow so we use the method's arg count, if we know it.
       
    78   var addMethod = function(length, method, attribute) {
       
    79     switch (length) {
       
    80       case 1: return function() {
       
    81         return _[method](this[attribute]);
       
    82       };
       
    83       case 2: return function(value) {
       
    84         return _[method](this[attribute], value);
       
    85       };
       
    86       case 3: return function(iteratee, context) {
       
    87         return _[method](this[attribute], cb(iteratee, this), context);
       
    88       };
       
    89       case 4: return function(iteratee, defaultVal, context) {
       
    90         return _[method](this[attribute], cb(iteratee, this), defaultVal, context);
       
    91       };
       
    92       default: return function() {
       
    93         var args = slice.call(arguments);
       
    94         args.unshift(this[attribute]);
       
    95         return _[method].apply(_, args);
       
    96       };
       
    97     }
       
    98   };
       
    99   var addUnderscoreMethods = function(Class, methods, attribute) {
       
   100     _.each(methods, function(length, method) {
       
   101       if (_[method]) Class.prototype[method] = addMethod(length, method, attribute);
       
   102     });
       
   103   };
       
   104 
       
   105   // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
       
   106   var cb = function(iteratee, instance) {
       
   107     if (_.isFunction(iteratee)) return iteratee;
       
   108     if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
       
   109     if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
       
   110     return iteratee;
       
   111   };
       
   112   var modelMatcher = function(attrs) {
       
   113     var matcher = _.matches(attrs);
       
   114     return function(model) {
       
   115       return matcher(model.attributes);
       
   116     };
       
   117   };
       
   118 
    70 
   119   // Backbone.Events
    71   // Backbone.Events
   120   // ---------------
    72   // ---------------
   121 
    73 
   122   // A module that can be mixed in to *any object* in order to provide it with
    74   // A module that can be mixed in to *any object* in order to provide it with
   132   var Events = Backbone.Events = {};
    84   var Events = Backbone.Events = {};
   133 
    85 
   134   // Regular expression used to split event strings.
    86   // Regular expression used to split event strings.
   135   var eventSplitter = /\s+/;
    87   var eventSplitter = /\s+/;
   136 
    88 
       
    89   // A private global variable to share between listeners and listenees.
       
    90   var _listening;
       
    91 
   137   // Iterates over the standard `event, callback` (as well as the fancy multiple
    92   // Iterates over the standard `event, callback` (as well as the fancy multiple
   138   // space-separated events `"change blur", callback` and jQuery-style event
    93   // space-separated events `"change blur", callback` and jQuery-style event
   139   // maps `{event: callback}`).
    94   // maps `{event: callback}`).
   140   var eventsApi = function(iteratee, events, name, callback, opts) {
    95   var eventsApi = function(iteratee, events, name, callback, opts) {
   141     var i = 0, names;
    96     var i = 0, names;
   158   };
   113   };
   159 
   114 
   160   // Bind an event to a `callback` function. Passing `"all"` will bind
   115   // Bind an event to a `callback` function. Passing `"all"` will bind
   161   // the callback to all events fired.
   116   // the callback to all events fired.
   162   Events.on = function(name, callback, context) {
   117   Events.on = function(name, callback, context) {
   163     return internalOn(this, name, callback, context);
   118     this._events = eventsApi(onApi, this._events || {}, name, callback, {
   164   };
       
   165 
       
   166   // Guard the `listening` argument from the public API.
       
   167   var internalOn = function(obj, name, callback, context, listening) {
       
   168     obj._events = eventsApi(onApi, obj._events || {}, name, callback, {
       
   169       context: context,
   119       context: context,
   170       ctx: obj,
   120       ctx: this,
   171       listening: listening
   121       listening: _listening
   172     });
   122     });
   173 
   123 
   174     if (listening) {
   124     if (_listening) {
   175       var listeners = obj._listeners || (obj._listeners = {});
   125       var listeners = this._listeners || (this._listeners = {});
   176       listeners[listening.id] = listening;
   126       listeners[_listening.id] = _listening;
   177     }
   127       // Allow the listening to use a counter, instead of tracking
   178 
   128       // callbacks for library interop
   179     return obj;
   129       _listening.interop = false;
       
   130     }
       
   131 
       
   132     return this;
   180   };
   133   };
   181 
   134 
   182   // Inversion-of-control versions of `on`. Tell *this* object to listen to
   135   // Inversion-of-control versions of `on`. Tell *this* object to listen to
   183   // an event in another object... keeping track of what it's listening to
   136   // an event in another object... keeping track of what it's listening to
   184   // for easier unbinding later.
   137   // for easier unbinding later.
   185   Events.listenTo = function(obj, name, callback) {
   138   Events.listenTo = function(obj, name, callback) {
   186     if (!obj) return this;
   139     if (!obj) return this;
   187     var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
   140     var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
   188     var listeningTo = this._listeningTo || (this._listeningTo = {});
   141     var listeningTo = this._listeningTo || (this._listeningTo = {});
   189     var listening = listeningTo[id];
   142     var listening = _listening = listeningTo[id];
   190 
   143 
   191     // This object is not listening to any other events on `obj` yet.
   144     // This object is not listening to any other events on `obj` yet.
   192     // Setup the necessary references to track the listening callbacks.
   145     // Setup the necessary references to track the listening callbacks.
   193     if (!listening) {
   146     if (!listening) {
   194       var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
   147       this._listenId || (this._listenId = _.uniqueId('l'));
   195       listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
   148       listening = _listening = listeningTo[id] = new Listening(this, obj);
   196     }
   149     }
   197 
   150 
   198     // Bind callbacks on obj, and keep track of them on listening.
   151     // Bind callbacks on obj.
   199     internalOn(obj, name, callback, this, listening);
   152     var error = tryCatchOn(obj, name, callback, this);
       
   153     _listening = void 0;
       
   154 
       
   155     if (error) throw error;
       
   156     // If the target obj is not Backbone.Events, track events manually.
       
   157     if (listening.interop) listening.on(name, callback);
       
   158 
   200     return this;
   159     return this;
   201   };
   160   };
   202 
   161 
   203   // The reducing API that adds a callback to the `events` object.
   162   // The reducing API that adds a callback to the `events` object.
   204   var onApi = function(events, name, callback, options) {
   163   var onApi = function(events, name, callback, options) {
   208       if (listening) listening.count++;
   167       if (listening) listening.count++;
   209 
   168 
   210       handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
   169       handlers.push({callback: callback, context: context, ctx: context || ctx, listening: listening});
   211     }
   170     }
   212     return events;
   171     return events;
       
   172   };
       
   173 
       
   174   // An try-catch guarded #on function, to prevent poisoning the global
       
   175   // `_listening` variable.
       
   176   var tryCatchOn = function(obj, name, callback, context) {
       
   177     try {
       
   178       obj.on(name, callback, context);
       
   179     } catch (e) {
       
   180       return e;
       
   181     }
   213   };
   182   };
   214 
   183 
   215   // Remove one or many callbacks. If `context` is null, removes all
   184   // Remove one or many callbacks. If `context` is null, removes all
   216   // callbacks with that function. If `callback` is null, removes all
   185   // callbacks with that function. If `callback` is null, removes all
   217   // callbacks for the event. If `name` is null, removes all bound
   186   // callbacks for the event. If `name` is null, removes all bound
   220     if (!this._events) return this;
   189     if (!this._events) return this;
   221     this._events = eventsApi(offApi, this._events, name, callback, {
   190     this._events = eventsApi(offApi, this._events, name, callback, {
   222       context: context,
   191       context: context,
   223       listeners: this._listeners
   192       listeners: this._listeners
   224     });
   193     });
       
   194 
   225     return this;
   195     return this;
   226   };
   196   };
   227 
   197 
   228   // Tell this object to stop listening to either specific events ... or
   198   // Tell this object to stop listening to either specific events ... or
   229   // to every object it's currently listening to.
   199   // to every object it's currently listening to.
   230   Events.stopListening = function(obj, name, callback) {
   200   Events.stopListening = function(obj, name, callback) {
   231     var listeningTo = this._listeningTo;
   201     var listeningTo = this._listeningTo;
   232     if (!listeningTo) return this;
   202     if (!listeningTo) return this;
   233 
   203 
   234     var ids = obj ? [obj._listenId] : _.keys(listeningTo);
   204     var ids = obj ? [obj._listenId] : _.keys(listeningTo);
   235 
       
   236     for (var i = 0; i < ids.length; i++) {
   205     for (var i = 0; i < ids.length; i++) {
   237       var listening = listeningTo[ids[i]];
   206       var listening = listeningTo[ids[i]];
   238 
   207 
   239       // If listening doesn't exist, this object is not currently
   208       // If listening doesn't exist, this object is not currently
   240       // listening to obj. Break out early.
   209       // listening to obj. Break out early.
   241       if (!listening) break;
   210       if (!listening) break;
   242 
   211 
   243       listening.obj.off(name, callback, this);
   212       listening.obj.off(name, callback, this);
   244     }
   213       if (listening.interop) listening.off(name, callback);
       
   214     }
       
   215     if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
   245 
   216 
   246     return this;
   217     return this;
   247   };
   218   };
   248 
   219 
   249   // The reducing API that removes a callback from the `events` object.
   220   // The reducing API that removes a callback from the `events` object.
   250   var offApi = function(events, name, callback, options) {
   221   var offApi = function(events, name, callback, options) {
   251     if (!events) return;
   222     if (!events) return;
   252 
   223 
   253     var i = 0, listening;
       
   254     var context = options.context, listeners = options.listeners;
   224     var context = options.context, listeners = options.listeners;
   255 
   225     var i = 0, names;
   256     // Delete all events listeners and "drop" events.
   226 
   257     if (!name && !callback && !context) {
   227     // Delete all event listeners and "drop" events.
   258       var ids = _.keys(listeners);
   228     if (!name && !context && !callback) {
   259       for (; i < ids.length; i++) {
   229       for (names = _.keys(listeners); i < names.length; i++) {
   260         listening = listeners[ids[i]];
   230         listeners[names[i]].cleanup();
   261         delete listeners[listening.id];
       
   262         delete listening.listeningTo[listening.objId];
       
   263       }
   231       }
   264       return;
   232       return;
   265     }
   233     }
   266 
   234 
   267     var names = name ? [name] : _.keys(events);
   235     names = name ? [name] : _.keys(events);
   268     for (; i < names.length; i++) {
   236     for (; i < names.length; i++) {
   269       name = names[i];
   237       name = names[i];
   270       var handlers = events[name];
   238       var handlers = events[name];
   271 
   239 
   272       // Bail out if there are no events stored.
   240       // Bail out if there are no events stored.
   273       if (!handlers) break;
   241       if (!handlers) break;
   274 
   242 
   275       // Replace events if there are any remaining.  Otherwise, clean up.
   243       // Find any remaining events.
   276       var remaining = [];
   244       var remaining = [];
   277       for (var j = 0; j < handlers.length; j++) {
   245       for (var j = 0; j < handlers.length; j++) {
   278         var handler = handlers[j];
   246         var handler = handlers[j];
   279         if (
   247         if (
   280           callback && callback !== handler.callback &&
   248           callback && callback !== handler.callback &&
   281             callback !== handler.callback._callback ||
   249             callback !== handler.callback._callback ||
   282               context && context !== handler.context
   250               context && context !== handler.context
   283         ) {
   251         ) {
   284           remaining.push(handler);
   252           remaining.push(handler);
   285         } else {
   253         } else {
   286           listening = handler.listening;
   254           var listening = handler.listening;
   287           if (listening && --listening.count === 0) {
   255           if (listening) listening.off(name, callback);
   288             delete listeners[listening.id];
       
   289             delete listening.listeningTo[listening.objId];
       
   290           }
       
   291         }
   256         }
   292       }
   257       }
   293 
   258 
   294       // Update tail event if the list has any events.  Otherwise, clean up.
   259       // Replace events if there are any remaining.  Otherwise, clean up.
   295       if (remaining.length) {
   260       if (remaining.length) {
   296         events[name] = remaining;
   261         events[name] = remaining;
   297       } else {
   262       } else {
   298         delete events[name];
   263         delete events[name];
   299       }
   264       }
   300     }
   265     }
       
   266 
   301     return events;
   267     return events;
   302   };
   268   };
   303 
   269 
   304   // Bind an event to only be triggered a single time. After the first time
   270   // Bind an event to only be triggered a single time. After the first time
   305   // the callback is invoked, its listener will be removed. If multiple events
   271   // the callback is invoked, its listener will be removed. If multiple events
   306   // are passed in using the space-separated syntax, the handler will fire
   272   // are passed in using the space-separated syntax, the handler will fire
   307   // once for each event, not once for a combination of all events.
   273   // once for each event, not once for a combination of all events.
   308   Events.once = function(name, callback, context) {
   274   Events.once = function(name, callback, context) {
   309     // Map the event into a `{event: once}` object.
   275     // Map the event into a `{event: once}` object.
   310     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
   276     var events = eventsApi(onceMap, {}, name, callback, this.off.bind(this));
   311     if (typeof name === 'string' && context == null) callback = void 0;
   277     if (typeof name === 'string' && context == null) callback = void 0;
   312     return this.on(events, callback, context);
   278     return this.on(events, callback, context);
   313   };
   279   };
   314 
   280 
   315   // Inversion-of-control versions of `once`.
   281   // Inversion-of-control versions of `once`.
   316   Events.listenToOnce = function(obj, name, callback) {
   282   Events.listenToOnce = function(obj, name, callback) {
   317     // Map the event into a `{event: once}` object.
   283     // Map the event into a `{event: once}` object.
   318     var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
   284     var events = eventsApi(onceMap, {}, name, callback, this.stopListening.bind(this, obj));
   319     return this.listenTo(obj, events);
   285     return this.listenTo(obj, events);
   320   };
   286   };
   321 
   287 
   322   // Reduces the event callbacks into a map of `{event: onceWrapper}`.
   288   // Reduces the event callbacks into a map of `{event: onceWrapper}`.
   323   // `offer` unbinds the `onceWrapper` after it has been called.
   289   // `offer` unbinds the `onceWrapper` after it has been called.
   371       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   337       case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
   372       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   338       default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
   373     }
   339     }
   374   };
   340   };
   375 
   341 
       
   342   // A listening class that tracks and cleans up memory bindings
       
   343   // when all callbacks have been offed.
       
   344   var Listening = function(listener, obj) {
       
   345     this.id = listener._listenId;
       
   346     this.listener = listener;
       
   347     this.obj = obj;
       
   348     this.interop = true;
       
   349     this.count = 0;
       
   350     this._events = void 0;
       
   351   };
       
   352 
       
   353   Listening.prototype.on = Events.on;
       
   354 
       
   355   // Offs a callback (or several).
       
   356   // Uses an optimized counter if the listenee uses Backbone.Events.
       
   357   // Otherwise, falls back to manual tracking to support events
       
   358   // library interop.
       
   359   Listening.prototype.off = function(name, callback) {
       
   360     var cleanup;
       
   361     if (this.interop) {
       
   362       this._events = eventsApi(offApi, this._events, name, callback, {
       
   363         context: void 0,
       
   364         listeners: void 0
       
   365       });
       
   366       cleanup = !this._events;
       
   367     } else {
       
   368       this.count--;
       
   369       cleanup = this.count === 0;
       
   370     }
       
   371     if (cleanup) this.cleanup();
       
   372   };
       
   373 
       
   374   // Cleans up memory bindings between the listener and the listenee.
       
   375   Listening.prototype.cleanup = function() {
       
   376     delete this.listener._listeningTo[this.obj._listenId];
       
   377     if (!this.interop) delete this.obj._listeners[this.id];
       
   378   };
       
   379 
   376   // Aliases for backwards compatibility.
   380   // Aliases for backwards compatibility.
   377   Events.bind   = Events.on;
   381   Events.bind   = Events.on;
   378   Events.unbind = Events.off;
   382   Events.unbind = Events.off;
   379 
   383 
   380   // Allow the `Backbone` object to serve as a global event bus, for folks who
   384   // Allow the `Backbone` object to serve as a global event bus, for folks who
   392   // Create a new model with the specified attributes. A client id (`cid`)
   396   // Create a new model with the specified attributes. A client id (`cid`)
   393   // is automatically generated and assigned for you.
   397   // is automatically generated and assigned for you.
   394   var Model = Backbone.Model = function(attributes, options) {
   398   var Model = Backbone.Model = function(attributes, options) {
   395     var attrs = attributes || {};
   399     var attrs = attributes || {};
   396     options || (options = {});
   400     options || (options = {});
       
   401     this.preinitialize.apply(this, arguments);
   397     this.cid = _.uniqueId(this.cidPrefix);
   402     this.cid = _.uniqueId(this.cidPrefix);
   398     this.attributes = {};
   403     this.attributes = {};
   399     if (options.collection) this.collection = options.collection;
   404     if (options.collection) this.collection = options.collection;
   400     if (options.parse) attrs = this.parse(attrs, options) || {};
   405     if (options.parse) attrs = this.parse(attrs, options) || {};
   401     var defaults = _.result(this, 'defaults');
   406     var defaults = _.result(this, 'defaults');
   419     idAttribute: 'id',
   424     idAttribute: 'id',
   420 
   425 
   421     // The prefix is used to create the client id which is used to identify models locally.
   426     // The prefix is used to create the client id which is used to identify models locally.
   422     // You may want to override this if you're experiencing name clashes with model ids.
   427     // You may want to override this if you're experiencing name clashes with model ids.
   423     cidPrefix: 'c',
   428     cidPrefix: 'c',
       
   429 
       
   430     // preinitialize is an empty function by default. You can override it with a function
       
   431     // or object.  preinitialize will run before any instantiation logic is run in the Model.
       
   432     preinitialize: function(){},
   424 
   433 
   425     // Initialize is an empty function by default. Override it with your own
   434     // Initialize is an empty function by default. Override it with your own
   426     // initialization logic.
   435     // initialization logic.
   427     initialize: function(){},
   436     initialize: function(){},
   428 
   437 
   560     // determining if there *would be* a change.
   569     // determining if there *would be* a change.
   561     changedAttributes: function(diff) {
   570     changedAttributes: function(diff) {
   562       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   571       if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
   563       var old = this._changing ? this._previousAttributes : this.attributes;
   572       var old = this._changing ? this._previousAttributes : this.attributes;
   564       var changed = {};
   573       var changed = {};
       
   574       var hasChanged;
   565       for (var attr in diff) {
   575       for (var attr in diff) {
   566         var val = diff[attr];
   576         var val = diff[attr];
   567         if (_.isEqual(old[attr], val)) continue;
   577         if (_.isEqual(old[attr], val)) continue;
   568         changed[attr] = val;
   578         changed[attr] = val;
   569       }
   579         hasChanged = true;
   570       return _.size(changed) ? changed : false;
   580       }
       
   581       return hasChanged ? changed : false;
   571     },
   582     },
   572 
   583 
   573     // Get the previous value of an attribute, recorded at the time the last
   584     // Get the previous value of an attribute, recorded at the time the last
   574     // `"change"` event was fired.
   585     // `"change"` event was fired.
   575     previous: function(attr) {
   586     previous: function(attr) {
   641       wrapError(this, options);
   652       wrapError(this, options);
   642 
   653 
   643       // Set temporary attributes if `{wait: true}` to properly find new ids.
   654       // Set temporary attributes if `{wait: true}` to properly find new ids.
   644       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
   655       if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
   645 
   656 
   646       var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
   657       var method = this.isNew() ? 'create' : options.patch ? 'patch' : 'update';
   647       if (method === 'patch' && !options.attrs) options.attrs = attrs;
   658       if (method === 'patch' && !options.attrs) options.attrs = attrs;
   648       var xhr = this.sync(method, this, options);
   659       var xhr = this.sync(method, this, options);
   649 
   660 
   650       // Restore attributes.
   661       // Restore attributes.
   651       this.attributes = attributes;
   662       this.attributes = attributes;
   729       return false;
   740       return false;
   730     }
   741     }
   731 
   742 
   732   });
   743   });
   733 
   744 
   734   // Underscore methods that we want to implement on the Model, mapped to the
       
   735   // number of arguments they take.
       
   736   var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
       
   737       omit: 0, chain: 1, isEmpty: 1};
       
   738 
       
   739   // Mix in each Underscore method as a proxy to `Model#attributes`.
       
   740   addUnderscoreMethods(Model, modelMethods, 'attributes');
       
   741 
       
   742   // Backbone.Collection
   745   // Backbone.Collection
   743   // -------------------
   746   // -------------------
   744 
   747 
   745   // If models tend to represent a single row of data, a Backbone Collection is
   748   // If models tend to represent a single row of data, a Backbone Collection is
   746   // more analogous to a table full of data ... or a small slice or page of that
   749   // more analogous to a table full of data ... or a small slice or page of that
   752   // Create a new **Collection**, perhaps to contain a specific type of `model`.
   755   // Create a new **Collection**, perhaps to contain a specific type of `model`.
   753   // If a `comparator` is specified, the Collection will maintain
   756   // If a `comparator` is specified, the Collection will maintain
   754   // its models in sort order, as they're added and removed.
   757   // its models in sort order, as they're added and removed.
   755   var Collection = Backbone.Collection = function(models, options) {
   758   var Collection = Backbone.Collection = function(models, options) {
   756     options || (options = {});
   759     options || (options = {});
       
   760     this.preinitialize.apply(this, arguments);
   757     if (options.model) this.model = options.model;
   761     if (options.model) this.model = options.model;
   758     if (options.comparator !== void 0) this.comparator = options.comparator;
   762     if (options.comparator !== void 0) this.comparator = options.comparator;
   759     this._reset();
   763     this._reset();
   760     this.initialize.apply(this, arguments);
   764     this.initialize.apply(this, arguments);
   761     if (models) this.reset(models, _.extend({silent: true}, options));
   765     if (models) this.reset(models, _.extend({silent: true}, options));
   780   _.extend(Collection.prototype, Events, {
   784   _.extend(Collection.prototype, Events, {
   781 
   785 
   782     // The default model for a collection is just a **Backbone.Model**.
   786     // The default model for a collection is just a **Backbone.Model**.
   783     // This should be overridden in most cases.
   787     // This should be overridden in most cases.
   784     model: Model,
   788     model: Model,
       
   789 
       
   790 
       
   791     // preinitialize is an empty function by default. You can override it with a function
       
   792     // or object.  preinitialize will run before any instantiation logic is run in the Collection.
       
   793     preinitialize: function(){},
   785 
   794 
   786     // Initialize is an empty function by default. Override it with your own
   795     // Initialize is an empty function by default. Override it with your own
   787     // initialization logic.
   796     // initialization logic.
   788     initialize: function(){},
   797     initialize: function(){},
   789 
   798 
   983     // Get a model from the set by id, cid, model object with id or cid
   992     // Get a model from the set by id, cid, model object with id or cid
   984     // properties, or an attributes object that is transformed through modelId.
   993     // properties, or an attributes object that is transformed through modelId.
   985     get: function(obj) {
   994     get: function(obj) {
   986       if (obj == null) return void 0;
   995       if (obj == null) return void 0;
   987       return this._byId[obj] ||
   996       return this._byId[obj] ||
   988         this._byId[this.modelId(obj.attributes || obj)] ||
   997         this._byId[this.modelId(this._isModel(obj) ? obj.attributes : obj)] ||
   989         obj.cid && this._byId[obj.cid];
   998         obj.cid && this._byId[obj.cid];
   990     },
   999     },
   991 
  1000 
   992     // Returns `true` if the model is in the collection.
  1001     // Returns `true` if the model is in the collection.
   993     has: function(obj) {
  1002     has: function(obj) {
  1019       var comparator = this.comparator;
  1028       var comparator = this.comparator;
  1020       if (!comparator) throw new Error('Cannot sort a set without a comparator');
  1029       if (!comparator) throw new Error('Cannot sort a set without a comparator');
  1021       options || (options = {});
  1030       options || (options = {});
  1022 
  1031 
  1023       var length = comparator.length;
  1032       var length = comparator.length;
  1024       if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
  1033       if (_.isFunction(comparator)) comparator = comparator.bind(this);
  1025 
  1034 
  1026       // Run sort based on type of `comparator`.
  1035       // Run sort based on type of `comparator`.
  1027       if (length === 1 || _.isString(comparator)) {
  1036       if (length === 1 || _.isString(comparator)) {
  1028         this.models = this.sortBy(comparator);
  1037         this.models = this.sortBy(comparator);
  1029       } else {
  1038       } else {
  1091     // Define how to uniquely identify models in the collection.
  1100     // Define how to uniquely identify models in the collection.
  1092     modelId: function(attrs) {
  1101     modelId: function(attrs) {
  1093       return attrs[this.model.prototype.idAttribute || 'id'];
  1102       return attrs[this.model.prototype.idAttribute || 'id'];
  1094     },
  1103     },
  1095 
  1104 
       
  1105     // Get an iterator of all models in this collection.
       
  1106     values: function() {
       
  1107       return new CollectionIterator(this, ITERATOR_VALUES);
       
  1108     },
       
  1109 
       
  1110     // Get an iterator of all model IDs in this collection.
       
  1111     keys: function() {
       
  1112       return new CollectionIterator(this, ITERATOR_KEYS);
       
  1113     },
       
  1114 
       
  1115     // Get an iterator of all [ID, model] tuples in this collection.
       
  1116     entries: function() {
       
  1117       return new CollectionIterator(this, ITERATOR_KEYSVALUES);
       
  1118     },
       
  1119 
  1096     // Private method to reset all internal state. Called when the collection
  1120     // Private method to reset all internal state. Called when the collection
  1097     // is first initialized or reset.
  1121     // is first initialized or reset.
  1098     _reset: function() {
  1122     _reset: function() {
  1099       this.length = 0;
  1123       this.length = 0;
  1100       this.models = [];
  1124       this.models = [];
  1187       this.trigger.apply(this, arguments);
  1211       this.trigger.apply(this, arguments);
  1188     }
  1212     }
  1189 
  1213 
  1190   });
  1214   });
  1191 
  1215 
  1192   // Underscore methods that we want to implement on the Collection.
  1216   // Defining an @@iterator method implements JavaScript's Iterable protocol.
  1193   // 90% of the core usefulness of Backbone Collections is actually implemented
  1217   // In modern ES2015 browsers, this value is found at Symbol.iterator.
  1194   // right here:
  1218   /* global Symbol */
  1195   var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
  1219   var $$iterator = typeof Symbol === 'function' && Symbol.iterator;
  1196       foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
  1220   if ($$iterator) {
  1197       select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
  1221     Collection.prototype[$$iterator] = Collection.prototype.values;
  1198       contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
  1222   }
  1199       head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
  1223 
  1200       without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
  1224   // CollectionIterator
  1201       isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
  1225   // ------------------
  1202       sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
  1226 
  1203 
  1227   // A CollectionIterator implements JavaScript's Iterator protocol, allowing the
  1204   // Mix in each Underscore method as a proxy to `Collection#models`.
  1228   // use of `for of` loops in modern browsers and interoperation between
  1205   addUnderscoreMethods(Collection, collectionMethods, 'models');
  1229   // Backbone.Collection and other JavaScript functions and third-party libraries
       
  1230   // which can operate on Iterables.
       
  1231   var CollectionIterator = function(collection, kind) {
       
  1232     this._collection = collection;
       
  1233     this._kind = kind;
       
  1234     this._index = 0;
       
  1235   };
       
  1236 
       
  1237   // This "enum" defines the three possible kinds of values which can be emitted
       
  1238   // by a CollectionIterator that correspond to the values(), keys() and entries()
       
  1239   // methods on Collection, respectively.
       
  1240   var ITERATOR_VALUES = 1;
       
  1241   var ITERATOR_KEYS = 2;
       
  1242   var ITERATOR_KEYSVALUES = 3;
       
  1243 
       
  1244   // All Iterators should themselves be Iterable.
       
  1245   if ($$iterator) {
       
  1246     CollectionIterator.prototype[$$iterator] = function() {
       
  1247       return this;
       
  1248     };
       
  1249   }
       
  1250 
       
  1251   CollectionIterator.prototype.next = function() {
       
  1252     if (this._collection) {
       
  1253 
       
  1254       // Only continue iterating if the iterated collection is long enough.
       
  1255       if (this._index < this._collection.length) {
       
  1256         var model = this._collection.at(this._index);
       
  1257         this._index++;
       
  1258 
       
  1259         // Construct a value depending on what kind of values should be iterated.
       
  1260         var value;
       
  1261         if (this._kind === ITERATOR_VALUES) {
       
  1262           value = model;
       
  1263         } else {
       
  1264           var id = this._collection.modelId(model.attributes);
       
  1265           if (this._kind === ITERATOR_KEYS) {
       
  1266             value = id;
       
  1267           } else { // ITERATOR_KEYSVALUES
       
  1268             value = [id, model];
       
  1269           }
       
  1270         }
       
  1271         return {value: value, done: false};
       
  1272       }
       
  1273 
       
  1274       // Once exhausted, remove the reference to the collection so future
       
  1275       // calls to the next method always return done.
       
  1276       this._collection = void 0;
       
  1277     }
       
  1278 
       
  1279     return {value: void 0, done: true};
       
  1280   };
  1206 
  1281 
  1207   // Backbone.View
  1282   // Backbone.View
  1208   // -------------
  1283   // -------------
  1209 
  1284 
  1210   // Backbone Views are almost more convention than they are actual code. A View
  1285   // Backbone Views are almost more convention than they are actual code. A View
  1217 
  1292 
  1218   // Creating a Backbone.View creates its initial element outside of the DOM,
  1293   // Creating a Backbone.View creates its initial element outside of the DOM,
  1219   // if an existing element is not provided...
  1294   // if an existing element is not provided...
  1220   var View = Backbone.View = function(options) {
  1295   var View = Backbone.View = function(options) {
  1221     this.cid = _.uniqueId('view');
  1296     this.cid = _.uniqueId('view');
       
  1297     this.preinitialize.apply(this, arguments);
  1222     _.extend(this, _.pick(options, viewOptions));
  1298     _.extend(this, _.pick(options, viewOptions));
  1223     this._ensureElement();
  1299     this._ensureElement();
  1224     this.initialize.apply(this, arguments);
  1300     this.initialize.apply(this, arguments);
  1225   };
  1301   };
  1226 
  1302 
  1239     // jQuery delegate for element lookup, scoped to DOM elements within the
  1315     // jQuery delegate for element lookup, scoped to DOM elements within the
  1240     // current view. This should be preferred to global lookups where possible.
  1316     // current view. This should be preferred to global lookups where possible.
  1241     $: function(selector) {
  1317     $: function(selector) {
  1242       return this.$el.find(selector);
  1318       return this.$el.find(selector);
  1243     },
  1319     },
       
  1320 
       
  1321     // preinitialize is an empty function by default. You can override it with a function
       
  1322     // or object.  preinitialize will run before any instantiation logic is run in the View
       
  1323     preinitialize: function(){},
  1244 
  1324 
  1245     // Initialize is an empty function by default. Override it with your own
  1325     // Initialize is an empty function by default. Override it with your own
  1246     // initialization logic.
  1326     // initialization logic.
  1247     initialize: function(){},
  1327     initialize: function(){},
  1248 
  1328 
  1307       for (var key in events) {
  1387       for (var key in events) {
  1308         var method = events[key];
  1388         var method = events[key];
  1309         if (!_.isFunction(method)) method = this[method];
  1389         if (!_.isFunction(method)) method = this[method];
  1310         if (!method) continue;
  1390         if (!method) continue;
  1311         var match = key.match(delegateEventSplitter);
  1391         var match = key.match(delegateEventSplitter);
  1312         this.delegate(match[1], match[2], _.bind(method, this));
  1392         this.delegate(match[1], match[2], method.bind(this));
  1313       }
  1393       }
  1314       return this;
  1394       return this;
  1315     },
  1395     },
  1316 
  1396 
  1317     // Add a single event listener to the view's element (or a child element
  1397     // Add a single event listener to the view's element (or a child element
  1363     // subclasses using an alternative DOM manipulation API.
  1443     // subclasses using an alternative DOM manipulation API.
  1364     _setAttributes: function(attributes) {
  1444     _setAttributes: function(attributes) {
  1365       this.$el.attr(attributes);
  1445       this.$el.attr(attributes);
  1366     }
  1446     }
  1367 
  1447 
       
  1448   });
       
  1449 
       
  1450   // Proxy Backbone class methods to Underscore functions, wrapping the model's
       
  1451   // `attributes` object or collection's `models` array behind the scenes.
       
  1452   //
       
  1453   // collection.filter(function(model) { return model.get('age') > 10 });
       
  1454   // collection.each(this.addView);
       
  1455   //
       
  1456   // `Function#apply` can be slow so we use the method's arg count, if we know it.
       
  1457   var addMethod = function(base, length, method, attribute) {
       
  1458     switch (length) {
       
  1459       case 1: return function() {
       
  1460         return base[method](this[attribute]);
       
  1461       };
       
  1462       case 2: return function(value) {
       
  1463         return base[method](this[attribute], value);
       
  1464       };
       
  1465       case 3: return function(iteratee, context) {
       
  1466         return base[method](this[attribute], cb(iteratee, this), context);
       
  1467       };
       
  1468       case 4: return function(iteratee, defaultVal, context) {
       
  1469         return base[method](this[attribute], cb(iteratee, this), defaultVal, context);
       
  1470       };
       
  1471       default: return function() {
       
  1472         var args = slice.call(arguments);
       
  1473         args.unshift(this[attribute]);
       
  1474         return base[method].apply(base, args);
       
  1475       };
       
  1476     }
       
  1477   };
       
  1478 
       
  1479   var addUnderscoreMethods = function(Class, base, methods, attribute) {
       
  1480     _.each(methods, function(length, method) {
       
  1481       if (base[method]) Class.prototype[method] = addMethod(base, length, method, attribute);
       
  1482     });
       
  1483   };
       
  1484 
       
  1485   // Support `collection.sortBy('attr')` and `collection.findWhere({id: 1})`.
       
  1486   var cb = function(iteratee, instance) {
       
  1487     if (_.isFunction(iteratee)) return iteratee;
       
  1488     if (_.isObject(iteratee) && !instance._isModel(iteratee)) return modelMatcher(iteratee);
       
  1489     if (_.isString(iteratee)) return function(model) { return model.get(iteratee); };
       
  1490     return iteratee;
       
  1491   };
       
  1492   var modelMatcher = function(attrs) {
       
  1493     var matcher = _.matches(attrs);
       
  1494     return function(model) {
       
  1495       return matcher(model.attributes);
       
  1496     };
       
  1497   };
       
  1498 
       
  1499   // Underscore methods that we want to implement on the Collection.
       
  1500   // 90% of the core usefulness of Backbone Collections is actually implemented
       
  1501   // right here:
       
  1502   var collectionMethods = {forEach: 3, each: 3, map: 3, collect: 3, reduce: 0,
       
  1503     foldl: 0, inject: 0, reduceRight: 0, foldr: 0, find: 3, detect: 3, filter: 3,
       
  1504     select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
       
  1505     contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
       
  1506     head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
       
  1507     without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
       
  1508     isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
       
  1509     sortBy: 3, indexBy: 3, findIndex: 3, findLastIndex: 3};
       
  1510 
       
  1511 
       
  1512   // Underscore methods that we want to implement on the Model, mapped to the
       
  1513   // number of arguments they take.
       
  1514   var modelMethods = {keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
       
  1515     omit: 0, chain: 1, isEmpty: 1};
       
  1516 
       
  1517   // Mix in each Underscore method as a proxy to `Collection#models`.
       
  1518 
       
  1519   _.each([
       
  1520     [Collection, collectionMethods, 'models'],
       
  1521     [Model, modelMethods, 'attributes']
       
  1522   ], function(config) {
       
  1523     var Base = config[0],
       
  1524         methods = config[1],
       
  1525         attribute = config[2];
       
  1526 
       
  1527     Base.mixin = function(obj) {
       
  1528       var mappings = _.reduce(_.functions(obj), function(memo, name) {
       
  1529         memo[name] = 0;
       
  1530         return memo;
       
  1531       }, {});
       
  1532       addUnderscoreMethods(Base, obj, mappings, attribute);
       
  1533     };
       
  1534 
       
  1535     addUnderscoreMethods(Base, _, methods, attribute);
  1368   });
  1536   });
  1369 
  1537 
  1370   // Backbone.sync
  1538   // Backbone.sync
  1371   // -------------
  1539   // -------------
  1372 
  1540 
  1445     return xhr;
  1613     return xhr;
  1446   };
  1614   };
  1447 
  1615 
  1448   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1616   // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
  1449   var methodMap = {
  1617   var methodMap = {
  1450     'create': 'POST',
  1618     create: 'POST',
  1451     'update': 'PUT',
  1619     update: 'PUT',
  1452     'patch': 'PATCH',
  1620     patch: 'PATCH',
  1453     'delete': 'DELETE',
  1621     delete: 'DELETE',
  1454     'read': 'GET'
  1622     read: 'GET'
  1455   };
  1623   };
  1456 
  1624 
  1457   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
  1625   // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
  1458   // Override this if you'd like to use a different library.
  1626   // Override this if you'd like to use a different library.
  1459   Backbone.ajax = function() {
  1627   Backbone.ajax = function() {
  1465 
  1633 
  1466   // Routers map faux-URLs to actions, and fire events when routes are
  1634   // Routers map faux-URLs to actions, and fire events when routes are
  1467   // matched. Creating a new one sets its `routes` hash, if not set statically.
  1635   // matched. Creating a new one sets its `routes` hash, if not set statically.
  1468   var Router = Backbone.Router = function(options) {
  1636   var Router = Backbone.Router = function(options) {
  1469     options || (options = {});
  1637     options || (options = {});
       
  1638     this.preinitialize.apply(this, arguments);
  1470     if (options.routes) this.routes = options.routes;
  1639     if (options.routes) this.routes = options.routes;
  1471     this._bindRoutes();
  1640     this._bindRoutes();
  1472     this.initialize.apply(this, arguments);
  1641     this.initialize.apply(this, arguments);
  1473   };
  1642   };
  1474 
  1643 
  1479   var splatParam    = /\*\w+/g;
  1648   var splatParam    = /\*\w+/g;
  1480   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  1649   var escapeRegExp  = /[\-{}\[\]+?.,\\\^$|#\s]/g;
  1481 
  1650 
  1482   // Set up all inheritable **Backbone.Router** properties and methods.
  1651   // Set up all inheritable **Backbone.Router** properties and methods.
  1483   _.extend(Router.prototype, Events, {
  1652   _.extend(Router.prototype, Events, {
       
  1653 
       
  1654     // preinitialize is an empty function by default. You can override it with a function
       
  1655     // or object.  preinitialize will run before any instantiation logic is run in the Router.
       
  1656     preinitialize: function(){},
  1484 
  1657 
  1485     // Initialize is an empty function by default. Override it with your own
  1658     // Initialize is an empty function by default. Override it with your own
  1486     // initialization logic.
  1659     // initialization logic.
  1487     initialize: function(){},
  1660     initialize: function(){},
  1488 
  1661 
  1537 
  1710 
  1538     // Convert a route string into a regular expression, suitable for matching
  1711     // Convert a route string into a regular expression, suitable for matching
  1539     // against the current location hash.
  1712     // against the current location hash.
  1540     _routeToRegExp: function(route) {
  1713     _routeToRegExp: function(route) {
  1541       route = route.replace(escapeRegExp, '\\$&')
  1714       route = route.replace(escapeRegExp, '\\$&')
  1542                    .replace(optionalParam, '(?:$1)?')
  1715         .replace(optionalParam, '(?:$1)?')
  1543                    .replace(namedParam, function(match, optional) {
  1716         .replace(namedParam, function(match, optional) {
  1544                      return optional ? match : '([^/?]+)';
  1717           return optional ? match : '([^/?]+)';
  1545                    })
  1718         })
  1546                    .replace(splatParam, '([^?]*?)');
  1719         .replace(splatParam, '([^?]*?)');
  1547       return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  1720       return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
  1548     },
  1721     },
  1549 
  1722 
  1550     // Given a route, and a URL fragment that it matches, return the array of
  1723     // Given a route, and a URL fragment that it matches, return the array of
  1551     // extracted decoded parameters. Empty or unmatched parameters will be
  1724     // extracted decoded parameters. Empty or unmatched parameters will be
  1569   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1742   // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
  1570   // and URL fragments. If the browser supports neither (old IE, natch),
  1743   // and URL fragments. If the browser supports neither (old IE, natch),
  1571   // falls back to polling.
  1744   // falls back to polling.
  1572   var History = Backbone.History = function() {
  1745   var History = Backbone.History = function() {
  1573     this.handlers = [];
  1746     this.handlers = [];
  1574     this.checkUrl = _.bind(this.checkUrl, this);
  1747     this.checkUrl = this.checkUrl.bind(this);
  1575 
  1748 
  1576     // Ensure that `History` can be used outside of the browser.
  1749     // Ensure that `History` can be used outside of the browser.
  1577     if (typeof window !== 'undefined') {
  1750     if (typeof window !== 'undefined') {
  1578       this.location = window.location;
  1751       this.location = window.location;
  1579       this.history = window.history;
  1752       this.history = window.history;
  1810       if (fragment === '' || fragment.charAt(0) === '?') {
  1983       if (fragment === '' || fragment.charAt(0) === '?') {
  1811         rootPath = rootPath.slice(0, -1) || '/';
  1984         rootPath = rootPath.slice(0, -1) || '/';
  1812       }
  1985       }
  1813       var url = rootPath + fragment;
  1986       var url = rootPath + fragment;
  1814 
  1987 
  1815       // Strip the hash and decode for matching.
  1988       // Strip the fragment of the query and hash for matching.
  1816       fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
  1989       fragment = fragment.replace(pathStripper, '');
  1817 
  1990 
  1818       if (this.fragment === fragment) return;
  1991       // Decode for matching.
  1819       this.fragment = fragment;
  1992       var decodedFragment = this.decodeFragment(fragment);
       
  1993 
       
  1994       if (this.fragment === decodedFragment) return;
       
  1995       this.fragment = decodedFragment;
  1820 
  1996 
  1821       // If pushState is available, we use it to set the fragment as a real URL.
  1997       // If pushState is available, we use it to set the fragment as a real URL.
  1822       if (this._usePushState) {
  1998       if (this._usePushState) {
  1823         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1999         this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
  1824 
  2000