~openerp-dev/openerp-web/trunk-radio-button-tpa

« back to all changes in this revision

Viewing changes to addons/web/static/lib/backbone/backbone.js

  • Committer: Turkesh Patel (Open ERP)
  • Date: 2014-03-06 06:44:30 UTC
  • mfrom: (3840.1.111 openerp-web)
  • Revision ID: tpa@tinyerp.com-20140306064430-mo911iyioohr1tnb
[MRG] merge sith lp:openerp-web

Show diffs side-by-side

added added

removed removed

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