(function() { var Backbone, root = this, previousBackbone = root.Backbone, slice = [].slice; (Backbone = "undefined" != typeof exports ? exports : root.Backbone = {}).VERSION = "1.1.0"; var _ = root._; _ || "undefined" == typeof require || (_ = require("underscore")), Backbone.$ = root.jQuery || root.Zepto || root.ender || root.$, Backbone.noConflict = function() { return root.Backbone = previousBackbone, this; }, Backbone.emulateHTTP = !1, Backbone.emulateJSON = !1; var Events = Backbone.Events = { on: function(name, callback, context) { return eventsApi(this, "on", name, [ callback, context ]) && callback && (this._events || (this._events = {}), (this._events[name] || (this._events[name] = [])).push({ callback: callback, context: context, ctx: context || this })), this; }, once: function(name, callback, context) { if (!eventsApi(this, "once", name, [ callback, context ]) || !callback) return this; var self = this, once = _.once(function() { self.off(name, once), callback.apply(this, arguments); }); return once._callback = callback, this.on(name, once, context); }, off: function(name, callback, context) { var retain, ev, events, names, i, l, j, k; if (!this._events || !eventsApi(this, "off", name, [ callback, context ])) return this; if (!name && !callback && !context) return this._events = {}, this; for(i = 0, l = (names = name ? [ name ] : _.keys(this._events)).length; i < l; i++)if (name = names[i], events = this._events[name]) { if (this._events[name] = retain = [], callback || context) for(j = 0, k = events.length; j < k; j++)ev = events[j], (callback && callback !== ev.callback && callback !== ev.callback._callback || context && context !== ev.context) && retain.push(ev); retain.length || delete this._events[name]; } return this; }, trigger: function(name) { if (!this._events) return this; var args = slice.call(arguments, 1); if (!eventsApi(this, "trigger", name, args)) return this; var events = this._events[name], allEvents = this._events.all; return events && triggerEvents(events, args), allEvents && triggerEvents(allEvents, arguments), this; }, stopListening: function(obj, name, callback) { var listeningTo = this._listeningTo; if (!listeningTo) return this; var remove = !name && !callback; for(var id in callback || "object" != typeof name || (callback = this), obj && ((listeningTo = {})[obj._listenId] = obj), listeningTo)(obj = listeningTo[id]).off(name, callback, this), (remove || _.isEmpty(obj._events)) && delete this._listeningTo[id]; return this; } }, eventSplitter = /\s+/, eventsApi = function(obj, action, name, rest) { if (!name) return !0; if ("object" == typeof name) { for(var key in name)obj[action].apply(obj, [ key, name[key] ].concat(rest)); return !1; } if (eventSplitter.test(name)) { for(var names = name.split(eventSplitter), i = 0, l = names.length; i < l; i++)obj[action].apply(obj, [ names[i] ].concat(rest)); return !1; } return !0; }, triggerEvents = function(events, args) { var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2]; switch(args.length){ case 0: for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx); return; case 1: for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1); return; case 2: for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1, a2); return; case 3: for(; ++i < l;)(ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return; default: for(; ++i < l;)(ev = events[i]).callback.apply(ev.ctx, args); } }; _.each({ listenTo: "on", listenToOnce: "once" }, function(implementation, method) { Events[method] = function(obj, name, callback) { return (this._listeningTo || (this._listeningTo = {}))[obj._listenId || (obj._listenId = _.uniqueId("l"))] = obj, callback || "object" != typeof name || (callback = this), obj[implementation](name, callback, this), this; }; }), Events.bind = Events.on, Events.unbind = Events.off, _.extend(Backbone, Events); var Model = Backbone.Model = function(attributes, options) { var attrs = attributes || {}; options || (options = {}), this.cid = _.uniqueId("c"), this.attributes = {}, options.collection && (this.collection = options.collection), options.parse && (attrs = this.parse(attrs, options) || {}), attrs = _.defaults({}, attrs, _.result(this, "defaults")), this.set(attrs, options), this.changed = {}, this.initialize.apply(this, arguments); }; _.extend(Model.prototype, Events, { changed: null, validationError: null, idAttribute: "id", initialize: function() {}, toJSON: function(options) { return _.clone(this.attributes); }, sync: function() { return Backbone.sync.apply(this, arguments); }, get: function(attr) { return this.attributes[attr]; }, escape: function(attr) { return _.escape(this.get(attr)); }, has: function(attr) { return null != this.get(attr); }, set: function(key, val, options) { var attr, attrs, unset, changes, silent, changing, prev, current; if (null == key) return this; if ("object" == typeof key ? (attrs = key, options = val) : (attrs = {})[key] = val, options || (options = {}), !this._validate(attrs, options)) return !1; for(attr in unset = options.unset, silent = options.silent, changes = [], changing = this._changing, this._changing = !0, changing || (this._previousAttributes = _.clone(this.attributes), this.changed = {}), current = this.attributes, prev = this._previousAttributes, this.idAttribute in attrs && (this.id = attrs[this.idAttribute]), attrs)val = attrs[attr], _.isEqual(current[attr], val) || changes.push(attr), _.isEqual(prev[attr], val) ? delete this.changed[attr] : this.changed[attr] = val, unset ? delete current[attr] : current[attr] = val; if (!silent) { changes.length && (this._pending = !0); for(var i = 0, l = changes.length; i < l; i++)this.trigger("change:" + changes[i], this, current[changes[i]], options); } if (changing) return this; if (!silent) for(; this._pending;)this._pending = !1, this.trigger("change", this, options); return this._pending = !1, this._changing = !1, this; }, unset: function(attr, options) { return this.set(attr, void 0, _.extend({}, options, { unset: !0 })); }, clear: function(options) { var attrs = {}; for(var key in this.attributes)attrs[key] = void 0; return this.set(attrs, _.extend({}, options, { unset: !0 })); }, hasChanged: function(attr) { return null == attr ? !_.isEmpty(this.changed) : _.has(this.changed, attr); }, changedAttributes: function(diff) { if (!diff) return !!this.hasChanged() && _.clone(this.changed); var val, changed = !1, old = this._changing ? this._previousAttributes : this.attributes; for(var attr in diff)_.isEqual(old[attr], val = diff[attr]) || ((changed || (changed = {}))[attr] = val); return changed; }, previous: function(attr) { return null != attr && this._previousAttributes ? this._previousAttributes[attr] : null; }, previousAttributes: function() { return _.clone(this._previousAttributes); }, fetch: function(options) { void 0 === (options = options ? _.clone(options) : {}).parse && (options.parse = !0); var model = this, success = options.success; return options.success = function(resp) { if (!model.set(model.parse(resp, options), options)) return !1; success && success(model, resp, options), model.trigger("sync", model, resp, options); }, wrapError(this, options), this.sync("read", this, options); }, save: function(key, val, options) { var attrs, method, xhr, attributes = this.attributes; if (null == key || "object" == typeof key ? (attrs = key, options = val) : (attrs = {})[key] = val, options = _.extend({ validate: !0 }, options), attrs && !options.wait) { if (!this.set(attrs, options)) return !1; } else if (!this._validate(attrs, options)) return !1; attrs && options.wait && (this.attributes = _.extend({}, attributes, attrs)), void 0 === options.parse && (options.parse = !0); var model = this, success = options.success; return options.success = function(resp) { model.attributes = attributes; var serverAttrs = model.parse(resp, options); if (options.wait && (serverAttrs = _.extend(attrs || {}, serverAttrs)), _.isObject(serverAttrs) && !model.set(serverAttrs, options)) return !1; success && success(model, resp, options), model.trigger("sync", model, resp, options); }, wrapError(this, options), "patch" == (method = this.isNew() ? "create" : options.patch ? "patch" : "update") && (options.attrs = attrs), xhr = this.sync(method, this, options), attrs && options.wait && (this.attributes = attributes), xhr; }, destroy: function(options) { options = options ? _.clone(options) : {}; var model = this, success = options.success, destroy = function() { model.trigger("destroy", model, model.collection, options); }; if (options.success = function(resp) { (options.wait || model.isNew()) && destroy(), success && success(model, resp, options), model.isNew() || model.trigger("sync", model, resp, options); }, this.isNew()) return options.success(), !1; wrapError(this, options); var xhr = this.sync("delete", this, options); return options.wait || destroy(), xhr; }, url: function() { var base = _.result(this, "urlRoot") || _.result(this.collection, "url") || urlError(); return this.isNew() ? base : base + ("/" === base.charAt(base.length - 1) ? "" : "/") + encodeURIComponent(this.id); }, parse: function(resp, options) { return resp; }, clone: function() { return new this.constructor(this.attributes); }, isNew: function() { return null == this.id; }, isValid: function(options) { return this._validate({}, _.extend(options || {}, { validate: !0 })); }, _validate: function(attrs, options) { if (!options.validate || !this.validate) return !0; attrs = _.extend({}, this.attributes, attrs); var error = this.validationError = this.validate(attrs, options) || null; return !error || (this.trigger("invalid", this, error, _.extend(options, { validationError: error })), !1); } }), _.each([ "keys", "values", "pairs", "invert", "pick", "omit" ], function(method) { Model.prototype[method] = function() { var args = slice.call(arguments); return args.unshift(this.attributes), _[method].apply(_, args); }; }); var Collection = Backbone.Collection = function(models, options) { options || (options = {}), options.model && (this.model = options.model), void 0 !== options.comparator && (this.comparator = options.comparator), this._reset(), this.initialize.apply(this, arguments), models && this.reset(models, _.extend({ silent: !0 }, options)); }, setOptions = { add: !0, remove: !0, merge: !0 }, addOptions = { add: !0, remove: !1 }; _.extend(Collection.prototype, Events, { model: Model, initialize: function() {}, toJSON: function(options) { return this.map(function(model) { return model.toJSON(options); }); }, sync: function() { return Backbone.sync.apply(this, arguments); }, add: function(models, options) { return this.set(models, _.extend({ merge: !1 }, options, addOptions)); }, remove: function(models, options) { var i, l, index, model, singular = !_.isArray(models); for(models = singular ? [ models ] : _.clone(models), options || (options = {}), i = 0, l = models.length; i < l; i++)(model = models[i] = this.get(models[i])) && (delete this._byId[model.id], delete this._byId[model.cid], index = this.indexOf(model), this.models.splice(index, 1), this.length--, options.silent || (options.index = index, model.trigger("remove", model, this, options)), this._removeReference(model)); return singular ? models[0] : models; }, set: function(models, options) { (options = _.defaults({}, options, setOptions)).parse && (models = this.parse(models, options)); var i, l, id, model, attrs, existing, sort, singular = !_.isArray(models); models = singular ? models ? [ models ] : [] : _.clone(models); var at = options.at, targetModel = this.model, sortable = this.comparator && null == at && !1 !== options.sort, sortAttr = _.isString(this.comparator) ? this.comparator : null, toAdd = [], toRemove = [], modelMap = {}, add = options.add, merge = options.merge, remove = options.remove, order = !sortable && !!add && !!remove && []; for(i = 0, l = models.length; i < l; i++){ if (id = (attrs = models[i]) instanceof Model ? model = attrs : attrs[targetModel.prototype.idAttribute], existing = this.get(id)) remove && (modelMap[existing.cid] = !0), merge && (attrs = attrs === model ? model.attributes : attrs, options.parse && (attrs = existing.parse(attrs, options)), existing.set(attrs, options), sortable && !sort && existing.hasChanged(sortAttr) && (sort = !0)), models[i] = existing; else if (add) { if (!(model = models[i] = this._prepareModel(attrs, options))) continue; toAdd.push(model), model.on("all", this._onModelEvent, this), this._byId[model.cid] = model, null != model.id && (this._byId[model.id] = model); } order && order.push(existing || model); } if (remove) { for(i = 0, l = this.length; i < l; ++i)modelMap[(model = this.models[i]).cid] || toRemove.push(model); toRemove.length && this.remove(toRemove, options); } if (toAdd.length || order && order.length) { if (sortable && (sort = !0), this.length += toAdd.length, null != at) for(i = 0, l = toAdd.length; i < l; i++)this.models.splice(at + i, 0, toAdd[i]); else { order && (this.models.length = 0); var orderedModels = order || toAdd; for(i = 0, l = orderedModels.length; i < l; i++)this.models.push(orderedModels[i]); } } if (sort && this.sort({ silent: !0 }), !options.silent) { for(i = 0, l = toAdd.length; i < l; i++)(model = toAdd[i]).trigger("add", model, this, options); (sort || order && order.length) && this.trigger("sort", this, options); } return singular ? models[0] : models; }, reset: function(models, options) { options || (options = {}); for(var i = 0, l = this.models.length; i < l; i++)this._removeReference(this.models[i]); return options.previousModels = this.models, this._reset(), models = this.add(models, _.extend({ silent: !0 }, options)), options.silent || this.trigger("reset", this, options), models; }, push: function(model, options) { return this.add(model, _.extend({ at: this.length }, options)); }, pop: function(options) { var model = this.at(this.length - 1); return this.remove(model, options), model; }, unshift: function(model, options) { return this.add(model, _.extend({ at: 0 }, options)); }, shift: function(options) { var model = this.at(0); return this.remove(model, options), model; }, slice: function() { return slice.apply(this.models, arguments); }, get: function(obj) { if (null != obj) return this._byId[obj.id] || this._byId[obj.cid] || this._byId[obj]; }, at: function(index) { return this.models[index]; }, where: function(attrs, first) { return _.isEmpty(attrs) ? first ? void 0 : [] : this[first ? "find" : "filter"](function(model) { for(var key in attrs)if (attrs[key] !== model.get(key)) return !1; return !0; }); }, findWhere: function(attrs) { return this.where(attrs, !0); }, sort: function(options) { if (!this.comparator) throw Error("Cannot sort a set without a comparator"); return options || (options = {}), _.isString(this.comparator) || 1 === this.comparator.length ? this.models = this.sortBy(this.comparator, this) : this.models.sort(_.bind(this.comparator, this)), options.silent || this.trigger("sort", this, options), this; }, pluck: function(attr) { return _.invoke(this.models, "get", attr); }, fetch: function(options) { void 0 === (options = options ? _.clone(options) : {}).parse && (options.parse = !0); var success = options.success, collection = this; return options.success = function(resp) { collection[options.reset ? "reset" : "set"](resp, options), success && success(collection, resp, options), collection.trigger("sync", collection, resp, options); }, wrapError(this, options), this.sync("read", this, options); }, create: function(model, options) { if (options = options ? _.clone(options) : {}, !(model = this._prepareModel(model, options))) return !1; options.wait || this.add(model, options); var collection = this, success = options.success; return options.success = function(model, resp, options) { options.wait && collection.add(model, options), success && success(model, resp, options); }, model.save(null, options), model; }, parse: function(resp, options) { return resp; }, clone: function() { return new this.constructor(this.models); }, _reset: function() { this.length = 0, this.models = [], this._byId = {}; }, _prepareModel: function(attrs, options) { if (attrs instanceof Model) return attrs.collection || (attrs.collection = this), attrs; (options = options ? _.clone(options) : {}).collection = this; var model = new this.model(attrs, options); return model.validationError ? (this.trigger("invalid", this, model.validationError, options), !1) : model; }, _removeReference: function(model) { this === model.collection && delete model.collection, model.off("all", this._onModelEvent, this); }, _onModelEvent: function(event, model, collection, options) { ("add" !== event && "remove" !== event || collection === this) && ("destroy" === event && this.remove(model, options), model && event === "change:" + model.idAttribute && (delete this._byId[model.previous(model.idAttribute)], null != model.id && (this._byId[model.id] = model)), this.trigger.apply(this, arguments)); } }), _.each([ "forEach", "each", "map", "collect", "reduce", "foldl", "inject", "reduceRight", "foldr", "find", "detect", "filter", "select", "reject", "every", "all", "some", "any", "include", "contains", "invoke", "max", "min", "toArray", "size", "first", "head", "take", "initial", "rest", "tail", "drop", "last", "without", "difference", "indexOf", "shuffle", "lastIndexOf", "isEmpty", "chain" ], function(method) { Collection.prototype[method] = function() { var args = slice.call(arguments); return args.unshift(this.models), _[method].apply(_, args); }; }), _.each([ "groupBy", "countBy", "sortBy" ], function(method) { Collection.prototype[method] = function(value, context) { var iterator = _.isFunction(value) ? value : function(model) { return model.get(value); }; return _[method](this.models, iterator, context); }; }); var View = Backbone.View = function(options) { this.cid = _.uniqueId("view"), options || (options = {}), _.extend(this, _.pick(options, viewOptions)), this._ensureElement(), this.initialize.apply(this, arguments), this.delegateEvents(); }, delegateEventSplitter = /^(\S+)\s*(.*)$/, viewOptions = [ "model", "collection", "el", "id", "attributes", "className", "tagName", "events" ]; _.extend(View.prototype, Events, { tagName: "div", $: function(selector) { return this.$el.find(selector); }, initialize: function() {}, render: function() { return this; }, remove: function() { return this.$el.remove(), this.stopListening(), this; }, setElement: function(element, delegate) { return this.$el && this.undelegateEvents(), this.$el = element instanceof Backbone.$ ? element : Backbone.$(element), this.el = this.$el[0], !1 !== delegate && this.delegateEvents(), this; }, delegateEvents: function(events) { if (!(events || (events = _.result(this, "events")))) return this; for(var key in this.undelegateEvents(), events){ var method = events[key]; if (_.isFunction(method) || (method = this[events[key]]), method) { var match = key.match(delegateEventSplitter), eventName = match[1], selector = match[2]; method = _.bind(method, this), eventName += ".delegateEvents" + this.cid, "" === selector ? this.$el.on(eventName, method) : this.$el.on(eventName, selector, method); } } return this; }, undelegateEvents: function() { return this.$el.off(".delegateEvents" + this.cid), this; }, _ensureElement: function() { if (this.el) this.setElement(_.result(this, "el"), !1); else { var attrs = _.extend({}, _.result(this, "attributes")); this.id && (attrs.id = _.result(this, "id")), this.className && (attrs.class = _.result(this, "className")); var $el = Backbone.$("<" + _.result(this, "tagName") + ">").attr(attrs); this.setElement($el, !1); } } }), Backbone.sync = function(method, model, options) { var type = methodMap[method]; _.defaults(options || (options = {}), { emulateHTTP: Backbone.emulateHTTP, emulateJSON: Backbone.emulateJSON }); var params = { type: type, dataType: "json" }; if (options.url || (params.url = _.result(model, "url") || urlError()), null == options.data && model && ("create" === method || "update" === method || "patch" === method) && (params.contentType = "application/json", params.data = JSON.stringify(options.attrs || model.toJSON(options))), options.emulateJSON && (params.contentType = "application/x-www-form-urlencoded", params.data = params.data ? { model: params.data } : {}), options.emulateHTTP && ("PUT" === type || "DELETE" === type || "PATCH" === type)) { params.type = "POST", options.emulateJSON && (params.data._method = type); var beforeSend = options.beforeSend; options.beforeSend = function(xhr) { if (xhr.setRequestHeader("X-HTTP-Method-Override", type), beforeSend) return beforeSend.apply(this, arguments); }; } "GET" === params.type || options.emulateJSON || (params.processData = !1), "PATCH" === params.type && noXhrPatch && (params.xhr = function() { return new ActiveXObject("Microsoft.XMLHTTP"); }); var xhr = options.xhr = Backbone.ajax(_.extend(params, options)); return model.trigger("request", model, xhr, options), xhr; }; var noXhrPatch = "undefined" != typeof window && !!window.ActiveXObject && !(window.XMLHttpRequest && new XMLHttpRequest().dispatchEvent), methodMap = { create: "POST", update: "PUT", patch: "PATCH", delete: "DELETE", read: "GET" }; Backbone.ajax = function() { return Backbone.$.ajax.apply(Backbone.$, arguments); }; var Router = Backbone.Router = function(options) { options || (options = {}), options.routes && (this.routes = options.routes), this._bindRoutes(), this.initialize.apply(this, arguments); }, optionalParam = /\((.*?)\)/g, namedParam = /(\(\?)?:\w+/g, splatParam = /\*\w+/g, escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g; _.extend(Router.prototype, Events, { initialize: function() {}, route: function(route, name, callback) { _.isRegExp(route) || (route = this._routeToRegExp(route)), _.isFunction(name) && (callback = name, name = ""), callback || (callback = this[name]); var router = this; return Backbone.history.route(route, function(fragment) { var args = router._extractParameters(route, fragment); callback && callback.apply(router, args), router.trigger.apply(router, [ "route:" + name ].concat(args)), router.trigger("route", name, args), Backbone.history.trigger("route", router, name, args); }), this; }, navigate: function(fragment, options) { return Backbone.history.navigate(fragment, options), this; }, _bindRoutes: function() { if (this.routes) { this.routes = _.result(this, "routes"); for(var route, routes = _.keys(this.routes); null != (route = routes.pop());)this.route(route, this.routes[route]); } }, _routeToRegExp: function(route) { return RegExp("^" + (route = route.replace(escapeRegExp, "\\$&").replace(optionalParam, "(?:$1)?").replace(namedParam, function(match, optional) { return optional ? match : "([^/]+)"; }).replace(splatParam, "(.*?)")) + "$"); }, _extractParameters: function(route, fragment) { var params = route.exec(fragment).slice(1); return _.map(params, function(param) { return param ? decodeURIComponent(param) : null; }); } }); var History = Backbone.History = function() { this.handlers = [], _.bindAll(this, "checkUrl"), "undefined" != typeof window && (this.location = window.location, this.history = window.history); }, routeStripper = /^[#\/]|\s+$/g, rootStripper = /^\/+|\/+$/g, isExplorer = /msie [\w.]+/, trailingSlash = /\/$/, pathStripper = /[?#].*$/; History.started = !1, _.extend(History.prototype, Events, { interval: 50, getHash: function(window1) { var match = (window1 || this).location.href.match(/#(.*)$/); return match ? match[1] : ""; }, getFragment: function(fragment, forcePushState) { if (null == fragment) { if (this._hasPushState || !this._wantsHashChange || forcePushState) { fragment = this.location.pathname; var root = this.root.replace(trailingSlash, ""); fragment.indexOf(root) || (fragment = fragment.slice(root.length)); } else fragment = this.getHash(); } return fragment.replace(routeStripper, ""); }, start: function(options) { if (History.started) throw Error("Backbone.history has already been started"); History.started = !0, this.options = _.extend({ root: "/" }, this.options, options), this.root = this.options.root, this._wantsHashChange = !1 !== this.options.hashChange, this._wantsPushState = !!this.options.pushState, this._hasPushState = !!(this.options.pushState && this.history && this.history.pushState); var fragment = this.getFragment(), docMode = document.documentMode, oldIE = isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7); this.root = ("/" + this.root + "/").replace(rootStripper, "/"), oldIE && this._wantsHashChange && (this.iframe = Backbone.$('