PageSourceSearch

https://www.bestbassgear.com/js/mirasvit/core/backbone.js?q=051216

js bestbassgear.com collected 2026-10-02 17:58:27 UTC 53,103 bytes, 1,431 lines download raw bytes

1//     Backbone.js 0.9.2
2
3//     (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
4//     Backbone may be freely distributed under the MIT license.
5//     For all details and documentation:
6//     http://backbonejs.org
7
8(function(){
9
10  // Initial Setup
11  // -------------
12
13  // Save a reference to the global object (`window` in the browser, `global`
14  // on the server).
15  var root = this;
16
17  // Save the previous value of the `Backbone` variable, so that it can be
18  // restored later on, if `noConflict` is used.
19  var previousBackbone = root.Backbone;
20
21  // Create a local reference to slice/splice.
22  var slice = Array.prototype.slice;
23  var splice = Array.prototype.splice;
24
25  // The top-level namespace. All public Backbone classes and modules will
26  // be attached to this. Exported for both CommonJS and the browser.
27  var Backbone;
28  if (typeof exports !== 'undefined') {
29    Backbone = exports;
30  } else {
31    Backbone = root.Backbone = {};
32  }
33
34  // Current version of the library. Keep in sync with `package.json`.
35  Backbone.VERSION = '0.9.2';
36
37  // Require Underscore, if we're on the server, and it's not already present.
38  var _ = root._;
39  if (!_ && (typeof require !== 'undefined')) _ = require('underscore');
40
41  // For Backbone's purposes, jQuery, Zepto, or Ender owns the `$` variable.
42  var $ = root.jQuery || root.Zepto || root.ender;
43
44  // 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  };
52
53  // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
54  // to its previous owner. Returns a reference to this Backbone object.
55  Backbone.noConflict = function() {
56    root.Backbone = previousBackbone;
57    return this;
58  };
59
60  // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
61  // will fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and
62  // set a `X-Http-Method-Override` header.
63  Backbone.emulateHTTP = false;
64
65  // Turn on `emulateJSON` to support legacy servers that can't deal with direct
66  // `application/json` requests ... will encode the body as
67  // `application/x-www-form-urlencoded` instead and will send the model in a
68  // form param named `model`.
69  Backbone.emulateJSON = false;
70
71  // Backbone.Events
72  // -----------------
73
74  // Regular expression used to split event strings
75  var eventSplitter = /\s+/;
76
77  // A module that can be mixed in to *any object* in order to provide it with
78  // custom events. You may bind with `on` or remove with `off` callback functions
79  // to an event; trigger`-ing an event fires all callbacks in succession.
80  //
81  //     var object = {};
82  //     _.extend(object, Backbone.Events);
83  //     object.on('expand', function(){ alert('expanded'); });
84  //     object.trigger('expand');
85  //
86  var Events = Backbone.Events = {
87
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
109      return this;
110    },
111
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;
117
118      // No events, or removing *all* events.
119      if (!(calls = this._callbacks)) return;
120      if (!(events || callback || context)) {
121        delete this._callbacks;
122        return this;
123      }
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);
139          }
140        }
141      }
142
143      return this;
144    },
145
146    // Trigger one or many events, firing all bound callbacks. Callbacks are
147    // passed the same arguments as `trigger` is, apart from the event name
148    // (unless you're listening on `"all"`, which will cause your callback to
149    // 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  };
179
180  // Aliases for backwards compatibility.
181  Events.bind   = Events.on;
182  Events.unbind = Events.off;
183
184  // Backbone.Model
185  // --------------
186
187  // Create a new model, with defined attributes. A client id (`cid`)
188  // is automatically generated and assigned for you.
189  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;
197    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);
209    this.initialize.apply(this, arguments);
210  };
211
212  // Attach all inheritable methods to the Model prototype.
213  _.extend(Model.prototype, Events, {
214
215    // A hash of attributes whose current and previous value differ.
216    changed: null,
217
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,
225
226    // The default name for the JSON `id` attribute is `"id"`. MongoDB and
227    // CouchDB users may want to set this to `"_id"`.
228    idAttribute: 'id',
229
230    // Initialize is an empty function by default. Override it with your own
231    // initialization logic.
232    initialize: function(){},
233
234    // Return a copy of the model's `attributes` object.
235    toJSON: function(options) {
236      return _.clone(this.attributes);
237    },
238
239    // Get the value of an attribute.
240    get: function(attr) {
241      return this.attributes[attr];
242    },
243
244    // Get the HTML-escaped value of an attribute.
245    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);
250    },
251
252    // Returns `true` if the attribute contains a value that is not null
253    // or undefined.
254    has: function(attr) {
255      return this.get(attr) != null;
256    },
257
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;
262
263      // Handle both
264      if (_.isObject(key) || key == null) {
265        attrs = key;
266        options = value;
267      } else {
268        attrs = {};
269        attrs[key] = value;
270      }
271
272      // Extract attributes and options.
273      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
278      // Run validation.
279      if (!this._validate(attrs, options)) return false;
280
281      // Check for changes of `id`.
282      if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
283
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...
290      for (attr in attrs) {
291        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))) {
305          this.changed[attr] = val;
306          if (!options.silent) this._pending[attr] = true;
307        } else {
308          delete this.changed[attr];
309          delete this._pending[attr];
310        }
311      }
312
313      // Fire the `"change"` events.
314      if (!options.silent) this.change(options);
315      return this;
316    },
317
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.
320    unset: function(attr, options) {
321      (options || (options = {})).unset = true;
322      return this.set(attr, null, options);
323    },
324
325    // Clear all attributes on the model, firing `"change"` unless you choose
326    // to silence it.
327    clear: function(options) {
328      (options || (options = {})).unset = true;
329      return this.set(_.clone(this.attributes), options);
330    },
331
332    // Fetch the model from the server. If the server's representation of the
333    // model differs from its current attributes, they will be overriden,
334    // triggering a `"change"` event.
335    fetch: function(options) {
336      options = options ? _.clone(options) : {};
337      var model = this;
338      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);
342      };
343      options.error = Backbone.wrapError(options.error, model, options);
344      return (this.sync || Backbone.sync).call(this, 'read', this, options);
345    },
346
347    // Set a hash of model attributes, and sync the model to the server.
348    // If the server returns an attributes hash that differs, the model's
349    // state will be `set` again.
350    save: function(key, value, options) {
351      var attrs, current;
352
353      // Handle both `("key", value)` and `({key: value})` -style calls.
354      if (_.isObject(key) || key == null) {
355        attrs = key;
356        options = value;
357      } else {
358        attrs = {};
359        attrs[key] = value;
360      }
361      options = options ? _.clone(options) : {};
362
363      // If we're "wait"-ing to set changed attributes, validate early.
364      if (options.wait) {
365        if (!this._validate(attrs, options)) return false;
366        current = _.clone(this.attributes);
367      }
368
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;
373      }
374
375      // After a successful server-side save, the client is (optionally)
376      // updated with the server-side state.
377      var model = this;
378      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        }
391      };
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);
398      return xhr;
399    },
400
401    // Destroy this model on the server if it was already persisted.
402    // Optimistically removes the model from its collection, if it has one.
403    // If `wait: true` is passed, waits for the server to respond before removal.
404    destroy: function(options) {
405      options = options ? _.clone(options) : {};
406      var model = this;
407      var success = options.success;
408
409      var triggerDestroy = function() {
410        model.trigger('destroy', model, model.collection, options);
411      };
412
413      if (this.isNew()) {
414        triggerDestroy();
415        return false;
416      }
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();
430      return xhr;
431    },
432
433    // Default URL for the model's representation on the server -- if you're
434    // using Backbone's restful methods, override this to change the endpoint
435    // that will be called.
436    url: function() {
437      var base = getValue(this, 'urlRoot') || getValue(this.collection, 'url') || urlError();
438      if (this.isNew()) return base;
439      return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
440    },
441
442    // **parse** converts a response into the hash of attributes to be `set` on
443    // the model. The default implementation is just to pass the response along.
444    parse: function(resp, xhr) {
445      return resp;
446    },
447
448    // Create a new model with identical attributes to this one.
449    clone: function() {
450      return new this.constructor(this.attributes);
451    },
452
453    // A model is new if it has never been saved to the server, and lacks an id.
454    isNew: function() {
455      return this.id == null;
456    },
457
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);
533    },
534
535    // 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.
538    _validate: function(attrs, options) {
539      if (options.silent || !this.validate) return true;
540      attrs = _.extend({}, this.attributes, attrs);
541      var error = this.validate(attrs, options);
542      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      }
548      return false;
549    }
550
551  });
552
553  // Backbone.Collection
554  // -------------------
555
556  // Provides a standard collection class for our sets of models, ordered
557  // or unordered. If a `comparator` is specified, the Collection will maintain
558  // its models in sort order, as they're added and removed.
559  var Collection = Backbone.Collection = function(models, options) {
560    options || (options = {});
561    if (options.model) this.model = options.model;
562    if (options.comparator) this.comparator = options.comparator;
563    this._reset();
564    this.initialize.apply(this, arguments);
565    if (models) this.reset(models, {silent: true, parse: options.parse});
566  };
567
568  // Define the Collection's inheritable methods.
569  _.extend(Collection.prototype, Events, {
570
571    // The default model for a collection is just a **Backbone.Model**.
572    // This should be overridden in most cases.
573    model: Model,
574
575    // Initialize is an empty function by default. Override it with your own
576    // initialization logic.
577    initialize: function(){},
578
579    // The JSON representation of a Collection is an array of the
580    // models' attributes.
581    toJSON: function(options) {
582      return this.map(function(model){ return model.toJSON(options); });
583    },
584
585    // Add a model, or list of models to the set. Pass **silent** to avoid
586    // firing the `add` event for every new model.
587    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;
634    },
635
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.
638    remove: function(models, options) {
639      var i, l, index, model;
640      options || (options = {});
641      models = _.isArray(models) ? models.slice() : [models];
642      for (i = 0, l = models.length; i < l; i++) {
643        model = this.getByCid(models[i]) || this.get(models[i]);
644        if (!model) continue;
645        delete this._byId[model.id];
646        delete this._byCid[model.cid];
647        index = this.indexOf(model);
648        this.models.splice(index, 1);
649        this.length--;
650        if (!options.silent) {
651          options.index = index;
652          model.trigger('remove', model, this, options);
653        }
654        this._removeReference(model);
655      }
656      return this;
657    },
658
659    // Add a model to the end of the collection.
660    push: function(model, options) {
661      model = this._prepareModel(model, options);
662      this.add(model, options);
663      return model;
664    },
665
666    // Remove a model from the end of the collection.
667    pop: function(options) {
668      var model = this.at(this.length - 1);
669      this.remove(model, options);
670      return model;
671    },
672
673    // Add a model to the beginning of the collection.
674    unshift: function(model, options) {
675      model = this._prepareModel(model, options);
676      this.add(model, _.extend({at: 0}, options));
677      return model;
678    },
679
680    // Remove a model from the beginning of the collection.
681    shift: function(options) {
682      var model = this.at(0);
683      this.remove(model, options);
684      return model;
685    },
686
687    // 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];
696    },
697
698    // Get the model at the given index.
699    at: function(index) {
700      return this.models[index];
701    },
702
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) {
707        for (var key in attrs) {
708          if (attrs[key] !== model.get(key)) return false;
709        }
710        return true;
711      });
712    },
713
714    // Force the collection to re-sort itself. You don't need to call this under
715    // normal circumstances, as the set will maintain sort order as each item
716    // is added.
717    sort: function(options) {
718      options || (options = {});
719      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);
723      } else {
724        this.models.sort(boundComparator);
725      }
726      if (!options.silent) this.trigger('reset', this, options);
727      return this;
728    },
729
730    // Pluck an attribute from each model in the collection.
731    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;
748    },
749
750    // 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.
753    fetch: function(options) {
754      options = options ? _.clone(options) : {};
755      if (options.parse === undefined) options.parse = true;
756      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);
761      };
762      options.error = Backbone.wrapError(options.error, collection, options);
763      return (this.sync || Backbone.sync).call(this, 'read', this, options);
764    },
765
766    // Create a new instance of a model in this collection. Add the model to the
767    // collection immediately, unless `wait: true` is passed, in which case we
768    // wait for the server to agree.
769    create: function(model, options) {
770      var coll = this;
771      options = options ? _.clone(options) : {};
772      model = this._prepareModel(model, options);
773      if (!model) return false;
774      if (!options.wait) coll.add(model, options);
775      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        }
783      };
784      model.save(null, options);
785      return model;
786    },
787
788    // **parse** converts a response into a list of models to be added to the
789    // collection. The default implementation is just to pass it through.
790    parse: function(resp, xhr) {
791      return resp;
792    },
793
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();
799    },
800
801    // Reset all internal state. Called when the collection is reset.
802    _reset: function(options) {
803      this.length = 0;
804      this.models = [];
805      this._byId  = {};
806      this._byCid = {};
807    },
808
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;
819      }
820      return model;
821    },
822
823    // Internal method to remove a model's ties to a collection.
824    _removeReference: function(model) {
825      if (this == model.collection) {
826        delete model.collection;
827      }
828      model.off('all', this._onModelEvent, this);
829    },
830
831    // Internal method called every time a model in the set fires an event.
832    // Sets need to update their indexes when models change ids. All other
833    // events simply proxy through. "add" and "remove" events that originate
834    // in other collections are ignored.
835    _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      }
840      if (model && event === 'change:' + model.idAttribute) {
841        delete this._byId[model.previous(model.idAttribute)];
842        this._byId[model.id] = model;
843      }
844      this.trigger.apply(this, arguments);
845    }
846
847  });
848
849  // 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'];
855
856  // Mix in each Underscore method as a proxy to `Collection#models`.
857  _.each(methods, function(method) {
858    Collection.prototype[method] = function() {
859      return _[method].apply(_, [this.models].concat(_.toArray(arguments)));
860    };
861  });
862
863  // Backbone.Router
864  // -------------------
865
866  // Routers map faux-URLs to actions, and fire events when routes are
867  // matched. Creating a new one sets its `routes` hash, if not set statically.
868  var Router = Backbone.Router = function(options) {
869    options || (options = {});
870    if (options.routes) this.routes = options.routes;
871    this._bindRoutes();
872    this.initialize.apply(this, arguments);
873  };
874
875  // Cached regular expressions for matching named param parts and splatted
876  // parts of route strings.
877  var namedParam    = /:\w+/g;
878  var splatParam    = /\*\w+/g;
879  var escapeRegExp  = /[-[\]{}()+?.,\\^$|#\s]/g;
880
881  // Set up all inheritable **Backbone.Router** properties and methods.
882  _.extend(Router.prototype, Events, {
883
884    // Initialize is an empty function by default. Override it with your own
885    // initialization logic.
886    initialize: function(){},
887
888    // Manually bind a single named route to a callback. For example:
889    //
890    //     this.route('search/:query/p:num', 'search', function(query, num) {
891    //       ...
892    //     });
893    //
894    route: function(route, name, callback) {
895      Backbone.history || (Backbone.history = new History);
896      if (!_.isRegExp(route)) route = this._routeToRegExp(route);
897      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));
904      return this;
905    },
906
907    // Simple proxy to `Backbone.history` to save a fragment into the history.
908    navigate: function(fragment, options) {
909      Backbone.history.navigate(fragment, options);
910    },
911
912    // Bind all defined routes to `Backbone.history`. We have to reverse the
913    // order of the routes here to support behavior where the most general
914    // routes can be defined at the bottom of the route map.
915    _bindRoutes: function() {
916      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]]);
923      }
924    },
925
926    // Convert a route string into a regular expression, suitable for matching
927    // against the current location hash.
928    _routeToRegExp: function(route) {
929      route = route.replace(escapeRegExp, '\\$&')
930                   .replace(namedParam, '([^\/]+)')
931                   .replace(splatParam, '(.*?)');
932      return new RegExp('^' + route + '$');
933    },
934
935    // Given a route, and a URL fragment that it matches, return the array of
936    // extracted parameters.
937    _extractParameters: function(route, fragment) {
938      return route.exec(fragment).slice(1);
939    }
940
941  });
942
943  // Backbone.History
944  // ----------------
945
946  // Handles cross-browser history management, based on URL fragments. If the
947  // browser does not support `onhashchange`, falls back to polling.
948  var History = Backbone.History = function() {
949    this.handlers = [];
950    _.bindAll(this, 'checkUrl');
951  };
952
953  // Cached regex for cleaning leading hashes and slashes .
954  var routeStripper = /^[#\/]/;
955
956  // Cached regex for detecting MSIE.
957  var isExplorer = /msie [\w.]+/;
958
959  // Has the history handling already been started?
960  History.started = false;
961
962  // Set up all inheritable **Backbone.History** properties and methods.
963  _.extend(History.prototype, Events, {
964
965    // The default interval to poll for hash changes, if necessary, is
966    // twenty times a second.
967    interval: 50,
968
969    // Gets the true hash value. Cannot use location.hash directly due to bug
970    // 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(/#(.*)$/);
974      return match ? match[1] : '';
975    },
976
977    // Get the cross-browser normalized URL fragment, either from the URL,
978    // the hash, or the override.
979    getFragment: function(fragment, forcePushState) {
980      if (fragment == null) {
981        if (this._hasPushState || forcePushState) {
982          fragment = window.location.pathname;
983          var search = window.location.search;
984          if (search) fragment += search;
985        } else {
986          fragment = this.getHash();
987        }
988      }
989      if (!fragment.indexOf(this.options.root)) fragment = fragment.substr(this.options.root.length);
990      return fragment.replace(routeStripper, '');
991    },
992
993    // Start the hash change handling, returning `true` if the current URL matches
994    // an existing route, and `false` otherwise.
995    start: function(options) {
996      if (History.started) throw new Error("Backbone.history has already been started");
997      History.started = true;
998
999      // Figure out the initial configuration. Do we need an iframe?
1000      // Is pushState desired ... is it available?
1001      this.options          = _.extend({}, {root: '/'}, this.options, options);
1002      this._wantsHashChange = this.options.hashChange !== false;
1003      this._wantsPushState  = !!this.options.pushState;
1004      this._hasPushState    = !!(this.options.pushState && window.history && window.history.pushState);
1005      var fragment          = this.getFragment();
1006      var docMode           = document.documentMode;
1007      var oldIE             = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
1008
1009      if (oldIE) {
1010        this.iframe = $('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo('body')[0].contentWindow;
1011        this.navigate(fragment);
1012      }
1013
1014      // Depending on whether we're using pushState or hashes, and whether
1015      // 'onhashchange' is supported, determine how we check the URL state.
1016      if (this._hasPushState) {
1017        $(window).bind('popstate', this.checkUrl);
1018      } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
1019        $(window).bind('hashchange', this.checkUrl);
1020      } else if (this._wantsHashChange) {
1021        this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
1022      }
1023
1024      // Determine if we need to change the base url, for a pushState link
1025      // opened by a non-pushState browser.
1026      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      }
1048    },
1049
1050    // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
1051    // but possibly useful for unit testing Routers.
1052    stop: function() {
1053      $(window).unbind('popstate', this.checkUrl).unbind('hashchange', this.checkUrl);
1054      clearInterval(this._checkUrlInterval);
1055      History.started = false;
1056    },
1057
1058    // Add a route to be tested when the fragment changes. Routes added later
1059    // may override previous routes.
1060    route: function(route, callback) {
1061      this.handlers.unshift({route: route, callback: callback});
1062    },
1063
1064    // Checks the current URL to see if it has changed, and if it has,
1065    // calls `loadUrl`, normalizing across the hidden iframe.
1066    checkUrl: function(e) {
1067      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;
1070      if (this.iframe) this.navigate(current);
1071      this.loadUrl() || this.loadUrl(this.getHash());
1072    },
1073
1074    // Attempt to load the current URL fragment. If a route succeeds with a
1075    // match, returns `true`. If no defined routes matches the fragment,
1076    // returns `false`.
1077    loadUrl: function(fragmentOverride) {
1078      var fragment = this.fragment = this.getFragment(fragmentOverride);
1079      var matched = _.any(this.handlers, function(handler) {
1080        if (handler.route.test(fragment)) {
1081          handler.callback(fragment);
1082          return true;
1083        }
1084      });
1085      return matched;
1086    },
1087
1088    // Save a fragment into the hash history, or replace the URL state if the
1089    // 'replace' option is passed. You are responsible for properly URL-encoding
1090    // the fragment in advance.
1091    //
1092    // The options object can contain `trigger: true` if you wish to have the
1093    // route callback be fired (not usually desirable), or `replace: true`, if
1094    // you wish to modify the current URL without adding an entry to the history.
1095    navigate: function(fragment, options) {
1096      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;
1100
1101      // If pushState is available, we use it to set the fragment as a real URL.
1102      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);
1106
1107      // If hash changes haven't been explicitly disabled, update the hash
1108      // fragment to store history.
1109      } 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.
1115          if(!options.replace) this.iframe.document.open().close();
1116          this._updateHash(this.iframe.location, frag, options.replace);
1117        }
1118
1119      // If you've told us that you explicitly don't want fallback hashchange-
1120      // based history, then `navigate` becomes a page refresh.
1121      } else {
1122        window.location.assign(this.options.root + fragment);
1123      }
1124      if (options.trigger) this.loadUrl(fragment);
1125    },
1126
1127    // Update the hash location, either replacing the current entry, or adding
1128    // a new one to the browser history.
1129    _updateHash: function(location, fragment, replace) {
1130      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  };
1373
1374  // Helpers
1375  // -------
1376
1377  // Shared empty constructor function to aid in prototype-chain creation.
1378  var ctor = function(){};
1379
1380  // Helper function to correctly set up the prototype chain, for subclasses.
1381  // Similar to `goog.inherits`, but uses a hash of prototype properties and
1382  // class properties to be extended.
1383  var inherits = function(parent, protoProps, staticProps) {
1384    var child;
1385
1386    // The constructor function for the new subclass is either defined by you
1387    // (the "constructor" property in your `extend` definition), or defaulted
1388    // by us to simply call the parent's constructor.
1389    if (protoProps && protoProps.hasOwnProperty('constructor')) {
1390      child = protoProps.constructor;
1391    } else {
1392      child = function(){ parent.apply(this, arguments); };
1393    }
1394
1395    // Inherit class (static) properties from parent.
1396    _.extend(child, parent);
1397
1398    // Set the prototype chain to inherit from `parent`, without calling
1399    // `parent`'s constructor function.
1400    ctor.prototype = parent.prototype;
1401    child.prototype = new ctor();
1402
1403    // Add prototype properties (instance properties) to the subclass,
1404    // if supplied.
1405    if (protoProps) _.extend(child.prototype, protoProps);
1406
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.
1414    child.__super__ = parent.prototype;
1415
1416    return child;
1417  };
1418
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  };
1425
1426  // Throw an error when a URL is needed, and none is supplied.
1427  var urlError = function() {
1428    throw new Error('A "url" property or function must be specified');
1429  };
1430
1431}).call(this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.