+++ /dev/null
-// Backbone.js 0.5.3
-// (c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
-// Backbone may be freely distributed under the MIT license.
-// For all details and documentation:
-// http://documentcloud.github.com/backbone
-
-(function(){
-
- // Initial Setup
- // -------------
-
- // Save a reference to the global object.
- var root = this;
-
- // Save the previous value of the `Backbone` variable.
- var previousBackbone = root.Backbone;
-
- // The top-level namespace. All public Backbone classes and modules will
- // be attached to this. Exported for both CommonJS and the browser.
- var Backbone;
- if (typeof exports !== 'undefined') {
- Backbone = exports;
- } else {
- Backbone = root.Backbone = {};
- }
-
- // Current version of the library. Keep in sync with `package.json`.
- Backbone.VERSION = '0.5.3';
-
- // Require Underscore, if we're on the server, and it's not already present.
- var _ = root._;
- if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
-
- // For Backbone's purposes, jQuery or Zepto owns the `$` variable.
- var $ = root.jQuery || root.Zepto;
-
- // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
- // to its previous owner. Returns a reference to this Backbone object.
- Backbone.noConflict = function() {
- root.Backbone = previousBackbone;
- return this;
- };
-
- // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option will
- // fake `"PUT"` and `"DELETE"` requests via the `_method` parameter and set a
- // `X-Http-Method-Override` header.
- Backbone.emulateHTTP = false;
-
- // Turn on `emulateJSON` to support legacy servers that can't deal with direct
- // `application/json` requests ... will encode the body as
- // `application/x-www-form-urlencoded` instead and will send the model in a
- // form param named `model`.
- Backbone.emulateJSON = false;
-
- // Backbone.Events
- // -----------------
-
- // A module that can be mixed in to *any object* in order to provide it with
- // custom events. You may `bind` or `unbind` a callback function to an event;
- // `trigger`-ing an event fires all callbacks in succession.
- //
- // var object = {};
- // _.extend(object, Backbone.Events);
- // object.bind('expand', function(){ alert('expanded'); });
- // object.trigger('expand');
- //
- Backbone.Events = {
-
- // Bind an event, specified by a string name, `ev`, to a `callback` function.
- // Passing `"all"` will bind the callback to all events fired.
- bind : function(ev, callback, context) {
- var calls = this._callbacks || (this._callbacks = {});
- var list = calls[ev] || (calls[ev] = []);
- list.push([callback, context]);
- return this;
- },
-
- // Remove one or many callbacks. If `callback` is null, removes all
- // callbacks for the event. If `ev` is null, removes all bound callbacks
- // for all events.
- unbind : function(ev, callback) {
- var calls;
- if (!ev) {
- this._callbacks = {};
- } else if (calls = this._callbacks) {
- if (!callback) {
- calls[ev] = [];
- } else {
- var list = calls[ev];
- if (!list) return this;
- for (var i = 0, l = list.length; i < l; i++) {
- if (list[i] && callback === list[i][0]) {
- list[i] = null;
- break;
- }
- }
- }
- }
- return this;
- },
-
- // Trigger an event, firing all bound callbacks. Callbacks are passed the
- // same arguments as `trigger` is, apart from the event name.
- // Listening for `"all"` passes the true event name as the first argument.
- trigger : function(eventName) {
- var list, calls, ev, callback, args;
- var both = 2;
- if (!(calls = this._callbacks)) return this;
- while (both--) {
- ev = both ? eventName : 'all';
- if (list = calls[ev]) {
- for (var i = 0, l = list.length; i < l; i++) {
- if (!(callback = list[i])) {
- list.splice(i, 1); i--; l--;
- } else {
- args = both ? Array.prototype.slice.call(arguments, 1) : arguments;
- callback[0].apply(callback[1] || this, args);
- }
- }
- }
- }
- return this;
- }
-
- };
-
- // Backbone.Model
- // --------------
-
- // Create a new model, with defined attributes. A client id (`cid`)
- // is automatically generated and assigned for you.
- Backbone.Model = function(attributes, options) {
- var defaults;
- attributes || (attributes = {});
- if (defaults = this.defaults) {
- if (_.isFunction(defaults)) defaults = defaults.call(this);
- attributes = _.extend({}, defaults, attributes);
- }
- this.attributes = {};
- this._escapedAttributes = {};
- this.cid = _.uniqueId('c');
- this.set(attributes, {silent : true});
- this._changed = false;
- this._previousAttributes = _.clone(this.attributes);
- if (options && options.collection) this.collection = options.collection;
- this.initialize(attributes, options);
- };
-
- // Attach all inheritable methods to the Model prototype.
- _.extend(Backbone.Model.prototype, Backbone.Events, {
-
- // A snapshot of the model's previous attributes, taken immediately
- // after the last `"change"` event was fired.
- _previousAttributes : null,
-
- // Has the item been changed since the last `"change"` event?
- _changed : false,
-
- // The default name for the JSON `id` attribute is `"id"`. MongoDB and
- // CouchDB users may want to set this to `"_id"`.
- idAttribute : 'id',
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
-
- // Return a copy of the model's `attributes` object.
- toJSON : function() {
- return _.clone(this.attributes);
- },
-
- // Get the value of an attribute.
- get : function(attr) {
- return this.attributes[attr];
- },
-
- // Get the HTML-escaped value of an attribute.
- escape : function(attr) {
- var html;
- if (html = this._escapedAttributes[attr]) return html;
- var val = this.attributes[attr];
- return this._escapedAttributes[attr] = escapeHTML(val == null ? '' : '' + val);
- },
-
- // Returns `true` if the attribute contains a value that is not null
- // or undefined.
- has : function(attr) {
- return this.attributes[attr] != null;
- },
-
- // Set a hash of model attributes on the object, firing `"change"` unless you
- // choose to silence it.
- set : function(attrs, options) {
-
- // Extract attributes and options.
- options || (options = {});
- if (!attrs) return this;
- if (attrs.attributes) attrs = attrs.attributes;
- var now = this.attributes, escaped = this._escapedAttributes;
-
- // Run validation.
- if (!options.silent && this.validate && !this._performValidation(attrs, options)) return false;
-
- // Check for changes of `id`.
- if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
-
- // We're about to start triggering change events.
- var alreadyChanging = this._changing;
- this._changing = true;
-
- // Update attributes.
- for (var attr in attrs) {
- var val = attrs[attr];
- if (!_.isEqual(now[attr], val)) {
- now[attr] = val;
- delete escaped[attr];
- this._changed = true;
- if (!options.silent) this.trigger('change:' + attr, this, val, options);
- }
- }
-
- // Fire the `"change"` event, if the model has been changed.
- if (!alreadyChanging && !options.silent && this._changed) this.change(options);
- this._changing = false;
- return this;
- },
-
- // Remove an attribute from the model, firing `"change"` unless you choose
- // to silence it. `unset` is a noop if the attribute doesn't exist.
- unset : function(attr, options) {
- if (!(attr in this.attributes)) return this;
- options || (options = {});
- var value = this.attributes[attr];
-
- // Run validation.
- var validObj = {};
- validObj[attr] = void 0;
- if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
-
- // Remove the attribute.
- delete this.attributes[attr];
- delete this._escapedAttributes[attr];
- if (attr == this.idAttribute) delete this.id;
- this._changed = true;
- if (!options.silent) {
- this.trigger('change:' + attr, this, void 0, options);
- this.change(options);
- }
- return this;
- },
-
- // Clear all attributes on the model, firing `"change"` unless you choose
- // to silence it.
- clear : function(options) {
- options || (options = {});
- var attr;
- var old = this.attributes;
-
- // Run validation.
- var validObj = {};
- for (attr in old) validObj[attr] = void 0;
- if (!options.silent && this.validate && !this._performValidation(validObj, options)) return false;
-
- this.attributes = {};
- this._escapedAttributes = {};
- this._changed = true;
- if (!options.silent) {
- for (attr in old) {
- this.trigger('change:' + attr, this, void 0, options);
- }
- this.change(options);
- }
- return this;
- },
-
- // Fetch the model from the server. If the server's representation of the
- // model differs from its current attributes, they will be overriden,
- // triggering a `"change"` event.
- fetch : function(options) {
- options || (options = {});
- var model = this;
- var success = options.success;
- options.success = function(resp, status, xhr) {
- if (!model.set(model.parse(resp, xhr), options)) return false;
- if (success) success(model, resp);
- };
- options.error = wrapError(options.error, model, options);
- return (this.sync || Backbone.sync).call(this, 'read', this, options);
- },
-
- // Set a hash of model attributes, and sync the model to the server.
- // If the server returns an attributes hash that differs, the model's
- // state will be `set` again.
- save : function(attrs, options) {
- options || (options = {});
- if (attrs && !this.set(attrs, options)) return false;
- var model = this;
- var success = options.success;
- options.success = function(resp, status, xhr) {
- if (!model.set(model.parse(resp, xhr), options)) return false;
- if (success) success(model, resp, xhr);
- };
- options.error = wrapError(options.error, model, options);
- var method = this.isNew() ? 'create' : 'update';
- return (this.sync || Backbone.sync).call(this, method, this, options);
- },
-
- // Destroy this model on the server if it was already persisted. Upon success, the model is removed
- // from its collection, if it has one.
- destroy : function(options) {
- options || (options = {});
- if (this.isNew()) return this.trigger('destroy', this, this.collection, options);
- var model = this;
- var success = options.success;
- options.success = function(resp) {
- model.trigger('destroy', model, model.collection, options);
- if (success) success(model, resp);
- };
- options.error = wrapError(options.error, model, options);
- return (this.sync || Backbone.sync).call(this, 'delete', this, options);
- },
-
- // Default URL for the model's representation on the server -- if you're
- // using Backbone's restful methods, override this to change the endpoint
- // that will be called.
- url : function() {
- var base = getUrl(this.collection) || this.urlRoot || urlError();
- if (this.isNew()) return base;
- return base + (base.charAt(base.length - 1) == '/' ? '' : '/') + encodeURIComponent(this.id);
- },
-
- // **parse** converts a response into the hash of attributes to be `set` on
- // the model. The default implementation is just to pass the response along.
- parse : function(resp, xhr) {
- return resp;
- },
-
- // Create a new model with identical attributes to this one.
- clone : function() {
- return new this.constructor(this);
- },
-
- // A model is new if it has never been saved to the server, and lacks an id.
- isNew : function() {
- return this.id == null;
- },
-
- // Call this method to manually fire a `change` event for this model.
- // Calling this will cause all objects observing the model to update.
- change : function(options) {
- this.trigger('change', this, options);
- this._previousAttributes = _.clone(this.attributes);
- this._changed = false;
- },
-
- // Determine if the model has changed since the last `"change"` event.
- // If you specify an attribute name, determine if that attribute has changed.
- hasChanged : function(attr) {
- if (attr) return this._previousAttributes[attr] != this.attributes[attr];
- return this._changed;
- },
-
- // Return an object containing all the attributes that have changed, or false
- // if there are no changed attributes. Useful for determining what parts of a
- // view need to be updated and/or what attributes need to be persisted to
- // the server.
- changedAttributes : function(now) {
- now || (now = this.attributes);
- var old = this._previousAttributes;
- var changed = false;
- for (var attr in now) {
- if (!_.isEqual(old[attr], now[attr])) {
- changed = changed || {};
- changed[attr] = now[attr];
- }
- }
- return changed;
- },
-
- // Get the previous value of an attribute, recorded at the time the last
- // `"change"` event was fired.
- previous : function(attr) {
- if (!attr || !this._previousAttributes) return null;
- return this._previousAttributes[attr];
- },
-
- // Get all of the attributes of the model at the time of the previous
- // `"change"` event.
- previousAttributes : function() {
- return _.clone(this._previousAttributes);
- },
-
- // Run validation against a set of incoming attributes, returning `true`
- // if all is well. If a specific `error` callback has been passed,
- // call that instead of firing the general `"error"` event.
- _performValidation : function(attrs, options) {
- var error = this.validate(attrs);
- if (error) {
- if (options.error) {
- options.error(this, error, options);
- } else {
- this.trigger('error', this, error, options);
- }
- return false;
- }
- return true;
- }
-
- });
-
- // Backbone.Collection
- // -------------------
-
- // Provides a standard collection class for our sets of models, ordered
- // or unordered. If a `comparator` is specified, the Collection will maintain
- // its models in sort order, as they're added and removed.
- Backbone.Collection = function(models, options) {
- options || (options = {});
- if (options.comparator) this.comparator = options.comparator;
- _.bindAll(this, '_onModelEvent', '_removeReference');
- this._reset();
- if (models) this.reset(models, {silent: true});
- this.initialize.apply(this, arguments);
- };
-
- // Define the Collection's inheritable methods.
- _.extend(Backbone.Collection.prototype, Backbone.Events, {
-
- // The default model for a collection is just a **Backbone.Model**.
- // This should be overridden in most cases.
- model : Backbone.Model,
-
- // Initialize is an empty function by default. Override it with your own
- // initialization logic.
- initialize : function(){},
-
- // The JSON representation of a Collection is an array of the
- // models' attributes.
- toJSON : function() {
- return this.map(function(model){ return model.toJSON(); });
- },
-
- // Add a model, or list of models to the set. Pass **silent** to avoid
- // firing the `added` event for every new model.
- add : function(models, options) {
- if (_.isArray(models)) {
- for (var i = 0, l = models.length; i < l; i++) {
- this._add(models[i], options);
- }
- } else {
- this._add(models, options);
- }
- return this;
- },
-
- // Remove a model, or a list of models from the set. Pass silent to avoid
- // firing the `removed` event for every model removed.
- remove : function(models, options) {
- if (_.isArray(models)) {
- for (var i = 0, l = models.length; i < l; i++) {
- this._remove(models[i], options);
- }
- } else {
- this._remove(models, options);
- }
- return this;
- },
-
- // Get a model from the set by id.
- get : function(id) {
- if (id == null) return null;
- return this._byId[id.id != null ? id.id : id];
- },
-
- // Get a model from the set by client id.
- getByCid : function(cid) {
- return cid && this._byCid[cid.cid || cid];
- },
-
- // Get the model at the given index.
- at: function(index) {
- return this.models[index];
- },
-
- // Force the collection to re-sort itself. You don't need to call this under normal
- // circumstances, as the set will maintain sort order as each item is added.
- sort : function(options) {
- options || (options = {});
- if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
- this.models = this.sortBy(this.comparator);
- if (!options.silent) this.trigger('reset', this, options);
- return