client/lib/backbone.js
changeset 138 e5be5cc9f09b
parent 23 70c8af9b44ec
equal deleted inserted replaced
137:efc0fce30814 138:e5be5cc9f09b
     1 //     Backbone.js 0.9.2
     1 (function(){var t=this;var e=t.Backbone;var i=[];var r=i.push;var s=i.slice;var n=i.splice;var a;if(typeof exports!=="undefined"){a=exports}else{a=t.Backbone={}}a.VERSION="1.0.0";var h=t._;if(!h&&typeof require!=="undefined")h=require("underscore");a.$=t.jQuery||t.Zepto||t.ender||t.$;a.noConflict=function(){t.Backbone=e;return this};a.emulateHTTP=false;a.emulateJSON=false;var o=a.Events={on:function(t,e,i){if(!l(this,"on",t,[e,i])||!e)return this;this._events||(this._events={});var r=this._events[t]||(this._events[t]=[]);r.push({callback:e,context:i,ctx:i||this});return this},once:function(t,e,i){if(!l(this,"once",t,[e,i])||!e)return this;var r=this;var s=h.once(function(){r.off(t,s);e.apply(this,arguments)});s._callback=e;return this.on(t,s,i)},off:function(t,e,i){var r,s,n,a,o,u,c,f;if(!this._events||!l(this,"off",t,[e,i]))return this;if(!t&&!e&&!i){this._events={};return this}a=t?[t]:h.keys(this._events);for(o=0,u=a.length;o<u;o++){t=a[o];if(n=this._events[t]){this._events[t]=r=[];if(e||i){for(c=0,f=n.length;c<f;c++){s=n[c];if(e&&e!==s.callback&&e!==s.callback._callback||i&&i!==s.context){r.push(s)}}}if(!r.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=s.call(arguments,1);if(!l(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)c(i,e);if(r)c(r,arguments);return this},stopListening:function(t,e,i){var r=this._listeners;if(!r)return this;var s=!e&&!i;if(typeof e==="object")i=this;if(t)(r={})[t._listenerId]=t;for(var n in r){r[n].off(e,i,this);if(s)delete this._listeners[n]}return this}};var u=/\s+/;var l=function(t,e,i,r){if(!i)return true;if(typeof i==="object"){for(var s in i){t[e].apply(t,[s,i[s]].concat(r))}return false}if(u.test(i)){var n=i.split(u);for(var a=0,h=n.length;a<h;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var c=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],h=e[2];switch(e.length){case 0:while(++r<s)(i=t[r]).callback.call(i.ctx);return;case 1:while(++r<s)(i=t[r]).callback.call(i.ctx,n);return;case 2:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a);return;case 3:while(++r<s)(i=t[r]).callback.call(i.ctx,n,a,h);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e)}};var f={listenTo:"on",listenToOnce:"once"};h.each(f,function(t,e){o[e]=function(e,i,r){var s=this._listeners||(this._listeners={});var n=e._listenerId||(e._listenerId=h.uniqueId("l"));s[n]=e;if(typeof i==="object")r=this;e[t](i,r,this);return this}});o.bind=o.on;o.unbind=o.off;h.extend(a,o);var d=a.Model=function(t,e){var i;var r=t||{};e||(e={});this.cid=h.uniqueId("c");this.attributes={};h.extend(this,h.pick(e,p));if(e.parse)r=this.parse(r,e)||{};if(i=h.result(this,"defaults")){r=h.defaults({},r,i)}this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};var p=["url","urlRoot","collection"];h.extend(d.prototype,o,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return h.clone(this.attributes)},sync:function(){return a.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return h.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,i){var r,s,n,a,o,u,l,c;if(t==null)return this;if(typeof t==="object"){s=t;i=e}else{(s={})[t]=e}i||(i={});if(!this._validate(s,i))return false;n=i.unset;o=i.silent;a=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=h.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in s)this.id=s[this.idAttribute];for(r in s){e=s[r];if(!h.isEqual(c[r],e))a.push(r);if(!h.isEqual(l[r],e)){this.changed[r]=e}else{delete this.changed[r]}n?delete c[r]:c[r]=e}if(!o){if(a.length)this._pending=true;for(var f=0,d=a.length;f<d;f++){this.trigger("change:"+a[f],this,c[a[f]],i)}}if(u)return this;if(!o){while(this._pending){this._pending=false;this.trigger("change",this,i)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,h.extend({},e,{unset:true}))},clear:function(t){var e={};for(var i in this.attributes)e[i]=void 0;return this.set(e,h.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!h.isEmpty(this.changed);return h.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?h.clone(this.changed):false;var e,i=false;var r=this._changing?this._previousAttributes:this.attributes;for(var s in t){if(h.isEqual(r[s],e=t[s]))continue;(i||(i={}))[s]=e}return i},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return h.clone(this._previousAttributes)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var i=t.success;t.success=function(r){if(!e.set(e.parse(r,t),t))return false;if(i)i(e,r,t);e.trigger("sync",e,r,t)};R(this,t);return this.sync("read",this,t)},save:function(t,e,i){var r,s,n,a=this.attributes;if(t==null||typeof t==="object"){r=t;i=e}else{(r={})[t]=e}if(r&&(!i||!i.wait)&&!this.set(r,i))return false;i=h.extend({validate:true},i);if(!this._validate(r,i))return false;if(r&&i.wait){this.attributes=h.extend({},a,r)}if(i.parse===void 0)i.parse=true;var o=this;var u=i.success;i.success=function(t){o.attributes=a;var e=o.parse(t,i);if(i.wait)e=h.extend(r||{},e);if(h.isObject(e)&&!o.set(e,i)){return false}if(u)u(o,t,i);o.trigger("sync",o,t,i)};R(this,i);s=this.isNew()?"create":i.patch?"patch":"update";if(s==="patch")i.attrs=r;n=this.sync(s,this,i);if(r&&i.wait)this.attributes=a;return n},destroy:function(t){t=t?h.clone(t):{};var e=this;var i=t.success;var r=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(s){if(t.wait||e.isNew())r();if(i)i(e,s,t);if(!e.isNew())e.trigger("sync",e,s,t)};if(this.isNew()){t.success();return false}R(this,t);var s=this.sync("delete",this,t);if(!t.wait)r();return s},url:function(){var t=h.result(this,"urlRoot")||h.result(this.collection,"url")||U();if(this.isNew())return t;return t+(t.charAt(t.length-1)==="/"?"":"/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return this.id==null},isValid:function(t){return this._validate({},h.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=h.extend({},this.attributes,t);var i=this.validationError=this.validate(t,e)||null;if(!i)return true;this.trigger("invalid",this,i,h.extend(e||{},{validationError:i}));return false}});var v=["keys","values","pairs","invert","pick","omit"];h.each(v,function(t){d.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.attributes);return h[t].apply(h,e)}});var g=a.Collection=function(t,e){e||(e={});if(e.url)this.url=e.url;if(e.model)this.model=e.model;if(e.comparator!==void 0)this.comparator=e.comparator;this._reset();this.initialize.apply(this,arguments);if(t)this.reset(t,h.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,merge:false,remove:false};h.extend(g.prototype,o,{model:d,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return a.sync.apply(this,arguments)},add:function(t,e){return this.set(t,h.defaults(e||{},y))},remove:function(t,e){t=h.isArray(t)?t.slice():[t];e||(e={});var i,r,s,n;for(i=0,r=t.length;i<r;i++){n=this.get(t[i]);if(!n)continue;delete this._byId[n.id];delete this._byId[n.cid];s=this.indexOf(n);this.models.splice(s,1);this.length--;if(!e.silent){e.index=s;n.trigger("remove",n,this,e)}this._removeReference(n)}return this},set:function(t,e){e=h.defaults(e||{},m);if(e.parse)t=this.parse(t,e);if(!h.isArray(t))t=t?[t]:[];var i,s,a,o,u,l;var c=e.at;var f=this.comparator&&c==null&&e.sort!==false;var d=h.isString(this.comparator)?this.comparator:null;var p=[],v=[],g={};for(i=0,s=t.length;i<s;i++){if(!(a=this._prepareModel(t[i],e)))continue;if(u=this.get(a)){if(e.remove)g[u.cid]=true;if(e.merge){u.set(a.attributes,e);if(f&&!l&&u.hasChanged(d))l=true}}else if(e.add){p.push(a);a.on("all",this._onModelEvent,this);this._byId[a.cid]=a;if(a.id!=null)this._byId[a.id]=a}}if(e.remove){for(i=0,s=this.length;i<s;++i){if(!g[(a=this.models[i]).cid])v.push(a)}if(v.length)this.remove(v,e)}if(p.length){if(f)l=true;this.length+=p.length;if(c!=null){n.apply(this.models,[c,0].concat(p))}else{r.apply(this.models,p)}}if(l)this.sort({silent:true});if(e.silent)return this;for(i=0,s=p.length;i<s;i++){(a=p[i]).trigger("add",a,this,e)}if(l)this.trigger("sort",this,e);return this},reset:function(t,e){e||(e={});for(var i=0,r=this.models.length;i<r;i++){this._removeReference(this.models[i])}e.previousModels=this.models;this._reset();this.add(t,h.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return this},push:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:this.length},e));return t},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){t=this._prepareModel(t,e);this.add(t,h.extend({at:0},e));return t},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(t,e){return this.models.slice(t,e)},get:function(t){if(t==null)return void 0;return this._byId[t.id!=null?t.id:t.cid||t]},at:function(t){return this.models[t]},where:function(t,e){if(h.isEmpty(t))return e?void 0:[];return this[e?"find":"filter"](function(e){for(var i in t){if(t[i]!==e.get(i))return false}return true})},findWhere:function(t){return this.where(t,true)},sort:function(t){if(!this.comparator)throw new Error("Cannot sort a set without a comparator");t||(t={});if(h.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(h.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},sortedIndex:function(t,e,i){e||(e=this.comparator);var r=h.isFunction(e)?e:function(t){return t.get(e)};return h.sortedIndex(this.models,t,r,i)},pluck:function(t){return h.invoke(this.models,"get",t)},fetch:function(t){t=t?h.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var i=this;t.success=function(r){var s=t.reset?"reset":"set";i[s](r,t);if(e)e(i,r,t);i.trigger("sync",i,r,t)};R(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?h.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var i=this;var r=e.success;e.success=function(s){if(e.wait)i.add(t,e);if(r)r(t,s,e)};t.save(null,e);return t},parse:function(t,e){return t},clone:function(){return new this.constructor(this.models)},_reset:function(){this.length=0;this.models=[];this._byId={}},_prepareModel:function(t,e){if(t instanceof d){if(!t.collection)t.collection=this;return t}e||(e={});e.collection=this;var i=new this.model(t,e);if(!i._validate(t,e)){this.trigger("invalid",this,t,e);return false}return i},_removeReference:function(t){if(this===t.collection)delete t.collection;t.off("all",this._onModelEvent,this)},_onModelEvent:function(t,e,i,r){if((t==="add"||t==="remove")&&i!==this)return;if(t==="destroy")this.remove(e,r);if(e&&t==="change:"+e.idAttribute){delete this._byId[e.previous(e.idAttribute)];if(e.id!=null)this._byId[e.id]=e}this.trigger.apply(this,arguments)}});var _=["forEach","each","map","collect","reduce","foldl","inject","reduceRight","foldr","find","detect","filter","select","reject","every","all","some","any","include","contains","invoke","max","min","toArray","size","first","head","take","initial","rest","tail","drop","last","without","indexOf","shuffle","lastIndexOf","isEmpty","chain"];h.each(_,function(t){g.prototype[t]=function(){var e=s.call(arguments);e.unshift(this.models);return h[t].apply(h,e)}});var w=["groupBy","countBy","sortBy"];h.each(w,function(t){g.prototype[t]=function(e,i){var r=h.isFunction(e)?e:function(t){return t.get(e)};return h[t](this.models,r,i)}});var b=a.View=function(t){this.cid=h.uniqueId("view");this._configure(t||{});this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];h.extend(b.prototype,o,{tagName:"div",$:function(t){return this.$el.find(t)},initialize:function(){},render:function(){return this},remove:function(){this.$el.remove();this.stopListening();return this},setElement:function(t,e){if(this.$el)this.undelegateEvents();this.$el=t instanceof a.$?t:a.$(t);this.el=this.$el[0];if(e!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=h.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var i=t[e];if(!h.isFunction(i))i=this[t[e]];if(!i)continue;var r=e.match(x);var s=r[1],n=r[2];i=h.bind(i,this);s+=".delegateEvents"+this.cid;if(n===""){this.$el.on(s,i)}else{this.$el.on(s,n,i)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_configure:function(t){if(this.options)t=h.extend({},h.result(this,"options"),t);h.extend(this,h.pick(t,E));this.options=t},_ensureElement:function(){if(!this.el){var t=h.extend({},h.result(this,"attributes"));if(this.id)t.id=h.result(this,"id");if(this.className)t["class"]=h.result(this,"className");var e=a.$("<"+h.result(this,"tagName")+">").attr(t);this.setElement(e,false)}else{this.setElement(h.result(this,"el"),false)}}});a.sync=function(t,e,i){var r=k[t];h.defaults(i||(i={}),{emulateHTTP:a.emulateHTTP,emulateJSON:a.emulateJSON});var s={type:r,dataType:"json"};if(!i.url){s.url=h.result(e,"url")||U()}if(i.data==null&&e&&(t==="create"||t==="update"||t==="patch")){s.contentType="application/json";s.data=JSON.stringify(i.attrs||e.toJSON(i))}if(i.emulateJSON){s.contentType="application/x-www-form-urlencoded";s.data=s.data?{model:s.data}:{}}if(i.emulateHTTP&&(r==="PUT"||r==="DELETE"||r==="PATCH")){s.type="POST";if(i.emulateJSON)s.data._method=r;var n=i.beforeSend;i.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",r);if(n)return n.apply(this,arguments)}}if(s.type!=="GET"&&!i.emulateJSON){s.processData=false}if(s.type==="PATCH"&&window.ActiveXObject&&!(window.external&&window.external.msActiveXFilteringEnabled)){s.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var o=i.xhr=a.ajax(h.extend(s,i));e.trigger("request",e,o,i);return o};var k={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};a.ajax=function(){return a.$.ajax.apply(a.$,arguments)};var S=a.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var $=/\((.*?)\)/g;var T=/(\(\?)?:\w+/g;var H=/\*\w+/g;var A=/[\-{}\[\]+?.,\\\^$|#\s]/g;h.extend(S.prototype,o,{initialize:function(){},route:function(t,e,i){if(!h.isRegExp(t))t=this._routeToRegExp(t);if(h.isFunction(e)){i=e;e=""}if(!i)i=this[e];var r=this;a.history.route(t,function(s){var n=r._extractParameters(t,s);i&&i.apply(r,n);r.trigger.apply(r,["route:"+e].concat(n));r.trigger("route",e,n);a.history.trigger("route",r,e,n)});return this},navigate:function(t,e){a.history.navigate(t,e);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=h.result(this,"routes");var t,e=h.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(A,"\\$&").replace($,"(?:$1)?").replace(T,function(t,e){return e?t:"([^/]+)"}).replace(H,"(.*?)");return new RegExp("^"+t+"$")},_extractParameters:function(t,e){var i=t.exec(e).slice(1);return h.map(i,function(t){return t?decodeURIComponent(t):null})}});var I=a.History=function(){this.handlers=[];h.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var N=/^[#\/]|\s+$/g;var P=/^\/+|\/+$/g;var O=/msie [\w.]+/;var C=/\/$/;I.started=false;h.extend(I.prototype,o,{interval:50,getHash:function(t){var e=(t||this).location.href.match(/#(.*)$/);return e?e[1]:""},getFragment:function(t,e){if(t==null){if(this._hasPushState||!this._wantsHashChange||e){t=this.location.pathname;var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.substr(i.length)}else{t=this.getHash()}}return t.replace(N,"")},start:function(t){if(I.started)throw new Error("Backbone.history has already been started");I.started=true;this.options=h.extend({},{root:"/"},this.options,t);this.root=this.options.root;this._wantsHashChange=this.options.hashChange!==false;this._wantsPushState=!!this.options.pushState;this._hasPushState=!!(this.options.pushState&&this.history&&this.history.pushState);var e=this.getFragment();var i=document.documentMode;var r=O.exec(navigator.userAgent.toLowerCase())&&(!i||i<=7);this.root=("/"+this.root+"/").replace(P,"/");if(r&&this._wantsHashChange){this.iframe=a.$('<iframe src="javascript:0" tabindex="-1" />').hide().appendTo("body")[0].contentWindow;this.navigate(e)}if(this._hasPushState){a.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!r){a.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=e;var s=this.location;var n=s.pathname.replace(/[^\/]$/,"$&/")===this.root;if(this._wantsHashChange&&this._wantsPushState&&!this._hasPushState&&!n){this.fragment=this.getFragment(null,true);this.location.replace(this.root+this.location.search+"#"+this.fragment);return true}else if(this._wantsPushState&&this._hasPushState&&n&&s.hash){this.fragment=this.getHash().replace(N,"");this.history.replaceState({},document.title,this.root+this.fragment+s.search)}if(!this.options.silent)return this.loadUrl()},stop:function(){a.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);clearInterval(this._checkUrlInterval);I.started=false},route:function(t,e){this.handlers.unshift({route:t,callback:e})},checkUrl:function(t){var e=this.getFragment();if(e===this.fragment&&this.iframe){e=this.getFragment(this.getHash(this.iframe))}if(e===this.fragment)return false;if(this.iframe)this.navigate(e);this.loadUrl()||this.loadUrl(this.getHash())},loadUrl:function(t){var e=this.fragment=this.getFragment(t);var i=h.any(this.handlers,function(t){if(t.route.test(e)){t.callback(e);return true}});return i},navigate:function(t,e){if(!I.started)return false;if(!e||e===true)e={trigger:e};t=this.getFragment(t||"");if(this.fragment===t)return;this.fragment=t;var i=this.root+t;if(this._hasPushState){this.history[e.replace?"replaceState":"pushState"]({},document.title,i)}else if(this._wantsHashChange){this._updateHash(this.location,t,e.replace);if(this.iframe&&t!==this.getFragment(this.getHash(this.iframe))){if(!e.replace)this.iframe.document.open().close();this._updateHash(this.iframe.location,t,e.replace)}}else{return this.location.assign(i)}if(e.trigger)this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});a.history=new I;var j=function(t,e){var i=this;var r;if(t&&h.has(t,"constructor")){r=t.constructor}else{r=function(){return i.apply(this,arguments)}}h.extend(r,i,e);var s=function(){this.constructor=r};s.prototype=i.prototype;r.prototype=new s;if(t)h.extend(r.prototype,t);r.__super__=i.prototype;return r};d.extend=g.extend=S.extend=b.extend=I.extend=j;var U=function(){throw new Error('A "url" property or function must be specified')};var R=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}}}).call(this);
     2 
     2 /*
     3 //     (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc.
     3 //@ sourceMappingURL=backbone-min.map
     4 //     Backbone may be freely distributed under the MIT license.
     4 */
     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 `"key", value` and `{key: value}` -style arguments.
       
   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);