WordPress.org

Make WordPress Core

Ticket #34350: 34350.diff

File 34350.diff, 113.7 KB (added by adamsilverstein, 6 years ago)
  • src/wp-includes/js/backbone.js

     
    1 //     Backbone.js 1.1.2
     1//     Backbone.js 1.2.3
    22
    3 //     (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
     3//     (c) 2010-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
    44//     Backbone may be freely distributed under the MIT license.
    55//     For all details and documentation:
    66//     http://backbonejs.org
    77
    8 (function(root, factory) {
     8(function(factory) {
    99
     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.
     12  var root = (typeof self == 'object' && self.self == self && self) ||
     13            (typeof global == 'object' && global.global == global && global);
     14
    1015  // Set up Backbone appropriately for the environment. Start with AMD.
    1116  if (typeof define === 'function' && define.amd) {
    1217    define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
     
    1722
    1823  // Next for Node.js or CommonJS. jQuery may not be needed as a module.
    1924  } else if (typeof exports !== 'undefined') {
    20     var _ = require('underscore');
    21     factory(root, exports, _);
     25    var _ = require('underscore'), $;
     26    try { $ = require('jquery'); } catch(e) {}
     27    factory(root, exports, _, $);
    2228
    2329  // Finally, as a browser global.
    2430  } else {
     
    2531    root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
    2632  }
    2733
    28 }(this, function(root, Backbone, _, $) {
     34}(function(root, Backbone, _, $) {
    2935
    3036  // Initial Setup
    3137  // -------------
     
    3440  // restored later on, if `noConflict` is used.
    3541  var previousBackbone = root.Backbone;
    3642
    37   // Create local references to array methods we'll want to use later.
    38   var array = [];
    39   var push = array.push;
    40   var slice = array.slice;
    41   var splice = array.splice;
     43  // Create a local reference to a common array method we'll want to use later.
     44  var slice = Array.prototype.slice;
    4245
    4346  // Current version of the library. Keep in sync with `package.json`.
    44   Backbone.VERSION = '1.1.2';
     47  Backbone.VERSION = '1.2.3';
    4548
    4649  // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
    4750  // the `$` variable.
     
    6063  Backbone.emulateHTTP = false;
    6164
    6265  // Turn on `emulateJSON` to support legacy servers that can't deal with direct
    63   // `application/json` requests ... will encode the body as
     66  // `application/json` requests ... this will encode the body as
    6467  // `application/x-www-form-urlencoded` instead and will send the model in a
    6568  // form param named `model`.
    6669  Backbone.emulateJSON = false;
    6770
     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
    68119  // Backbone.Events
    69120  // ---------------
    70121
    71122  // A module that can be mixed in to *any object* in order to provide it with
    72   // custom events. You may bind with `on` or remove with `off` callback
    73   // functions to an event; `trigger`-ing an event fires all callbacks in
     123  // a custom event channel. You may bind a callback to an event with `on` or
     124  // remove with `off`; `trigger`-ing an event fires all callbacks in
    74125  // succession.
    75126  //
    76127  //     var object = {};
     
    78129  //     object.on('expand', function(){ alert('expanded'); });
    79130  //     object.trigger('expand');
    80131  //
    81   var Events = Backbone.Events = {
     132  var Events = Backbone.Events = {};
    82133
    83     // Bind an event to a `callback` function. Passing `"all"` will bind
    84     // the callback to all events fired.
    85     on: function(name, callback, context) {
    86       if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
    87       this._events || (this._events = {});
    88       var events = this._events[name] || (this._events[name] = []);
    89       events.push({callback: callback, context: context, ctx: context || this});
    90       return this;
    91     },
     134  // Regular expression used to split event strings.
     135  var eventSplitter = /\s+/;
    92136
    93     // Bind an event to only be triggered a single time. After the first time
    94     // the callback is invoked, it will be removed.
    95     once: function(name, callback, context) {
    96       if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
    97       var self = this;
    98       var once = _.once(function() {
    99         self.off(name, once);
    100         callback.apply(this, arguments);
    101       });
    102       once._callback = callback;
    103       return this.on(name, once, context);
    104     },
    105 
    106     // Remove one or many callbacks. If `context` is null, removes all
    107     // callbacks with that function. If `callback` is null, removes all
    108     // callbacks for the event. If `name` is null, removes all bound
    109     // callbacks for all events.
    110     off: function(name, callback, context) {
    111       var retain, ev, events, names, i, l, j, k;
    112       if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
    113       if (!name && !callback && !context) {
    114         this._events = void 0;
    115         return this;
     137  // Iterates over the standard `event, callback` (as well as the fancy multiple
     138  // space-separated events `"change blur", callback` and jQuery-style event
     139  // maps `{event: callback}`).
     140  var eventsApi = function(iteratee, events, name, callback, opts) {
     141    var i = 0, names;
     142    if (name && typeof name === 'object') {
     143      // Handle event maps.
     144      if (callback !== void 0 && 'context' in opts && opts.context === void 0) opts.context = callback;
     145      for (names = _.keys(name); i < names.length ; i++) {
     146        events = eventsApi(iteratee, events, names[i], name[names[i]], opts);
    116147      }
    117       names = name ? [name] : _.keys(this._events);
    118       for (i = 0, l = names.length; i < l; i++) {
    119         name = names[i];
    120         if (events = this._events[name]) {
    121           this._events[name] = retain = [];
    122           if (callback || context) {
    123             for (j = 0, k = events.length; j < k; j++) {
    124               ev = events[j];
    125               if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
    126                   (context && context !== ev.context)) {
    127                 retain.push(ev);
    128               }
    129             }
    130           }
    131           if (!retain.length) delete this._events[name];
    132         }
     148    } else if (name && eventSplitter.test(name)) {
     149      // Handle space separated event names by delegating them individually.
     150      for (names = name.split(eventSplitter); i < names.length; i++) {
     151        events = iteratee(events, names[i], callback, opts);
    133152      }
     153    } else {
     154      // Finally, standard events.
     155      events = iteratee(events, name, callback, opts);
     156    }
     157    return events;
     158  };
    134159
    135       return this;
    136     },
     160  // Bind an event to a `callback` function. Passing `"all"` will bind
     161  // the callback to all events fired.
     162  Events.on = function(name, callback, context) {
     163    return internalOn(this, name, callback, context);
     164  };
    137165
    138     // Trigger one or many events, firing all bound callbacks. Callbacks are
    139     // passed the same arguments as `trigger` is, apart from the event name
    140     // (unless you're listening on `"all"`, which will cause your callback to
    141     // receive the true name of the event as the first argument).
    142     trigger: function(name) {
    143       if (!this._events) return this;
    144       var args = slice.call(arguments, 1);
    145       if (!eventsApi(this, 'trigger', name, args)) return this;
    146       var events = this._events[name];
    147       var allEvents = this._events.all;
    148       if (events) triggerEvents(events, args);
    149       if (allEvents) triggerEvents(allEvents, arguments);
    150       return this;
    151     },
     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,
     170        ctx: obj,
     171        listening: listening
     172    });
    152173
    153     // Tell this object to stop listening to either specific events ... or
    154     // to every object it's currently listening to.
    155     stopListening: function(obj, name, callback) {
    156       var listeningTo = this._listeningTo;
    157       if (!listeningTo) return this;
    158       var remove = !name && !callback;
    159       if (!callback && typeof name === 'object') callback = this;
    160       if (obj) (listeningTo = {})[obj._listenId] = obj;
    161       for (var id in listeningTo) {
    162         obj = listeningTo[id];
    163         obj.off(name, callback, this);
    164         if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
    165       }
    166       return this;
     174    if (listening) {
     175      var listeners = obj._listeners || (obj._listeners = {});
     176      listeners[listening.id] = listening;
    167177    }
    168178
     179    return obj;
    169180  };
    170181
    171   // Regular expression used to split event strings.
    172   var eventSplitter = /\s+/;
     182  // 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
     184  // for easier unbinding later.
     185  Events.listenTo =  function(obj, name, callback) {
     186    if (!obj) return this;
     187    var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
     188    var listeningTo = this._listeningTo || (this._listeningTo = {});
     189    var listening = listeningTo[id];
    173190
    174   // Implement fancy features of the Events API such as multiple event
    175   // names `"change blur"` and jQuery-style event maps `{change: action}`
    176   // in terms of the existing API.
    177   var eventsApi = function(obj, action, name, rest) {
    178     if (!name) return true;
     191    // This object is not listening to any other events on `obj` yet.
     192    // Setup the necessary references to track the listening callbacks.
     193    if (!listening) {
     194      var thisId = this._listenId || (this._listenId = _.uniqueId('l'));
     195      listening = listeningTo[id] = {obj: obj, objId: id, id: thisId, listeningTo: listeningTo, count: 0};
     196    }
    179197
    180     // Handle event maps.
    181     if (typeof name === 'object') {
    182       for (var key in name) {
    183         obj[action].apply(obj, [key, name[key]].concat(rest));
     198    // Bind callbacks on obj, and keep track of them on listening.
     199    internalOn(obj, name, callback, this, listening);
     200    return this;
     201  };
     202
     203  // The reducing API that adds a callback to the `events` object.
     204  var onApi = function(events, name, callback, options) {
     205    if (callback) {
     206      var handlers = events[name] || (events[name] = []);
     207      var context = options.context, ctx = options.ctx, listening = options.listening;
     208      if (listening) listening.count++;
     209
     210      handlers.push({ callback: callback, context: context, ctx: context || ctx, listening: listening });
     211    }
     212    return events;
     213  };
     214
     215  // Remove one or many callbacks. If `context` is null, removes all
     216  // callbacks with that function. If `callback` is null, removes all
     217  // callbacks for the event. If `name` is null, removes all bound
     218  // callbacks for all events.
     219  Events.off =  function(name, callback, context) {
     220    if (!this._events) return this;
     221    this._events = eventsApi(offApi, this._events, name, callback, {
     222        context: context,
     223        listeners: this._listeners
     224    });
     225    return this;
     226  };
     227
     228  // Tell this object to stop listening to either specific events ... or
     229  // to every object it's currently listening to.
     230  Events.stopListening =  function(obj, name, callback) {
     231    var listeningTo = this._listeningTo;
     232    if (!listeningTo) return this;
     233
     234    var ids = obj ? [obj._listenId] : _.keys(listeningTo);
     235
     236    for (var i = 0; i < ids.length; i++) {
     237      var listening = listeningTo[ids[i]];
     238
     239      // If listening doesn't exist, this object is not currently
     240      // listening to obj. Break out early.
     241      if (!listening) break;
     242
     243      listening.obj.off(name, callback, this);
     244    }
     245    if (_.isEmpty(listeningTo)) this._listeningTo = void 0;
     246
     247    return this;
     248  };
     249
     250  // The reducing API that removes a callback from the `events` object.
     251  var offApi = function(events, name, callback, options) {
     252    if (!events) return;
     253
     254    var i = 0, listening;
     255    var context = options.context, listeners = options.listeners;
     256
     257    // Delete all events listeners and "drop" events.
     258    if (!name && !callback && !context) {
     259      var ids = _.keys(listeners);
     260      for (; i < ids.length; i++) {
     261        listening = listeners[ids[i]];
     262        delete listeners[listening.id];
     263        delete listening.listeningTo[listening.objId];
    184264      }
    185       return false;
     265      return;
    186266    }
    187267
    188     // Handle space separated event names.
    189     if (eventSplitter.test(name)) {
    190       var names = name.split(eventSplitter);
    191       for (var i = 0, l = names.length; i < l; i++) {
    192         obj[action].apply(obj, [names[i]].concat(rest));
     268    var names = name ? [name] : _.keys(events);
     269    for (; i < names.length; i++) {
     270      name = names[i];
     271      var handlers = events[name];
     272
     273      // Bail out if there are no events stored.
     274      if (!handlers) break;
     275
     276      // Replace events if there are any remaining.  Otherwise, clean up.
     277      var remaining = [];
     278      for (var j = 0; j < handlers.length; j++) {
     279        var handler = handlers[j];
     280        if (
     281          callback && callback !== handler.callback &&
     282            callback !== handler.callback._callback ||
     283              context && context !== handler.context
     284        ) {
     285          remaining.push(handler);
     286        } else {
     287          listening = handler.listening;
     288          if (listening && --listening.count === 0) {
     289            delete listeners[listening.id];
     290            delete listening.listeningTo[listening.objId];
     291          }
     292        }
    193293      }
    194       return false;
     294
     295      // Update tail event if the list has any events.  Otherwise, clean up.
     296      if (remaining.length) {
     297        events[name] = remaining;
     298      } else {
     299        delete events[name];
     300      }
    195301    }
     302    if (_.size(events)) return events;
     303  };
    196304
    197     return true;
     305  // Bind an event to only be triggered a single time. After the first time
     306  // the callback is invoked, its listener will be removed. If multiple events
     307  // are passed in using the space-separated syntax, the handler will fire
     308  // once for each event, not once for a combination of all events.
     309  Events.once =  function(name, callback, context) {
     310    // Map the event into a `{event: once}` object.
     311    var events = eventsApi(onceMap, {}, name, callback, _.bind(this.off, this));
     312    return this.on(events, void 0, context);
    198313  };
    199314
     315  // Inversion-of-control versions of `once`.
     316  Events.listenToOnce =  function(obj, name, callback) {
     317    // Map the event into a `{event: once}` object.
     318    var events = eventsApi(onceMap, {}, name, callback, _.bind(this.stopListening, this, obj));
     319    return this.listenTo(obj, events);
     320  };
     321
     322  // Reduces the event callbacks into a map of `{event: onceWrapper}`.
     323  // `offer` unbinds the `onceWrapper` after it has been called.
     324  var onceMap = function(map, name, callback, offer) {
     325    if (callback) {
     326      var once = map[name] = _.once(function() {
     327        offer(name, once);
     328        callback.apply(this, arguments);
     329      });
     330      once._callback = callback;
     331    }
     332    return map;
     333  };
     334
     335  // Trigger one or many events, firing all bound callbacks. Callbacks are
     336  // passed the same arguments as `trigger` is, apart from the event name
     337  // (unless you're listening on `"all"`, which will cause your callback to
     338  // receive the true name of the event as the first argument).
     339  Events.trigger =  function(name) {
     340    if (!this._events) return this;
     341
     342    var length = Math.max(0, arguments.length - 1);
     343    var args = Array(length);
     344    for (var i = 0; i < length; i++) args[i] = arguments[i + 1];
     345
     346    eventsApi(triggerApi, this._events, name, void 0, args);
     347    return this;
     348  };
     349
     350  // Handles triggering the appropriate event callbacks.
     351  var triggerApi = function(objEvents, name, cb, args) {
     352    if (objEvents) {
     353      var events = objEvents[name];
     354      var allEvents = objEvents.all;
     355      if (events && allEvents) allEvents = allEvents.slice();
     356      if (events) triggerEvents(events, args);
     357      if (allEvents) triggerEvents(allEvents, [name].concat(args));
     358    }
     359    return objEvents;
     360  };
     361
    200362  // A difficult-to-believe, but optimized internal dispatch function for
    201363  // triggering events. Tries to keep the usual cases speedy (most internal
    202364  // Backbone events have 3 arguments).
     
    211373    }
    212374  };
    213375
    214   var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
    215 
    216   // Inversion-of-control versions of `on` and `once`. Tell *this* object to
    217   // listen to an event in another object ... keeping track of what it's
    218   // listening to.
    219   _.each(listenMethods, function(implementation, method) {
    220     Events[method] = function(obj, name, callback) {
    221       var listeningTo = this._listeningTo || (this._listeningTo = {});
    222       var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
    223       listeningTo[id] = obj;
    224       if (!callback && typeof name === 'object') callback = this;
    225       obj[implementation](name, callback, this);
    226       return this;
    227     };
    228   });
    229 
    230376  // Aliases for backwards compatibility.
    231377  Events.bind   = Events.on;
    232378  Events.unbind = Events.off;
     
    248394  var Model = Backbone.Model = function(attributes, options) {
    249395    var attrs = attributes || {};
    250396    options || (options = {});
    251     this.cid = _.uniqueId('c');
     397    this.cid = _.uniqueId(this.cidPrefix);
    252398    this.attributes = {};
    253399    if (options.collection) this.collection = options.collection;
    254400    if (options.parse) attrs = this.parse(attrs, options) || {};
     
    271417    // CouchDB users may want to set this to `"_id"`.
    272418    idAttribute: 'id',
    273419
     420    // The prefix is used to create the client id which is used to identify models locally.
     421    // You may want to override this if you're experiencing name clashes with model ids.
     422    cidPrefix: 'c',
     423
    274424    // Initialize is an empty function by default. Override it with your own
    275425    // initialization logic.
    276426    initialize: function(){},
     
    302452      return this.get(attr) != null;
    303453    },
    304454
     455    // Special-cased proxy to underscore's `_.matches` method.
     456    matches: function(attrs) {
     457      return !!_.iteratee(attrs, this)(this.attributes);
     458    },
     459
    305460    // Set a hash of model attributes on the object, firing `"change"`. This is
    306461    // the core primitive operation of a model, updating the data and notifying
    307462    // anyone who needs to know about the change in state. The heart of the beast.
    308463    set: function(key, val, options) {
    309       var attr, attrs, unset, changes, silent, changing, prev, current;
    310464      if (key == null) return this;
    311465
    312466      // Handle both `"key", value` and `{key: value}` -style arguments.
     467      var attrs;
    313468      if (typeof key === 'object') {
    314469        attrs = key;
    315470        options = val;
     
    323478      if (!this._validate(attrs, options)) return false;
    324479
    325480      // Extract attributes and options.
    326       unset           = options.unset;
    327       silent          = options.silent;
    328       changes         = [];
    329       changing        = this._changing;
    330       this._changing  = true;
     481      var unset      = options.unset;
     482      var silent     = options.silent;
     483      var changes    = [];
     484      var changing   = this._changing;
     485      this._changing = true;
    331486
    332487      if (!changing) {
    333488        this._previousAttributes = _.clone(this.attributes);
    334489        this.changed = {};
    335490      }
    336       current = this.attributes, prev = this._previousAttributes;
    337491
    338       // Check for changes of `id`.
    339       if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
     492      var current = this.attributes;
     493      var changed = this.changed;
     494      var prev    = this._previousAttributes;
    340495
    341496      // For each `set` attribute, update or delete the current value.
    342       for (attr in attrs) {
     497      for (var attr in attrs) {
    343498        val = attrs[attr];
    344499        if (!_.isEqual(current[attr], val)) changes.push(attr);
    345500        if (!_.isEqual(prev[attr], val)) {
    346           this.changed[attr] = val;
     501          changed[attr] = val;
    347502        } else {
    348           delete this.changed[attr];
     503          delete changed[attr];
    349504        }
    350505        unset ? delete current[attr] : current[attr] = val;
    351506      }
    352507
     508      // Update the `id`.
     509      this.id = this.get(this.idAttribute);
     510
    353511      // Trigger all relevant attribute changes.
    354512      if (!silent) {
    355513        if (changes.length) this._pending = options;
    356         for (var i = 0, l = changes.length; i < l; i++) {
     514        for (var i = 0; i < changes.length; i++) {
    357515          this.trigger('change:' + changes[i], this, current[changes[i]], options);
    358516        }
    359517      }
     
    401559    // determining if there *would be* a change.
    402560    changedAttributes: function(diff) {
    403561      if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
    404       var val, changed = false;
    405562      var old = this._changing ? this._previousAttributes : this.attributes;
     563      var changed = {};
    406564      for (var attr in diff) {
    407         if (_.isEqual(old[attr], (val = diff[attr]))) continue;
    408         (changed || (changed = {}))[attr] = val;
     565        var val = diff[attr];
     566        if (_.isEqual(old[attr], val)) continue;
     567        changed[attr] = val;
    409568      }
    410       return changed;
     569      return _.size(changed) ? changed : false;
    411570    },
    412571
    413572    // Get the previous value of an attribute, recorded at the time the last
     
    423582      return _.clone(this._previousAttributes);
    424583    },
    425584
    426     // Fetch the model from the server. If the server's representation of the
    427     // model differs from its current attributes, they will be overridden,
    428     // triggering a `"change"` event.
     585    // Fetch the model from the server, merging the response with the model's
     586    // local attributes. Any changed attributes will trigger a "change" event.
    429587    fetch: function(options) {
    430       options = options ? _.clone(options) : {};
    431       if (options.parse === void 0) options.parse = true;
     588      options = _.extend({parse: true}, options);
    432589      var model = this;
    433590      var success = options.success;
    434591      options.success = function(resp) {
    435         if (!model.set(model.parse(resp, options), options)) return false;
    436         if (success) success(model, resp, options);
     592        var serverAttrs = options.parse ? model.parse(resp, options) : resp;
     593        if (!model.set(serverAttrs, options)) return false;
     594        if (success) success.call(options.context, model, resp, options);
    437595        model.trigger('sync', model, resp, options);
    438596      };
    439597      wrapError(this, options);
     
    444602    // If the server returns an attributes hash that differs, the model's
    445603    // state will be `set` again.
    446604    save: function(key, val, options) {
    447       var attrs, method, xhr, attributes = this.attributes;
    448 
    449605      // Handle both `"key", value` and `{key: value}` -style arguments.
     606      var attrs;
    450607      if (key == null || typeof key === 'object') {
    451608        attrs = key;
    452609        options = val;
     
    454611        (attrs = {})[key] = val;
    455612      }
    456613
    457       options = _.extend({validate: true}, options);
     614      options = _.extend({validate: true, parse: true}, options);
     615      var wait = options.wait;
    458616
    459617      // If we're not waiting and attributes exist, save acts as
    460618      // `set(attr).save(null, opts)` with validation. Otherwise, check if
    461619      // the model will be valid when the attributes, if any, are set.
    462       if (attrs && !options.wait) {
     620      if (attrs && !wait) {
    463621        if (!this.set(attrs, options)) return false;
    464622      } else {
    465623        if (!this._validate(attrs, options)) return false;
    466624      }
    467625
    468       // Set temporary attributes if `{wait: true}`.
    469       if (attrs && options.wait) {
    470         this.attributes = _.extend({}, attributes, attrs);
    471       }
    472 
    473626      // After a successful server-side save, the client is (optionally)
    474627      // updated with the server-side state.
    475       if (options.parse === void 0) options.parse = true;
    476628      var model = this;
    477629      var success = options.success;
     630      var attributes = this.attributes;
    478631      options.success = function(resp) {
    479632        // Ensure attributes are restored during synchronous saves.
    480633        model.attributes = attributes;
    481         var serverAttrs = model.parse(resp, options);
    482         if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
    483         if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
    484           return false;
    485         }
    486         if (success) success(model, resp, options);
     634        var serverAttrs = options.parse ? model.parse(resp, options) : resp;
     635        if (wait) serverAttrs = _.extend({}, attrs, serverAttrs);
     636        if (serverAttrs && !model.set(serverAttrs, options)) return false;
     637        if (success) success.call(options.context, model, resp, options);
    487638        model.trigger('sync', model, resp, options);
    488639      };
    489640      wrapError(this, options);
    490641
    491       method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
    492       if (method === 'patch') options.attrs = attrs;
    493       xhr = this.sync(method, this, options);
     642      // Set temporary attributes if `{wait: true}` to properly find new ids.
     643      if (attrs && wait) this.attributes = _.extend({}, attributes, attrs);
    494644
     645      var method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
     646      if (method === 'patch' && !options.attrs) options.attrs = attrs;
     647      var xhr = this.sync(method, this, options);
     648
    495649      // Restore attributes.
    496       if (attrs && options.wait) this.attributes = attributes;
     650      this.attributes = attributes;
    497651
    498652      return xhr;
    499653    },
     
    505659      options = options ? _.clone(options) : {};
    506660      var model = this;
    507661      var success = options.success;
     662      var wait = options.wait;
    508663
    509664      var destroy = function() {
     665        model.stopListening();
    510666        model.trigger('destroy', model, model.collection, options);
    511667      };
    512668
    513669      options.success = function(resp) {
    514         if (options.wait || model.isNew()) destroy();
    515         if (success) success(model, resp, options);
     670        if (wait) destroy();
     671        if (success) success.call(options.context, model, resp, options);
    516672        if (!model.isNew()) model.trigger('sync', model, resp, options);
    517673      };
    518674
     675      var xhr = false;
    519676      if (this.isNew()) {
    520         options.success();
    521         return false;
     677        _.defer(options.success);
     678      } else {
     679        wrapError(this, options);
     680        xhr = this.sync('delete', this, options);
    522681      }
    523       wrapError(this, options);
    524 
    525       var xhr = this.sync('delete', this, options);
    526       if (!options.wait) destroy();
     682      if (!wait) destroy();
    527683      return xhr;
    528684    },
    529685
     
    536692        _.result(this.collection, 'url') ||
    537693        urlError();
    538694      if (this.isNew()) return base;
    539       return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
     695      var id = this.get(this.idAttribute);
     696      return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
    540697    },
    541698
    542699    // **parse** converts a response into the hash of attributes to be `set` on
     
    557714
    558715    // Check if the model is currently in a valid state.
    559716    isValid: function(options) {
    560       return this._validate({}, _.extend(options || {}, { validate: true }));
     717      return this._validate({}, _.defaults({validate: true}, options));
    561718    },
    562719
    563720    // Run validation against the next complete set of model attributes,
     
    573730
    574731  });
    575732
    576   // Underscore methods that we want to implement on the Model.
    577   var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
     733  // Underscore methods that we want to implement on the Model, mapped to the
     734  // number of arguments they take.
     735  var modelMethods = { keys: 1, values: 1, pairs: 1, invert: 1, pick: 0,
     736      omit: 0, chain: 1, isEmpty: 1 };
    578737
    579738  // Mix in each Underscore method as a proxy to `Model#attributes`.
    580   _.each(modelMethods, function(method) {
    581     Model.prototype[method] = function() {
    582       var args = slice.call(arguments);
    583       args.unshift(this.attributes);
    584       return _[method].apply(_, args);
    585     };
    586   });
     739  addUnderscoreMethods(Model, modelMethods, 'attributes');
    587740
    588741  // Backbone.Collection
    589742  // -------------------
    590743
    591744  // If models tend to represent a single row of data, a Backbone Collection is
    592   // more analagous to a table full of data ... or a small slice or page of that
     745  // more analogous to a table full of data ... or a small slice or page of that
    593746  // table, or a collection of rows that belong together for a particular reason
    594747  // -- all of the messages in this particular folder, all of the documents
    595748  // belonging to this particular author, and so on. Collections maintain
     
    611764  var setOptions = {add: true, remove: true, merge: true};
    612765  var addOptions = {add: true, remove: false};
    613766
     767  // Splices `insert` into `array` at index `at`.
     768  var splice = function(array, insert, at) {
     769    at = Math.min(Math.max(at, 0), array.length);
     770    var tail = Array(array.length - at);
     771    var length = insert.length;
     772    for (var i = 0; i < tail.length; i++) tail[i] = array[i + at];
     773    for (i = 0; i < length; i++) array[i + at] = insert[i];
     774    for (i = 0; i < tail.length; i++) array[i + length + at] = tail[i];
     775  };
     776
    614777  // Define the Collection's inheritable methods.
    615778  _.extend(Collection.prototype, Events, {
    616779
     
    625788    // The JSON representation of a Collection is an array of the
    626789    // models' attributes.
    627790    toJSON: function(options) {
    628       return this.map(function(model){ return model.toJSON(options); });
     791      return this.map(function(model) { return model.toJSON(options); });
    629792    },
    630793
    631794    // Proxy `Backbone.sync` by default.
     
    633796      return Backbone.sync.apply(this, arguments);
    634797    },
    635798
    636     // Add a model, or list of models to the set.
     799    // Add a model, or list of models to the set. `models` may be Backbone
     800    // Models or raw JavaScript objects to be converted to Models, or any
     801    // combination of the two.
    637802    add: function(models, options) {
    638803      return this.set(models, _.extend({merge: false}, options, addOptions));
    639804    },
     
    640805
    641806    // Remove a model, or a list of models from the set.
    642807    remove: function(models, options) {
     808      options = _.extend({}, options);
    643809      var singular = !_.isArray(models);
    644810      models = singular ? [models] : _.clone(models);
    645       options || (options = {});
    646       var i, l, index, model;
    647       for (i = 0, l = models.length; i < l; i++) {
    648         model = models[i] = this.get(models[i]);
    649         if (!model) continue;
    650         delete this._byId[model.id];
    651         delete this._byId[model.cid];
    652         index = this.indexOf(model);
    653         this.models.splice(index, 1);
    654         this.length--;
    655         if (!options.silent) {
    656           options.index = index;
    657           model.trigger('remove', model, this, options);
    658         }
    659         this._removeReference(model, options);
    660       }
    661       return singular ? models[0] : models;
     811      var removed = this._removeModels(models, options);
     812      if (!options.silent && removed) this.trigger('update', this, options);
     813      return singular ? removed[0] : removed;
    662814    },
    663815
    664816    // Update a collection by `set`-ing a new list of models, adding new ones,
     
    666818    // already exist in the collection, as necessary. Similar to **Model#set**,
    667819    // the core operation for updating the data contained by the collection.
    668820    set: function(models, options) {
     821      if (models == null) return;
     822
    669823      options = _.defaults({}, options, setOptions);
    670       if (options.parse) models = this.parse(models, options);
     824      if (options.parse && !this._isModel(models)) models = this.parse(models, options);
     825
    671826      var singular = !_.isArray(models);
    672       models = singular ? (models ? [models] : []) : _.clone(models);
    673       var i, l, id, model, attrs, existing, sort;
     827      models = singular ? [models] : models.slice();
     828
    674829      var at = options.at;
    675       var targetModel = this.model;
     830      if (at != null) at = +at;
     831      if (at < 0) at += this.length + 1;
     832
     833      var set = [];
     834      var toAdd = [];
     835      var toRemove = [];
     836      var modelMap = {};
     837
     838      var add = options.add;
     839      var merge = options.merge;
     840      var remove = options.remove;
     841
     842      var sort = false;
    676843      var sortable = this.comparator && (at == null) && options.sort !== false;
    677844      var sortAttr = _.isString(this.comparator) ? this.comparator : null;
    678       var toAdd = [], toRemove = [], modelMap = {};
    679       var add = options.add, merge = options.merge, remove = options.remove;
    680       var order = !sortable && add && remove ? [] : false;
    681845
    682846      // Turn bare objects into model references, and prevent invalid models
    683847      // from being added.
    684       for (i = 0, l = models.length; i < l; i++) {
    685         attrs = models[i] || {};
    686         if (attrs instanceof Model) {
    687           id = model = attrs;
    688         } else {
    689           id = attrs[targetModel.prototype.idAttribute || 'id'];
    690         }
     848      var model;
     849      for (var i = 0; i < models.length; i++) {
     850        model = models[i];
    691851
    692852        // If a duplicate is found, prevent it from being added and
    693853        // optionally merge it into the existing model.
    694         if (existing = this.get(id)) {
    695           if (remove) modelMap[existing.cid] = true;
    696           if (merge) {
    697             attrs = attrs === model ? model.attributes : attrs;
     854        var existing = this.get(model);
     855        if (existing) {
     856          if (merge && model !== existing) {
     857            var attrs = this._isModel(model) ? model.attributes : model;
    698858            if (options.parse) attrs = existing.parse(attrs, options);
    699859            existing.set(attrs, options);
    700             if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
     860            if (sortable && !sort) sort = existing.hasChanged(sortAttr);
    701861          }
     862          if (!modelMap[existing.cid]) {
     863            modelMap[existing.cid] = true;
     864            set.push(existing);
     865          }
    702866          models[i] = existing;
    703867
    704868        // If this is a new, valid model, push it to the `toAdd` list.
    705869        } else if (add) {
    706           model = models[i] = this._prepareModel(attrs, options);
    707           if (!model) continue;
    708           toAdd.push(model);
    709           this._addReference(model, options);
     870          model = models[i] = this._prepareModel(model, options);
     871          if (model) {
     872            toAdd.push(model);
     873            this._addReference(model, options);
     874            modelMap[model.cid] = true;
     875            set.push(model);
     876          }
    710877        }
    711 
    712         // Do not add multiple models with the same `id`.
    713         model = existing || model;
    714         if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
    715         modelMap[model.id] = true;
    716878      }
    717879
    718       // Remove nonexistent models if appropriate.
     880      // Remove stale models.
    719881      if (remove) {
    720         for (i = 0, l = this.length; i < l; ++i) {
    721           if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
     882        for (i = 0; i < this.length; i++) {
     883          model = this.models[i];
     884          if (!modelMap[model.cid]) toRemove.push(model);
    722885        }
    723         if (toRemove.length) this.remove(toRemove, options);
     886        if (toRemove.length) this._removeModels(toRemove, options);
    724887      }
    725888
    726889      // See if sorting is needed, update `length` and splice in new models.
    727       if (toAdd.length || (order && order.length)) {
     890      var orderChanged = false;
     891      var replace = !sortable && add && remove;
     892      if (set.length && replace) {
     893        orderChanged = this.length != set.length || _.some(this.models, function(model, index) {
     894          return model !== set[index];
     895        });
     896        this.models.length = 0;
     897        splice(this.models, set, 0);
     898        this.length = this.models.length;
     899      } else if (toAdd.length) {
    728900        if (sortable) sort = true;
    729         this.length += toAdd.length;
    730         if (at != null) {
    731           for (i = 0, l = toAdd.length; i < l; i++) {
    732             this.models.splice(at + i, 0, toAdd[i]);
    733           }
    734         } else {
    735           if (order) this.models.length = 0;
    736           var orderedModels = order || toAdd;
    737           for (i = 0, l = orderedModels.length; i < l; i++) {
    738             this.models.push(orderedModels[i]);
    739           }
    740         }
     901        splice(this.models, toAdd, at == null ? this.length : at);
     902        this.length = this.models.length;
    741903      }
    742904
    743905      // Silently sort the collection if appropriate.
     
    745907
    746908      // Unless silenced, it's time to fire all appropriate add/sort events.
    747909      if (!options.silent) {
    748         for (i = 0, l = toAdd.length; i < l; i++) {
    749           (model = toAdd[i]).trigger('add', model, this, options);
     910        for (i = 0; i < toAdd.length; i++) {
     911          if (at != null) options.index = at + i;
     912          model = toAdd[i];
     913          model.trigger('add', model, this, options);
    750914        }
    751         if (sort || (order && order.length)) this.trigger('sort', this, options);
     915        if (sort || orderChanged) this.trigger('sort', this, options);
     916        if (toAdd.length || toRemove.length) this.trigger('update', this, options);
    752917      }
    753918
    754919      // Return the added (or merged) model (or models).
     
    760925    // any granular `add` or `remove` events. Fires `reset` when finished.
    761926    // Useful for bulk operations and optimizations.
    762927    reset: function(models, options) {
    763       options || (options = {});
    764       for (var i = 0, l = this.models.length; i < l; i++) {
     928      options = options ? _.clone(options) : {};
     929      for (var i = 0; i < this.models.length; i++) {
    765930        this._removeReference(this.models[i], options);
    766931      }
    767932      options.previousModels = this.models;
     
    779944    // Remove a model from the end of the collection.
    780945    pop: function(options) {
    781946      var model = this.at(this.length - 1);
    782       this.remove(model, options);
    783       return model;
     947      return this.remove(model, options);
    784948    },
    785949
    786950    // Add a model to the beginning of the collection.
     
    791955    // Remove a model from the beginning of the collection.
    792956    shift: function(options) {
    793957      var model = this.at(0);
    794       this.remove(model, options);
    795       return model;
     958      return this.remove(model, options);
    796959    },
    797960
    798961    // Slice out a sub-array of models from the collection.
     
    803966    // Get a model from the set by id.
    804967    get: function(obj) {
    805968      if (obj == null) return void 0;
    806       return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
     969      var id = this.modelId(this._isModel(obj) ? obj.attributes : obj);
     970      return this._byId[obj] || this._byId[id] || this._byId[obj.cid];
    807971    },
    808972
    809973    // Get the model at the given index.
    810974    at: function(index) {
     975      if (index < 0) index += this.length;
    811976      return this.models[index];
    812977    },
    813978
     
    814979    // Return models with matching attributes. Useful for simple cases of
    815980    // `filter`.
    816981    where: function(attrs, first) {
    817       if (_.isEmpty(attrs)) return first ? void 0 : [];
    818       return this[first ? 'find' : 'filter'](function(model) {
    819         for (var key in attrs) {
    820           if (attrs[key] !== model.get(key)) return false;
    821         }
    822         return true;
    823       });
     982      return this[first ? 'find' : 'filter'](attrs);
    824983    },
    825984
    826985    // Return the first model with matching attributes. Useful for simple cases
     
    833992    // normal circumstances, as the set will maintain sort order as each item
    834993    // is added.
    835994    sort: function(options) {
    836       if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
     995      var comparator = this.comparator;
     996      if (!comparator) throw new Error('Cannot sort a set without a comparator');
    837997      options || (options = {});
    838998
     999      var length = comparator.length;
     1000      if (_.isFunction(comparator)) comparator = _.bind(comparator, this);
     1001
    8391002      // Run sort based on type of `comparator`.
    840       if (_.isString(this.comparator) || this.comparator.length === 1) {
    841         this.models = this.sortBy(this.comparator, this);
     1003      if (length === 1 || _.isString(comparator)) {
     1004        this.models = this.sortBy(comparator);
    8421005      } else {
    843         this.models.sort(_.bind(this.comparator, this));
     1006        this.models.sort(comparator);
    8441007      }
    845 
    8461008      if (!options.silent) this.trigger('sort', this, options);
    8471009      return this;
    8481010    },
     
    8561018    // collection when they arrive. If `reset: true` is passed, the response
    8571019    // data will be passed through the `reset` method instead of `set`.
    8581020    fetch: function(options) {
    859       options = options ? _.clone(options) : {};
    860       if (options.parse === void 0) options.parse = true;
     1021      options = _.extend({parse: true}, options);
    8611022      var success = options.success;
    8621023      var collection = this;
    8631024      options.success = function(resp) {
    8641025        var method = options.reset ? 'reset' : 'set';
    8651026        collection[method](resp, options);
    866         if (success) success(collection, resp, options);
     1027        if (success) success.call(options.context, collection, resp, options);
    8671028        collection.trigger('sync', collection, resp, options);
    8681029      };
    8691030      wrapError(this, options);
     
    8751036    // wait for the server to agree.
    8761037    create: function(model, options) {
    8771038      options = options ? _.clone(options) : {};
    878       if (!(model = this._prepareModel(model, options))) return false;
    879       if (!options.wait) this.add(model, options);
     1039      var wait = options.wait;
     1040      model = this._prepareModel(model, options);
     1041      if (!model) return false;
     1042      if (!wait) this.add(model, options);
    8801043      var collection = this;
    8811044      var success = options.success;
    882       options.success = function(model, resp) {
    883         if (options.wait) collection.add(model, options);
    884         if (success) success(model, resp, options);
     1045      options.success = function(model, resp, callbackOpts) {
     1046        if (wait) collection.add(model, callbackOpts);
     1047        if (success) success.call(callbackOpts.context, model, resp, callbackOpts);
    8851048      };
    8861049      model.save(null, options);
    8871050      return model;
     
    8951058
    8961059    // Create a new collection with an identical list of models as this one.
    8971060    clone: function() {
    898       return new this.constructor(this.models);
     1061      return new this.constructor(this.models, {
     1062        model: this.model,
     1063        comparator: this.comparator
     1064      });
    8991065    },
    9001066
     1067    // Define how to uniquely identify models in the collection.
     1068    modelId: function (attrs) {
     1069      return attrs[this.model.prototype.idAttribute || 'id'];
     1070    },
     1071
    9011072    // Private method to reset all internal state. Called when the collection
    9021073    // is first initialized or reset.
    9031074    _reset: function() {
     
    9091080    // Prepare a hash of attributes (or other model) to be added to this
    9101081    // collection.
    9111082    _prepareModel: function(attrs, options) {
    912       if (attrs instanceof Model) return attrs;
     1083      if (this._isModel(attrs)) {
     1084        if (!attrs.collection) attrs.collection = this;
     1085        return attrs;
     1086      }
    9131087      options = options ? _.clone(options) : {};
    9141088      options.collection = this;
    9151089      var model = new this.model(attrs, options);
     
    9181092      return false;
    9191093    },
    9201094
     1095    // Internal method called by both remove and set.
     1096    _removeModels: function(models, options) {
     1097      var removed = [];
     1098      for (var i = 0; i < models.length; i++) {
     1099        var model = this.get(models[i]);
     1100        if (!model) continue;
     1101
     1102        var index = this.indexOf(model);
     1103        this.models.splice(index, 1);
     1104        this.length--;
     1105
     1106        if (!options.silent) {
     1107          options.index = index;
     1108          model.trigger('remove', model, this, options);
     1109        }
     1110
     1111        removed.push(model);
     1112        this._removeReference(model, options);
     1113      }
     1114      return removed.length ? removed : false;
     1115    },
     1116
     1117    // Method for checking whether an object should be considered a model for
     1118    // the purposes of adding to the collection.
     1119    _isModel: function (model) {
     1120      return model instanceof Model;
     1121    },
     1122
    9211123    // Internal method to create a model's ties to a collection.
    9221124    _addReference: function(model, options) {
    9231125      this._byId[model.cid] = model;
    924       if (model.id != null) this._byId[model.id] = model;
    925       if (!model.collection) model.collection = this;
     1126      var id = this.modelId(model.attributes);
     1127      if (id != null) this._byId[id] = model;
    9261128      model.on('all', this._onModelEvent, this);
    9271129    },
    9281130
    9291131    // Internal method to sever a model's ties to a collection.
    9301132    _removeReference: function(model, options) {
     1133      delete this._byId[model.cid];
     1134      var id = this.modelId(model.attributes);
     1135      if (id != null) delete this._byId[id];
    9311136      if (this === model.collection) delete model.collection;
    9321137      model.off('all', this._onModelEvent, this);
    9331138    },
     
    9391144    _onModelEvent: function(event, model, collection, options) {
    9401145      if ((event === 'add' || event === 'remove') && collection !== this) return;
    9411146      if (event === 'destroy') this.remove(model, options);
    942       if (model && event === 'change:' + model.idAttribute) {
    943         delete this._byId[model.previous(model.idAttribute)];
    944         if (model.id != null) this._byId[model.id] = model;
     1147      if (event === 'change') {
     1148        var prevId = this.modelId(model.previousAttributes());
     1149        var id = this.modelId(model.attributes);
     1150        if (prevId !== id) {
     1151          if (prevId != null) delete this._byId[prevId];
     1152          if (id != null) this._byId[id] = model;
     1153        }
    9451154      }
    9461155      this.trigger.apply(this, arguments);
    9471156    }
     
    9511160  // Underscore methods that we want to implement on the Collection.
    9521161  // 90% of the core usefulness of Backbone Collections is actually implemented
    9531162  // right here:
    954   var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
    955     'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
    956     'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
    957     'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
    958     'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
    959     'lastIndexOf', 'isEmpty', 'chain', 'sample'];
     1163  var collectionMethods = { forEach: 3, each: 3, map: 3, collect: 3, reduce: 4,
     1164      foldl: 4, inject: 4, reduceRight: 4, foldr: 4, find: 3, detect: 3, filter: 3,
     1165      select: 3, reject: 3, every: 3, all: 3, some: 3, any: 3, include: 3, includes: 3,
     1166      contains: 3, invoke: 0, max: 3, min: 3, toArray: 1, size: 1, first: 3,
     1167      head: 3, take: 3, initial: 3, rest: 3, tail: 3, drop: 3, last: 3,
     1168      without: 0, difference: 0, indexOf: 3, shuffle: 1, lastIndexOf: 3,
     1169      isEmpty: 1, chain: 1, sample: 3, partition: 3, groupBy: 3, countBy: 3,
     1170      sortBy: 3, indexBy: 3};
    9601171
    9611172  // Mix in each Underscore method as a proxy to `Collection#models`.
    962   _.each(methods, function(method) {
    963     Collection.prototype[method] = function() {
    964       var args = slice.call(arguments);
    965       args.unshift(this.models);
    966       return _[method].apply(_, args);
    967     };
    968   });
     1173  addUnderscoreMethods(Collection, collectionMethods, 'models');
    9691174
    970   // Underscore methods that take a property name as an argument.
    971   var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
    972 
    973   // Use attributes instead of properties.
    974   _.each(attributeMethods, function(method) {
    975     Collection.prototype[method] = function(value, context) {
    976       var iterator = _.isFunction(value) ? value : function(model) {
    977         return model.get(value);
    978       };
    979       return _[method](this.models, iterator, context);
    980     };
    981   });
    982 
    9831175  // Backbone.View
    9841176  // -------------
    9851177
     
    9951187  // if an existing element is not provided...
    9961188  var View = Backbone.View = function(options) {
    9971189    this.cid = _.uniqueId('view');
    998     options || (options = {});
    9991190    _.extend(this, _.pick(options, viewOptions));
    10001191    this._ensureElement();
    10011192    this.initialize.apply(this, arguments);
    1002     this.delegateEvents();
    10031193  };
    10041194
    10051195  // Cached regex to split keys for `delegate`.
    10061196  var delegateEventSplitter = /^(\S+)\s*(.*)$/;
    10071197
    1008   // List of view options to be merged as properties.
     1198  // List of view options to be set as properties.
    10091199  var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
    10101200
    10111201  // Set up all inheritable **Backbone.View** properties and methods.
     
    10341224    // Remove this view by taking the element out of the DOM, and removing any
    10351225    // applicable Backbone.Events listeners.
    10361226    remove: function() {
    1037       this.$el.remove();
     1227      this._removeElement();
    10381228      this.stopListening();
    10391229      return this;
    10401230    },
    10411231
    1042     // Change the view's element (`this.el` property), including event
    1043     // re-delegation.
    1044     setElement: function(element, delegate) {
    1045       if (this.$el) this.undelegateEvents();
    1046       this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
    1047       this.el = this.$el[0];
    1048       if (delegate !== false) this.delegateEvents();
     1232    // Remove this view's element from the document and all event listeners
     1233    // attached to it. Exposed for subclasses using an alternative DOM
     1234    // manipulation API.
     1235    _removeElement: function() {
     1236      this.$el.remove();
     1237    },
     1238
     1239    // Change the view's element (`this.el` property) and re-delegate the
     1240    // view's events on the new element.
     1241    setElement: function(element) {
     1242      this.undelegateEvents();
     1243      this._setElement(element);
     1244      this.delegateEvents();
    10491245      return this;
    10501246    },
    10511247
     1248    // Creates the `this.el` and `this.$el` references for this view using the
     1249    // given `el`. `el` can be a CSS selector or an HTML string, a jQuery
     1250    // context or an element. Subclasses can override this to utilize an
     1251    // alternative DOM manipulation API and are only required to set the
     1252    // `this.el` property.
     1253    _setElement: function(el) {
     1254      this.$el = el instanceof Backbone.$ ? el : Backbone.$(el);
     1255      this.el = this.$el[0];
     1256    },
     1257
    10521258    // Set callbacks, where `this.events` is a hash of
    10531259    //
    10541260    // *{"event selector": "callback"}*
     
    10621268    // pairs. Callbacks will be bound to the view, with `this` set properly.
    10631269    // Uses event delegation for efficiency.
    10641270    // Omitting the selector binds the event to `this.el`.
    1065     // This only works for delegate-able events: not `focus`, `blur`, and
    1066     // not `change`, `submit`, and `reset` in Internet Explorer.
    10671271    delegateEvents: function(events) {
    1068       if (!(events || (events = _.result(this, 'events')))) return this;
     1272      events || (events = _.result(this, 'events'));
     1273      if (!events) return this;
    10691274      this.undelegateEvents();
    10701275      for (var key in events) {
    10711276        var method = events[key];
    1072         if (!_.isFunction(method)) method = this[events[key]];
     1277        if (!_.isFunction(method)) method = this[method];
    10731278        if (!method) continue;
    1074 
    10751279        var match = key.match(delegateEventSplitter);
    1076         var eventName = match[1], selector = match[2];
    1077         method = _.bind(method, this);
    1078         eventName += '.delegateEvents' + this.cid;
    1079         if (selector === '') {
    1080           this.$el.on(eventName, method);
    1081         } else {
    1082           this.$el.on(eventName, selector, method);
    1083         }
     1280        this.delegate(match[1], match[2], _.bind(method, this));
    10841281      }
    10851282      return this;
    10861283    },
    10871284
    1088     // Clears all callbacks previously bound to the view with `delegateEvents`.
     1285    // Add a single event listener to the view's element (or a child element
     1286    // using `selector`). This only works for delegate-able events: not `focus`,
     1287    // `blur`, and not `change`, `submit`, and `reset` in Internet Explorer.
     1288    delegate: function(eventName, selector, listener) {
     1289      this.$el.on(eventName + '.delegateEvents' + this.cid, selector, listener);
     1290      return this;
     1291    },
     1292
     1293    // Clears all callbacks previously bound to the view by `delegateEvents`.
    10891294    // You usually don't need to use this, but may wish to if you have multiple
    10901295    // Backbone views attached to the same DOM element.
    10911296    undelegateEvents: function() {
    1092       this.$el.off('.delegateEvents' + this.cid);
     1297      if (this.$el) this.$el.off('.delegateEvents' + this.cid);
    10931298      return this;
    10941299    },
    10951300
     1301    // A finer-grained `undelegateEvents` for removing a single delegated event.
     1302    // `selector` and `listener` are both optional.
     1303    undelegate: function(eventName, selector, listener) {
     1304      this.$el.off(eventName + '.delegateEvents' + this.cid, selector, listener);
     1305      return this;
     1306    },
     1307
     1308    // Produces a DOM element to be assigned to your view. Exposed for
     1309    // subclasses using an alternative DOM manipulation API.
     1310    _createElement: function(tagName) {
     1311      return document.createElement(tagName);
     1312    },
     1313
    10961314    // Ensure that the View has a DOM element to render into.
    10971315    // If `this.el` is a string, pass it through `$()`, take the first
    10981316    // matching element, and re-assign it to `el`. Otherwise, create
     
    11021320        var attrs = _.extend({}, _.result(this, 'attributes'));
    11031321        if (this.id) attrs.id = _.result(this, 'id');
    11041322        if (this.className) attrs['class'] = _.result(this, 'className');
    1105         var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
    1106         this.setElement($el, false);
     1323        this.setElement(this._createElement(_.result(this, 'tagName')));
     1324        this._setAttributes(attrs);
    11071325      } else {
    1108         this.setElement(_.result(this, 'el'), false);
     1326        this.setElement(_.result(this, 'el'));
    11091327      }
     1328    },
     1329
     1330    // Set attributes from a hash on this view's element.  Exposed for
     1331    // subclasses using an alternative DOM manipulation API.
     1332    _setAttributes: function(attributes) {
     1333      this.$el.attr(attributes);
    11101334    }
    11111335
    11121336  });
     
    11751399      params.processData = false;
    11761400    }
    11771401
    1178     // If we're sending a `PATCH` request, and we're in an old Internet Explorer
    1179     // that still has ActiveX enabled by default, override jQuery to use that
    1180     // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
    1181     if (params.type === 'PATCH' && noXhrPatch) {
    1182       params.xhr = function() {
    1183         return new ActiveXObject("Microsoft.XMLHTTP");
    1184       };
    1185     }
     1402    // Pass along `textStatus` and `errorThrown` from jQuery.
     1403    var error = options.error;
     1404    options.error = function(xhr, textStatus, errorThrown) {
     1405      options.textStatus = textStatus;
     1406      options.errorThrown = errorThrown;
     1407      if (error) error.call(options.context, xhr, textStatus, errorThrown);
     1408    };
    11861409
    11871410    // Make the request, allowing the user to override any Ajax options.
    11881411    var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
     
    11901413    return xhr;
    11911414  };
    11921415
    1193   var noXhrPatch =
    1194     typeof window !== 'undefined' && !!window.ActiveXObject &&
    1195       !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
    1196 
    11971416  // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
    11981417  var methodMap = {
    11991418    'create': 'POST',
     
    12511470      var router = this;
    12521471      Backbone.history.route(route, function(fragment) {
    12531472        var args = router._extractParameters(route, fragment);
    1254         router.execute(callback, args);
    1255         router.trigger.apply(router, ['route:' + name].concat(args));
    1256         router.trigger('route', name, args);
    1257         Backbone.history.trigger('route', router, name, args);
     1473        if (router.execute(callback, args, name) !== false) {
     1474          router.trigger.apply(router, ['route:' + name].concat(args));
     1475          router.trigger('route', name, args);
     1476          Backbone.history.trigger('route', router, name, args);
     1477        }
    12581478      });
    12591479      return this;
    12601480    },
     
    12611481
    12621482    // Execute a route handler with the provided parameters.  This is an
    12631483    // excellent place to do pre-route setup or post-route cleanup.
    1264     execute: function(callback, args) {
     1484    execute: function(callback, args, name) {
    12651485      if (callback) callback.apply(this, args);
    12661486    },
    12671487
     
    13191539  // falls back to polling.
    13201540  var History = Backbone.History = function() {
    13211541    this.handlers = [];
    1322     _.bindAll(this, 'checkUrl');
     1542    this.checkUrl = _.bind(this.checkUrl, this);
    13231543
    13241544    // Ensure that `History` can be used outside of the browser.
    13251545    if (typeof window !== 'undefined') {
     
    13341554  // Cached regex for stripping leading and trailing slashes.
    13351555  var rootStripper = /^\/+|\/+$/g;
    13361556
    1337   // Cached regex for detecting MSIE.
    1338   var isExplorer = /msie [\w.]+/;
    1339 
    1340   // Cached regex for removing a trailing slash.
    1341   var trailingSlash = /\/$/;
    1342 
    13431557  // Cached regex for stripping urls of hash.
    13441558  var pathStripper = /#.*$/;
    13451559
     
    13551569
    13561570    // Are we at the app root?
    13571571    atRoot: function() {
    1358       return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
     1572      var path = this.location.pathname.replace(/[^\/]$/, '$&/');
     1573      return path === this.root && !this.getSearch();
    13591574    },
    13601575
     1576    // Does the pathname match the root?
     1577    matchRoot: function() {
     1578      var path = this.decodeFragment(this.location.pathname);
     1579      var root = path.slice(0, this.root.length - 1) + '/';
     1580      return root === this.root;
     1581    },
     1582
     1583    // Unicode characters in `location.pathname` are percent encoded so they're
     1584    // decoded for comparison. `%25` should not be decoded since it may be part
     1585    // of an encoded parameter.
     1586    decodeFragment: function(fragment) {
     1587      return decodeURI(fragment.replace(/%25/g, '%2525'));
     1588    },
     1589
     1590    // In IE6, the hash fragment and search params are incorrect if the
     1591    // fragment contains `?`.
     1592    getSearch: function() {
     1593      var match = this.location.href.replace(/#.*/, '').match(/\?.+/);
     1594      return match ? match[0] : '';
     1595    },
     1596
    13611597    // Gets the true hash value. Cannot use location.hash directly due to bug
    13621598    // in Firefox where location.hash will always be decoded.
    13631599    getHash: function(window) {
     
    13651601      return match ? match[1] : '';
    13661602    },
    13671603
    1368     // Get the cross-browser normalized URL fragment, either from the URL,
    1369     // the hash, or the override.
    1370     getFragment: function(fragment, forcePushState) {
     1604    // Get the pathname and search params, without the root.
     1605    getPath: function() {
     1606      var path = this.decodeFragment(
     1607        this.location.pathname + this.getSearch()
     1608      ).slice(this.root.length - 1);
     1609      return path.charAt(0) === '/' ? path.slice(1) : path;
     1610    },
     1611
     1612    // Get the cross-browser normalized URL fragment from the path or hash.
     1613    getFragment: function(fragment) {
    13711614      if (fragment == null) {
    1372         if (this._hasPushState || !this._wantsHashChange || forcePushState) {
    1373           fragment = decodeURI(this.location.pathname + this.location.search);
    1374           var root = this.root.replace(trailingSlash, '');
    1375           if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
     1615        if (this._usePushState || !this._wantsHashChange) {
     1616          fragment = this.getPath();
    13761617        } else {
    13771618          fragment = this.getHash();
    13781619        }
     
    13831624    // Start the hash change handling, returning `true` if the current URL matches
    13841625    // an existing route, and `false` otherwise.
    13851626    start: function(options) {
    1386       if (History.started) throw new Error("Backbone.history has already been started");
     1627      if (History.started) throw new Error('Backbone.history has already been started');
    13871628      History.started = true;
    13881629
    13891630      // Figure out the initial configuration. Do we need an iframe?
     
    13911632      this.options          = _.extend({root: '/'}, this.options, options);
    13921633      this.root             = this.options.root;
    13931634      this._wantsHashChange = this.options.hashChange !== false;
     1635      this._hasHashChange   = 'onhashchange' in window && (document.documentMode === void 0 || document.documentMode > 7);
     1636      this._useHashChange   = this._wantsHashChange && this._hasHashChange;
    13941637      this._wantsPushState  = !!this.options.pushState;
    1395       this._hasPushState    = !!(this.options.pushState && this.history && this.history.pushState);
    1396       var fragment          = this.getFragment();
    1397       var docMode           = document.documentMode;
    1398       var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
     1638      this._hasPushState    = !!(this.history && this.history.pushState);
     1639      this._usePushState    = this._wantsPushState && this._hasPushState;
     1640      this.fragment         = this.getFragment();
    13991641
    14001642      // Normalize root to always include a leading and trailing slash.
    14011643      this.root = ('/' + this.root + '/').replace(rootStripper, '/');
    14021644
    1403       if (oldIE && this._wantsHashChange) {
    1404         var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
    1405         this.iframe = frame.hide().appendTo('body')[0].contentWindow;
    1406         this.navigate(fragment);
    1407       }
    1408 
    1409       // Depending on whether we're using pushState or hashes, and whether
    1410       // 'onhashchange' is supported, determine how we check the URL state.
    1411       if (this._hasPushState) {
    1412         Backbone.$(window).on('popstate', this.checkUrl);
    1413       } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
    1414         Backbone.$(window).on('hashchange', this.checkUrl);
    1415       } else if (this._wantsHashChange) {
    1416         this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
    1417       }
    1418 
    1419       // Determine if we need to change the base url, for a pushState link
    1420       // opened by a non-pushState browser.
    1421       this.fragment = fragment;
    1422       var loc = this.location;
    1423 
    14241645      // Transition from hashChange to pushState or vice versa if both are
    14251646      // requested.
    14261647      if (this._wantsHashChange && this._wantsPushState) {
     
    14281649        // If we've started off with a route from a `pushState`-enabled
    14291650        // browser, but we're currently in a browser that doesn't support it...
    14301651        if (!this._hasPushState && !this.atRoot()) {
    1431           this.fragment = this.getFragment(null, true);
    1432           this.location.replace(this.root + '#' + this.fragment);
     1652          var root = this.root.slice(0, -1) || '/';
     1653          this.location.replace(root + '#' + this.getPath());
    14331654          // Return immediately as browser will do redirect to new url
    14341655          return true;
    14351656
    14361657        // Or if we've started out with a hash-based route, but we're currently
    14371658        // in a browser where it could be `pushState`-based instead...
    1438         } else if (this._hasPushState && this.atRoot() && loc.hash) {
    1439           this.fragment = this.getHash().replace(routeStripper, '');
    1440           this.history.replaceState({}, document.title, this.root + this.fragment);
     1659        } else if (this._hasPushState && this.atRoot()) {
     1660          this.navigate(this.getHash(), {replace: true});
    14411661        }
    14421662
    14431663      }
    14441664
     1665      // Proxy an iframe to handle location events if the browser doesn't
     1666      // support the `hashchange` event, HTML5 history, or the user wants
     1667      // `hashChange` but not `pushState`.
     1668      if (!this._hasHashChange && this._wantsHashChange && !this._usePushState) {
     1669        this.iframe = document.createElement('iframe');
     1670        this.iframe.src = 'javascript:0';
     1671        this.iframe.style.display = 'none';
     1672        this.iframe.tabIndex = -1;
     1673        var body = document.body;
     1674        // Using `appendChild` will throw on IE < 9 if the document is not ready.
     1675        var iWindow = body.insertBefore(this.iframe, body.firstChild).contentWindow;
     1676        iWindow.document.open();
     1677        iWindow.document.close();
     1678        iWindow.location.hash = '#' + this.fragment;
     1679      }
     1680
     1681      // Add a cross-platform `addEventListener` shim for older browsers.
     1682      var addEventListener = window.addEventListener || function (eventName, listener) {
     1683        return attachEvent('on' + eventName, listener);
     1684      };
     1685
     1686      // Depending on whether we're using pushState or hashes, and whether
     1687      // 'onhashchange' is supported, determine how we check the URL state.
     1688      if (this._usePushState) {
     1689        addEventListener('popstate', this.checkUrl, false);
     1690      } else if (this._useHashChange && !this.iframe) {
     1691        addEventListener('hashchange', this.checkUrl, false);
     1692      } else if (this._wantsHashChange) {
     1693        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
     1694      }
     1695
    14451696      if (!this.options.silent) return this.loadUrl();
    14461697    },
    14471698
     
    14481699    // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
    14491700    // but possibly useful for unit testing Routers.
    14501701    stop: function() {
    1451       Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
     1702      // Add a cross-platform `removeEventListener` shim for older browsers.
     1703      var removeEventListener = window.removeEventListener || function (eventName, listener) {
     1704        return detachEvent('on' + eventName, listener);
     1705      };
     1706
     1707      // Remove window listeners.
     1708      if (this._usePushState) {
     1709        removeEventListener('popstate', this.checkUrl, false);
     1710      } else if (this._useHashChange && !this.iframe) {
     1711        removeEventListener('hashchange', this.checkUrl, false);
     1712      }
     1713
     1714      // Clean up the iframe if necessary.
     1715      if (this.iframe) {
     1716        document.body.removeChild(this.iframe);
     1717        this.iframe = null;
     1718      }
     1719
     1720      // Some environments will throw when clearing an undefined interval.
    14521721      if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
    14531722      History.started = false;
    14541723    },
     
    14631732    // calls `loadUrl`, normalizing across the hidden iframe.
    14641733    checkUrl: function(e) {
    14651734      var current = this.getFragment();
     1735
     1736      // If the user pressed the back button, the iframe's hash will have
     1737      // changed and we should use that for comparison.
    14661738      if (current === this.fragment && this.iframe) {
    1467         current = this.getFragment(this.getHash(this.iframe));
     1739        current = this.getHash(this.iframe.contentWindow);
    14681740      }
     1741
    14691742      if (current === this.fragment) return false;
    14701743      if (this.iframe) this.navigate(current);
    14711744      this.loadUrl();
     
    14751748    // match, returns `true`. If no defined routes matches the fragment,
    14761749    // returns `false`.
    14771750    loadUrl: function(fragment) {
     1751      // If the root doesn't match, no routes can match either.
     1752      if (!this.matchRoot()) return false;
    14781753      fragment = this.fragment = this.getFragment(fragment);
    1479       return _.any(this.handlers, function(handler) {
     1754      return _.some(this.handlers, function(handler) {
    14801755        if (handler.route.test(fragment)) {
    14811756          handler.callback(fragment);
    14821757          return true;
     
    14951770      if (!History.started) return false;
    14961771      if (!options || options === true) options = {trigger: !!options};
    14971772
    1498       var url = this.root + (fragment = this.getFragment(fragment || ''));
     1773      // Normalize the fragment.
     1774      fragment = this.getFragment(fragment || '');
    14991775
    1500       // Strip the hash for matching.
    1501       fragment = fragment.replace(pathStripper, '');
     1776      // Don't include a trailing slash on the root.
     1777      var root = this.root;
     1778      if (fragment === '' || fragment.charAt(0) === '?') {
     1779        root = root.slice(0, -1) || '/';
     1780      }
     1781      var url = root + fragment;
    15021782
     1783      // Strip the hash and decode for matching.
     1784      fragment = this.decodeFragment(fragment.replace(pathStripper, ''));
     1785
    15031786      if (this.fragment === fragment) return;
    15041787      this.fragment = fragment;
    15051788
    1506       // Don't include a trailing slash on the root.
    1507       if (fragment === '' && url !== '/') url = url.slice(0, -1);
    1508 
    15091789      // If pushState is available, we use it to set the fragment as a real URL.
    1510       if (this._hasPushState) {
     1790      if (this._usePushState) {
    15111791        this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
    15121792
    15131793      // If hash changes haven't been explicitly disabled, update the hash
     
    15141794      // fragment to store history.
    15151795      } else if (this._wantsHashChange) {
    15161796        this._updateHash(this.location, fragment, options.replace);
    1517         if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
     1797        if (this.iframe && (fragment !== this.getHash(this.iframe.contentWindow))) {
     1798          var iWindow = this.iframe.contentWindow;
     1799
    15181800          // Opening and closing the iframe tricks IE7 and earlier to push a
    15191801          // history entry on hash-tag change.  When replace is true, we don't
    15201802          // want this.
    1521           if(!options.replace) this.iframe.document.open().close();
    1522           this._updateHash(this.iframe.location, fragment, options.replace);
     1803          if (!options.replace) {
     1804            iWindow.document.open();
     1805            iWindow.document.close();
     1806          }
     1807
     1808          this._updateHash(iWindow.location, fragment, options.replace);
    15231809        }
    15241810
    15251811      // If you've told us that you explicitly don't want fallback hashchange-
     
    15501836  // Helpers
    15511837  // -------
    15521838
    1553   // Helper function to correctly set up the prototype chain, for subclasses.
     1839  // Helper function to correctly set up the prototype chain for subclasses.
    15541840  // Similar to `goog.inherits`, but uses a hash of prototype properties and
    15551841  // class properties to be extended.
    15561842  var extend = function(protoProps, staticProps) {
     
    15591845
    15601846    // The constructor function for the new subclass is either defined by you
    15611847    // (the "constructor" property in your `extend` definition), or defaulted
    1562     // by us to simply call the parent's constructor.
     1848    // by us to simply call the parent constructor.
    15631849    if (protoProps && _.has(protoProps, 'constructor')) {
    15641850      child = protoProps.constructor;
    15651851    } else {
     
    15701856    _.extend(child, parent, staticProps);
    15711857
    15721858    // Set the prototype chain to inherit from `parent`, without calling
    1573     // `parent`'s constructor function.
     1859    // `parent` constructor function.
    15741860    var Surrogate = function(){ this.constructor = child; };
    15751861    Surrogate.prototype = parent.prototype;
    15761862    child.prototype = new Surrogate;
     
    15981884  var wrapError = function(model, options) {
    15991885    var error = options.error;
    16001886    options.error = function(resp) {
    1601       if (error) error(model, resp, options);
     1887      if (error) error.call(options.context, model, resp, options);
    16021888      model.trigger('error', model, resp, options);
    16031889    };
    16041890  };
  • src/wp-includes/js/backbone.min.js

     
    1 (function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});
     1(function(t){var e=typeof self=="object"&&self.self==self&&self||typeof global=="object"&&global.global==global&&global;if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,n){e.Backbone=t(e,n,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore"),r;try{r=require("jquery")}catch(n){}t(e,exports,i,r)}else{e.Backbone=t(e,{},e._,e.jQuery||e.Zepto||e.ender||e.$)}})(function(t,e,i,r){var n=t.Backbone;var s=Array.prototype.slice;e.VERSION="1.2.3";e.$=r;e.noConflict=function(){t.Backbone=n;return this};e.emulateHTTP=false;e.emulateJSON=false;var a=function(t,e,r){switch(t){case 1:return function(){return i[e](this[r])};case 2:return function(t){return i[e](this[r],t)};case 3:return function(t,n){return i[e](this[r],h(t,this),n)};case 4:return function(t,n,s){return i[e](this[r],h(t,this),n,s)};default:return function(){var t=s.call(arguments);t.unshift(this[r]);return i[e].apply(i,t)}}};var o=function(t,e,r){i.each(e,function(e,n){if(i[n])t.prototype[n]=a(e,n,r)})};var h=function(t,e){if(i.isFunction(t))return t;if(i.isObject(t)&&!e._isModel(t))return u(t);if(i.isString(t))return function(e){return e.get(t)};return t};var u=function(t){var e=i.matches(t);return function(t){return e(t.attributes)}};var l=e.Events={};var c=/\s+/;var f=function(t,e,r,n,s){var a=0,o;if(r&&typeof r==="object"){if(n!==void 0&&"context"in s&&s.context===void 0)s.context=n;for(o=i.keys(r);a<o.length;a++){e=f(t,e,o[a],r[o[a]],s)}}else if(r&&c.test(r)){for(o=r.split(c);a<o.length;a++){e=t(e,o[a],n,s)}}else{e=t(e,r,n,s)}return e};l.on=function(t,e,i){return d(this,t,e,i)};var d=function(t,e,i,r,n){t._events=f(v,t._events||{},e,i,{context:r,ctx:t,listening:n});if(n){var s=t._listeners||(t._listeners={});s[n.id]=n}return t};l.listenTo=function(t,e,r){if(!t)return this;var n=t._listenId||(t._listenId=i.uniqueId("l"));var s=this._listeningTo||(this._listeningTo={});var a=s[n];if(!a){var o=this._listenId||(this._listenId=i.uniqueId("l"));a=s[n]={obj:t,objId:n,id:o,listeningTo:s,count:0}}d(t,e,r,this,a);return this};var v=function(t,e,i,r){if(i){var n=t[e]||(t[e]=[]);var s=r.context,a=r.ctx,o=r.listening;if(o)o.count++;n.push({callback:i,context:s,ctx:s||a,listening:o})}return t};l.off=function(t,e,i){if(!this._events)return this;this._events=f(g,this._events,t,e,{context:i,listeners:this._listeners});return this};l.stopListening=function(t,e,r){var n=this._listeningTo;if(!n)return this;var s=t?[t._listenId]:i.keys(n);for(var a=0;a<s.length;a++){var o=n[s[a]];if(!o)break;o.obj.off(e,r,this)}if(i.isEmpty(n))this._listeningTo=void 0;return this};var g=function(t,e,r,n){if(!t)return;var s=0,a;var o=n.context,h=n.listeners;if(!e&&!r&&!o){var u=i.keys(h);for(;s<u.length;s++){a=h[u[s]];delete h[a.id];delete a.listeningTo[a.objId]}return}var l=e?[e]:i.keys(t);for(;s<l.length;s++){e=l[s];var c=t[e];if(!c)break;var f=[];for(var d=0;d<c.length;d++){var v=c[d];if(r&&r!==v.callback&&r!==v.callback._callback||o&&o!==v.context){f.push(v)}else{a=v.listening;if(a&&--a.count===0){delete h[a.id];delete a.listeningTo[a.objId]}}}if(f.length){t[e]=f}else{delete t[e]}}if(i.size(t))return t};l.once=function(t,e,r){var n=f(p,{},t,e,i.bind(this.off,this));return this.on(n,void 0,r)};l.listenToOnce=function(t,e,r){var n=f(p,{},e,r,i.bind(this.stopListening,this,t));return this.listenTo(t,n)};var p=function(t,e,r,n){if(r){var s=t[e]=i.once(function(){n(e,s);r.apply(this,arguments)});s._callback=r}return t};l.trigger=function(t){if(!this._events)return this;var e=Math.max(0,arguments.length-1);var i=Array(e);for(var r=0;r<e;r++)i[r]=arguments[r+1];f(m,this._events,t,void 0,i);return this};var m=function(t,e,i,r){if(t){var n=t[e];var s=t.all;if(n&&s)s=s.slice();if(n)_(n,r);if(s)_(s,[e].concat(r))}return t};var _=function(t,e){var i,r=-1,n=t.length,s=e[0],a=e[1],o=e[2];switch(e.length){case 0:while(++r<n)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<n)(i=t[r]).callback.call(i.ctx,s);return;case 2:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a);return;case 3:while(++r<n)(i=t[r]).callback.call(i.ctx,s,a,o);return;default:while(++r<n)(i=t[r]).callback.apply(i.ctx,e);return}};l.bind=l.on;l.unbind=l.off;i.extend(e,l);var y=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId(this.cidPrefix);this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(y.prototype,l,{changed:null,validationError:null,idAttribute:"id",cidPrefix:"c",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},matches:function(t){return!!i.iteratee(t,this)(this.attributes)},set:function(t,e,r){if(t==null)return this;var n;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;var s=r.unset;var a=r.silent;var o=[];var h=this._changing;this._changing=true;if(!h){this._previousAttributes=i.clone(this.attributes);this.changed={}}var u=this.attributes;var l=this.changed;var c=this._previousAttributes;for(var f in n){e=n[f];if(!i.isEqual(u[f],e))o.push(f);if(!i.isEqual(c[f],e)){l[f]=e}else{delete l[f]}s?delete u[f]:u[f]=e}this.id=this.get(this.idAttribute);if(!a){if(o.length)this._pending=r;for(var d=0;d<o.length;d++){this.trigger("change:"+o[d],this,u[o[d]],r)}}if(h)return this;if(!a){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e=this._changing?this._previousAttributes:this.attributes;var r={};for(var n in t){var s=t[n];if(i.isEqual(e[n],s))continue;r[n]=s}return i.size(r)?r:false},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=i.extend({parse:true},t);var e=this;var r=t.success;t.success=function(i){var n=t.parse?e.parse(i,t):i;if(!e.set(n,t))return false;if(r)r.call(t.context,e,i,t);e.trigger("sync",e,i,t)};z(this,t);return this.sync("read",this,t)},save:function(t,e,r){var n;if(t==null||typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r=i.extend({validate:true,parse:true},r);var s=r.wait;if(n&&!s){if(!this.set(n,r))return false}else{if(!this._validate(n,r))return false}var a=this;var o=r.success;var h=this.attributes;r.success=function(t){a.attributes=h;var e=r.parse?a.parse(t,r):t;if(s)e=i.extend({},n,e);if(e&&!a.set(e,r))return false;if(o)o.call(r.context,a,t,r);a.trigger("sync",a,t,r)};z(this,r);if(n&&s)this.attributes=i.extend({},h,n);var u=this.isNew()?"create":r.patch?"patch":"update";if(u==="patch"&&!r.attrs)r.attrs=n;var l=this.sync(u,this,r);this.attributes=h;return l},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var n=t.wait;var s=function(){e.stopListening();e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(n)s();if(r)r.call(t.context,e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};var a=false;if(this.isNew()){i.defer(t.success)}else{z(this,t);a=this.sync("delete",this,t)}if(!n)s();return a},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||F();if(this.isNew())return t;var e=this.get(this.idAttribute);return t.replace(/[^\/]$/,"$&/")+encodeURIComponent(e)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.defaults({validate:true},t))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var b={keys:1,values:1,pairs:1,invert:1,pick:0,omit:0,chain:1,isEmpty:1};o(y,b,"attributes");var x=e.Collection=function(t,e){e||(e={});if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,i.extend({silent:true},e))};var w={add:true,remove:true,merge:true};var E={add:true,remove:false};var k=function(t,e,i){i=Math.min(Math.max(i,0),t.length);var r=Array(t.length-i);var n=e.length;for(var s=0;s<r.length;s++)r[s]=t[s+i];for(s=0;s<n;s++)t[s+i]=e[s];for(s=0;s<r.length;s++)t[s+n+i]=r[s]};i.extend(x.prototype,l,{model:y,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,E))},remove:function(t,e){e=i.extend({},e);var r=!i.isArray(t);t=r?[t]:i.clone(t);var n=this._removeModels(t,e);if(!e.silent&&n)this.trigger("update",this,e);return r?n[0]:n},set:function(t,e){if(t==null)return;e=i.defaults({},e,w);if(e.parse&&!this._isModel(t))t=this.parse(t,e);var r=!i.isArray(t);t=r?[t]:t.slice();var n=e.at;if(n!=null)n=+n;if(n<0)n+=this.length+1;var s=[];var a=[];var o=[];var h={};var u=e.add;var l=e.merge;var c=e.remove;var f=false;var d=this.comparator&&n==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g;for(var p=0;p<t.length;p++){g=t[p];var m=this.get(g);if(m){if(l&&g!==m){var _=this._isModel(g)?g.attributes:g;if(e.parse)_=m.parse(_,e);m.set(_,e);if(d&&!f)f=m.hasChanged(v)}if(!h[m.cid]){h[m.cid]=true;s.push(m)}t[p]=m}else if(u){g=t[p]=this._prepareModel(g,e);if(g){a.push(g);this._addReference(g,e);h[g.cid]=true;s.push(g)}}}if(c){for(p=0;p<this.length;p++){g=this.models[p];if(!h[g.cid])o.push(g)}if(o.length)this._removeModels(o,e)}var y=false;var b=!d&&u&&c;if(s.length&&b){y=this.length!=s.length||i.some(this.models,function(t,e){return t!==s[e]});this.models.length=0;k(this.models,s,0);this.length=this.models.length}else if(a.length){if(d)f=true;k(this.models,a,n==null?this.length:n);this.length=this.models.length}if(f)this.sort({silent:true});if(!e.silent){for(p=0;p<a.length;p++){if(n!=null)e.index=n+p;g=a[p];g.trigger("add",g,this,e)}if(f||y)this.trigger("sort",this,e);if(a.length||o.length)this.trigger("update",this,e)}return r?t[0]:t},reset:function(t,e){e=e?i.clone(e):{};for(var r=0;r<this.models.length;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);return this.remove(e,t)},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);return this.remove(e,t)},slice:function(){return s.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;var e=this.modelId(this._isModel(t)?t.attributes:t);return this._byId[t]||this._byId[e]||this._byId[t.cid]},at:function(t){if(t<0)t+=this.length;return this.models[t]},where:function(t,e){return this[e?"find":"filter"](t)},findWhere:function(t){return this.where(t,true)},sort:function(t){var e=this.comparator;if(!e)throw new Error("Cannot sort a set without a comparator");t||(t={});var r=e.length;if(i.isFunction(e))e=i.bind(e,this);if(r===1||i.isString(e)){this.models=this.sortBy(e)}else{this.models.sort(e)}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=i.extend({parse:true},t);var e=t.success;var r=this;t.success=function(i){var n=t.reset?"reset":"set";r[n](i,t);if(e)e.call(t.context,r,i,t);r.trigger("sync",r,i,t)};z(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};var r=e.wait;t=this._prepareModel(t,e);if(!t)return false;if(!r)this.add(t,e);var n=this;var s=e.success;e.success=function(t,e,i){if(r)n.add(t,i);if(s)s.call(i.context,t,e,i)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models,{model:this.model,comparator:this.comparator})},modelId:function(t){return t[this.model.prototype.idAttribute||"id"]},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(this._isModel(t)){if(!t.collection)t.collection=this;return t}e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_removeModels:function(t,e){var i=[];for(var r=0;r<t.length;r++){var n=this.get(t[r]);if(!n)continue;var s=this.indexOf(n);this.models.splice(s,1);this.length--;if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}i.push(n);this._removeReference(n,e)}return i.length?i:false},_isModel:function(t){return t instanceof y},_addReference:function(t,e){this._byId[t.cid]=t;var i=this.modelId(t.attributes);if(i!=null)this._byId[i]=t;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){delete this._byId[t.cid];var i=this.modelId(t.attributes);if(i!=null)delete this._byId[i];if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(t==="change"){var n=this.modelId(e.previousAttributes());var s=this.modelId(e.attributes);if(n!==s){if(n!=null)delete this._byId[n];if(s!=null)this._byId[s]=e}}this.trigger.apply(this,arguments)}});var S={forEach:3,each:3,map:3,collect:3,reduce:4,foldl:4,inject:4,reduceRight:4,foldr:4,find:3,detect:3,filter:3,select:3,reject:3,every:3,all:3,some:3,any:3,include:3,includes:3,contains:3,invoke:0,max:3,min:3,toArray:1,size:1,first:3,head:3,take:3,initial:3,rest:3,tail:3,drop:3,last:3,without:0,difference:0,indexOf:3,shuffle:1,lastIndexOf:3,isEmpty:1,chain:1,sample:3,partition:3,groupBy:3,countBy:3,sortBy:3,indexBy:3};o(x,S,"models");var I=e.View=function(t){this.cid=i.uniqueId("view");i.extend(this,i.pick(t,P));this._ensureElement();this.initialize.apply(this,arguments)};var T=/^(\S+)\s*(.*)$/;var P=["model","collection","el","id","attributes","className","tagName","events"];i.extend(I.prototype,l,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this._removeElement();this.stopListening();return this},_removeElement:function(){this.$el.remove()},setElement:function(t){this.undelegateEvents();this._setElement(t);this.delegateEvents();return this},_setElement:function(t){this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0]},delegateEvents:function(t){t||(t=i.result(this,"events"));if(!t)return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[r];if(!r)continue;var n=e.match(T);this.delegate(n[1],n[2],i.bind(r,this))}return this},delegate:function(t,e,i){this.$el.on(t+".delegateEvents"+this.cid,e,i);return this},undelegateEvents:function(){if(this.$el)this.$el.off(".delegateEvents"+this.cid);return this},undelegate:function(t,e,i){this.$el.off(t+".delegateEvents"+this.cid,e,i);return this},_createElement:function(t){return document.createElement(t)},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");this.setElement(this._createElement(i.result(this,"tagName")));this._setAttributes(t)}else{this.setElement(i.result(this,"el"))}},_setAttributes:function(t){this.$el.attr(t)}});e.sync=function(t,r,n){var s=H[t];i.defaults(n||(n={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:s,dataType:"json"};if(!n.url){a.url=i.result(r,"url")||F()}if(n.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(n.attrs||r.toJSON(n))}if(n.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(n.emulateHTTP&&(s==="PUT"||s==="DELETE"||s==="PATCH")){a.type="POST";if(n.emulateJSON)a.data._method=s;var o=n.beforeSend;n.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",s);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!n.emulateJSON){a.processData=false}var h=n.error;n.error=function(t,e,i){n.textStatus=e;n.errorThrown=i;if(h)h.call(n.context,t,e,i)};var u=n.xhr=e.ajax(i.extend(a,n));r.trigger("request",r,u,n);return u};var H={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var A=/\((.*?)\)/g;var C=/(\(\?)?:\w+/g;var R=/\*\w+/g;var j=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,l,{initialize:function(){},route:function(t,r,n){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){n=r;r=""}if(!n)n=this[r];var s=this;e.history.route(t,function(i){var a=s._extractParameters(t,i);if(s.execute(n,a,r)!==false){s.trigger.apply(s,["route:"+r].concat(a));s.trigger("route",r,a);e.history.trigger("route",s,r,a)}});return this},execute:function(t,e,i){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(j,"\\$&").replace(A,"(?:$1)?").replace(C,function(t,e){return e?t:"([^/?]+)"}).replace(R,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var M=e.History=function(){this.handlers=[];this.checkUrl=i.bind(this.checkUrl,this);if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var U=/#.*$/;M.started=false;i.extend(M.prototype,l,{interval:50,atRoot:function(){var t=this.location.pathname.replace(/[^\/]$/,"$&/");return t===this.root&&!this.getSearch()},matchRoot:function(){var t=this.decodeFragment(this.location.pathname);var e=t.slice(0,this.root.length-1)+"/";return e===this.root},decodeFragment:function(t){return decodeURI(t.replace(/%25/g,"%2525"))},getSearch:function(){var t=this.location.href.replace(/#.*/,"").match(/\?.+/);return t?t[0]:""},getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getPath:function(){var t=this.decodeFragment(this.location.pathname+this.getSearch()).slice(this.root.length-1);return t.charAt(0)==="/"?t.slice(1):t},getFragment:function(t){if(t==null){if(this._usePushState||!this._wantsHashChange){t=this.getPath()}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(M.started)throw new Error("Backbone.history has already been started");M.started=true;this.options=i.extend({root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._hasHashChange="onhashchange"in window&&(document.documentMode===void 0||document.documentMode>7);this._useHashChange=this._wantsHashChange&&this._hasHashChange;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.history&&this.history.pushState);this._usePushState=this._wantsPushState&&this._hasPushState;this.fragment=this.getFragment();this.root=("/"+this.root+"/").replace(O,"/");if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){var e=this.root.slice(0,-1)||"/";this.location.replace(e+"#"+this.getPath());return true}else if(this._hasPushState&&this.atRoot()){this.navigate(this.getHash(),{replace:true})}}if(!this._hasHashChange&&this._wantsHashChange&&!this._usePushState){this.iframe=document.createElement("iframe");this.iframe.src="javascript:0";this.iframe.style.display="none";this.iframe.tabIndex=-1;var r=document.body;var n=r.insertBefore(this.iframe,r.firstChild).contentWindow;n.document.open();n.document.close();n.location.hash="#"+this.fragment}var s=window.addEventListener||function(t,e){return attachEvent("on"+t,e)};if(this._usePushState){s("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){s("hashchange",this.checkUrl,false)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}if(!this.options.silent)return this.loadUrl()},stop:function(){var t=window.removeEventListener||function(t,e){return detachEvent("on"+t,e)};if(this._usePushState){t("popstate",this.checkUrl,false)}else if(this._useHashChange&&!this.iframe){t("hashchange",this.checkUrl,false)}if(this.iframe){document.body.removeChild(this.iframe);this.iframe=null}if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);M.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getHash(this.iframe.contentWindow)}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()},loadUrl:function(t){if(!this.matchRoot())return false;t=this.fragment=this.getFragment(t);return i.some(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!M.started)return false;if(!e||e===true)e={trigger:!!e};t=this.getFragment(t||"");var i=this.root;if(t===""||t.charAt(0)==="?"){i=i.slice(0,-1)||"/"}var r=i+t;t=this.decodeFragment(t.replace(U,""));if(this.fragment===t)return;this.fragment=t;if(this._usePushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,r)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getHash(this.iframe.contentWindow)){var n=this.iframe.contentWindow;if(!e.replace){n.document.open();n.document.close()}this._updateHash(n.location,t,e.replace)}}else{return this.location.assign(r)}if(e.trigger)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new M;var q=function(t,e){var r=this;var n;if(t&&i.has(t,"constructor")){n=t.constructor}else{n=function(){return r.apply(this,arguments)}}i.extend(n,r,e);var s=function(){this.constructor=n};s.prototype=r.prototype;n.prototype=new s;if(t)i.extend(n.prototype,t);n.__super__=r.prototype;return n};y.extend=x.extend=$.extend=I.extend=M.extend=q;var F=function(){throw new Error('A "url" property or function must be specified')};var z=function(t,e){var i=e.error;e.error=function(r){if(i)i.call(e.context,t,r,e);t.trigger("error",t,r,e)}};return e});