--- /dev/null
+from django.contrib import admin
+
+# Register your models here.
--- /dev/null
+from Queue import Queue, Empty
+import re
+from threading import Event
+
+from gridftp import FTPClient, Buffer, HandleAttr, OperationAttr
+
+
+class FTPException(Exception):
+ pass
+
+
+class FTPOperation:
+ def __init__(self, proxy=None):
+ self.end = Event()
+ self.attr = HandleAttr()
+ self.op_attr = OperationAttr()
+
+ if proxy is not None:
+ self.op_attr.set_authorization(proxy)
+
+ self.buffer = Buffer(4096)
+ self.cli = FTPClient(self.attr)
+
+ self.stream = Queue()
+ self.error = None
+
+ def __del__(self):
+ self.attr.destroy()
+ self.op_attr.destroy()
+ self.cli.destroy()
+
+ def report_error(self):
+ if self.error is not None:
+ match = re.search(r'A system call failed: (.*)$', self.error.replace('\r\n', '\n'), re.MULTILINE)
+
+ msg = match.groups()[0] if match else "Unknown error"
+
+ raise FTPException(msg)
+
+ def _read(self, arg, handle, error, buff, length, offset, eof):
+ if not error:
+ self.stream.put(str(buff))
+
+ if not eof:
+ self.cli.register_read(self.buffer, self._read, None)
+
+ def _done(self, arg, handle, error):
+ self.error = error
+ self.end.set()
+
+ def listing(self, url):
+ self.cli.verbose_list(url, self._done, None, self.op_attr)
+ self.cli.register_read(self.buffer, self._read, None)
+
+ self.end.wait()
+ self.end.clear()
+
+ self.report_error()
+
+ result = ''
+ while not self.stream.empty():
+ result += self.stream.get()
+
+ return result
+
+ def get(self, url):
+ self.cli.get(url, self._done, None, self.op_attr)
+ self.cli.register_read(self.buffer, self._read, None)
+
+ while True:
+ try:
+ yield self.stream.get(timeout=0.1)
+ except Empty:
+ if self.end.wait(0):
+ break
+
+ self.end.clear()
+
+ self.report_error()
--- /dev/null
+from django.db import models
+
+# Create your models here.
--- /dev/null
+(function(t,e){if(typeof define==="function"&&define.amd){define(["underscore","jquery","exports"],function(i,r,s){t.Backbone=e(t,s,i,r)})}else if(typeof exports!=="undefined"){var i=require("underscore");e(t,exports,i)}else{t.Backbone=e(t,{},t._,t.jQuery||t.Zepto||t.ender||t.$)}})(this,function(t,e,i,r){var s=t.Backbone;var n=[];var a=n.push;var o=n.slice;var h=n.splice;e.VERSION="1.1.2";e.$=r;e.noConflict=function(){t.Backbone=s;return this};e.emulateHTTP=false;e.emulateJSON=false;var u=e.Events={on:function(t,e,i){if(!c(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,r){if(!c(this,"once",t,[e,r])||!e)return this;var s=this;var n=i.once(function(){s.off(t,n);e.apply(this,arguments)});n._callback=e;return this.on(t,n,r)},off:function(t,e,r){var s,n,a,o,h,u,l,f;if(!this._events||!c(this,"off",t,[e,r]))return this;if(!t&&!e&&!r){this._events=void 0;return this}o=t?[t]:i.keys(this._events);for(h=0,u=o.length;h<u;h++){t=o[h];if(a=this._events[t]){this._events[t]=s=[];if(e||r){for(l=0,f=a.length;l<f;l++){n=a[l];if(e&&e!==n.callback&&e!==n.callback._callback||r&&r!==n.context){s.push(n)}}}if(!s.length)delete this._events[t]}}return this},trigger:function(t){if(!this._events)return this;var e=o.call(arguments,1);if(!c(this,"trigger",t,e))return this;var i=this._events[t];var r=this._events.all;if(i)f(i,e);if(r)f(r,arguments);return this},stopListening:function(t,e,r){var s=this._listeningTo;if(!s)return this;var n=!e&&!r;if(!r&&typeof e==="object")r=this;if(t)(s={})[t._listenId]=t;for(var a in s){t=s[a];t.off(e,r,this);if(n||i.isEmpty(t._events))delete this._listeningTo[a]}return this}};var l=/\s+/;var c=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(l.test(i)){var n=i.split(l);for(var a=0,o=n.length;a<o;a++){t[e].apply(t,[n[a]].concat(r))}return false}return true};var f=function(t,e){var i,r=-1,s=t.length,n=e[0],a=e[1],o=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,o);return;default:while(++r<s)(i=t[r]).callback.apply(i.ctx,e);return}};var d={listenTo:"on",listenToOnce:"once"};i.each(d,function(t,e){u[e]=function(e,r,s){var n=this._listeningTo||(this._listeningTo={});var a=e._listenId||(e._listenId=i.uniqueId("l"));n[a]=e;if(!s&&typeof r==="object")s=this;e[t](r,s,this);return this}});u.bind=u.on;u.unbind=u.off;i.extend(e,u);var p=e.Model=function(t,e){var r=t||{};e||(e={});this.cid=i.uniqueId("c");this.attributes={};if(e.collection)this.collection=e.collection;if(e.parse)r=this.parse(r,e)||{};r=i.defaults({},r,i.result(this,"defaults"));this.set(r,e);this.changed={};this.initialize.apply(this,arguments)};i.extend(p.prototype,u,{changed:null,validationError:null,idAttribute:"id",initialize:function(){},toJSON:function(t){return i.clone(this.attributes)},sync:function(){return e.sync.apply(this,arguments)},get:function(t){return this.attributes[t]},escape:function(t){return i.escape(this.get(t))},has:function(t){return this.get(t)!=null},set:function(t,e,r){var s,n,a,o,h,u,l,c;if(t==null)return this;if(typeof t==="object"){n=t;r=e}else{(n={})[t]=e}r||(r={});if(!this._validate(n,r))return false;a=r.unset;h=r.silent;o=[];u=this._changing;this._changing=true;if(!u){this._previousAttributes=i.clone(this.attributes);this.changed={}}c=this.attributes,l=this._previousAttributes;if(this.idAttribute in n)this.id=n[this.idAttribute];for(s in n){e=n[s];if(!i.isEqual(c[s],e))o.push(s);if(!i.isEqual(l[s],e)){this.changed[s]=e}else{delete this.changed[s]}a?delete c[s]:c[s]=e}if(!h){if(o.length)this._pending=r;for(var f=0,d=o.length;f<d;f++){this.trigger("change:"+o[f],this,c[o[f]],r)}}if(u)return this;if(!h){while(this._pending){r=this._pending;this._pending=false;this.trigger("change",this,r)}}this._pending=false;this._changing=false;return this},unset:function(t,e){return this.set(t,void 0,i.extend({},e,{unset:true}))},clear:function(t){var e={};for(var r in this.attributes)e[r]=void 0;return this.set(e,i.extend({},t,{unset:true}))},hasChanged:function(t){if(t==null)return!i.isEmpty(this.changed);return i.has(this.changed,t)},changedAttributes:function(t){if(!t)return this.hasChanged()?i.clone(this.changed):false;var e,r=false;var s=this._changing?this._previousAttributes:this.attributes;for(var n in t){if(i.isEqual(s[n],e=t[n]))continue;(r||(r={}))[n]=e}return r},previous:function(t){if(t==null||!this._previousAttributes)return null;return this._previousAttributes[t]},previousAttributes:function(){return i.clone(this._previousAttributes)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=this;var r=t.success;t.success=function(i){if(!e.set(e.parse(i,t),t))return false;if(r)r(e,i,t);e.trigger("sync",e,i,t)};q(this,t);return this.sync("read",this,t)},save:function(t,e,r){var s,n,a,o=this.attributes;if(t==null||typeof t==="object"){s=t;r=e}else{(s={})[t]=e}r=i.extend({validate:true},r);if(s&&!r.wait){if(!this.set(s,r))return false}else{if(!this._validate(s,r))return false}if(s&&r.wait){this.attributes=i.extend({},o,s)}if(r.parse===void 0)r.parse=true;var h=this;var u=r.success;r.success=function(t){h.attributes=o;var e=h.parse(t,r);if(r.wait)e=i.extend(s||{},e);if(i.isObject(e)&&!h.set(e,r)){return false}if(u)u(h,t,r);h.trigger("sync",h,t,r)};q(this,r);n=this.isNew()?"create":r.patch?"patch":"update";if(n==="patch")r.attrs=s;a=this.sync(n,this,r);if(s&&r.wait)this.attributes=o;return a},destroy:function(t){t=t?i.clone(t):{};var e=this;var r=t.success;var s=function(){e.trigger("destroy",e,e.collection,t)};t.success=function(i){if(t.wait||e.isNew())s();if(r)r(e,i,t);if(!e.isNew())e.trigger("sync",e,i,t)};if(this.isNew()){t.success();return false}q(this,t);var n=this.sync("delete",this,t);if(!t.wait)s();return n},url:function(){var t=i.result(this,"urlRoot")||i.result(this.collection,"url")||M();if(this.isNew())return t;return t.replace(/([^\/])$/,"$1/")+encodeURIComponent(this.id)},parse:function(t,e){return t},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return!this.has(this.idAttribute)},isValid:function(t){return this._validate({},i.extend(t||{},{validate:true}))},_validate:function(t,e){if(!e.validate||!this.validate)return true;t=i.extend({},this.attributes,t);var r=this.validationError=this.validate(t,e)||null;if(!r)return true;this.trigger("invalid",this,r,i.extend(e,{validationError:r}));return false}});var v=["keys","values","pairs","invert","pick","omit"];i.each(v,function(t){p.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.attributes);return i[t].apply(i,e)}});var g=e.Collection=function(t,e){e||(e={});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,i.extend({silent:true},e))};var m={add:true,remove:true,merge:true};var y={add:true,remove:false};i.extend(g.prototype,u,{model:p,initialize:function(){},toJSON:function(t){return this.map(function(e){return e.toJSON(t)})},sync:function(){return e.sync.apply(this,arguments)},add:function(t,e){return this.set(t,i.extend({merge:false},e,y))},remove:function(t,e){var r=!i.isArray(t);t=r?[t]:i.clone(t);e||(e={});var s,n,a,o;for(s=0,n=t.length;s<n;s++){o=t[s]=this.get(t[s]);if(!o)continue;delete this._byId[o.id];delete this._byId[o.cid];a=this.indexOf(o);this.models.splice(a,1);this.length--;if(!e.silent){e.index=a;o.trigger("remove",o,this,e)}this._removeReference(o,e)}return r?t[0]:t},set:function(t,e){e=i.defaults({},e,m);if(e.parse)t=this.parse(t,e);var r=!i.isArray(t);t=r?t?[t]:[]:i.clone(t);var s,n,a,o,h,u,l;var c=e.at;var f=this.model;var d=this.comparator&&c==null&&e.sort!==false;var v=i.isString(this.comparator)?this.comparator:null;var g=[],y=[],_={};var b=e.add,w=e.merge,x=e.remove;var E=!d&&b&&x?[]:false;for(s=0,n=t.length;s<n;s++){h=t[s]||{};if(h instanceof p){a=o=h}else{a=h[f.prototype.idAttribute||"id"]}if(u=this.get(a)){if(x)_[u.cid]=true;if(w){h=h===o?o.attributes:h;if(e.parse)h=u.parse(h,e);u.set(h,e);if(d&&!l&&u.hasChanged(v))l=true}t[s]=u}else if(b){o=t[s]=this._prepareModel(h,e);if(!o)continue;g.push(o);this._addReference(o,e)}o=u||o;if(E&&(o.isNew()||!_[o.id]))E.push(o);_[o.id]=true}if(x){for(s=0,n=this.length;s<n;++s){if(!_[(o=this.models[s]).cid])y.push(o)}if(y.length)this.remove(y,e)}if(g.length||E&&E.length){if(d)l=true;this.length+=g.length;if(c!=null){for(s=0,n=g.length;s<n;s++){this.models.splice(c+s,0,g[s])}}else{if(E)this.models.length=0;var k=E||g;for(s=0,n=k.length;s<n;s++){this.models.push(k[s])}}}if(l)this.sort({silent:true});if(!e.silent){for(s=0,n=g.length;s<n;s++){(o=g[s]).trigger("add",o,this,e)}if(l||E&&E.length)this.trigger("sort",this,e)}return r?t[0]:t},reset:function(t,e){e||(e={});for(var r=0,s=this.models.length;r<s;r++){this._removeReference(this.models[r],e)}e.previousModels=this.models;this._reset();t=this.add(t,i.extend({silent:true},e));if(!e.silent)this.trigger("reset",this,e);return t},push:function(t,e){return this.add(t,i.extend({at:this.length},e))},pop:function(t){var e=this.at(this.length-1);this.remove(e,t);return e},unshift:function(t,e){return this.add(t,i.extend({at:0},e))},shift:function(t){var e=this.at(0);this.remove(e,t);return e},slice:function(){return o.apply(this.models,arguments)},get:function(t){if(t==null)return void 0;return this._byId[t]||this._byId[t.id]||this._byId[t.cid]},at:function(t){return this.models[t]},where:function(t,e){if(i.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(i.isString(this.comparator)||this.comparator.length===1){this.models=this.sortBy(this.comparator,this)}else{this.models.sort(i.bind(this.comparator,this))}if(!t.silent)this.trigger("sort",this,t);return this},pluck:function(t){return i.invoke(this.models,"get",t)},fetch:function(t){t=t?i.clone(t):{};if(t.parse===void 0)t.parse=true;var e=t.success;var r=this;t.success=function(i){var s=t.reset?"reset":"set";r[s](i,t);if(e)e(r,i,t);r.trigger("sync",r,i,t)};q(this,t);return this.sync("read",this,t)},create:function(t,e){e=e?i.clone(e):{};if(!(t=this._prepareModel(t,e)))return false;if(!e.wait)this.add(t,e);var r=this;var s=e.success;e.success=function(t,i){if(e.wait)r.add(t,e);if(s)s(t,i,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 p)return t;e=e?i.clone(e):{};e.collection=this;var r=new this.model(t,e);if(!r.validationError)return r;this.trigger("invalid",this,r.validationError,e);return false},_addReference:function(t,e){this._byId[t.cid]=t;if(t.id!=null)this._byId[t.id]=t;if(!t.collection)t.collection=this;t.on("all",this._onModelEvent,this)},_removeReference:function(t,e){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","difference","indexOf","shuffle","lastIndexOf","isEmpty","chain","sample"];i.each(_,function(t){g.prototype[t]=function(){var e=o.call(arguments);e.unshift(this.models);return i[t].apply(i,e)}});var b=["groupBy","countBy","sortBy","indexBy"];i.each(b,function(t){g.prototype[t]=function(e,r){var s=i.isFunction(e)?e:function(t){return t.get(e)};return i[t](this.models,s,r)}});var w=e.View=function(t){this.cid=i.uniqueId("view");t||(t={});i.extend(this,i.pick(t,E));this._ensureElement();this.initialize.apply(this,arguments);this.delegateEvents()};var x=/^(\S+)\s*(.*)$/;var E=["model","collection","el","id","attributes","className","tagName","events"];i.extend(w.prototype,u,{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,i){if(this.$el)this.undelegateEvents();this.$el=t instanceof e.$?t:e.$(t);this.el=this.$el[0];if(i!==false)this.delegateEvents();return this},delegateEvents:function(t){if(!(t||(t=i.result(this,"events"))))return this;this.undelegateEvents();for(var e in t){var r=t[e];if(!i.isFunction(r))r=this[t[e]];if(!r)continue;var s=e.match(x);var n=s[1],a=s[2];r=i.bind(r,this);n+=".delegateEvents"+this.cid;if(a===""){this.$el.on(n,r)}else{this.$el.on(n,a,r)}}return this},undelegateEvents:function(){this.$el.off(".delegateEvents"+this.cid);return this},_ensureElement:function(){if(!this.el){var t=i.extend({},i.result(this,"attributes"));if(this.id)t.id=i.result(this,"id");if(this.className)t["class"]=i.result(this,"className");var r=e.$("<"+i.result(this,"tagName")+">").attr(t);this.setElement(r,false)}else{this.setElement(i.result(this,"el"),false)}}});e.sync=function(t,r,s){var n=T[t];i.defaults(s||(s={}),{emulateHTTP:e.emulateHTTP,emulateJSON:e.emulateJSON});var a={type:n,dataType:"json"};if(!s.url){a.url=i.result(r,"url")||M()}if(s.data==null&&r&&(t==="create"||t==="update"||t==="patch")){a.contentType="application/json";a.data=JSON.stringify(s.attrs||r.toJSON(s))}if(s.emulateJSON){a.contentType="application/x-www-form-urlencoded";a.data=a.data?{model:a.data}:{}}if(s.emulateHTTP&&(n==="PUT"||n==="DELETE"||n==="PATCH")){a.type="POST";if(s.emulateJSON)a.data._method=n;var o=s.beforeSend;s.beforeSend=function(t){t.setRequestHeader("X-HTTP-Method-Override",n);if(o)return o.apply(this,arguments)}}if(a.type!=="GET"&&!s.emulateJSON){a.processData=false}if(a.type==="PATCH"&&k){a.xhr=function(){return new ActiveXObject("Microsoft.XMLHTTP")}}var h=s.xhr=e.ajax(i.extend(a,s));r.trigger("request",r,h,s);return h};var k=typeof window!=="undefined"&&!!window.ActiveXObject&&!(window.XMLHttpRequest&&(new XMLHttpRequest).dispatchEvent);var T={create:"POST",update:"PUT",patch:"PATCH","delete":"DELETE",read:"GET"};e.ajax=function(){return e.$.ajax.apply(e.$,arguments)};var $=e.Router=function(t){t||(t={});if(t.routes)this.routes=t.routes;this._bindRoutes();this.initialize.apply(this,arguments)};var S=/\((.*?)\)/g;var H=/(\(\?)?:\w+/g;var A=/\*\w+/g;var I=/[\-{}\[\]+?.,\\\^$|#\s]/g;i.extend($.prototype,u,{initialize:function(){},route:function(t,r,s){if(!i.isRegExp(t))t=this._routeToRegExp(t);if(i.isFunction(r)){s=r;r=""}if(!s)s=this[r];var n=this;e.history.route(t,function(i){var a=n._extractParameters(t,i);n.execute(s,a);n.trigger.apply(n,["route:"+r].concat(a));n.trigger("route",r,a);e.history.trigger("route",n,r,a)});return this},execute:function(t,e){if(t)t.apply(this,e)},navigate:function(t,i){e.history.navigate(t,i);return this},_bindRoutes:function(){if(!this.routes)return;this.routes=i.result(this,"routes");var t,e=i.keys(this.routes);while((t=e.pop())!=null){this.route(t,this.routes[t])}},_routeToRegExp:function(t){t=t.replace(I,"\\$&").replace(S,"(?:$1)?").replace(H,function(t,e){return e?t:"([^/?]+)"}).replace(A,"([^?]*?)");return new RegExp("^"+t+"(?:\\?([\\s\\S]*))?$")},_extractParameters:function(t,e){var r=t.exec(e).slice(1);return i.map(r,function(t,e){if(e===r.length-1)return t||null;return t?decodeURIComponent(t):null})}});var N=e.History=function(){this.handlers=[];i.bindAll(this,"checkUrl");if(typeof window!=="undefined"){this.location=window.location;this.history=window.history}};var R=/^[#\/]|\s+$/g;var O=/^\/+|\/+$/g;var P=/msie [\w.]+/;var C=/\/$/;var j=/#.*$/;N.started=false;i.extend(N.prototype,u,{interval:50,atRoot:function(){return this.location.pathname.replace(/[^\/]$/,"$&/")===this.root},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=decodeURI(this.location.pathname+this.location.search);var i=this.root.replace(C,"");if(!t.indexOf(i))t=t.slice(i.length)}else{t=this.getHash()}}return t.replace(R,"")},start:function(t){if(N.started)throw new Error("Backbone.history has already been started");N.started=true;this.options=i.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 r=this.getFragment();var s=document.documentMode;var n=P.exec(navigator.userAgent.toLowerCase())&&(!s||s<=7);this.root=("/"+this.root+"/").replace(O,"/");if(n&&this._wantsHashChange){var a=e.$('<iframe src="javascript:0" tabindex="-1">');this.iframe=a.hide().appendTo("body")[0].contentWindow;this.navigate(r)}if(this._hasPushState){e.$(window).on("popstate",this.checkUrl)}else if(this._wantsHashChange&&"onhashchange"in window&&!n){e.$(window).on("hashchange",this.checkUrl)}else if(this._wantsHashChange){this._checkUrlInterval=setInterval(this.checkUrl,this.interval)}this.fragment=r;var o=this.location;if(this._wantsHashChange&&this._wantsPushState){if(!this._hasPushState&&!this.atRoot()){this.fragment=this.getFragment(null,true);this.location.replace(this.root+"#"+this.fragment);return true}else if(this._hasPushState&&this.atRoot()&&o.hash){this.fragment=this.getHash().replace(R,"");this.history.replaceState({},document.title,this.root+this.fragment)}}if(!this.options.silent)return this.loadUrl()},stop:function(){e.$(window).off("popstate",this.checkUrl).off("hashchange",this.checkUrl);if(this._checkUrlInterval)clearInterval(this._checkUrlInterval);N.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()},loadUrl:function(t){t=this.fragment=this.getFragment(t);return i.any(this.handlers,function(e){if(e.route.test(t)){e.callback(t);return true}})},navigate:function(t,e){if(!N.started)return false;if(!e||e===true)e={trigger:!!e};var i=this.root+(t=this.getFragment(t||""));t=t.replace(j,"");if(this.fragment===t)return;this.fragment=t;if(t===""&&i!=="/")i=i.slice(0,-1);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)return this.loadUrl(t)},_updateHash:function(t,e,i){if(i){var r=t.href.replace(/(javascript:|#).*$/,"");t.replace(r+"#"+e)}else{t.hash="#"+e}}});e.history=new N;var U=function(t,e){var r=this;var s;if(t&&i.has(t,"constructor")){s=t.constructor}else{s=function(){return r.apply(this,arguments)}}i.extend(s,r,e);var n=function(){this.constructor=s};n.prototype=r.prototype;s.prototype=new n;if(t)i.extend(s.prototype,t);s.__super__=r.prototype;return s};p.extend=g.extend=$.extend=w.extend=N.extend=U;var M=function(){throw new Error('A "url" property or function must be specified')};var q=function(t,e){var i=e.error;e.error=function(r){if(i)i(t,r,e);t.trigger("error",t,r,e)}};return e});
+//# sourceMappingURL=backbone-min.map
\ No newline at end of file
--- /dev/null
+{"version":3,"file":"backbone-min.js","sources":["backbone.js"],"names":["root","factory","define","amd","_","$","exports","Backbone","require","jQuery","Zepto","ender","this","previousBackbone","array","push","slice","splice","VERSION","noConflict","emulateHTTP","emulateJSON","Events","on","name","callback","context","eventsApi","_events","events","ctx","once","self","off","apply","arguments","_callback","retain","ev","names","i","l","j","k","keys","length","trigger","args","call","allEvents","all","triggerEvents","stopListening","obj","listeningTo","_listeningTo","remove","_listenId","id","isEmpty","eventSplitter","action","rest","key","concat","test","split","a1","a2","a3","listenMethods","listenTo","listenToOnce","each","implementation","method","uniqueId","bind","unbind","extend","Model","attributes","options","attrs","cid","collection","parse","defaults","result","set","changed","initialize","prototype","validationError","idAttribute","toJSON","clone","sync","get","attr","escape","has","val","unset","changes","silent","changing","prev","current","_validate","_changing","_previousAttributes","isEqual","_pending","clear","hasChanged","changedAttributes","diff","old","previous","previousAttributes","fetch","model","success","resp","wrapError","save","xhr","validate","wait","serverAttrs","isObject","isNew","patch","destroy","url","base","urlError","replace","encodeURIComponent","constructor","isValid","error","modelMethods","unshift","Collection","models","comparator","_reset","reset","setOptions","add","merge","addOptions","map","singular","isArray","index","_byId","indexOf","_removeReference","existing","sort","at","targetModel","sortable","sortAttr","isString","toAdd","toRemove","modelMap","order","_prepareModel","_addReference","orderedModels","previousModels","pop","shift","where","first","findWhere","Error","sortBy","pluck","invoke","create","_onModelEvent","event","methods","attributeMethods","value","iterator","isFunction","View","pick","viewOptions","_ensureElement","delegateEvents","delegateEventSplitter","tagName","selector","$el","find","render","setElement","element","delegate","undelegateEvents","el","match","eventName","className","type","methodMap","params","dataType","data","contentType","JSON","stringify","_method","beforeSend","setRequestHeader","processData","noXhrPatch","ActiveXObject","ajax","window","XMLHttpRequest","dispatchEvent","update","delete","read","Router","routes","_bindRoutes","optionalParam","namedParam","splatParam","escapeRegExp","route","isRegExp","_routeToRegExp","router","history","fragment","_extractParameters","execute","navigate","optional","RegExp","exec","param","decodeURIComponent","History","handlers","bindAll","location","routeStripper","rootStripper","isExplorer","trailingSlash","pathStripper","started","interval","atRoot","pathname","getHash","href","getFragment","forcePushState","_hasPushState","_wantsHashChange","decodeURI","search","start","hashChange","_wantsPushState","pushState","docMode","document","documentMode","oldIE","navigator","userAgent","toLowerCase","frame","iframe","hide","appendTo","contentWindow","checkUrl","_checkUrlInterval","setInterval","loc","hash","replaceState","title","loadUrl","stop","clearInterval","e","any","handler","_updateHash","open","close","assign","protoProps","staticProps","parent","child","Surrogate","__super__"],"mappings":"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"}
\ No newline at end of file
--- /dev/null
+// Backbone.js 1.1.2
+
+// (c) 2010-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Backbone may be freely distributed under the MIT license.
+// For all details and documentation:
+// http://backbonejs.org
+
+(function(root, factory) {
+
+ // Set up Backbone appropriately for the environment. Start with AMD.
+ if (typeof define === 'function' && define.amd) {
+ define(['underscore', 'jquery', 'exports'], function(_, $, exports) {
+ // Export global even in AMD case in case this script is loaded with
+ // others that may still expect a global Backbone.
+ root.Backbone = factory(root, exports, _, $);
+ });
+
+ // Next for Node.js or CommonJS. jQuery may not be needed as a module.
+ } else if (typeof exports !== 'undefined') {
+ var _ = require('underscore');
+ factory(root, exports, _);
+
+ // Finally, as a browser global.
+ } else {
+ root.Backbone = factory(root, {}, root._, (root.jQuery || root.Zepto || root.ender || root.$));
+ }
+
+}(this, function(root, Backbone, _, $) {
+
+ // Initial Setup
+ // -------------
+
+ // Save the previous value of the `Backbone` variable, so that it can be
+ // restored later on, if `noConflict` is used.
+ var previousBackbone = root.Backbone;
+
+ // Create local references to array methods we'll want to use later.
+ var array = [];
+ var push = array.push;
+ var slice = array.slice;
+ var splice = array.splice;
+
+ // Current version of the library. Keep in sync with `package.json`.
+ Backbone.VERSION = '1.1.2';
+
+ // For Backbone's purposes, jQuery, Zepto, Ender, or My Library (kidding) owns
+ // the `$` variable.
+ Backbone.$ = $;
+
+ // Runs Backbone.js in *noConflict* mode, returning the `Backbone` variable
+ // to its previous owner. Returns a reference to this Backbone object.
+ Backbone.noConflict = function() {
+ root.Backbone = previousBackbone;
+ return this;
+ };
+
+ // Turn on `emulateHTTP` to support legacy HTTP servers. Setting this option
+ // will fake `"PATCH"`, `"PUT"` and `"DELETE"` requests via the `_method` parameter and
+ // set a `X-Http-Method-Override` header.
+ Backbone.emulateHTTP = false;
+
+ // Turn on `emulateJSON` to support legacy servers that can't deal with direct
+ // `application/json` requests ... will encode the body as
+ // `application/x-www-form-urlencoded` instead and will send the model in a
+ // form param named `model`.
+ Backbone.emulateJSON = false;
+
+ // Backbone.Events
+ // ---------------
+
+ // A module that can be mixed in to *any object* in order to provide it with
+ // custom events. You may bind with `on` or remove with `off` callback
+ // functions to an event; `trigger`-ing an event fires all callbacks in
+ // succession.
+ //
+ // var object = {};
+ // _.extend(object, Backbone.Events);
+ // object.on('expand', function(){ alert('expanded'); });
+ // object.trigger('expand');
+ //
+ var Events = Backbone.Events = {
+
+ // Bind an event to a `callback` function. Passing `"all"` will bind
+ // the callback to all events fired.
+ on: function(name, callback, context) {
+ if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this;
+ this._events || (this._events = {});
+ var events = this._events[name] || (this._events[name] = []);
+ events.push({callback: callback, context: context, ctx: context || this});
+ return this;
+ },
+
+ // Bind an event to only be triggered a single time. After the first time
+ // the callback is invoked, it will be removed.
+ once: function(name, callback, context) {
+ if (!eventsApi(this, 'once', name, [callback, context]) || !callback) return this;
+ var self = this;
+ var once = _.once(function() {
+ self.off(name, once);
+ callback.apply(this, arguments);
+ });
+ once._callback = callback;
+ return this.on(name, once, context);
+ },
+
+ // Remove one or many callbacks. If `context` is null, removes all
+ // callbacks with that function. If `callback` is null, removes all
+ // callbacks for the event. If `name` is null, removes all bound
+ // callbacks for all events.
+ off: function(name, callback, context) {
+ var retain, ev, events, names, i, l, j, k;
+ if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this;
+ if (!name && !callback && !context) {
+ this._events = void 0;
+ return this;
+ }
+ names = name ? [name] : _.keys(this._events);
+ for (i = 0, l = names.length; i < l; i++) {
+ name = names[i];
+ if (events = this._events[name]) {
+ this._events[name] = retain = [];
+ if (callback || context) {
+ for (j = 0, k = events.length; j < k; j++) {
+ ev = events[j];
+ if ((callback && callback !== ev.callback && callback !== ev.callback._callback) ||
+ (context && context !== ev.context)) {
+ retain.push(ev);
+ }
+ }
+ }
+ if (!retain.length) delete this._events[name];
+ }
+ }
+
+ return this;
+ },
+
+ // Trigger one or many events, firing all bound callbacks. Callbacks are
+ // passed the same arguments as `trigger` is, apart from the event name
+ // (unless you're listening on `"all"`, which will cause your callback to
+ // receive the true name of the event as the first argument).
+ trigger: function(name) {
+ if (!this._events) return this;
+ var args = slice.call(arguments, 1);
+ if (!eventsApi(this, 'trigger', name, args)) return this;
+ var events = this._events[name];
+ var allEvents = this._events.all;
+ if (events) triggerEvents(events, args);
+ if (allEvents) triggerEvents(allEvents, arguments);
+ return this;
+ },
+
+ // Tell this object to stop listening to either specific events ... or
+ // to every object it's currently listening to.
+ stopListening: function(obj, name, callback) {
+ var listeningTo = this._listeningTo;
+ if (!listeningTo) return this;
+ var remove = !name && !callback;
+ if (!callback && typeof name === 'object') callback = this;
+ if (obj) (listeningTo = {})[obj._listenId] = obj;
+ for (var id in listeningTo) {
+ obj = listeningTo[id];
+ obj.off(name, callback, this);
+ if (remove || _.isEmpty(obj._events)) delete this._listeningTo[id];
+ }
+ return this;
+ }
+
+ };
+
+ // Regular expression used to split event strings.
+ var eventSplitter = /\s+/;
+
+ // Implement fancy features of the Events API such as multiple event
+ // names `"change blur"` and jQuery-style event maps `{change: action}`
+ // in terms of the existing API.
+ var eventsApi = function(obj, action, name, rest) {
+ if (!name) return true;
+
+ // Handle event maps.
+ if (typeof name === 'object') {
+ for (var key in name) {
+ obj[action].apply(obj, [key, name[key]].concat(rest));
+ }
+ return false;
+ }
+
+ // Handle space separated event names.
+ if (eventSplitter.test(name)) {
+ var names = name.split(eventSplitter);
+ for (var i = 0, l = names.length; i < l; i++) {
+ obj[action].apply(obj, [names[i]].concat(rest));
+ }
+ return false;
+ }
+
+ return true;
+ };
+
+ // A difficult-to-believe, but optimized internal dispatch function for
+ // triggering events. Tries to keep the usual cases speedy (most internal
+ // Backbone events have 3 arguments).
+ var triggerEvents = function(events, args) {
+ var ev, i = -1, l = events.length, a1 = args[0], a2 = args[1], a3 = args[2];
+ switch (args.length) {
+ case 0: while (++i < l) (ev = events[i]).callback.call(ev.ctx); return;
+ case 1: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1); return;
+ case 2: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2); return;
+ case 3: while (++i < l) (ev = events[i]).callback.call(ev.ctx, a1, a2, a3); return;
+ default: while (++i < l) (ev = events[i]).callback.apply(ev.ctx, args); return;
+ }
+ };
+
+ var listenMethods = {listenTo: 'on', listenToOnce: 'once'};
+
+ // Inversion-of-control versions of `on` and `once`. Tell *this* object to
+ // listen to an event in another object ... keeping track of what it's
+ // listening to.
+ _.each(listenMethods, function(implementation, method) {
+ Events[method] = function(obj, name, callback) {
+ var listeningTo = this._listeningTo || (this._listeningTo = {});
+ var id = obj._listenId || (obj._listenId = _.uniqueId('l'));
+ listeningTo[id] = obj;
+ if (!callback && typeof name === 'object') callback = this;
+ obj[implementation](name, callback, this);
+ return this;
+ };
+ });
+
+ // Aliases for backwards compatibility.
+ Events.bind = Events.on;
+ Events.unbind = Events.off;
+
+ // Allow the `Backbone` object to serve as a global event bus, for folks who
+ // want global "pubsub" in a convenient place.
+ _.extend(Backbone, Events);
+
+ // Backbone.Model
+ // --------------
+
+ // Backbone **Models** are the basic data object in the framework --
+ // frequently representing a row in a table in a database on your server.
+ // A discrete chunk of data and a bunch of useful, related methods for
+ // performing computations and transformations on that data.
+
+ // Create a new model with the specified attributes. A client id (`cid`)
+ // is automatically generated and assigned for you.
+ var Model = Backbone.Model = function(attributes, options) {
+ var attrs = attributes || {};
+ options || (options = {});
+ this.cid = _.uniqueId('c');
+ this.attributes = {};
+ if (options.collection) this.collection = options.collection;
+ if (options.parse) attrs = this.parse(attrs, options) || {};
+ attrs = _.defaults({}, attrs, _.result(this, 'defaults'));
+ this.set(attrs, options);
+ this.changed = {};
+ this.initialize.apply(this, arguments);
+ };
+
+ // Attach all inheritable methods to the Model prototype.
+ _.extend(Model.prototype, Events, {
+
+ // A hash of attributes whose current and previous value differ.
+ changed: null,
+
+ // The value returned during the last failed validation.
+ validationError: null,
+
+ // The default name for the JSON `id` attribute is `"id"`. MongoDB and
+ // CouchDB users may want to set this to `"_id"`.
+ idAttribute: 'id',
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // Return a copy of the model's `attributes` object.
+ toJSON: function(options) {
+ return _.clone(this.attributes);
+ },
+
+ // Proxy `Backbone.sync` by default -- but override this if you need
+ // custom syncing semantics for *this* particular model.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
+
+ // Get the value of an attribute.
+ get: function(attr) {
+ return this.attributes[attr];
+ },
+
+ // Get the HTML-escaped value of an attribute.
+ escape: function(attr) {
+ return _.escape(this.get(attr));
+ },
+
+ // Returns `true` if the attribute contains a value that is not null
+ // or undefined.
+ has: function(attr) {
+ return this.get(attr) != null;
+ },
+
+ // Set a hash of model attributes on the object, firing `"change"`. This is
+ // the core primitive operation of a model, updating the data and notifying
+ // anyone who needs to know about the change in state. The heart of the beast.
+ set: function(key, val, options) {
+ var attr, attrs, unset, changes, silent, changing, prev, current;
+ if (key == null) return this;
+
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ if (typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
+
+ options || (options = {});
+
+ // Run validation.
+ if (!this._validate(attrs, options)) return false;
+
+ // Extract attributes and options.
+ unset = options.unset;
+ silent = options.silent;
+ changes = [];
+ changing = this._changing;
+ this._changing = true;
+
+ if (!changing) {
+ this._previousAttributes = _.clone(this.attributes);
+ this.changed = {};
+ }
+ current = this.attributes, prev = this._previousAttributes;
+
+ // Check for changes of `id`.
+ if (this.idAttribute in attrs) this.id = attrs[this.idAttribute];
+
+ // For each `set` attribute, update or delete the current value.
+ for (attr in attrs) {
+ val = attrs[attr];
+ if (!_.isEqual(current[attr], val)) changes.push(attr);
+ if (!_.isEqual(prev[attr], val)) {
+ this.changed[attr] = val;
+ } else {
+ delete this.changed[attr];
+ }
+ unset ? delete current[attr] : current[attr] = val;
+ }
+
+ // Trigger all relevant attribute changes.
+ if (!silent) {
+ if (changes.length) this._pending = options;
+ for (var i = 0, l = changes.length; i < l; i++) {
+ this.trigger('change:' + changes[i], this, current[changes[i]], options);
+ }
+ }
+
+ // You might be wondering why there's a `while` loop here. Changes can
+ // be recursively nested within `"change"` events.
+ if (changing) return this;
+ if (!silent) {
+ while (this._pending) {
+ options = this._pending;
+ this._pending = false;
+ this.trigger('change', this, options);
+ }
+ }
+ this._pending = false;
+ this._changing = false;
+ return this;
+ },
+
+ // Remove an attribute from the model, firing `"change"`. `unset` is a noop
+ // if the attribute doesn't exist.
+ unset: function(attr, options) {
+ return this.set(attr, void 0, _.extend({}, options, {unset: true}));
+ },
+
+ // Clear all attributes on the model, firing `"change"`.
+ clear: function(options) {
+ var attrs = {};
+ for (var key in this.attributes) attrs[key] = void 0;
+ return this.set(attrs, _.extend({}, options, {unset: true}));
+ },
+
+ // Determine if the model has changed since the last `"change"` event.
+ // If you specify an attribute name, determine if that attribute has changed.
+ hasChanged: function(attr) {
+ if (attr == null) return !_.isEmpty(this.changed);
+ return _.has(this.changed, attr);
+ },
+
+ // Return an object containing all the attributes that have changed, or
+ // false if there are no changed attributes. Useful for determining what
+ // parts of a view need to be updated and/or what attributes need to be
+ // persisted to the server. Unset attributes will be set to undefined.
+ // You can also pass an attributes object to diff against the model,
+ // determining if there *would be* a change.
+ changedAttributes: function(diff) {
+ if (!diff) return this.hasChanged() ? _.clone(this.changed) : false;
+ var val, changed = false;
+ var old = this._changing ? this._previousAttributes : this.attributes;
+ for (var attr in diff) {
+ if (_.isEqual(old[attr], (val = diff[attr]))) continue;
+ (changed || (changed = {}))[attr] = val;
+ }
+ return changed;
+ },
+
+ // Get the previous value of an attribute, recorded at the time the last
+ // `"change"` event was fired.
+ previous: function(attr) {
+ if (attr == null || !this._previousAttributes) return null;
+ return this._previousAttributes[attr];
+ },
+
+ // Get all of the attributes of the model at the time of the previous
+ // `"change"` event.
+ previousAttributes: function() {
+ return _.clone(this._previousAttributes);
+ },
+
+ // Fetch the model from the server. If the server's representation of the
+ // model differs from its current attributes, they will be overridden,
+ // triggering a `"change"` event.
+ fetch: function(options) {
+ options = options ? _.clone(options) : {};
+ if (options.parse === void 0) options.parse = true;
+ var model = this;
+ var success = options.success;
+ options.success = function(resp) {
+ if (!model.set(model.parse(resp, options), options)) return false;
+ if (success) success(model, resp, options);
+ model.trigger('sync', model, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
+
+ // Set a hash of model attributes, and sync the model to the server.
+ // If the server returns an attributes hash that differs, the model's
+ // state will be `set` again.
+ save: function(key, val, options) {
+ var attrs, method, xhr, attributes = this.attributes;
+
+ // Handle both `"key", value` and `{key: value}` -style arguments.
+ if (key == null || typeof key === 'object') {
+ attrs = key;
+ options = val;
+ } else {
+ (attrs = {})[key] = val;
+ }
+
+ options = _.extend({validate: true}, options);
+
+ // If we're not waiting and attributes exist, save acts as
+ // `set(attr).save(null, opts)` with validation. Otherwise, check if
+ // the model will be valid when the attributes, if any, are set.
+ if (attrs && !options.wait) {
+ if (!this.set(attrs, options)) return false;
+ } else {
+ if (!this._validate(attrs, options)) return false;
+ }
+
+ // Set temporary attributes if `{wait: true}`.
+ if (attrs && options.wait) {
+ this.attributes = _.extend({}, attributes, attrs);
+ }
+
+ // After a successful server-side save, the client is (optionally)
+ // updated with the server-side state.
+ if (options.parse === void 0) options.parse = true;
+ var model = this;
+ var success = options.success;
+ options.success = function(resp) {
+ // Ensure attributes are restored during synchronous saves.
+ model.attributes = attributes;
+ var serverAttrs = model.parse(resp, options);
+ if (options.wait) serverAttrs = _.extend(attrs || {}, serverAttrs);
+ if (_.isObject(serverAttrs) && !model.set(serverAttrs, options)) {
+ return false;
+ }
+ if (success) success(model, resp, options);
+ model.trigger('sync', model, resp, options);
+ };
+ wrapError(this, options);
+
+ method = this.isNew() ? 'create' : (options.patch ? 'patch' : 'update');
+ if (method === 'patch') options.attrs = attrs;
+ xhr = this.sync(method, this, options);
+
+ // Restore attributes.
+ if (attrs && options.wait) this.attributes = attributes;
+
+ return xhr;
+ },
+
+ // Destroy this model on the server if it was already persisted.
+ // Optimistically removes the model from its collection, if it has one.
+ // If `wait: true` is passed, waits for the server to respond before removal.
+ destroy: function(options) {
+ options = options ? _.clone(options) : {};
+ var model = this;
+ var success = options.success;
+
+ var destroy = function() {
+ model.trigger('destroy', model, model.collection, options);
+ };
+
+ options.success = function(resp) {
+ if (options.wait || model.isNew()) destroy();
+ if (success) success(model, resp, options);
+ if (!model.isNew()) model.trigger('sync', model, resp, options);
+ };
+
+ if (this.isNew()) {
+ options.success();
+ return false;
+ }
+ wrapError(this, options);
+
+ var xhr = this.sync('delete', this, options);
+ if (!options.wait) destroy();
+ return xhr;
+ },
+
+ // Default URL for the model's representation on the server -- if you're
+ // using Backbone's restful methods, override this to change the endpoint
+ // that will be called.
+ url: function() {
+ var base =
+ _.result(this, 'urlRoot') ||
+ _.result(this.collection, 'url') ||
+ urlError();
+ if (this.isNew()) return base;
+ return base.replace(/([^\/])$/, '$1/') + encodeURIComponent(this.id);
+ },
+
+ // **parse** converts a response into the hash of attributes to be `set` on
+ // the model. The default implementation is just to pass the response along.
+ parse: function(resp, options) {
+ return resp;
+ },
+
+ // Create a new model with identical attributes to this one.
+ clone: function() {
+ return new this.constructor(this.attributes);
+ },
+
+ // A model is new if it has never been saved to the server, and lacks an id.
+ isNew: function() {
+ return !this.has(this.idAttribute);
+ },
+
+ // Check if the model is currently in a valid state.
+ isValid: function(options) {
+ return this._validate({}, _.extend(options || {}, { validate: true }));
+ },
+
+ // Run validation against the next complete set of model attributes,
+ // returning `true` if all is well. Otherwise, fire an `"invalid"` event.
+ _validate: function(attrs, options) {
+ if (!options.validate || !this.validate) return true;
+ attrs = _.extend({}, this.attributes, attrs);
+ var error = this.validationError = this.validate(attrs, options) || null;
+ if (!error) return true;
+ this.trigger('invalid', this, error, _.extend(options, {validationError: error}));
+ return false;
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Model.
+ var modelMethods = ['keys', 'values', 'pairs', 'invert', 'pick', 'omit'];
+
+ // Mix in each Underscore method as a proxy to `Model#attributes`.
+ _.each(modelMethods, function(method) {
+ Model.prototype[method] = function() {
+ var args = slice.call(arguments);
+ args.unshift(this.attributes);
+ return _[method].apply(_, args);
+ };
+ });
+
+ // Backbone.Collection
+ // -------------------
+
+ // If models tend to represent a single row of data, a Backbone Collection is
+ // more analagous to a table full of data ... or a small slice or page of that
+ // table, or a collection of rows that belong together for a particular reason
+ // -- all of the messages in this particular folder, all of the documents
+ // belonging to this particular author, and so on. Collections maintain
+ // indexes of their models, both in order, and for lookup by `id`.
+
+ // Create a new **Collection**, perhaps to contain a specific type of `model`.
+ // If a `comparator` is specified, the Collection will maintain
+ // its models in sort order, as they're added and removed.
+ var Collection = Backbone.Collection = function(models, options) {
+ options || (options = {});
+ if (options.model) this.model = options.model;
+ if (options.comparator !== void 0) this.comparator = options.comparator;
+ this._reset();
+ this.initialize.apply(this, arguments);
+ if (models) this.reset(models, _.extend({silent: true}, options));
+ };
+
+ // Default options for `Collection#set`.
+ var setOptions = {add: true, remove: true, merge: true};
+ var addOptions = {add: true, remove: false};
+
+ // Define the Collection's inheritable methods.
+ _.extend(Collection.prototype, Events, {
+
+ // The default model for a collection is just a **Backbone.Model**.
+ // This should be overridden in most cases.
+ model: Model,
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // The JSON representation of a Collection is an array of the
+ // models' attributes.
+ toJSON: function(options) {
+ return this.map(function(model){ return model.toJSON(options); });
+ },
+
+ // Proxy `Backbone.sync` by default.
+ sync: function() {
+ return Backbone.sync.apply(this, arguments);
+ },
+
+ // Add a model, or list of models to the set.
+ add: function(models, options) {
+ return this.set(models, _.extend({merge: false}, options, addOptions));
+ },
+
+ // Remove a model, or a list of models from the set.
+ remove: function(models, options) {
+ var singular = !_.isArray(models);
+ models = singular ? [models] : _.clone(models);
+ options || (options = {});
+ var i, l, index, model;
+ for (i = 0, l = models.length; i < l; i++) {
+ model = models[i] = this.get(models[i]);
+ if (!model) continue;
+ delete this._byId[model.id];
+ delete this._byId[model.cid];
+ index = this.indexOf(model);
+ this.models.splice(index, 1);
+ this.length--;
+ if (!options.silent) {
+ options.index = index;
+ model.trigger('remove', model, this, options);
+ }
+ this._removeReference(model, options);
+ }
+ return singular ? models[0] : models;
+ },
+
+ // Update a collection by `set`-ing a new list of models, adding new ones,
+ // removing models that are no longer present, and merging models that
+ // already exist in the collection, as necessary. Similar to **Model#set**,
+ // the core operation for updating the data contained by the collection.
+ set: function(models, options) {
+ options = _.defaults({}, options, setOptions);
+ if (options.parse) models = this.parse(models, options);
+ var singular = !_.isArray(models);
+ models = singular ? (models ? [models] : []) : _.clone(models);
+ var i, l, id, model, attrs, existing, sort;
+ var at = options.at;
+ var targetModel = this.model;
+ var sortable = this.comparator && (at == null) && options.sort !== false;
+ var sortAttr = _.isString(this.comparator) ? this.comparator : null;
+ var toAdd = [], toRemove = [], modelMap = {};
+ var add = options.add, merge = options.merge, remove = options.remove;
+ var order = !sortable && add && remove ? [] : false;
+
+ // Turn bare objects into model references, and prevent invalid models
+ // from being added.
+ for (i = 0, l = models.length; i < l; i++) {
+ attrs = models[i] || {};
+ if (attrs instanceof Model) {
+ id = model = attrs;
+ } else {
+ id = attrs[targetModel.prototype.idAttribute || 'id'];
+ }
+
+ // If a duplicate is found, prevent it from being added and
+ // optionally merge it into the existing model.
+ if (existing = this.get(id)) {
+ if (remove) modelMap[existing.cid] = true;
+ if (merge) {
+ attrs = attrs === model ? model.attributes : attrs;
+ if (options.parse) attrs = existing.parse(attrs, options);
+ existing.set(attrs, options);
+ if (sortable && !sort && existing.hasChanged(sortAttr)) sort = true;
+ }
+ models[i] = existing;
+
+ // If this is a new, valid model, push it to the `toAdd` list.
+ } else if (add) {
+ model = models[i] = this._prepareModel(attrs, options);
+ if (!model) continue;
+ toAdd.push(model);
+ this._addReference(model, options);
+ }
+
+ // Do not add multiple models with the same `id`.
+ model = existing || model;
+ if (order && (model.isNew() || !modelMap[model.id])) order.push(model);
+ modelMap[model.id] = true;
+ }
+
+ // Remove nonexistent models if appropriate.
+ if (remove) {
+ for (i = 0, l = this.length; i < l; ++i) {
+ if (!modelMap[(model = this.models[i]).cid]) toRemove.push(model);
+ }
+ if (toRemove.length) this.remove(toRemove, options);
+ }
+
+ // See if sorting is needed, update `length` and splice in new models.
+ if (toAdd.length || (order && order.length)) {
+ if (sortable) sort = true;
+ this.length += toAdd.length;
+ if (at != null) {
+ for (i = 0, l = toAdd.length; i < l; i++) {
+ this.models.splice(at + i, 0, toAdd[i]);
+ }
+ } else {
+ if (order) this.models.length = 0;
+ var orderedModels = order || toAdd;
+ for (i = 0, l = orderedModels.length; i < l; i++) {
+ this.models.push(orderedModels[i]);
+ }
+ }
+ }
+
+ // Silently sort the collection if appropriate.
+ if (sort) this.sort({silent: true});
+
+ // Unless silenced, it's time to fire all appropriate add/sort events.
+ if (!options.silent) {
+ for (i = 0, l = toAdd.length; i < l; i++) {
+ (model = toAdd[i]).trigger('add', model, this, options);
+ }
+ if (sort || (order && order.length)) this.trigger('sort', this, options);
+ }
+
+ // Return the added (or merged) model (or models).
+ return singular ? models[0] : models;
+ },
+
+ // When you have more items than you want to add or remove individually,
+ // you can reset the entire set with a new list of models, without firing
+ // any granular `add` or `remove` events. Fires `reset` when finished.
+ // Useful for bulk operations and optimizations.
+ reset: function(models, options) {
+ options || (options = {});
+ for (var i = 0, l = this.models.length; i < l; i++) {
+ this._removeReference(this.models[i], options);
+ }
+ options.previousModels = this.models;
+ this._reset();
+ models = this.add(models, _.extend({silent: true}, options));
+ if (!options.silent) this.trigger('reset', this, options);
+ return models;
+ },
+
+ // Add a model to the end of the collection.
+ push: function(model, options) {
+ return this.add(model, _.extend({at: this.length}, options));
+ },
+
+ // Remove a model from the end of the collection.
+ pop: function(options) {
+ var model = this.at(this.length - 1);
+ this.remove(model, options);
+ return model;
+ },
+
+ // Add a model to the beginning of the collection.
+ unshift: function(model, options) {
+ return this.add(model, _.extend({at: 0}, options));
+ },
+
+ // Remove a model from the beginning of the collection.
+ shift: function(options) {
+ var model = this.at(0);
+ this.remove(model, options);
+ return model;
+ },
+
+ // Slice out a sub-array of models from the collection.
+ slice: function() {
+ return slice.apply(this.models, arguments);
+ },
+
+ // Get a model from the set by id.
+ get: function(obj) {
+ if (obj == null) return void 0;
+ return this._byId[obj] || this._byId[obj.id] || this._byId[obj.cid];
+ },
+
+ // Get the model at the given index.
+ at: function(index) {
+ return this.models[index];
+ },
+
+ // Return models with matching attributes. Useful for simple cases of
+ // `filter`.
+ where: function(attrs, first) {
+ if (_.isEmpty(attrs)) return first ? void 0 : [];
+ return this[first ? 'find' : 'filter'](function(model) {
+ for (var key in attrs) {
+ if (attrs[key] !== model.get(key)) return false;
+ }
+ return true;
+ });
+ },
+
+ // Return the first model with matching attributes. Useful for simple cases
+ // of `find`.
+ findWhere: function(attrs) {
+ return this.where(attrs, true);
+ },
+
+ // Force the collection to re-sort itself. You don't need to call this under
+ // normal circumstances, as the set will maintain sort order as each item
+ // is added.
+ sort: function(options) {
+ if (!this.comparator) throw new Error('Cannot sort a set without a comparator');
+ options || (options = {});
+
+ // Run sort based on type of `comparator`.
+ if (_.isString(this.comparator) || this.comparator.length === 1) {
+ this.models = this.sortBy(this.comparator, this);
+ } else {
+ this.models.sort(_.bind(this.comparator, this));
+ }
+
+ if (!options.silent) this.trigger('sort', this, options);
+ return this;
+ },
+
+ // Pluck an attribute from each model in the collection.
+ pluck: function(attr) {
+ return _.invoke(this.models, 'get', attr);
+ },
+
+ // Fetch the default set of models for this collection, resetting the
+ // collection when they arrive. If `reset: true` is passed, the response
+ // data will be passed through the `reset` method instead of `set`.
+ fetch: function(options) {
+ options = options ? _.clone(options) : {};
+ if (options.parse === void 0) options.parse = true;
+ var success = options.success;
+ var collection = this;
+ options.success = function(resp) {
+ var method = options.reset ? 'reset' : 'set';
+ collection[method](resp, options);
+ if (success) success(collection, resp, options);
+ collection.trigger('sync', collection, resp, options);
+ };
+ wrapError(this, options);
+ return this.sync('read', this, options);
+ },
+
+ // Create a new instance of a model in this collection. Add the model to the
+ // collection immediately, unless `wait: true` is passed, in which case we
+ // wait for the server to agree.
+ create: function(model, options) {
+ options = options ? _.clone(options) : {};
+ if (!(model = this._prepareModel(model, options))) return false;
+ if (!options.wait) this.add(model, options);
+ var collection = this;
+ var success = options.success;
+ options.success = function(model, resp) {
+ if (options.wait) collection.add(model, options);
+ if (success) success(model, resp, options);
+ };
+ model.save(null, options);
+ return model;
+ },
+
+ // **parse** converts a response into a list of models to be added to the
+ // collection. The default implementation is just to pass it through.
+ parse: function(resp, options) {
+ return resp;
+ },
+
+ // Create a new collection with an identical list of models as this one.
+ clone: function() {
+ return new this.constructor(this.models);
+ },
+
+ // Private method to reset all internal state. Called when the collection
+ // is first initialized or reset.
+ _reset: function() {
+ this.length = 0;
+ this.models = [];
+ this._byId = {};
+ },
+
+ // Prepare a hash of attributes (or other model) to be added to this
+ // collection.
+ _prepareModel: function(attrs, options) {
+ if (attrs instanceof Model) return attrs;
+ options = options ? _.clone(options) : {};
+ options.collection = this;
+ var model = new this.model(attrs, options);
+ if (!model.validationError) return model;
+ this.trigger('invalid', this, model.validationError, options);
+ return false;
+ },
+
+ // Internal method to create a model's ties to a collection.
+ _addReference: function(model, options) {
+ this._byId[model.cid] = model;
+ if (model.id != null) this._byId[model.id] = model;
+ if (!model.collection) model.collection = this;
+ model.on('all', this._onModelEvent, this);
+ },
+
+ // Internal method to sever a model's ties to a collection.
+ _removeReference: function(model, options) {
+ if (this === model.collection) delete model.collection;
+ model.off('all', this._onModelEvent, this);
+ },
+
+ // Internal method called every time a model in the set fires an event.
+ // Sets need to update their indexes when models change ids. All other
+ // events simply proxy through. "add" and "remove" events that originate
+ // in other collections are ignored.
+ _onModelEvent: function(event, model, collection, options) {
+ if ((event === 'add' || event === 'remove') && collection !== this) return;
+ if (event === 'destroy') this.remove(model, options);
+ if (model && event === 'change:' + model.idAttribute) {
+ delete this._byId[model.previous(model.idAttribute)];
+ if (model.id != null) this._byId[model.id] = model;
+ }
+ this.trigger.apply(this, arguments);
+ }
+
+ });
+
+ // Underscore methods that we want to implement on the Collection.
+ // 90% of the core usefulness of Backbone Collections is actually implemented
+ // right here:
+ var methods = ['forEach', 'each', 'map', 'collect', 'reduce', 'foldl',
+ 'inject', 'reduceRight', 'foldr', 'find', 'detect', 'filter', 'select',
+ 'reject', 'every', 'all', 'some', 'any', 'include', 'contains', 'invoke',
+ 'max', 'min', 'toArray', 'size', 'first', 'head', 'take', 'initial', 'rest',
+ 'tail', 'drop', 'last', 'without', 'difference', 'indexOf', 'shuffle',
+ 'lastIndexOf', 'isEmpty', 'chain', 'sample'];
+
+ // Mix in each Underscore method as a proxy to `Collection#models`.
+ _.each(methods, function(method) {
+ Collection.prototype[method] = function() {
+ var args = slice.call(arguments);
+ args.unshift(this.models);
+ return _[method].apply(_, args);
+ };
+ });
+
+ // Underscore methods that take a property name as an argument.
+ var attributeMethods = ['groupBy', 'countBy', 'sortBy', 'indexBy'];
+
+ // Use attributes instead of properties.
+ _.each(attributeMethods, function(method) {
+ Collection.prototype[method] = function(value, context) {
+ var iterator = _.isFunction(value) ? value : function(model) {
+ return model.get(value);
+ };
+ return _[method](this.models, iterator, context);
+ };
+ });
+
+ // Backbone.View
+ // -------------
+
+ // Backbone Views are almost more convention than they are actual code. A View
+ // is simply a JavaScript object that represents a logical chunk of UI in the
+ // DOM. This might be a single item, an entire list, a sidebar or panel, or
+ // even the surrounding frame which wraps your whole app. Defining a chunk of
+ // UI as a **View** allows you to define your DOM events declaratively, without
+ // having to worry about render order ... and makes it easy for the view to
+ // react to specific changes in the state of your models.
+
+ // Creating a Backbone.View creates its initial element outside of the DOM,
+ // if an existing element is not provided...
+ var View = Backbone.View = function(options) {
+ this.cid = _.uniqueId('view');
+ options || (options = {});
+ _.extend(this, _.pick(options, viewOptions));
+ this._ensureElement();
+ this.initialize.apply(this, arguments);
+ this.delegateEvents();
+ };
+
+ // Cached regex to split keys for `delegate`.
+ var delegateEventSplitter = /^(\S+)\s*(.*)$/;
+
+ // List of view options to be merged as properties.
+ var viewOptions = ['model', 'collection', 'el', 'id', 'attributes', 'className', 'tagName', 'events'];
+
+ // Set up all inheritable **Backbone.View** properties and methods.
+ _.extend(View.prototype, Events, {
+
+ // The default `tagName` of a View's element is `"div"`.
+ tagName: 'div',
+
+ // jQuery delegate for element lookup, scoped to DOM elements within the
+ // current view. This should be preferred to global lookups where possible.
+ $: function(selector) {
+ return this.$el.find(selector);
+ },
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // **render** is the core function that your view should override, in order
+ // to populate its element (`this.el`), with the appropriate HTML. The
+ // convention is for **render** to always return `this`.
+ render: function() {
+ return this;
+ },
+
+ // Remove this view by taking the element out of the DOM, and removing any
+ // applicable Backbone.Events listeners.
+ remove: function() {
+ this.$el.remove();
+ this.stopListening();
+ return this;
+ },
+
+ // Change the view's element (`this.el` property), including event
+ // re-delegation.
+ setElement: function(element, delegate) {
+ if (this.$el) this.undelegateEvents();
+ this.$el = element instanceof Backbone.$ ? element : Backbone.$(element);
+ this.el = this.$el[0];
+ if (delegate !== false) this.delegateEvents();
+ return this;
+ },
+
+ // Set callbacks, where `this.events` is a hash of
+ //
+ // *{"event selector": "callback"}*
+ //
+ // {
+ // 'mousedown .title': 'edit',
+ // 'click .button': 'save',
+ // 'click .open': function(e) { ... }
+ // }
+ //
+ // pairs. Callbacks will be bound to the view, with `this` set properly.
+ // Uses event delegation for efficiency.
+ // Omitting the selector binds the event to `this.el`.
+ // This only works for delegate-able events: not `focus`, `blur`, and
+ // not `change`, `submit`, and `reset` in Internet Explorer.
+ delegateEvents: function(events) {
+ if (!(events || (events = _.result(this, 'events')))) return this;
+ this.undelegateEvents();
+ for (var key in events) {
+ var method = events[key];
+ if (!_.isFunction(method)) method = this[events[key]];
+ if (!method) continue;
+
+ var match = key.match(delegateEventSplitter);
+ var eventName = match[1], selector = match[2];
+ method = _.bind(method, this);
+ eventName += '.delegateEvents' + this.cid;
+ if (selector === '') {
+ this.$el.on(eventName, method);
+ } else {
+ this.$el.on(eventName, selector, method);
+ }
+ }
+ return this;
+ },
+
+ // Clears all callbacks previously bound to the view with `delegateEvents`.
+ // You usually don't need to use this, but may wish to if you have multiple
+ // Backbone views attached to the same DOM element.
+ undelegateEvents: function() {
+ this.$el.off('.delegateEvents' + this.cid);
+ return this;
+ },
+
+ // Ensure that the View has a DOM element to render into.
+ // If `this.el` is a string, pass it through `$()`, take the first
+ // matching element, and re-assign it to `el`. Otherwise, create
+ // an element from the `id`, `className` and `tagName` properties.
+ _ensureElement: function() {
+ if (!this.el) {
+ var attrs = _.extend({}, _.result(this, 'attributes'));
+ if (this.id) attrs.id = _.result(this, 'id');
+ if (this.className) attrs['class'] = _.result(this, 'className');
+ var $el = Backbone.$('<' + _.result(this, 'tagName') + '>').attr(attrs);
+ this.setElement($el, false);
+ } else {
+ this.setElement(_.result(this, 'el'), false);
+ }
+ }
+
+ });
+
+ // Backbone.sync
+ // -------------
+
+ // Override this function to change the manner in which Backbone persists
+ // models to the server. You will be passed the type of request, and the
+ // model in question. By default, makes a RESTful Ajax request
+ // to the model's `url()`. Some possible customizations could be:
+ //
+ // * Use `setTimeout` to batch rapid-fire updates into a single request.
+ // * Send up the models as XML instead of JSON.
+ // * Persist models via WebSockets instead of Ajax.
+ //
+ // Turn on `Backbone.emulateHTTP` in order to send `PUT` and `DELETE` requests
+ // as `POST`, with a `_method` parameter containing the true HTTP method,
+ // as well as all requests with the body as `application/x-www-form-urlencoded`
+ // instead of `application/json` with the model in a param named `model`.
+ // Useful when interfacing with server-side languages like **PHP** that make
+ // it difficult to read the body of `PUT` requests.
+ Backbone.sync = function(method, model, options) {
+ var type = methodMap[method];
+
+ // Default options, unless specified.
+ _.defaults(options || (options = {}), {
+ emulateHTTP: Backbone.emulateHTTP,
+ emulateJSON: Backbone.emulateJSON
+ });
+
+ // Default JSON-request options.
+ var params = {type: type, dataType: 'json'};
+
+ // Ensure that we have a URL.
+ if (!options.url) {
+ params.url = _.result(model, 'url') || urlError();
+ }
+
+ // Ensure that we have the appropriate request data.
+ if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
+ params.contentType = 'application/json';
+ params.data = JSON.stringify(options.attrs || model.toJSON(options));
+ }
+
+ // For older servers, emulate JSON by encoding the request into an HTML-form.
+ if (options.emulateJSON) {
+ params.contentType = 'application/x-www-form-urlencoded';
+ params.data = params.data ? {model: params.data} : {};
+ }
+
+ // For older servers, emulate HTTP by mimicking the HTTP method with `_method`
+ // And an `X-HTTP-Method-Override` header.
+ if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
+ params.type = 'POST';
+ if (options.emulateJSON) params.data._method = type;
+ var beforeSend = options.beforeSend;
+ options.beforeSend = function(xhr) {
+ xhr.setRequestHeader('X-HTTP-Method-Override', type);
+ if (beforeSend) return beforeSend.apply(this, arguments);
+ };
+ }
+
+ // Don't process data on a non-GET request.
+ if (params.type !== 'GET' && !options.emulateJSON) {
+ params.processData = false;
+ }
+
+ // If we're sending a `PATCH` request, and we're in an old Internet Explorer
+ // that still has ActiveX enabled by default, override jQuery to use that
+ // for XHR instead. Remove this line when jQuery supports `PATCH` on IE8.
+ if (params.type === 'PATCH' && noXhrPatch) {
+ params.xhr = function() {
+ return new ActiveXObject("Microsoft.XMLHTTP");
+ };
+ }
+
+ // Make the request, allowing the user to override any Ajax options.
+ var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
+ model.trigger('request', model, xhr, options);
+ return xhr;
+ };
+
+ var noXhrPatch =
+ typeof window !== 'undefined' && !!window.ActiveXObject &&
+ !(window.XMLHttpRequest && (new XMLHttpRequest).dispatchEvent);
+
+ // Map from CRUD to HTTP for our default `Backbone.sync` implementation.
+ var methodMap = {
+ 'create': 'POST',
+ 'update': 'PUT',
+ 'patch': 'PATCH',
+ 'delete': 'DELETE',
+ 'read': 'GET'
+ };
+
+ // Set the default implementation of `Backbone.ajax` to proxy through to `$`.
+ // Override this if you'd like to use a different library.
+ Backbone.ajax = function() {
+ return Backbone.$.ajax.apply(Backbone.$, arguments);
+ };
+
+ // Backbone.Router
+ // ---------------
+
+ // Routers map faux-URLs to actions, and fire events when routes are
+ // matched. Creating a new one sets its `routes` hash, if not set statically.
+ var Router = Backbone.Router = function(options) {
+ options || (options = {});
+ if (options.routes) this.routes = options.routes;
+ this._bindRoutes();
+ this.initialize.apply(this, arguments);
+ };
+
+ // Cached regular expressions for matching named param parts and splatted
+ // parts of route strings.
+ var optionalParam = /\((.*?)\)/g;
+ var namedParam = /(\(\?)?:\w+/g;
+ var splatParam = /\*\w+/g;
+ var escapeRegExp = /[\-{}\[\]+?.,\\\^$|#\s]/g;
+
+ // Set up all inheritable **Backbone.Router** properties and methods.
+ _.extend(Router.prototype, Events, {
+
+ // Initialize is an empty function by default. Override it with your own
+ // initialization logic.
+ initialize: function(){},
+
+ // Manually bind a single named route to a callback. For example:
+ //
+ // this.route('search/:query/p:num', 'search', function(query, num) {
+ // ...
+ // });
+ //
+ route: function(route, name, callback) {
+ if (!_.isRegExp(route)) route = this._routeToRegExp(route);
+ if (_.isFunction(name)) {
+ callback = name;
+ name = '';
+ }
+ if (!callback) callback = this[name];
+ var router = this;
+ Backbone.history.route(route, function(fragment) {
+ var args = router._extractParameters(route, fragment);
+ router.execute(callback, args);
+ router.trigger.apply(router, ['route:' + name].concat(args));
+ router.trigger('route', name, args);
+ Backbone.history.trigger('route', router, name, args);
+ });
+ return this;
+ },
+
+ // Execute a route handler with the provided parameters. This is an
+ // excellent place to do pre-route setup or post-route cleanup.
+ execute: function(callback, args) {
+ if (callback) callback.apply(this, args);
+ },
+
+ // Simple proxy to `Backbone.history` to save a fragment into the history.
+ navigate: function(fragment, options) {
+ Backbone.history.navigate(fragment, options);
+ return this;
+ },
+
+ // Bind all defined routes to `Backbone.history`. We have to reverse the
+ // order of the routes here to support behavior where the most general
+ // routes can be defined at the bottom of the route map.
+ _bindRoutes: function() {
+ if (!this.routes) return;
+ this.routes = _.result(this, 'routes');
+ var route, routes = _.keys(this.routes);
+ while ((route = routes.pop()) != null) {
+ this.route(route, this.routes[route]);
+ }
+ },
+
+ // Convert a route string into a regular expression, suitable for matching
+ // against the current location hash.
+ _routeToRegExp: function(route) {
+ route = route.replace(escapeRegExp, '\\$&')
+ .replace(optionalParam, '(?:$1)?')
+ .replace(namedParam, function(match, optional) {
+ return optional ? match : '([^/?]+)';
+ })
+ .replace(splatParam, '([^?]*?)');
+ return new RegExp('^' + route + '(?:\\?([\\s\\S]*))?$');
+ },
+
+ // Given a route, and a URL fragment that it matches, return the array of
+ // extracted decoded parameters. Empty or unmatched parameters will be
+ // treated as `null` to normalize cross-browser behavior.
+ _extractParameters: function(route, fragment) {
+ var params = route.exec(fragment).slice(1);
+ return _.map(params, function(param, i) {
+ // Don't decode the search params.
+ if (i === params.length - 1) return param || null;
+ return param ? decodeURIComponent(param) : null;
+ });
+ }
+
+ });
+
+ // Backbone.History
+ // ----------------
+
+ // Handles cross-browser history management, based on either
+ // [pushState](http://diveintohtml5.info/history.html) and real URLs, or
+ // [onhashchange](https://developer.mozilla.org/en-US/docs/DOM/window.onhashchange)
+ // and URL fragments. If the browser supports neither (old IE, natch),
+ // falls back to polling.
+ var History = Backbone.History = function() {
+ this.handlers = [];
+ _.bindAll(this, 'checkUrl');
+
+ // Ensure that `History` can be used outside of the browser.
+ if (typeof window !== 'undefined') {
+ this.location = window.location;
+ this.history = window.history;
+ }
+ };
+
+ // Cached regex for stripping a leading hash/slash and trailing space.
+ var routeStripper = /^[#\/]|\s+$/g;
+
+ // Cached regex for stripping leading and trailing slashes.
+ var rootStripper = /^\/+|\/+$/g;
+
+ // Cached regex for detecting MSIE.
+ var isExplorer = /msie [\w.]+/;
+
+ // Cached regex for removing a trailing slash.
+ var trailingSlash = /\/$/;
+
+ // Cached regex for stripping urls of hash.
+ var pathStripper = /#.*$/;
+
+ // Has the history handling already been started?
+ History.started = false;
+
+ // Set up all inheritable **Backbone.History** properties and methods.
+ _.extend(History.prototype, Events, {
+
+ // The default interval to poll for hash changes, if necessary, is
+ // twenty times a second.
+ interval: 50,
+
+ // Are we at the app root?
+ atRoot: function() {
+ return this.location.pathname.replace(/[^\/]$/, '$&/') === this.root;
+ },
+
+ // Gets the true hash value. Cannot use location.hash directly due to bug
+ // in Firefox where location.hash will always be decoded.
+ getHash: function(window) {
+ var match = (window || this).location.href.match(/#(.*)$/);
+ return match ? match[1] : '';
+ },
+
+ // Get the cross-browser normalized URL fragment, either from the URL,
+ // the hash, or the override.
+ getFragment: function(fragment, forcePushState) {
+ if (fragment == null) {
+ if (this._hasPushState || !this._wantsHashChange || forcePushState) {
+ fragment = decodeURI(this.location.pathname + this.location.search);
+ var root = this.root.replace(trailingSlash, '');
+ if (!fragment.indexOf(root)) fragment = fragment.slice(root.length);
+ } else {
+ fragment = this.getHash();
+ }
+ }
+ return fragment.replace(routeStripper, '');
+ },
+
+ // Start the hash change handling, returning `true` if the current URL matches
+ // an existing route, and `false` otherwise.
+ start: function(options) {
+ if (History.started) throw new Error("Backbone.history has already been started");
+ History.started = true;
+
+ // Figure out the initial configuration. Do we need an iframe?
+ // Is pushState desired ... is it available?
+ this.options = _.extend({root: '/'}, this.options, options);
+ 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 fragment = this.getFragment();
+ var docMode = document.documentMode;
+ var oldIE = (isExplorer.exec(navigator.userAgent.toLowerCase()) && (!docMode || docMode <= 7));
+
+ // Normalize root to always include a leading and trailing slash.
+ this.root = ('/' + this.root + '/').replace(rootStripper, '/');
+
+ if (oldIE && this._wantsHashChange) {
+ var frame = Backbone.$('<iframe src="javascript:0" tabindex="-1">');
+ this.iframe = frame.hide().appendTo('body')[0].contentWindow;
+ this.navigate(fragment);
+ }
+
+ // Depending on whether we're using pushState or hashes, and whether
+ // 'onhashchange' is supported, determine how we check the URL state.
+ if (this._hasPushState) {
+ Backbone.$(window).on('popstate', this.checkUrl);
+ } else if (this._wantsHashChange && ('onhashchange' in window) && !oldIE) {
+ Backbone.$(window).on('hashchange', this.checkUrl);
+ } else if (this._wantsHashChange) {
+ this._checkUrlInterval = setInterval(this.checkUrl, this.interval);
+ }
+
+ // Determine if we need to change the base url, for a pushState link
+ // opened by a non-pushState browser.
+ this.fragment = fragment;
+ var loc = this.location;
+
+ // Transition from hashChange to pushState or vice versa if both are
+ // requested.
+ if (this._wantsHashChange && this._wantsPushState) {
+
+ // If we've started off with a route from a `pushState`-enabled
+ // browser, but we're currently in a browser that doesn't support it...
+ if (!this._hasPushState && !this.atRoot()) {
+ this.fragment = this.getFragment(null, true);
+ this.location.replace(this.root + '#' + this.fragment);
+ // Return immediately as browser will do redirect to new url
+ return true;
+
+ // Or if we've started out with a hash-based route, but we're currently
+ // in a browser where it could be `pushState`-based instead...
+ } else if (this._hasPushState && this.atRoot() && loc.hash) {
+ this.fragment = this.getHash().replace(routeStripper, '');
+ this.history.replaceState({}, document.title, this.root + this.fragment);
+ }
+
+ }
+
+ if (!this.options.silent) return this.loadUrl();
+ },
+
+ // Disable Backbone.history, perhaps temporarily. Not useful in a real app,
+ // but possibly useful for unit testing Routers.
+ stop: function() {
+ Backbone.$(window).off('popstate', this.checkUrl).off('hashchange', this.checkUrl);
+ if (this._checkUrlInterval) clearInterval(this._checkUrlInterval);
+ History.started = false;
+ },
+
+ // Add a route to be tested when the fragment changes. Routes added later
+ // may override previous routes.
+ route: function(route, callback) {
+ this.handlers.unshift({route: route, callback: callback});
+ },
+
+ // Checks the current URL to see if it has changed, and if it has,
+ // calls `loadUrl`, normalizing across the hidden iframe.
+ checkUrl: function(e) {
+ var current = this.getFragment();
+ if (current === this.fragment && this.iframe) {
+ current = this.getFragment(this.getHash(this.iframe));
+ }
+ if (current === this.fragment) return false;
+ if (this.iframe) this.navigate(current);
+ this.loadUrl();
+ },
+
+ // Attempt to load the current URL fragment. If a route succeeds with a
+ // match, returns `true`. If no defined routes matches the fragment,
+ // returns `false`.
+ loadUrl: function(fragment) {
+ fragment = this.fragment = this.getFragment(fragment);
+ return _.any(this.handlers, function(handler) {
+ if (handler.route.test(fragment)) {
+ handler.callback(fragment);
+ return true;
+ }
+ });
+ },
+
+ // Save a fragment into the hash history, or replace the URL state if the
+ // 'replace' option is passed. You are responsible for properly URL-encoding
+ // the fragment in advance.
+ //
+ // The options object can contain `trigger: true` if you wish to have the
+ // route callback be fired (not usually desirable), or `replace: true`, if
+ // you wish to modify the current URL without adding an entry to the history.
+ navigate: function(fragment, options) {
+ if (!History.started) return false;
+ if (!options || options === true) options = {trigger: !!options};
+
+ var url = this.root + (fragment = this.getFragment(fragment || ''));
+
+ // Strip the hash for matching.
+ fragment = fragment.replace(pathStripper, '');
+
+ if (this.fragment === fragment) return;
+ this.fragment = fragment;
+
+ // Don't include a trailing slash on the root.
+ if (fragment === '' && url !== '/') url = url.slice(0, -1);
+
+ // If pushState is available, we use it to set the fragment as a real URL.
+ if (this._hasPushState) {
+ this.history[options.replace ? 'replaceState' : 'pushState']({}, document.title, url);
+
+ // If hash changes haven't been explicitly disabled, update the hash
+ // fragment to store history.
+ } else if (this._wantsHashChange) {
+ this._updateHash(this.location, fragment, options.replace);
+ if (this.iframe && (fragment !== this.getFragment(this.getHash(this.iframe)))) {
+ // Opening and closing the iframe tricks IE7 and earlier to push a
+ // history entry on hash-tag change. When replace is true, we don't
+ // want this.
+ if(!options.replace) this.iframe.document.open().close();
+ this._updateHash(this.iframe.location, fragment, options.replace);
+ }
+
+ // If you've told us that you explicitly don't want fallback hashchange-
+ // based history, then `navigate` becomes a page refresh.
+ } else {
+ return this.location.assign(url);
+ }
+ if (options.trigger) return this.loadUrl(fragment);
+ },
+
+ // Update the hash location, either replacing the current entry, or adding
+ // a new one to the browser history.
+ _updateHash: function(location, fragment, replace) {
+ if (replace) {
+ var href = location.href.replace(/(javascript:|#).*$/, '');
+ location.replace(href + '#' + fragment);
+ } else {
+ // Some browsers require that `hash` contains a leading #.
+ location.hash = '#' + fragment;
+ }
+ }
+
+ });
+
+ // Create the default Backbone.history.
+ Backbone.history = new History;
+
+ // Helpers
+ // -------
+
+ // Helper function to correctly set up the prototype chain, for subclasses.
+ // Similar to `goog.inherits`, but uses a hash of prototype properties and
+ // class properties to be extended.
+ var extend = function(protoProps, staticProps) {
+ var parent = this;
+ var child;
+
+ // The constructor function for the new subclass is either defined by you
+ // (the "constructor" property in your `extend` definition), or defaulted
+ // by us to simply call the parent's constructor.
+ if (protoProps && _.has(protoProps, 'constructor')) {
+ child = protoProps.constructor;
+ } else {
+ child = function(){ return parent.apply(this, arguments); };
+ }
+
+ // Add static properties to the constructor function, if supplied.
+ _.extend(child, parent, staticProps);
+
+ // Set the prototype chain to inherit from `parent`, without calling
+ // `parent`'s constructor function.
+ var Surrogate = function(){ this.constructor = child; };
+ Surrogate.prototype = parent.prototype;
+ child.prototype = new Surrogate;
+
+ // Add prototype properties (instance properties) to the subclass,
+ // if supplied.
+ if (protoProps) _.extend(child.prototype, protoProps);
+
+ // Set a convenience property in case the parent's prototype is needed
+ // later.
+ child.__super__ = parent.prototype;
+
+ return child;
+ };
+
+ // Set up inheritance for the model, collection, router, view and history.
+ Model.extend = Collection.extend = Router.extend = View.extend = History.extend = extend;
+
+ // Throw an error when a URL is needed, and none is supplied.
+ var urlError = function() {
+ throw new Error('A "url" property or function must be specified');
+ };
+
+ // Wrap an optional error callback with a fallback error event.
+ var wrapError = function(model, options) {
+ var error = options.error;
+ options.error = function(resp) {
+ if (error) error(model, resp, options);
+ model.trigger('error', model, resp, options);
+ };
+ };
+
+ return Backbone;
+
+}));
--- /dev/null
+var Filex = Filex || {};
+
+$(function(){
+ 'use strict';
+
+ // ------------------------------------------------------------------------
+ // Models
+ // ------------------------------------------------------------------------
+
+ var OfflineModel = Backbone.Model.extend({
+ // prevent syncing with server
+ sync: function() {
+ return false;
+ }
+ });
+
+ Filex.File = OfflineModel.extend({
+ isDir: function() {
+ return false;
+ },
+
+ isFile: function() {
+ return true;
+ },
+
+ isHidden: function() {
+ return this.get('name')[0] == '.';
+ }
+ });
+
+ Filex.Directory = Filex.File.extend({
+ isDir: function() {
+ return true;
+ },
+
+ isFile: function() {
+ return false;
+ }
+ });
+
+ Filex.PathBit = OfflineModel.extend({
+ defaults: {
+ 'active': false
+ }
+ });
+
+
+ // ------------------------------------------------------------------------
+ // Collections
+ // ------------------------------------------------------------------------
+
+ Filex.FileList = Backbone.Collection.extend({
+ url: '/filex/list/',
+
+ model: function(attrs, options) {
+ switch (attrs['type']) {
+ case 'directory':
+ return new Filex.Directory(attrs, options);
+ case 'file':
+ return new Filex.File(attrs, options);
+ default:
+ console.log('Unknown model type:', attrs['type']);
+ }
+ },
+
+ comparator: function(a, b) {
+ if (a.isFile() == b.isFile())
+ return a.get('name').toLowerCase().localeCompare(b.get('name').toLowerCase());
+
+ return (a.isFile() && b.isDir()) ? 1 : -1;
+ },
+
+ hidden: function() {
+ return this.filter(function (item) {
+ return item.isHidden();
+ })
+ },
+
+ visible: function () {
+ return this.filter(function (item) {
+ return !item.isHidden();
+ })
+ }
+ });
+
+ Filex.Path = Backbone.Collection.extend({
+ model: Filex.PathBit,
+
+ initialize: function() {
+ this.listenTo(this, 'reset', this.setActive);
+ this.listenTo(this, 'add', this.setActive);
+ },
+
+ setActive: function() {
+ _.each(this.initial(), function(bit) {
+ bit.set('active', false);
+ });
+
+ this.last().set('active', true);
+ },
+
+ full: function() {
+ return this.pluck('path').join('/') || '/';
+ }
+ });
+
+
+ // ------------------------------------------------------------------------
+ // Views
+ // ------------------------------------------------------------------------
+
+ Filex.ListingItem = Backbone.View.extend({
+ tagName: 'tr',
+
+ events: {
+ 'click .link': 'selected'
+ },
+
+ initialize: function(options) {
+ this.view = options.view;
+
+ this.listenTo(this.model, 'remove', this.remove);
+ this.listenTo(this.model, 'hidden', this.toggleHidden);
+ },
+
+ template: function() {
+ var templateSelector = this.model.isDir() ? '#dir-template' : '#file-template';
+
+ return _.template($(templateSelector).html());
+ },
+
+ render: function() {
+ var data = this.model.toJSON();
+ data['url_params'] = $.param({
+ host: this.view.host,
+ path: this.view.path.full(),
+ name: this.model.get('name')
+ });
+
+ this.$el.html(this.template()(data));
+ this.toggleHidden();
+
+ return this;
+ },
+
+ toggleHidden: function() {
+ this.$el.toggleClass('hidden', this.model.isHidden() && !this.view.showHidden());
+ },
+
+ selected: function(e) {
+ if (this.model.isDir()) {
+ e.preventDefault();
+ this.model.trigger('selected:dir', this.model);
+ }
+ }
+ });
+
+ Filex.Breadcrumb = Backbone.View.extend({
+ tagName: 'li',
+
+ events: {
+ 'click a': 'selected'
+ },
+
+ initialize: function() {
+ this.listenTo(this.model, 'change:active', this.render);
+ this.listenTo(this.model, 'remove', this.remove);
+ },
+
+ render: function() {
+ if (this.model.get('active')) {
+ this.$el.text(this.model.get('text'));
+ }
+ else {
+ this.$el.html($('<a/>', {
+ href: '#',
+ text: this.model.get('text')
+ }));
+ }
+ this.$el.toggleClass('active', this.model.get('active'));
+
+ return this;
+ },
+
+ selected: function(e) {
+ e.preventDefault();
+ this.model.trigger('selected', this.model);
+ }
+ });
+
+ Filex.FilexView = Backbone.View.extend({
+ el: $('#filex'),
+
+ events: {
+ 'change #show-hidden': 'toggleHidden',
+ 'click #host-controls .view': 'hostEdit'
+ },
+
+ initialize: function(options) {
+ this.$noItems = $('#no-items');
+ this.$error = $('#error');
+ this.$showHidden = $('#show-hidden');
+ this.$host = $('#host-controls');
+
+ this.host = options.host;
+ this.path = new Filex.Path();
+ this.files = new Filex.FileList();
+
+ this.listenTo(this.path, 'reset', this.resetPath);
+ this.listenTo(this.path, 'add', this.reloadFiles);
+ this.listenTo(this.path, 'add', this.addPath);
+ this.listenTo(this.path, 'selected', this.selectedPath);
+ this.listenTo(this.files, 'reset', this.resetFiles);
+ this.listenTo(this.files, 'selected:dir', this.selectedDir);
+
+ // used in selectize callbacks
+ var view = this,
+ optionTemplate = _.template('<div><div><%= host %></div><div class="small text-muted"><%= path %></div></div>');
+
+ this.$('#host-selector').selectize({
+ valueField: 'host',
+ labelField: 'host',
+ searchField: ['host', 'path'],
+ sortField: [
+ {field: 'host', direction: 'asc'},
+ {field: 'path', direction: 'asc'}
+ ],
+ items: [this.host],
+ options: options.hostOptions,
+ render: {
+ option: function(item) {
+ return optionTemplate(item);
+ }
+ },
+ onDropdownClose: function() {
+ this.blur();
+ },
+ onBlur: function() {
+ view.$host.toggleClass('editing');
+
+ var value = this.getValue();
+ if (!value) {
+ this.addItem(view.host, true);
+ return;
+ }
+
+ if (value != view.host) {
+ var location = this.options[value];
+
+ view.host = location.host;
+ view.navigate(location.path);
+ }
+ }
+ });
+ this.hostSelectize = this.$('#host-selector')[0].selectize;
+
+ this.render();
+ this.navigate(this.hostSelectize.options[this.host].path);
+ },
+
+ render: function() {
+ this.$host.find('.view').text(this.host);
+ this.$noItems.toggle((this.showHidden() ? !Boolean(this.files.length) : !Boolean(this.files.visible().length)));
+ this.$error.hide();
+ },
+
+ navigate: function(path) {
+ var pathBits = [new Filex.PathBit({'text': '/', 'path': ''})];
+
+ pathBits = pathBits.concat(_.map(path.replace(/(^\/+|\/+$)/g, '').split('/'), function(name) {
+ return new Filex.PathBit({'text': name, 'path': name});
+ }));
+
+ this.path.reset(pathBits);
+ },
+
+ reloadFiles: function() {
+ this.busy();
+
+ var view = this;
+
+ this.files.fetch({
+ reset: true,
+ data: {host: this.host, path: this.path.full()},
+ success: function() {
+ view.render();
+ view.idle();
+ },
+ error: function(collection, response) {
+ view.files.reset();
+
+ var msg = (response.responseJSON || {}).msg || 'BÅ‚Ä…d serwera';
+
+ view.$error.find('.msg').text(msg);
+ view.$error.show();
+ view.idle();
+ }
+ });
+ },
+
+ addPath: function(bit) {
+ var view = new Filex.Breadcrumb({model: bit});
+ this.$('.path').append(view.render().el);
+ },
+
+ resetPath: function(models, options) {
+ this.reloadFiles();
+
+ _.each(options.previousModels, function(model) {
+ model.trigger('remove');
+ });
+
+ this.path.each(this.addPath, this);
+ },
+
+ resetFiles: function(models, options) {
+ _.each(options.previousModels, function(model) {
+ model.trigger('remove');
+ });
+
+ this.files.each(function(file) {
+ var view = new Filex.ListingItem({model: file, view: this});
+ this.$('tbody').append(view.render().el);
+ }, this);
+ },
+
+ toggleHidden: function() {
+ this.files.each(function(item) { item.trigger('hidden'); }, this);
+ this.render();
+ },
+
+ selectedDir: function(dir) {
+ this.path.add({'text': dir.get('name'), 'path': dir.get('name')});
+ },
+
+ selectedPath: function(bit) {
+ var newPath = this.path.slice(0, this.path.indexOf(bit) + 1);
+ this.path.set(newPath);
+ this.reloadFiles();
+ },
+
+ showHidden: function() {
+ return this.$showHidden[0].checked;
+ },
+
+ hostEdit: function() {
+ this.$host.toggleClass('editing');
+ this.hostSelectize.focus();
+ },
+
+ busy: function() {
+ this.$el.addClass('busy');
+ },
+
+ idle: function() {
+ this.$el.removeClass('busy');
+ }
+ });
+});
--- /dev/null
+// Underscore.js 1.8.2
+// http://underscorejs.org
+// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+(function(){function n(n){function t(t,r,e,u,i,o){for(;i>=0&&o>i;i+=n){var a=u?u[i]:i;e=r(e,t[a],a,t)}return e}return function(r,e,u,i){e=d(e,i,4);var o=!w(r)&&m.keys(r),a=(o||r).length,c=n>0?0:a-1;return arguments.length<3&&(u=r[o?o[c]:c],c+=n),t(r,e,u,o,c,a)}}function t(n){return function(t,r,e){r=b(r,e);for(var u=null!=t&&t.length,i=n>0?0:u-1;i>=0&&u>i;i+=n)if(r(t[i],i,t))return i;return-1}}function r(n,t){var r=S.length,e=n.constructor,u=m.isFunction(e)&&e.prototype||o,i="constructor";for(m.has(n,i)&&!m.contains(t,i)&&t.push(i);r--;)i=S[r],i in n&&n[i]!==u[i]&&!m.contains(t,i)&&t.push(i)}var e=this,u=e._,i=Array.prototype,o=Object.prototype,a=Function.prototype,c=i.push,l=i.slice,f=o.toString,s=o.hasOwnProperty,p=Array.isArray,h=Object.keys,v=a.bind,g=Object.create,y=function(){},m=function(n){return n instanceof m?n:this instanceof m?void(this._wrapped=n):new m(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=m),exports._=m):e._=m,m.VERSION="1.8.2";var d=function(n,t,r){if(t===void 0)return n;switch(null==r?3:r){case 1:return function(r){return n.call(t,r)};case 2:return function(r,e){return n.call(t,r,e)};case 3:return function(r,e,u){return n.call(t,r,e,u)};case 4:return function(r,e,u,i){return n.call(t,r,e,u,i)}}return function(){return n.apply(t,arguments)}},b=function(n,t,r){return null==n?m.identity:m.isFunction(n)?d(n,t,r):m.isObject(n)?m.matcher(n):m.property(n)};m.iteratee=function(n,t){return b(n,t,1/0)};var x=function(n,t){return function(r){var e=arguments.length;if(2>e||null==r)return r;for(var u=1;e>u;u++)for(var i=arguments[u],o=n(i),a=o.length,c=0;a>c;c++){var l=o[c];t&&r[l]!==void 0||(r[l]=i[l])}return r}},_=function(n){if(!m.isObject(n))return{};if(g)return g(n);y.prototype=n;var t=new y;return y.prototype=null,t},j=Math.pow(2,53)-1,w=function(n){var t=n&&n.length;return"number"==typeof t&&t>=0&&j>=t};m.each=m.forEach=function(n,t,r){t=d(t,r);var e,u;if(w(n))for(e=0,u=n.length;u>e;e++)t(n[e],e,n);else{var i=m.keys(n);for(e=0,u=i.length;u>e;e++)t(n[i[e]],i[e],n)}return n},m.map=m.collect=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=Array(u),o=0;u>o;o++){var a=e?e[o]:o;i[o]=t(n[a],a,n)}return i},m.reduce=m.foldl=m.inject=n(1),m.reduceRight=m.foldr=n(-1),m.find=m.detect=function(n,t,r){var e;return e=w(n)?m.findIndex(n,t,r):m.findKey(n,t,r),e!==void 0&&e!==-1?n[e]:void 0},m.filter=m.select=function(n,t,r){var e=[];return t=b(t,r),m.each(n,function(n,r,u){t(n,r,u)&&e.push(n)}),e},m.reject=function(n,t,r){return m.filter(n,m.negate(b(t)),r)},m.every=m.all=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(!t(n[o],o,n))return!1}return!0},m.some=m.any=function(n,t,r){t=b(t,r);for(var e=!w(n)&&m.keys(n),u=(e||n).length,i=0;u>i;i++){var o=e?e[i]:i;if(t(n[o],o,n))return!0}return!1},m.contains=m.includes=m.include=function(n,t,r){return w(n)||(n=m.values(n)),m.indexOf(n,t,"number"==typeof r&&r)>=0},m.invoke=function(n,t){var r=l.call(arguments,2),e=m.isFunction(t);return m.map(n,function(n){var u=e?t:n[t];return null==u?u:u.apply(n,r)})},m.pluck=function(n,t){return m.map(n,m.property(t))},m.where=function(n,t){return m.filter(n,m.matcher(t))},m.findWhere=function(n,t){return m.find(n,m.matcher(t))},m.max=function(n,t,r){var e,u,i=-1/0,o=-1/0;if(null==t&&null!=n){n=w(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],e>i&&(i=e)}else t=b(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(u>o||u===-1/0&&i===-1/0)&&(i=n,o=u)});return i},m.min=function(n,t,r){var e,u,i=1/0,o=1/0;if(null==t&&null!=n){n=w(n)?n:m.values(n);for(var a=0,c=n.length;c>a;a++)e=n[a],i>e&&(i=e)}else t=b(t,r),m.each(n,function(n,r,e){u=t(n,r,e),(o>u||1/0===u&&1/0===i)&&(i=n,o=u)});return i},m.shuffle=function(n){for(var t,r=w(n)?n:m.values(n),e=r.length,u=Array(e),i=0;e>i;i++)t=m.random(0,i),t!==i&&(u[i]=u[t]),u[t]=r[i];return u},m.sample=function(n,t,r){return null==t||r?(w(n)||(n=m.values(n)),n[m.random(n.length-1)]):m.shuffle(n).slice(0,Math.max(0,t))},m.sortBy=function(n,t,r){return t=b(t,r),m.pluck(m.map(n,function(n,r,e){return{value:n,index:r,criteria:t(n,r,e)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var A=function(n){return function(t,r,e){var u={};return r=b(r,e),m.each(t,function(e,i){var o=r(e,i,t);n(u,e,o)}),u}};m.groupBy=A(function(n,t,r){m.has(n,r)?n[r].push(t):n[r]=[t]}),m.indexBy=A(function(n,t,r){n[r]=t}),m.countBy=A(function(n,t,r){m.has(n,r)?n[r]++:n[r]=1}),m.toArray=function(n){return n?m.isArray(n)?l.call(n):w(n)?m.map(n,m.identity):m.values(n):[]},m.size=function(n){return null==n?0:w(n)?n.length:m.keys(n).length},m.partition=function(n,t,r){t=b(t,r);var e=[],u=[];return m.each(n,function(n,r,i){(t(n,r,i)?e:u).push(n)}),[e,u]},m.first=m.head=m.take=function(n,t,r){return null==n?void 0:null==t||r?n[0]:m.initial(n,n.length-t)},m.initial=function(n,t,r){return l.call(n,0,Math.max(0,n.length-(null==t||r?1:t)))},m.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:m.rest(n,Math.max(0,n.length-t))},m.rest=m.tail=m.drop=function(n,t,r){return l.call(n,null==t||r?1:t)},m.compact=function(n){return m.filter(n,m.identity)};var k=function(n,t,r,e){for(var u=[],i=0,o=e||0,a=n&&n.length;a>o;o++){var c=n[o];if(w(c)&&(m.isArray(c)||m.isArguments(c))){t||(c=k(c,t,r));var l=0,f=c.length;for(u.length+=f;f>l;)u[i++]=c[l++]}else r||(u[i++]=c)}return u};m.flatten=function(n,t){return k(n,t,!1)},m.without=function(n){return m.difference(n,l.call(arguments,1))},m.uniq=m.unique=function(n,t,r,e){if(null==n)return[];m.isBoolean(t)||(e=r,r=t,t=!1),null!=r&&(r=b(r,e));for(var u=[],i=[],o=0,a=n.length;a>o;o++){var c=n[o],l=r?r(c,o,n):c;t?(o&&i===l||u.push(c),i=l):r?m.contains(i,l)||(i.push(l),u.push(c)):m.contains(u,c)||u.push(c)}return u},m.union=function(){return m.uniq(k(arguments,!0,!0))},m.intersection=function(n){if(null==n)return[];for(var t=[],r=arguments.length,e=0,u=n.length;u>e;e++){var i=n[e];if(!m.contains(t,i)){for(var o=1;r>o&&m.contains(arguments[o],i);o++);o===r&&t.push(i)}}return t},m.difference=function(n){var t=k(arguments,!0,!0,1);return m.filter(n,function(n){return!m.contains(t,n)})},m.zip=function(){return m.unzip(arguments)},m.unzip=function(n){for(var t=n&&m.max(n,"length").length||0,r=Array(t),e=0;t>e;e++)r[e]=m.pluck(n,e);return r},m.object=function(n,t){for(var r={},e=0,u=n&&n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},m.indexOf=function(n,t,r){var e=0,u=n&&n.length;if("number"==typeof r)e=0>r?Math.max(0,u+r):r;else if(r&&u)return e=m.sortedIndex(n,t),n[e]===t?e:-1;if(t!==t)return m.findIndex(l.call(n,e),m.isNaN);for(;u>e;e++)if(n[e]===t)return e;return-1},m.lastIndexOf=function(n,t,r){var e=n?n.length:0;if("number"==typeof r&&(e=0>r?e+r+1:Math.min(e,r+1)),t!==t)return m.findLastIndex(l.call(n,0,e),m.isNaN);for(;--e>=0;)if(n[e]===t)return e;return-1},m.findIndex=t(1),m.findLastIndex=t(-1),m.sortedIndex=function(n,t,r,e){r=b(r,e,1);for(var u=r(t),i=0,o=n.length;o>i;){var a=Math.floor((i+o)/2);r(n[a])<u?i=a+1:o=a}return i},m.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=r||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=Array(e),i=0;e>i;i++,n+=r)u[i]=n;return u};var O=function(n,t,r,e,u){if(!(e instanceof t))return n.apply(r,u);var i=_(n.prototype),o=n.apply(i,u);return m.isObject(o)?o:i};m.bind=function(n,t){if(v&&n.bind===v)return v.apply(n,l.call(arguments,1));if(!m.isFunction(n))throw new TypeError("Bind must be called on a function");var r=l.call(arguments,2),e=function(){return O(n,e,t,this,r.concat(l.call(arguments)))};return e},m.partial=function(n){var t=l.call(arguments,1),r=function(){for(var e=0,u=t.length,i=Array(u),o=0;u>o;o++)i[o]=t[o]===m?arguments[e++]:t[o];for(;e<arguments.length;)i.push(arguments[e++]);return O(n,r,this,this,i)};return r},m.bindAll=function(n){var t,r,e=arguments.length;if(1>=e)throw new Error("bindAll must be passed function names");for(t=1;e>t;t++)r=arguments[t],n[r]=m.bind(n[r],n);return n},m.memoize=function(n,t){var r=function(e){var u=r.cache,i=""+(t?t.apply(this,arguments):e);return m.has(u,i)||(u[i]=n.apply(this,arguments)),u[i]};return r.cache={},r},m.delay=function(n,t){var r=l.call(arguments,2);return setTimeout(function(){return n.apply(null,r)},t)},m.defer=m.partial(m.delay,m,1),m.throttle=function(n,t,r){var e,u,i,o=null,a=0;r||(r={});var c=function(){a=r.leading===!1?0:m.now(),o=null,i=n.apply(e,u),o||(e=u=null)};return function(){var l=m.now();a||r.leading!==!1||(a=l);var f=t-(l-a);return e=this,u=arguments,0>=f||f>t?(o&&(clearTimeout(o),o=null),a=l,i=n.apply(e,u),o||(e=u=null)):o||r.trailing===!1||(o=setTimeout(c,f)),i}},m.debounce=function(n,t,r){var e,u,i,o,a,c=function(){var l=m.now()-o;t>l&&l>=0?e=setTimeout(c,t-l):(e=null,r||(a=n.apply(i,u),e||(i=u=null)))};return function(){i=this,u=arguments,o=m.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(a=n.apply(i,u),i=u=null),a}},m.wrap=function(n,t){return m.partial(t,n)},m.negate=function(n){return function(){return!n.apply(this,arguments)}},m.compose=function(){var n=arguments,t=n.length-1;return function(){for(var r=t,e=n[t].apply(this,arguments);r--;)e=n[r].call(this,e);return e}},m.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},m.before=function(n,t){var r;return function(){return--n>0&&(r=t.apply(this,arguments)),1>=n&&(t=null),r}},m.once=m.partial(m.before,2);var F=!{toString:null}.propertyIsEnumerable("toString"),S=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"];m.keys=function(n){if(!m.isObject(n))return[];if(h)return h(n);var t=[];for(var e in n)m.has(n,e)&&t.push(e);return F&&r(n,t),t},m.allKeys=function(n){if(!m.isObject(n))return[];var t=[];for(var e in n)t.push(e);return F&&r(n,t),t},m.values=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},m.mapObject=function(n,t,r){t=b(t,r);for(var e,u=m.keys(n),i=u.length,o={},a=0;i>a;a++)e=u[a],o[e]=t(n[e],e,n);return o},m.pairs=function(n){for(var t=m.keys(n),r=t.length,e=Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},m.invert=function(n){for(var t={},r=m.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},m.functions=m.methods=function(n){var t=[];for(var r in n)m.isFunction(n[r])&&t.push(r);return t.sort()},m.extend=x(m.allKeys),m.extendOwn=m.assign=x(m.keys),m.findKey=function(n,t,r){t=b(t,r);for(var e,u=m.keys(n),i=0,o=u.length;o>i;i++)if(e=u[i],t(n[e],e,n))return e},m.pick=function(n,t,r){var e,u,i={},o=n;if(null==o)return i;m.isFunction(t)?(u=m.allKeys(o),e=d(t,r)):(u=k(arguments,!1,!1,1),e=function(n,t,r){return t in r},o=Object(o));for(var a=0,c=u.length;c>a;a++){var l=u[a],f=o[l];e(f,l,o)&&(i[l]=f)}return i},m.omit=function(n,t,r){if(m.isFunction(t))t=m.negate(t);else{var e=m.map(k(arguments,!1,!1,1),String);t=function(n,t){return!m.contains(e,t)}}return m.pick(n,t,r)},m.defaults=x(m.allKeys,!0),m.clone=function(n){return m.isObject(n)?m.isArray(n)?n.slice():m.extend({},n):n},m.tap=function(n,t){return t(n),n},m.isMatch=function(n,t){var r=m.keys(t),e=r.length;if(null==n)return!e;for(var u=Object(n),i=0;e>i;i++){var o=r[i];if(t[o]!==u[o]||!(o in u))return!1}return!0};var E=function(n,t,r,e){if(n===t)return 0!==n||1/n===1/t;if(null==n||null==t)return n===t;n instanceof m&&(n=n._wrapped),t instanceof m&&(t=t._wrapped);var u=f.call(n);if(u!==f.call(t))return!1;switch(u){case"[object RegExp]":case"[object String]":return""+n==""+t;case"[object Number]":return+n!==+n?+t!==+t:0===+n?1/+n===1/t:+n===+t;case"[object Date]":case"[object Boolean]":return+n===+t}var i="[object Array]"===u;if(!i){if("object"!=typeof n||"object"!=typeof t)return!1;var o=n.constructor,a=t.constructor;if(o!==a&&!(m.isFunction(o)&&o instanceof o&&m.isFunction(a)&&a instanceof a)&&"constructor"in n&&"constructor"in t)return!1}r=r||[],e=e||[];for(var c=r.length;c--;)if(r[c]===n)return e[c]===t;if(r.push(n),e.push(t),i){if(c=n.length,c!==t.length)return!1;for(;c--;)if(!E(n[c],t[c],r,e))return!1}else{var l,s=m.keys(n);if(c=s.length,m.keys(t).length!==c)return!1;for(;c--;)if(l=s[c],!m.has(t,l)||!E(n[l],t[l],r,e))return!1}return r.pop(),e.pop(),!0};m.isEqual=function(n,t){return E(n,t)},m.isEmpty=function(n){return null==n?!0:w(n)&&(m.isArray(n)||m.isString(n)||m.isArguments(n))?0===n.length:0===m.keys(n).length},m.isElement=function(n){return!(!n||1!==n.nodeType)},m.isArray=p||function(n){return"[object Array]"===f.call(n)},m.isObject=function(n){var t=typeof n;return"function"===t||"object"===t&&!!n},m.each(["Arguments","Function","String","Number","Date","RegExp","Error"],function(n){m["is"+n]=function(t){return f.call(t)==="[object "+n+"]"}}),m.isArguments(arguments)||(m.isArguments=function(n){return m.has(n,"callee")}),"function"!=typeof/./&&"object"!=typeof Int8Array&&(m.isFunction=function(n){return"function"==typeof n||!1}),m.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},m.isNaN=function(n){return m.isNumber(n)&&n!==+n},m.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"===f.call(n)},m.isNull=function(n){return null===n},m.isUndefined=function(n){return n===void 0},m.has=function(n,t){return null!=n&&s.call(n,t)},m.noConflict=function(){return e._=u,this},m.identity=function(n){return n},m.constant=function(n){return function(){return n}},m.noop=function(){},m.property=function(n){return function(t){return null==t?void 0:t[n]}},m.propertyOf=function(n){return null==n?function(){}:function(t){return n[t]}},m.matcher=m.matches=function(n){return n=m.extendOwn({},n),function(t){return m.isMatch(t,n)}},m.times=function(n,t,r){var e=Array(Math.max(0,n));t=d(t,r,1);for(var u=0;n>u;u++)e[u]=t(u);return e},m.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},m.now=Date.now||function(){return(new Date).getTime()};var M={"&":"&","<":"<",">":">",'"':""","'":"'","`":"`"},N=m.invert(M),I=function(n){var t=function(t){return n[t]},r="(?:"+m.keys(n).join("|")+")",e=RegExp(r),u=RegExp(r,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,t):n}};m.escape=I(M),m.unescape=I(N),m.result=function(n,t,r){var e=null==n?void 0:n[t];return e===void 0&&(e=r),m.isFunction(e)?e.call(n):e};var B=0;m.uniqueId=function(n){var t=++B+"";return n?n+t:t},m.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var T=/(.)^/,R={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},q=/\\|'|\r|\n|\u2028|\u2029/g,K=function(n){return"\\"+R[n]};m.template=function(n,t,r){!t&&r&&(t=r),t=m.defaults({},t,m.templateSettings);var e=RegExp([(t.escape||T).source,(t.interpolate||T).source,(t.evaluate||T).source].join("|")+"|$","g"),u=0,i="__p+='";n.replace(e,function(t,r,e,o,a){return i+=n.slice(u,a).replace(q,K),u=a+t.length,r?i+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'":e?i+="'+\n((__t=("+e+"))==null?'':__t)+\n'":o&&(i+="';\n"+o+"\n__p+='"),t}),i+="';\n",t.variable||(i="with(obj||{}){\n"+i+"}\n"),i="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+i+"return __p;\n";try{var o=new Function(t.variable||"obj","_",i)}catch(a){throw a.source=i,a}var c=function(n){return o.call(this,n,m)},l=t.variable||"obj";return c.source="function("+l+"){\n"+i+"}",c},m.chain=function(n){var t=m(n);return t._chain=!0,t};var z=function(n,t){return n._chain?m(t).chain():t};m.mixin=function(n){m.each(m.functions(n),function(t){var r=m[t]=n[t];m.prototype[t]=function(){var n=[this._wrapped];return c.apply(n,arguments),z(this,r.apply(m,n))}})},m.mixin(m),m.each(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=i[n];m.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!==n&&"splice"!==n||0!==r.length||delete r[0],z(this,r)}}),m.each(["concat","join","slice"],function(n){var t=i[n];m.prototype[n]=function(){return z(this,t.apply(this._wrapped,arguments))}}),m.prototype.value=function(){return this._wrapped},m.prototype.valueOf=m.prototype.toJSON=m.prototype.value,m.prototype.toString=function(){return""+this._wrapped},"function"==typeof define&&define.amd&&define("underscore",[],function(){return m})}).call(this);
+//# sourceMappingURL=underscore-min.map
\ No newline at end of file
--- /dev/null
+{"version":3,"file":"underscore-min.js","sources":["underscore.js"],"names":["createReduce","dir","iterator","obj","iteratee","memo","keys","index","length","currentKey","context","optimizeCb","isArrayLike","_","arguments","createIndexFinder","array","predicate","cb","collectNonEnumProps","nonEnumIdx","nonEnumerableProps","constructor","proto","isFunction","prototype","ObjProto","prop","has","contains","push","root","this","previousUnderscore","ArrayProto","Array","Object","FuncProto","Function","slice","toString","hasOwnProperty","nativeIsArray","isArray","nativeKeys","nativeBind","bind","nativeCreate","create","Ctor","_wrapped","exports","module","VERSION","func","argCount","value","call","other","collection","accumulator","apply","identity","isObject","matcher","property","Infinity","createAssigner","keysFunc","undefinedOnly","source","l","i","key","baseCreate","result","MAX_ARRAY_INDEX","Math","pow","each","forEach","map","collect","results","reduce","foldl","inject","reduceRight","foldr","find","detect","findIndex","findKey","filter","select","list","reject","negate","every","all","some","any","includes","include","target","fromIndex","values","indexOf","invoke","method","args","isFunc","pluck","where","attrs","findWhere","max","computed","lastComputed","min","shuffle","rand","set","shuffled","random","sample","n","guard","sortBy","criteria","sort","left","right","a","b","group","behavior","groupBy","indexBy","countBy","toArray","size","partition","pass","fail","first","head","take","initial","last","rest","tail","drop","compact","flatten","input","shallow","strict","startIndex","output","idx","isArguments","j","len","without","difference","uniq","unique","isSorted","isBoolean","seen","union","intersection","argsLength","item","zip","unzip","object","sortedIndex","isNaN","lastIndexOf","from","findLastIndex","low","high","mid","floor","range","start","stop","step","ceil","executeBound","sourceFunc","boundFunc","callingContext","self","TypeError","bound","concat","partial","boundArgs","position","bindAll","Error","memoize","hasher","cache","address","delay","wait","setTimeout","defer","throttle","options","timeout","previous","later","leading","now","remaining","clearTimeout","trailing","debounce","immediate","timestamp","callNow","wrap","wrapper","compose","after","times","before","once","hasEnumBug","propertyIsEnumerable","allKeys","mapObject","pairs","invert","functions","methods","names","extend","extendOwn","assign","pick","oiteratee","omit","String","defaults","clone","tap","interceptor","isMatch","eq","aStack","bStack","className","areArrays","aCtor","bCtor","pop","isEqual","isEmpty","isString","isElement","nodeType","type","name","Int8Array","isFinite","parseFloat","isNumber","isNull","isUndefined","noConflict","constant","noop","propertyOf","matches","accum","Date","getTime","escapeMap","&","<",">","\"","'","`","unescapeMap","createEscaper","escaper","match","join","testRegexp","RegExp","replaceRegexp","string","test","replace","escape","unescape","fallback","idCounter","uniqueId","prefix","id","templateSettings","evaluate","interpolate","noMatch","escapes","\\","\r","\n","
","
","escapeChar","template","text","settings","oldSettings","offset","variable","render","e","data","argument","chain","instance","_chain","mixin","valueOf","toJSON","define","amd"],"mappings":"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"}
\ No newline at end of file
--- /dev/null
+// Underscore.js 1.8.2
+// http://underscorejs.org
+// (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+// Underscore may be freely distributed under the MIT license.
+
+(function() {
+
+ // Baseline setup
+ // --------------
+
+ // Establish the root object, `window` in the browser, or `exports` on the server.
+ var root = this;
+
+ // Save the previous value of the `_` variable.
+ var previousUnderscore = root._;
+
+ // Save bytes in the minified (but not gzipped) version:
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
+
+ // Create quick reference variables for speed access to core prototypes.
+ var
+ push = ArrayProto.push,
+ slice = ArrayProto.slice,
+ toString = ObjProto.toString,
+ hasOwnProperty = ObjProto.hasOwnProperty;
+
+ // All **ECMAScript 5** native function implementations that we hope to use
+ // are declared here.
+ var
+ nativeIsArray = Array.isArray,
+ nativeKeys = Object.keys,
+ nativeBind = FuncProto.bind,
+ nativeCreate = Object.create;
+
+ // Naked function reference for surrogate-prototype-swapping.
+ var Ctor = function(){};
+
+ // Create a safe reference to the Underscore object for use below.
+ var _ = function(obj) {
+ if (obj instanceof _) return obj;
+ if (!(this instanceof _)) return new _(obj);
+ this._wrapped = obj;
+ };
+
+ // Export the Underscore object for **Node.js**, with
+ // backwards-compatibility for the old `require()` API. If we're in
+ // the browser, add `_` as a global object.
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = _;
+ }
+ exports._ = _;
+ } else {
+ root._ = _;
+ }
+
+ // Current version.
+ _.VERSION = '1.8.2';
+
+ // Internal function that returns an efficient (for current engines) version
+ // of the passed-in callback, to be repeatedly applied in other Underscore
+ // functions.
+ var optimizeCb = function(func, context, argCount) {
+ if (context === void 0) return func;
+ switch (argCount == null ? 3 : argCount) {
+ case 1: return function(value) {
+ return func.call(context, value);
+ };
+ case 2: return function(value, other) {
+ return func.call(context, value, other);
+ };
+ case 3: return function(value, index, collection) {
+ return func.call(context, value, index, collection);
+ };
+ case 4: return function(accumulator, value, index, collection) {
+ return func.call(context, accumulator, value, index, collection);
+ };
+ }
+ return function() {
+ return func.apply(context, arguments);
+ };
+ };
+
+ // A mostly-internal function to generate callbacks that can be applied
+ // to each element in a collection, returning the desired result — either
+ // identity, an arbitrary callback, a property matcher, or a property accessor.
+ var cb = function(value, context, argCount) {
+ if (value == null) return _.identity;
+ if (_.isFunction(value)) return optimizeCb(value, context, argCount);
+ if (_.isObject(value)) return _.matcher(value);
+ return _.property(value);
+ };
+ _.iteratee = function(value, context) {
+ return cb(value, context, Infinity);
+ };
+
+ // An internal function for creating assigner functions.
+ var createAssigner = function(keysFunc, undefinedOnly) {
+ return function(obj) {
+ var length = arguments.length;
+ if (length < 2 || obj == null) return obj;
+ for (var index = 1; index < length; index++) {
+ var source = arguments[index],
+ keys = keysFunc(source),
+ l = keys.length;
+ for (var i = 0; i < l; i++) {
+ var key = keys[i];
+ if (!undefinedOnly || obj[key] === void 0) obj[key] = source[key];
+ }
+ }
+ return obj;
+ };
+ };
+
+ // An internal function for creating a new object that inherits from another.
+ var baseCreate = function(prototype) {
+ if (!_.isObject(prototype)) return {};
+ if (nativeCreate) return nativeCreate(prototype);
+ Ctor.prototype = prototype;
+ var result = new Ctor;
+ Ctor.prototype = null;
+ return result;
+ };
+
+ // Helper for collection methods to determine whether a collection
+ // should be iterated as an array or as an object
+ // Related: http://people.mozilla.org/~jorendorff/es6-draft.html#sec-tolength
+ var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1;
+ var isArrayLike = function(collection) {
+ var length = collection && collection.length;
+ return typeof length == 'number' && length >= 0 && length <= MAX_ARRAY_INDEX;
+ };
+
+ // Collection Functions
+ // --------------------
+
+ // The cornerstone, an `each` implementation, aka `forEach`.
+ // Handles raw objects in addition to array-likes. Treats all
+ // sparse array-likes as if they were dense.
+ _.each = _.forEach = function(obj, iteratee, context) {
+ iteratee = optimizeCb(iteratee, context);
+ var i, length;
+ if (isArrayLike(obj)) {
+ for (i = 0, length = obj.length; i < length; i++) {
+ iteratee(obj[i], i, obj);
+ }
+ } else {
+ var keys = _.keys(obj);
+ for (i = 0, length = keys.length; i < length; i++) {
+ iteratee(obj[keys[i]], keys[i], obj);
+ }
+ }
+ return obj;
+ };
+
+ // Return the results of applying the iteratee to each element.
+ _.map = _.collect = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length,
+ results = Array(length);
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ results[index] = iteratee(obj[currentKey], currentKey, obj);
+ }
+ return results;
+ };
+
+ // Create a reducing function iterating left or right.
+ function createReduce(dir) {
+ // Optimized iterator function as using arguments.length
+ // in the main function will deoptimize the, see #1991.
+ function iterator(obj, iteratee, memo, keys, index, length) {
+ for (; index >= 0 && index < length; index += dir) {
+ var currentKey = keys ? keys[index] : index;
+ memo = iteratee(memo, obj[currentKey], currentKey, obj);
+ }
+ return memo;
+ }
+
+ return function(obj, iteratee, memo, context) {
+ iteratee = optimizeCb(iteratee, context, 4);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length,
+ index = dir > 0 ? 0 : length - 1;
+ // Determine the initial value if none is provided.
+ if (arguments.length < 3) {
+ memo = obj[keys ? keys[index] : index];
+ index += dir;
+ }
+ return iterator(obj, iteratee, memo, keys, index, length);
+ };
+ }
+
+ // **Reduce** builds up a single result from a list of values, aka `inject`,
+ // or `foldl`.
+ _.reduce = _.foldl = _.inject = createReduce(1);
+
+ // The right-associative version of reduce, also known as `foldr`.
+ _.reduceRight = _.foldr = createReduce(-1);
+
+ // Return the first value which passes a truth test. Aliased as `detect`.
+ _.find = _.detect = function(obj, predicate, context) {
+ var key;
+ if (isArrayLike(obj)) {
+ key = _.findIndex(obj, predicate, context);
+ } else {
+ key = _.findKey(obj, predicate, context);
+ }
+ if (key !== void 0 && key !== -1) return obj[key];
+ };
+
+ // Return all the elements that pass a truth test.
+ // Aliased as `select`.
+ _.filter = _.select = function(obj, predicate, context) {
+ var results = [];
+ predicate = cb(predicate, context);
+ _.each(obj, function(value, index, list) {
+ if (predicate(value, index, list)) results.push(value);
+ });
+ return results;
+ };
+
+ // Return all the elements for which a truth test fails.
+ _.reject = function(obj, predicate, context) {
+ return _.filter(obj, _.negate(cb(predicate)), context);
+ };
+
+ // Determine whether all of the elements match a truth test.
+ // Aliased as `all`.
+ _.every = _.all = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length;
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ if (!predicate(obj[currentKey], currentKey, obj)) return false;
+ }
+ return true;
+ };
+
+ // Determine if at least one element in the object matches a truth test.
+ // Aliased as `any`.
+ _.some = _.any = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = !isArrayLike(obj) && _.keys(obj),
+ length = (keys || obj).length;
+ for (var index = 0; index < length; index++) {
+ var currentKey = keys ? keys[index] : index;
+ if (predicate(obj[currentKey], currentKey, obj)) return true;
+ }
+ return false;
+ };
+
+ // Determine if the array or object contains a given value (using `===`).
+ // Aliased as `includes` and `include`.
+ _.contains = _.includes = _.include = function(obj, target, fromIndex) {
+ if (!isArrayLike(obj)) obj = _.values(obj);
+ return _.indexOf(obj, target, typeof fromIndex == 'number' && fromIndex) >= 0;
+ };
+
+ // Invoke a method (with arguments) on every item in a collection.
+ _.invoke = function(obj, method) {
+ var args = slice.call(arguments, 2);
+ var isFunc = _.isFunction(method);
+ return _.map(obj, function(value) {
+ var func = isFunc ? method : value[method];
+ return func == null ? func : func.apply(value, args);
+ });
+ };
+
+ // Convenience version of a common use case of `map`: fetching a property.
+ _.pluck = function(obj, key) {
+ return _.map(obj, _.property(key));
+ };
+
+ // Convenience version of a common use case of `filter`: selecting only objects
+ // containing specific `key:value` pairs.
+ _.where = function(obj, attrs) {
+ return _.filter(obj, _.matcher(attrs));
+ };
+
+ // Convenience version of a common use case of `find`: getting the first object
+ // containing specific `key:value` pairs.
+ _.findWhere = function(obj, attrs) {
+ return _.find(obj, _.matcher(attrs));
+ };
+
+ // Return the maximum element (or element-based computation).
+ _.max = function(obj, iteratee, context) {
+ var result = -Infinity, lastComputed = -Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = isArrayLike(obj) ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value > result) {
+ result = value;
+ }
+ }
+ } else {
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed > lastComputed || computed === -Infinity && result === -Infinity) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
+ }
+ return result;
+ };
+
+ // Return the minimum element (or element-based computation).
+ _.min = function(obj, iteratee, context) {
+ var result = Infinity, lastComputed = Infinity,
+ value, computed;
+ if (iteratee == null && obj != null) {
+ obj = isArrayLike(obj) ? obj : _.values(obj);
+ for (var i = 0, length = obj.length; i < length; i++) {
+ value = obj[i];
+ if (value < result) {
+ result = value;
+ }
+ }
+ } else {
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index, list) {
+ computed = iteratee(value, index, list);
+ if (computed < lastComputed || computed === Infinity && result === Infinity) {
+ result = value;
+ lastComputed = computed;
+ }
+ });
+ }
+ return result;
+ };
+
+ // Shuffle a collection, using the modern version of the
+ // [Fisher-Yates shuffle](http://en.wikipedia.org/wiki/Fisher–Yates_shuffle).
+ _.shuffle = function(obj) {
+ var set = isArrayLike(obj) ? obj : _.values(obj);
+ var length = set.length;
+ var shuffled = Array(length);
+ for (var index = 0, rand; index < length; index++) {
+ rand = _.random(0, index);
+ if (rand !== index) shuffled[index] = shuffled[rand];
+ shuffled[rand] = set[index];
+ }
+ return shuffled;
+ };
+
+ // Sample **n** random values from a collection.
+ // If **n** is not specified, returns a single random element.
+ // The internal `guard` argument allows it to work with `map`.
+ _.sample = function(obj, n, guard) {
+ if (n == null || guard) {
+ if (!isArrayLike(obj)) obj = _.values(obj);
+ return obj[_.random(obj.length - 1)];
+ }
+ return _.shuffle(obj).slice(0, Math.max(0, n));
+ };
+
+ // Sort the object's values by a criterion produced by an iteratee.
+ _.sortBy = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ return _.pluck(_.map(obj, function(value, index, list) {
+ return {
+ value: value,
+ index: index,
+ criteria: iteratee(value, index, list)
+ };
+ }).sort(function(left, right) {
+ var a = left.criteria;
+ var b = right.criteria;
+ if (a !== b) {
+ if (a > b || a === void 0) return 1;
+ if (a < b || b === void 0) return -1;
+ }
+ return left.index - right.index;
+ }), 'value');
+ };
+
+ // An internal function used for aggregate "group by" operations.
+ var group = function(behavior) {
+ return function(obj, iteratee, context) {
+ var result = {};
+ iteratee = cb(iteratee, context);
+ _.each(obj, function(value, index) {
+ var key = iteratee(value, index, obj);
+ behavior(result, value, key);
+ });
+ return result;
+ };
+ };
+
+ // Groups the object's values by a criterion. Pass either a string attribute
+ // to group by, or a function that returns the criterion.
+ _.groupBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key].push(value); else result[key] = [value];
+ });
+
+ // Indexes the object's values by a criterion, similar to `groupBy`, but for
+ // when you know that your index values will be unique.
+ _.indexBy = group(function(result, value, key) {
+ result[key] = value;
+ });
+
+ // Counts instances of an object that group by a certain criterion. Pass
+ // either a string attribute to count by, or a function that returns the
+ // criterion.
+ _.countBy = group(function(result, value, key) {
+ if (_.has(result, key)) result[key]++; else result[key] = 1;
+ });
+
+ // Safely create a real, live array from anything iterable.
+ _.toArray = function(obj) {
+ if (!obj) return [];
+ if (_.isArray(obj)) return slice.call(obj);
+ if (isArrayLike(obj)) return _.map(obj, _.identity);
+ return _.values(obj);
+ };
+
+ // Return the number of elements in an object.
+ _.size = function(obj) {
+ if (obj == null) return 0;
+ return isArrayLike(obj) ? obj.length : _.keys(obj).length;
+ };
+
+ // Split a collection into two arrays: one whose elements all satisfy the given
+ // predicate, and one whose elements all do not satisfy the predicate.
+ _.partition = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var pass = [], fail = [];
+ _.each(obj, function(value, key, obj) {
+ (predicate(value, key, obj) ? pass : fail).push(value);
+ });
+ return [pass, fail];
+ };
+
+ // Array Functions
+ // ---------------
+
+ // Get the first element of an array. Passing **n** will return the first N
+ // values in the array. Aliased as `head` and `take`. The **guard** check
+ // allows it to work with `_.map`.
+ _.first = _.head = _.take = function(array, n, guard) {
+ if (array == null) return void 0;
+ if (n == null || guard) return array[0];
+ return _.initial(array, array.length - n);
+ };
+
+ // Returns everything but the last entry of the array. Especially useful on
+ // the arguments object. Passing **n** will return all the values in
+ // the array, excluding the last N.
+ _.initial = function(array, n, guard) {
+ return slice.call(array, 0, Math.max(0, array.length - (n == null || guard ? 1 : n)));
+ };
+
+ // Get the last element of an array. Passing **n** will return the last N
+ // values in the array.
+ _.last = function(array, n, guard) {
+ if (array == null) return void 0;
+ if (n == null || guard) return array[array.length - 1];
+ return _.rest(array, Math.max(0, array.length - n));
+ };
+
+ // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.
+ // Especially useful on the arguments object. Passing an **n** will return
+ // the rest N values in the array.
+ _.rest = _.tail = _.drop = function(array, n, guard) {
+ return slice.call(array, n == null || guard ? 1 : n);
+ };
+
+ // Trim out all falsy values from an array.
+ _.compact = function(array) {
+ return _.filter(array, _.identity);
+ };
+
+ // Internal implementation of a recursive `flatten` function.
+ var flatten = function(input, shallow, strict, startIndex) {
+ var output = [], idx = 0;
+ for (var i = startIndex || 0, length = input && input.length; i < length; i++) {
+ var value = input[i];
+ if (isArrayLike(value) && (_.isArray(value) || _.isArguments(value))) {
+ //flatten current level of array or arguments object
+ if (!shallow) value = flatten(value, shallow, strict);
+ var j = 0, len = value.length;
+ output.length += len;
+ while (j < len) {
+ output[idx++] = value[j++];
+ }
+ } else if (!strict) {
+ output[idx++] = value;
+ }
+ }
+ return output;
+ };
+
+ // Flatten out an array, either recursively (by default), or just one level.
+ _.flatten = function(array, shallow) {
+ return flatten(array, shallow, false);
+ };
+
+ // Return a version of the array that does not contain the specified value(s).
+ _.without = function(array) {
+ return _.difference(array, slice.call(arguments, 1));
+ };
+
+ // Produce a duplicate-free version of the array. If the array has already
+ // been sorted, you have the option of using a faster algorithm.
+ // Aliased as `unique`.
+ _.uniq = _.unique = function(array, isSorted, iteratee, context) {
+ if (array == null) return [];
+ if (!_.isBoolean(isSorted)) {
+ context = iteratee;
+ iteratee = isSorted;
+ isSorted = false;
+ }
+ if (iteratee != null) iteratee = cb(iteratee, context);
+ var result = [];
+ var seen = [];
+ for (var i = 0, length = array.length; i < length; i++) {
+ var value = array[i],
+ computed = iteratee ? iteratee(value, i, array) : value;
+ if (isSorted) {
+ if (!i || seen !== computed) result.push(value);
+ seen = computed;
+ } else if (iteratee) {
+ if (!_.contains(seen, computed)) {
+ seen.push(computed);
+ result.push(value);
+ }
+ } else if (!_.contains(result, value)) {
+ result.push(value);
+ }
+ }
+ return result;
+ };
+
+ // Produce an array that contains the union: each distinct element from all of
+ // the passed-in arrays.
+ _.union = function() {
+ return _.uniq(flatten(arguments, true, true));
+ };
+
+ // Produce an array that contains every item shared between all the
+ // passed-in arrays.
+ _.intersection = function(array) {
+ if (array == null) return [];
+ var result = [];
+ var argsLength = arguments.length;
+ for (var i = 0, length = array.length; i < length; i++) {
+ var item = array[i];
+ if (_.contains(result, item)) continue;
+ for (var j = 1; j < argsLength; j++) {
+ if (!_.contains(arguments[j], item)) break;
+ }
+ if (j === argsLength) result.push(item);
+ }
+ return result;
+ };
+
+ // Take the difference between one array and a number of other arrays.
+ // Only the elements present in just the first array will remain.
+ _.difference = function(array) {
+ var rest = flatten(arguments, true, true, 1);
+ return _.filter(array, function(value){
+ return !_.contains(rest, value);
+ });
+ };
+
+ // Zip together multiple lists into a single array -- elements that share
+ // an index go together.
+ _.zip = function() {
+ return _.unzip(arguments);
+ };
+
+ // Complement of _.zip. Unzip accepts an array of arrays and groups
+ // each array's elements on shared indices
+ _.unzip = function(array) {
+ var length = array && _.max(array, 'length').length || 0;
+ var result = Array(length);
+
+ for (var index = 0; index < length; index++) {
+ result[index] = _.pluck(array, index);
+ }
+ return result;
+ };
+
+ // Converts lists into objects. Pass either a single array of `[key, value]`
+ // pairs, or two parallel arrays of the same length -- one of keys, and one of
+ // the corresponding values.
+ _.object = function(list, values) {
+ var result = {};
+ for (var i = 0, length = list && list.length; i < length; i++) {
+ if (values) {
+ result[list[i]] = values[i];
+ } else {
+ result[list[i][0]] = list[i][1];
+ }
+ }
+ return result;
+ };
+
+ // Return the position of the first occurrence of an item in an array,
+ // or -1 if the item is not included in the array.
+ // If the array is large and already in sort order, pass `true`
+ // for **isSorted** to use binary search.
+ _.indexOf = function(array, item, isSorted) {
+ var i = 0, length = array && array.length;
+ if (typeof isSorted == 'number') {
+ i = isSorted < 0 ? Math.max(0, length + isSorted) : isSorted;
+ } else if (isSorted && length) {
+ i = _.sortedIndex(array, item);
+ return array[i] === item ? i : -1;
+ }
+ if (item !== item) {
+ return _.findIndex(slice.call(array, i), _.isNaN);
+ }
+ for (; i < length; i++) if (array[i] === item) return i;
+ return -1;
+ };
+
+ _.lastIndexOf = function(array, item, from) {
+ var idx = array ? array.length : 0;
+ if (typeof from == 'number') {
+ idx = from < 0 ? idx + from + 1 : Math.min(idx, from + 1);
+ }
+ if (item !== item) {
+ return _.findLastIndex(slice.call(array, 0, idx), _.isNaN);
+ }
+ while (--idx >= 0) if (array[idx] === item) return idx;
+ return -1;
+ };
+
+ // Generator function to create the findIndex and findLastIndex functions
+ function createIndexFinder(dir) {
+ return function(array, predicate, context) {
+ predicate = cb(predicate, context);
+ var length = array != null && array.length;
+ var index = dir > 0 ? 0 : length - 1;
+ for (; index >= 0 && index < length; index += dir) {
+ if (predicate(array[index], index, array)) return index;
+ }
+ return -1;
+ };
+ }
+
+ // Returns the first index on an array-like that passes a predicate test
+ _.findIndex = createIndexFinder(1);
+
+ _.findLastIndex = createIndexFinder(-1);
+
+ // Use a comparator function to figure out the smallest index at which
+ // an object should be inserted so as to maintain order. Uses binary search.
+ _.sortedIndex = function(array, obj, iteratee, context) {
+ iteratee = cb(iteratee, context, 1);
+ var value = iteratee(obj);
+ var low = 0, high = array.length;
+ while (low < high) {
+ var mid = Math.floor((low + high) / 2);
+ if (iteratee(array[mid]) < value) low = mid + 1; else high = mid;
+ }
+ return low;
+ };
+
+ // Generate an integer Array containing an arithmetic progression. A port of
+ // the native Python `range()` function. See
+ // [the Python documentation](http://docs.python.org/library/functions.html#range).
+ _.range = function(start, stop, step) {
+ if (arguments.length <= 1) {
+ stop = start || 0;
+ start = 0;
+ }
+ step = step || 1;
+
+ var length = Math.max(Math.ceil((stop - start) / step), 0);
+ var range = Array(length);
+
+ for (var idx = 0; idx < length; idx++, start += step) {
+ range[idx] = start;
+ }
+
+ return range;
+ };
+
+ // Function (ahem) Functions
+ // ------------------
+
+ // Determines whether to execute a function as a constructor
+ // or a normal function with the provided arguments
+ var executeBound = function(sourceFunc, boundFunc, context, callingContext, args) {
+ if (!(callingContext instanceof boundFunc)) return sourceFunc.apply(context, args);
+ var self = baseCreate(sourceFunc.prototype);
+ var result = sourceFunc.apply(self, args);
+ if (_.isObject(result)) return result;
+ return self;
+ };
+
+ // Create a function bound to a given object (assigning `this`, and arguments,
+ // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if
+ // available.
+ _.bind = function(func, context) {
+ if (nativeBind && func.bind === nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
+ if (!_.isFunction(func)) throw new TypeError('Bind must be called on a function');
+ var args = slice.call(arguments, 2);
+ var bound = function() {
+ return executeBound(func, bound, context, this, args.concat(slice.call(arguments)));
+ };
+ return bound;
+ };
+
+ // Partially apply a function by creating a version that has had some of its
+ // arguments pre-filled, without changing its dynamic `this` context. _ acts
+ // as a placeholder, allowing any combination of arguments to be pre-filled.
+ _.partial = function(func) {
+ var boundArgs = slice.call(arguments, 1);
+ var bound = function() {
+ var position = 0, length = boundArgs.length;
+ var args = Array(length);
+ for (var i = 0; i < length; i++) {
+ args[i] = boundArgs[i] === _ ? arguments[position++] : boundArgs[i];
+ }
+ while (position < arguments.length) args.push(arguments[position++]);
+ return executeBound(func, bound, this, this, args);
+ };
+ return bound;
+ };
+
+ // Bind a number of an object's methods to that object. Remaining arguments
+ // are the method names to be bound. Useful for ensuring that all callbacks
+ // defined on an object belong to it.
+ _.bindAll = function(obj) {
+ var i, length = arguments.length, key;
+ if (length <= 1) throw new Error('bindAll must be passed function names');
+ for (i = 1; i < length; i++) {
+ key = arguments[i];
+ obj[key] = _.bind(obj[key], obj);
+ }
+ return obj;
+ };
+
+ // Memoize an expensive function by storing its results.
+ _.memoize = function(func, hasher) {
+ var memoize = function(key) {
+ var cache = memoize.cache;
+ var address = '' + (hasher ? hasher.apply(this, arguments) : key);
+ if (!_.has(cache, address)) cache[address] = func.apply(this, arguments);
+ return cache[address];
+ };
+ memoize.cache = {};
+ return memoize;
+ };
+
+ // Delays a function for the given number of milliseconds, and then calls
+ // it with the arguments supplied.
+ _.delay = function(func, wait) {
+ var args = slice.call(arguments, 2);
+ return setTimeout(function(){
+ return func.apply(null, args);
+ }, wait);
+ };
+
+ // Defers a function, scheduling it to run after the current call stack has
+ // cleared.
+ _.defer = _.partial(_.delay, _, 1);
+
+ // Returns a function, that, when invoked, will only be triggered at most once
+ // during a given window of time. Normally, the throttled function will run
+ // as much as it can, without ever going more than once per `wait` duration;
+ // but if you'd like to disable the execution on the leading edge, pass
+ // `{leading: false}`. To disable execution on the trailing edge, ditto.
+ _.throttle = function(func, wait, options) {
+ var context, args, result;
+ var timeout = null;
+ var previous = 0;
+ if (!options) options = {};
+ var later = function() {
+ previous = options.leading === false ? 0 : _.now();
+ timeout = null;
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ };
+ return function() {
+ var now = _.now();
+ if (!previous && options.leading === false) previous = now;
+ var remaining = wait - (now - previous);
+ context = this;
+ args = arguments;
+ if (remaining <= 0 || remaining > wait) {
+ if (timeout) {
+ clearTimeout(timeout);
+ timeout = null;
+ }
+ previous = now;
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ } else if (!timeout && options.trailing !== false) {
+ timeout = setTimeout(later, remaining);
+ }
+ return result;
+ };
+ };
+
+ // Returns a function, that, as long as it continues to be invoked, will not
+ // be triggered. The function will be called after it stops being called for
+ // N milliseconds. If `immediate` is passed, trigger the function on the
+ // leading edge, instead of the trailing.
+ _.debounce = function(func, wait, immediate) {
+ var timeout, args, context, timestamp, result;
+
+ var later = function() {
+ var last = _.now() - timestamp;
+
+ if (last < wait && last >= 0) {
+ timeout = setTimeout(later, wait - last);
+ } else {
+ timeout = null;
+ if (!immediate) {
+ result = func.apply(context, args);
+ if (!timeout) context = args = null;
+ }
+ }
+ };
+
+ return function() {
+ context = this;
+ args = arguments;
+ timestamp = _.now();
+ var callNow = immediate && !timeout;
+ if (!timeout) timeout = setTimeout(later, wait);
+ if (callNow) {
+ result = func.apply(context, args);
+ context = args = null;
+ }
+
+ return result;
+ };
+ };
+
+ // Returns the first function passed as an argument to the second,
+ // allowing you to adjust arguments, run code before and after, and
+ // conditionally execute the original function.
+ _.wrap = function(func, wrapper) {
+ return _.partial(wrapper, func);
+ };
+
+ // Returns a negated version of the passed-in predicate.
+ _.negate = function(predicate) {
+ return function() {
+ return !predicate.apply(this, arguments);
+ };
+ };
+
+ // Returns a function that is the composition of a list of functions, each
+ // consuming the return value of the function that follows.
+ _.compose = function() {
+ var args = arguments;
+ var start = args.length - 1;
+ return function() {
+ var i = start;
+ var result = args[start].apply(this, arguments);
+ while (i--) result = args[i].call(this, result);
+ return result;
+ };
+ };
+
+ // Returns a function that will only be executed on and after the Nth call.
+ _.after = function(times, func) {
+ return function() {
+ if (--times < 1) {
+ return func.apply(this, arguments);
+ }
+ };
+ };
+
+ // Returns a function that will only be executed up to (but not including) the Nth call.
+ _.before = function(times, func) {
+ var memo;
+ return function() {
+ if (--times > 0) {
+ memo = func.apply(this, arguments);
+ }
+ if (times <= 1) func = null;
+ return memo;
+ };
+ };
+
+ // Returns a function that will be executed at most one time, no matter how
+ // often you call it. Useful for lazy initialization.
+ _.once = _.partial(_.before, 2);
+
+ // Object Functions
+ // ----------------
+
+ // Keys in IE < 9 that won't be iterated by `for key in ...` and thus missed.
+ var hasEnumBug = !{toString: null}.propertyIsEnumerable('toString');
+ var nonEnumerableProps = ['valueOf', 'isPrototypeOf', 'toString',
+ 'propertyIsEnumerable', 'hasOwnProperty', 'toLocaleString'];
+
+ function collectNonEnumProps(obj, keys) {
+ var nonEnumIdx = nonEnumerableProps.length;
+ var constructor = obj.constructor;
+ var proto = (_.isFunction(constructor) && constructor.prototype) || ObjProto;
+
+ // Constructor is a special case.
+ var prop = 'constructor';
+ if (_.has(obj, prop) && !_.contains(keys, prop)) keys.push(prop);
+
+ while (nonEnumIdx--) {
+ prop = nonEnumerableProps[nonEnumIdx];
+ if (prop in obj && obj[prop] !== proto[prop] && !_.contains(keys, prop)) {
+ keys.push(prop);
+ }
+ }
+ }
+
+ // Retrieve the names of an object's own properties.
+ // Delegates to **ECMAScript 5**'s native `Object.keys`
+ _.keys = function(obj) {
+ if (!_.isObject(obj)) return [];
+ if (nativeKeys) return nativeKeys(obj);
+ var keys = [];
+ for (var key in obj) if (_.has(obj, key)) keys.push(key);
+ // Ahem, IE < 9.
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
+ return keys;
+ };
+
+ // Retrieve all the property names of an object.
+ _.allKeys = function(obj) {
+ if (!_.isObject(obj)) return [];
+ var keys = [];
+ for (var key in obj) keys.push(key);
+ // Ahem, IE < 9.
+ if (hasEnumBug) collectNonEnumProps(obj, keys);
+ return keys;
+ };
+
+ // Retrieve the values of an object's properties.
+ _.values = function(obj) {
+ var keys = _.keys(obj);
+ var length = keys.length;
+ var values = Array(length);
+ for (var i = 0; i < length; i++) {
+ values[i] = obj[keys[i]];
+ }
+ return values;
+ };
+
+ // Returns the results of applying the iteratee to each element of the object
+ // In contrast to _.map it returns an object
+ _.mapObject = function(obj, iteratee, context) {
+ iteratee = cb(iteratee, context);
+ var keys = _.keys(obj),
+ length = keys.length,
+ results = {},
+ currentKey;
+ for (var index = 0; index < length; index++) {
+ currentKey = keys[index];
+ results[currentKey] = iteratee(obj[currentKey], currentKey, obj);
+ }
+ return results;
+ };
+
+ // Convert an object into a list of `[key, value]` pairs.
+ _.pairs = function(obj) {
+ var keys = _.keys(obj);
+ var length = keys.length;
+ var pairs = Array(length);
+ for (var i = 0; i < length; i++) {
+ pairs[i] = [keys[i], obj[keys[i]]];
+ }
+ return pairs;
+ };
+
+ // Invert the keys and values of an object. The values must be serializable.
+ _.invert = function(obj) {
+ var result = {};
+ var keys = _.keys(obj);
+ for (var i = 0, length = keys.length; i < length; i++) {
+ result[obj[keys[i]]] = keys[i];
+ }
+ return result;
+ };
+
+ // Return a sorted list of the function names available on the object.
+ // Aliased as `methods`
+ _.functions = _.methods = function(obj) {
+ var names = [];
+ for (var key in obj) {
+ if (_.isFunction(obj[key])) names.push(key);
+ }
+ return names.sort();
+ };
+
+ // Extend a given object with all the properties in passed-in object(s).
+ _.extend = createAssigner(_.allKeys);
+
+ // Assigns a given object with all the own properties in the passed-in object(s)
+ // (https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
+ _.extendOwn = _.assign = createAssigner(_.keys);
+
+ // Returns the first key on an object that passes a predicate test
+ _.findKey = function(obj, predicate, context) {
+ predicate = cb(predicate, context);
+ var keys = _.keys(obj), key;
+ for (var i = 0, length = keys.length; i < length; i++) {
+ key = keys[i];
+ if (predicate(obj[key], key, obj)) return key;
+ }
+ };
+
+ // Return a copy of the object only containing the whitelisted properties.
+ _.pick = function(object, oiteratee, context) {
+ var result = {}, obj = object, iteratee, keys;
+ if (obj == null) return result;
+ if (_.isFunction(oiteratee)) {
+ keys = _.allKeys(obj);
+ iteratee = optimizeCb(oiteratee, context);
+ } else {
+ keys = flatten(arguments, false, false, 1);
+ iteratee = function(value, key, obj) { return key in obj; };
+ obj = Object(obj);
+ }
+ for (var i = 0, length = keys.length; i < length; i++) {
+ var key = keys[i];
+ var value = obj[key];
+ if (iteratee(value, key, obj)) result[key] = value;
+ }
+ return result;
+ };
+
+ // Return a copy of the object without the blacklisted properties.
+ _.omit = function(obj, iteratee, context) {
+ if (_.isFunction(iteratee)) {
+ iteratee = _.negate(iteratee);
+ } else {
+ var keys = _.map(flatten(arguments, false, false, 1), String);
+ iteratee = function(value, key) {
+ return !_.contains(keys, key);
+ };
+ }
+ return _.pick(obj, iteratee, context);
+ };
+
+ // Fill in a given object with default properties.
+ _.defaults = createAssigner(_.allKeys, true);
+
+ // Create a (shallow-cloned) duplicate of an object.
+ _.clone = function(obj) {
+ if (!_.isObject(obj)) return obj;
+ return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
+ };
+
+ // Invokes interceptor with the obj, and then returns obj.
+ // The primary purpose of this method is to "tap into" a method chain, in
+ // order to perform operations on intermediate results within the chain.
+ _.tap = function(obj, interceptor) {
+ interceptor(obj);
+ return obj;
+ };
+
+ // Returns whether an object has a given set of `key:value` pairs.
+ _.isMatch = function(object, attrs) {
+ var keys = _.keys(attrs), length = keys.length;
+ if (object == null) return !length;
+ var obj = Object(object);
+ for (var i = 0; i < length; i++) {
+ var key = keys[i];
+ if (attrs[key] !== obj[key] || !(key in obj)) return false;
+ }
+ return true;
+ };
+
+
+ // Internal recursive comparison function for `isEqual`.
+ var eq = function(a, b, aStack, bStack) {
+ // Identical objects are equal. `0 === -0`, but they aren't identical.
+ // See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
+ if (a === b) return a !== 0 || 1 / a === 1 / b;
+ // A strict comparison is necessary because `null == undefined`.
+ if (a == null || b == null) return a === b;
+ // Unwrap any wrapped objects.
+ if (a instanceof _) a = a._wrapped;
+ if (b instanceof _) b = b._wrapped;
+ // Compare `[[Class]]` names.
+ var className = toString.call(a);
+ if (className !== toString.call(b)) return false;
+ switch (className) {
+ // Strings, numbers, regular expressions, dates, and booleans are compared by value.
+ case '[object RegExp]':
+ // RegExps are coerced to strings for comparison (Note: '' + /a/i === '/a/i')
+ case '[object String]':
+ // Primitives and their corresponding object wrappers are equivalent; thus, `"5"` is
+ // equivalent to `new String("5")`.
+ return '' + a === '' + b;
+ case '[object Number]':
+ // `NaN`s are equivalent, but non-reflexive.
+ // Object(NaN) is equivalent to NaN
+ if (+a !== +a) return +b !== +b;
+ // An `egal` comparison is performed for other numeric values.
+ return +a === 0 ? 1 / +a === 1 / b : +a === +b;
+ case '[object Date]':
+ case '[object Boolean]':
+ // Coerce dates and booleans to numeric primitive values. Dates are compared by their
+ // millisecond representations. Note that invalid dates with millisecond representations
+ // of `NaN` are not equivalent.
+ return +a === +b;
+ }
+
+ var areArrays = className === '[object Array]';
+ if (!areArrays) {
+ if (typeof a != 'object' || typeof b != 'object') return false;
+
+ // Objects with different constructors are not equivalent, but `Object`s or `Array`s
+ // from different frames are.
+ var aCtor = a.constructor, bCtor = b.constructor;
+ if (aCtor !== bCtor && !(_.isFunction(aCtor) && aCtor instanceof aCtor &&
+ _.isFunction(bCtor) && bCtor instanceof bCtor)
+ && ('constructor' in a && 'constructor' in b)) {
+ return false;
+ }
+ }
+ // Assume equality for cyclic structures. The algorithm for detecting cyclic
+ // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
+
+ // Initializing stack of traversed objects.
+ // It's done here since we only need them for objects and arrays comparison.
+ aStack = aStack || [];
+ bStack = bStack || [];
+ var length = aStack.length;
+ while (length--) {
+ // Linear search. Performance is inversely proportional to the number of
+ // unique nested structures.
+ if (aStack[length] === a) return bStack[length] === b;
+ }
+
+ // Add the first object to the stack of traversed objects.
+ aStack.push(a);
+ bStack.push(b);
+
+ // Recursively compare objects and arrays.
+ if (areArrays) {
+ // Compare array lengths to determine if a deep comparison is necessary.
+ length = a.length;
+ if (length !== b.length) return false;
+ // Deep compare the contents, ignoring non-numeric properties.
+ while (length--) {
+ if (!eq(a[length], b[length], aStack, bStack)) return false;
+ }
+ } else {
+ // Deep compare objects.
+ var keys = _.keys(a), key;
+ length = keys.length;
+ // Ensure that both objects contain the same number of properties before comparing deep equality.
+ if (_.keys(b).length !== length) return false;
+ while (length--) {
+ // Deep compare each member
+ key = keys[length];
+ if (!(_.has(b, key) && eq(a[key], b[key], aStack, bStack))) return false;
+ }
+ }
+ // Remove the first object from the stack of traversed objects.
+ aStack.pop();
+ bStack.pop();
+ return true;
+ };
+
+ // Perform a deep comparison to check if two objects are equal.
+ _.isEqual = function(a, b) {
+ return eq(a, b);
+ };
+
+ // Is a given array, string, or object empty?
+ // An "empty" object has no enumerable own-properties.
+ _.isEmpty = function(obj) {
+ if (obj == null) return true;
+ if (isArrayLike(obj) && (_.isArray(obj) || _.isString(obj) || _.isArguments(obj))) return obj.length === 0;
+ return _.keys(obj).length === 0;
+ };
+
+ // Is a given value a DOM element?
+ _.isElement = function(obj) {
+ return !!(obj && obj.nodeType === 1);
+ };
+
+ // Is a given value an array?
+ // Delegates to ECMA5's native Array.isArray
+ _.isArray = nativeIsArray || function(obj) {
+ return toString.call(obj) === '[object Array]';
+ };
+
+ // Is a given variable an object?
+ _.isObject = function(obj) {
+ var type = typeof obj;
+ return type === 'function' || type === 'object' && !!obj;
+ };
+
+ // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp, isError.
+ _.each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp', 'Error'], function(name) {
+ _['is' + name] = function(obj) {
+ return toString.call(obj) === '[object ' + name + ']';
+ };
+ });
+
+ // Define a fallback version of the method in browsers (ahem, IE < 9), where
+ // there isn't any inspectable "Arguments" type.
+ if (!_.isArguments(arguments)) {
+ _.isArguments = function(obj) {
+ return _.has(obj, 'callee');
+ };
+ }
+
+ // Optimize `isFunction` if appropriate. Work around some typeof bugs in old v8,
+ // IE 11 (#1621), and in Safari 8 (#1929).
+ if (typeof /./ != 'function' && typeof Int8Array != 'object') {
+ _.isFunction = function(obj) {
+ return typeof obj == 'function' || false;
+ };
+ }
+
+ // Is a given object a finite number?
+ _.isFinite = function(obj) {
+ return isFinite(obj) && !isNaN(parseFloat(obj));
+ };
+
+ // Is the given value `NaN`? (NaN is the only number which does not equal itself).
+ _.isNaN = function(obj) {
+ return _.isNumber(obj) && obj !== +obj;
+ };
+
+ // Is a given value a boolean?
+ _.isBoolean = function(obj) {
+ return obj === true || obj === false || toString.call(obj) === '[object Boolean]';
+ };
+
+ // Is a given value equal to null?
+ _.isNull = function(obj) {
+ return obj === null;
+ };
+
+ // Is a given variable undefined?
+ _.isUndefined = function(obj) {
+ return obj === void 0;
+ };
+
+ // Shortcut function for checking if an object has a given property directly
+ // on itself (in other words, not on a prototype).
+ _.has = function(obj, key) {
+ return obj != null && hasOwnProperty.call(obj, key);
+ };
+
+ // Utility Functions
+ // -----------------
+
+ // Run Underscore.js in *noConflict* mode, returning the `_` variable to its
+ // previous owner. Returns a reference to the Underscore object.
+ _.noConflict = function() {
+ root._ = previousUnderscore;
+ return this;
+ };
+
+ // Keep the identity function around for default iteratees.
+ _.identity = function(value) {
+ return value;
+ };
+
+ // Predicate-generating functions. Often useful outside of Underscore.
+ _.constant = function(value) {
+ return function() {
+ return value;
+ };
+ };
+
+ _.noop = function(){};
+
+ _.property = function(key) {
+ return function(obj) {
+ return obj == null ? void 0 : obj[key];
+ };
+ };
+
+ // Generates a function for a given object that returns a given property.
+ _.propertyOf = function(obj) {
+ return obj == null ? function(){} : function(key) {
+ return obj[key];
+ };
+ };
+
+ // Returns a predicate for checking whether an object has a given set of
+ // `key:value` pairs.
+ _.matcher = _.matches = function(attrs) {
+ attrs = _.extendOwn({}, attrs);
+ return function(obj) {
+ return _.isMatch(obj, attrs);
+ };
+ };
+
+ // Run a function **n** times.
+ _.times = function(n, iteratee, context) {
+ var accum = Array(Math.max(0, n));
+ iteratee = optimizeCb(iteratee, context, 1);
+ for (var i = 0; i < n; i++) accum[i] = iteratee(i);
+ return accum;
+ };
+
+ // Return a random integer between min and max (inclusive).
+ _.random = function(min, max) {
+ if (max == null) {
+ max = min;
+ min = 0;
+ }
+ return min + Math.floor(Math.random() * (max - min + 1));
+ };
+
+ // A (possibly faster) way to get the current timestamp as an integer.
+ _.now = Date.now || function() {
+ return new Date().getTime();
+ };
+
+ // List of HTML entities for escaping.
+ var escapeMap = {
+ '&': '&',
+ '<': '<',
+ '>': '>',
+ '"': '"',
+ "'": ''',
+ '`': '`'
+ };
+ var unescapeMap = _.invert(escapeMap);
+
+ // Functions for escaping and unescaping strings to/from HTML interpolation.
+ var createEscaper = function(map) {
+ var escaper = function(match) {
+ return map[match];
+ };
+ // Regexes for identifying a key that needs to be escaped
+ var source = '(?:' + _.keys(map).join('|') + ')';
+ var testRegexp = RegExp(source);
+ var replaceRegexp = RegExp(source, 'g');
+ return function(string) {
+ string = string == null ? '' : '' + string;
+ return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
+ };
+ };
+ _.escape = createEscaper(escapeMap);
+ _.unescape = createEscaper(unescapeMap);
+
+ // If the value of the named `property` is a function then invoke it with the
+ // `object` as context; otherwise, return it.
+ _.result = function(object, property, fallback) {
+ var value = object == null ? void 0 : object[property];
+ if (value === void 0) {
+ value = fallback;
+ }
+ return _.isFunction(value) ? value.call(object) : value;
+ };
+
+ // Generate a unique integer id (unique within the entire client session).
+ // Useful for temporary DOM ids.
+ var idCounter = 0;
+ _.uniqueId = function(prefix) {
+ var id = ++idCounter + '';
+ return prefix ? prefix + id : id;
+ };
+
+ // By default, Underscore uses ERB-style template delimiters, change the
+ // following template settings to use alternative delimiters.
+ _.templateSettings = {
+ evaluate : /<%([\s\S]+?)%>/g,
+ interpolate : /<%=([\s\S]+?)%>/g,
+ escape : /<%-([\s\S]+?)%>/g
+ };
+
+ // When customizing `templateSettings`, if you don't want to define an
+ // interpolation, evaluation or escaping regex, we need one that is
+ // guaranteed not to match.
+ var noMatch = /(.)^/;
+
+ // Certain characters need to be escaped so that they can be put into a
+ // string literal.
+ var escapes = {
+ "'": "'",
+ '\\': '\\',
+ '\r': 'r',
+ '\n': 'n',
+ '\u2028': 'u2028',
+ '\u2029': 'u2029'
+ };
+
+ var escaper = /\\|'|\r|\n|\u2028|\u2029/g;
+
+ var escapeChar = function(match) {
+ return '\\' + escapes[match];
+ };
+
+ // JavaScript micro-templating, similar to John Resig's implementation.
+ // Underscore templating handles arbitrary delimiters, preserves whitespace,
+ // and correctly escapes quotes within interpolated code.
+ // NB: `oldSettings` only exists for backwards compatibility.
+ _.template = function(text, settings, oldSettings) {
+ if (!settings && oldSettings) settings = oldSettings;
+ settings = _.defaults({}, settings, _.templateSettings);
+
+ // Combine delimiters into one regular expression via alternation.
+ var matcher = RegExp([
+ (settings.escape || noMatch).source,
+ (settings.interpolate || noMatch).source,
+ (settings.evaluate || noMatch).source
+ ].join('|') + '|$', 'g');
+
+ // Compile the template source, escaping string literals appropriately.
+ var index = 0;
+ var source = "__p+='";
+ text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {
+ source += text.slice(index, offset).replace(escaper, escapeChar);
+ index = offset + match.length;
+
+ if (escape) {
+ source += "'+\n((__t=(" + escape + "))==null?'':_.escape(__t))+\n'";
+ } else if (interpolate) {
+ source += "'+\n((__t=(" + interpolate + "))==null?'':__t)+\n'";
+ } else if (evaluate) {
+ source += "';\n" + evaluate + "\n__p+='";
+ }
+
+ // Adobe VMs need the match returned to produce the correct offest.
+ return match;
+ });
+ source += "';\n";
+
+ // If a variable is not specified, place data values in local scope.
+ if (!settings.variable) source = 'with(obj||{}){\n' + source + '}\n';
+
+ source = "var __t,__p='',__j=Array.prototype.join," +
+ "print=function(){__p+=__j.call(arguments,'');};\n" +
+ source + 'return __p;\n';
+
+ try {
+ var render = new Function(settings.variable || 'obj', '_', source);
+ } catch (e) {
+ e.source = source;
+ throw e;
+ }
+
+ var template = function(data) {
+ return render.call(this, data, _);
+ };
+
+ // Provide the compiled source as a convenience for precompilation.
+ var argument = settings.variable || 'obj';
+ template.source = 'function(' + argument + '){\n' + source + '}';
+
+ return template;
+ };
+
+ // Add a "chain" function. Start chaining a wrapped Underscore object.
+ _.chain = function(obj) {
+ var instance = _(obj);
+ instance._chain = true;
+ return instance;
+ };
+
+ // OOP
+ // ---------------
+ // If Underscore is called as a function, it returns a wrapped object that
+ // can be used OO-style. This wrapper holds altered versions of all the
+ // underscore functions. Wrapped objects may be chained.
+
+ // Helper function to continue chaining intermediate results.
+ var result = function(instance, obj) {
+ return instance._chain ? _(obj).chain() : obj;
+ };
+
+ // Add your own custom functions to the Underscore object.
+ _.mixin = function(obj) {
+ _.each(_.functions(obj), function(name) {
+ var func = _[name] = obj[name];
+ _.prototype[name] = function() {
+ var args = [this._wrapped];
+ push.apply(args, arguments);
+ return result(this, func.apply(_, args));
+ };
+ });
+ };
+
+ // Add all of the Underscore functions to the wrapper object.
+ _.mixin(_);
+
+ // Add all mutator Array functions to the wrapper.
+ _.each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ var obj = this._wrapped;
+ method.apply(obj, arguments);
+ if ((name === 'shift' || name === 'splice') && obj.length === 0) delete obj[0];
+ return result(this, obj);
+ };
+ });
+
+ // Add all accessor Array functions to the wrapper.
+ _.each(['concat', 'join', 'slice'], function(name) {
+ var method = ArrayProto[name];
+ _.prototype[name] = function() {
+ return result(this, method.apply(this._wrapped, arguments));
+ };
+ });
+
+ // Extracts the result from a wrapped and chained object.
+ _.prototype.value = function() {
+ return this._wrapped;
+ };
+
+ // Provide unwrapping proxy for some methods used in engine operations
+ // such as arithmetic and JSON stringification.
+ _.prototype.valueOf = _.prototype.toJSON = _.prototype.value;
+
+ _.prototype.toString = function() {
+ return '' + this._wrapped;
+ };
+
+ // AMD registration happens at the end for compatibility with AMD loaders
+ // that may not enforce next-turn semantics on modules. Even though general
+ // practice for AMD registration is to be anonymous, underscore registers
+ // as a named module because, like jQuery, it is a base library that is
+ // popular enough to be bundled in a third party lib, but not be part of
+ // an AMD load request. Those cases could generate an error when an
+ // anonymous define() is called outside of a loader request.
+ if (typeof define === 'function' && define.amd) {
+ define('underscore', [], function() {
+ return _;
+ });
+ }
+}.call(this));
--- /dev/null
+from django.test import TestCase
+
+# Create your tests here.
--- /dev/null
+from django.conf.urls import patterns, url
+
+from filex import views
+
+urlpatterns = patterns('',
+ url(r'^list/$', views.list_content, name='list'),
+ url(r'^download/$', views.download, name='download'),
+)
--- /dev/null
+from datetime import datetime
+
+from django.core.exceptions import PermissionDenied, SuspiciousOperation
+from django.http import JsonResponse, StreamingHttpResponse
+from django.template.defaultfilters import filesizeformat
+from django.utils.formats import date_format
+from django.utils.timezone import UTC, localtime
+import mimetypes
+
+from filex.ftp import FTPOperation, FTPException
+
+
+def check_auth(request):
+ if not request.user.is_authenticated():
+ raise PermissionDenied("Login required!")
+ if not request.session['proxy']:
+ raise PermissionDenied("No proxy found!")
+
+
+def list_content(request):
+ check_auth(request)
+
+ # TODO data validation
+ host = request.GET.get('host')
+ path = request.GET.get('path')
+ if not host or not path:
+ raise SuspiciousOperation("No path or host given!")
+
+ url = 'gsiftp://' + host + path
+
+ try:
+ listing = FTPOperation(request.session['proxy']).listing(url)
+ except FTPException as e:
+ return JsonResponse({'msg': e.message}, status=400)
+
+ data = []
+
+ # ignore first two: '.' and '..'
+ for item in listing.strip().splitlines()[2:]:
+ # we may receive empty string when there are multiple consecutive newlines in listing
+ if item:
+ attrs, name = item.split(' ', 1)
+
+ attrs = dict((attr.split('=') for attr in attrs.split(';') if attr))
+
+ date = localtime(datetime.strptime(attrs['Modify'], "%Y%m%d%H%M%S").replace(tzinfo=UTC()))
+
+ data.append({
+ 'name': name,
+ 'type': 'file' if attrs['Type'] == 'file' else 'directory',
+ 'size': filesizeformat(attrs['Size']),
+ 'date': date_format(date, 'DATETIME_FORMAT'),
+ })
+
+ return JsonResponse(data, safe=False)
+
+
+def download(request):
+ check_auth(request)
+
+ # TODO data validation
+ host = request.GET.get('host')
+ path = request.GET.get('path')
+ name = request.GET.get('name')
+ if not host or not path or not name:
+ raise SuspiciousOperation("No path or host or name given!")
+
+ url = 'gsiftp://' + host + path + '/' + name
+
+ mime_type, encoding = mimetypes.guess_type(name)
+
+ response = StreamingHttpResponse(FTPOperation(request.session['proxy']).get(url),
+ content_type=mime_type or 'application/octet-stream')
+ response['Content-Disposition'] = 'attachment; filename={}'.format(name)
+ # TODO Content-Length (?)
+
+ if encoding:
+ response['Content-Encoding'] = encoding
+
+ return response