Commit 90bb5cdc authored by Addy Osmani's avatar Addy Osmani

Merge pull request #235 from DimitarChristoff/labs-epitome

initial labs for epitome
parents 0952622c f9445c31
/* base.css overrides */
\ No newline at end of file
<!doctype html>
<html lang="en" xmlns="http://www.w3.org/1999/html">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>Epitome • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
<!-- CSS overrides - remove if you don't need it -->
<link rel="stylesheet" href="css/app.css">
<!--[if IE]>
<script src="../../../assets/ie.js"></script>
<![endif]-->
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" placeholder="What needs to be done?" autofocus>
</header>
<!-- This section should be hidden by default and shown when there are todos -->
<section id="main">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as complete</label>
<ul id="todo-list"></ul>
</section>
<!-- This footer should hidden by default and shown when there are todos -->
<footer id="footer"></footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<!-- Remove the below line ↓ -->
<!-- Change this out with your name and url ↓ -->
<p>Created by <a href="https://github.com/DimitarChristoff/">Dimitar Christoff</a></p>
<p>Powered by <a href="https://github.com/DimitarChristoff/Epitome">Epitome for MooTools</a><br/> <a href="http://travis-ci.org/DimitarChristoff/Epitome"></a><img src="https://secure.travis-ci.org/DimitarChristoff/Epitome.png?branch=master" /></a</p>
<p>Part of <a href="http://todomvc.com">TodoMVC</a></p>
</footer>
<script type="text/template" id="item-template">
<div class="view">
<input class="toggle" type="checkbox" <%=completedCheckbox%> />
<label><%=title%></label>
<button class="destroy"></button>
</div>
<input class="edit" type="text" value="<%=title%>" />
</script>
<script type="text/template" id="stats-template">
<span id="todo-count"><strong><%=remaining%></strong> item<% if (obj.remaining != 1) { %>s<% } %> left</span>
<ul id="filters">
<li>
<a class="selected" href="#!/">All</a>
</li>
<li>
<a href="#!/active">Active</a>
</li>
<li>
<a href="#!/completed">Completed</a>
</li>
</ul>
<% if (completed) { %>
<button id="clear-completed">Clear completed (<%=completed%>)</button>
<% } %>
</script>
<!-- Scripts here. Don't remove this ↓ -->
<script src="../assets/base.js"></script>
<!-- mootools -->
<script src="https://ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed.js"></script>
<!-- epitome pre-compiled -->
<script src="../../../Epitome-min.js"></script>
<!-- fallback outside the main repo submodule-->
<script>window.Epitome || document.write('<script src="js/Epitome-min.js">\x3C/script>')</script>
<!-- todo app -->
<script src="js/models/todo-model.js"></script>
<script src="js/collections/todo-collection.js"></script>
<script src="js/views/todo-list.js"></script>
<script src="js/views/todo-main.js"></script>
<script src="js/app.js"></script>
</body>
</html>
\ No newline at end of file
(function(exports){var Epitome={};typeof define=="function"&&define.amd?define("epitome",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome})(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome,eq=Epitome.isEqual=function(a,b,stack){stack=stack||[];if(a===b)return a!==0||1/a==1/b;if(a==null||b==null)return a===b;var typeA=typeOf(a),typeB=typeOf(b);if(typeA!=typeB)return!1;switch(typeA){case"string":return a==String(b);case"number":return a!=+a?b!=+b:a==0?1/a==1/b:a==+b;case"date":case"boolean":return+a==+b;case"regexp":return a.source==b.source&&a.global==b.global&&a.multiline==b.multiline&&a.ignoreCase==b.ignoreCase}if(typeof a!="object"||typeof b!="object")return!1;var length=stack.length;while(length--)if(stack[length]==a)return!0;stack.push(a);var size=0,result=!0;if(typeA=="array"){size=a.length,result=size==b.length;if(result)while(size--)if(!(result=size in a==size in b&&eq(a[size],b[size],stack)))break}else{if("constructor"in a!="constructor"in b||a.constructor!=b.constructor)return!1;for(var key in a)if(a.hasOwnProperty(key)){size++;if(!(result=b.hasOwnProperty(key)&&eq(a[key],b[key],stack)))break}if(result){for(key in b)if(b.hasOwnProperty(key)&&!(size--))break;result=!size}}return stack.pop(),result};typeof define=="function"&&define.amd?define("epitome-isequal",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome;Epitome.Model=new Class({Implements:[Options,Events],_attributes:{},properties:{id:{get:function(){var id=this._attributes.id||(this._attributes.id=String.uniqueID());return this.cid||(this.cid=id),id}}},options:{defaults:{}},collections:[],initialize:function(obj,options){return options&&options.defaults&&(this.options.defaults=Object.merge(this.options.defaults,options.defaults)),obj=obj&&typeOf(obj)==="object"?obj:{},this.set(Object.merge(this.options.defaults,obj)),this.setOptions(options),this.fireEvent("ready")},set:function(){this.propertiesChanged=[],this._set.apply(this,arguments),this.propertiesChanged.length&&this.fireEvent("change",this.get(this.propertiesChanged))},_set:function(key,value){return!key||typeof value=="undefined"?this:this.properties[key]&&this.properties[key].set?this.properties[key].set.call(this,value):this._attributes[key]&&Epitome.isEqual(this._attributes[key],value)?this:(value===null?delete this._attributes[key]:this._attributes[key]=value,this.fireEvent("change:"+key,value),this.propertiesChanged.push(key),this)}.overloadSetter(),get:function(key){return key&&this.properties[key]&&this.properties[key].get?this.properties[key].get.call(this):key&&typeof this._attributes[key]!="undefined"?this._attributes[key]:null}.overloadGetter(),unset:function(){var keys=Array.prototype.slice.apply(arguments),obj={},len=keys.length;return len?(Array.each(Array.flatten(keys),function(key){obj[key]=null}),this.set(obj),this):this},toJSON:function(){return Object.clone(this._attributes)},empty:function(){var keys=Object.keys(this.toJSON()),self=this;this.fireEvent("change",[keys]),Array.each(keys,function(key){self.fireEvent("change:"+key,null)},this),this._attributes={},this.fireEvent("empty")},destroy:function(){this._attributes={},this.fireEvent("destroy")}}),typeof define=="function"&&define.amd?define("epitome-model",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome-model"):exports.Epitome,Model=Epitome.Model,syncPseudo="sync:",methodMap={create:"POST",read:"GET",update:"PUT",delete_:"DELETE"};Model.Sync=new Class({Extends:Model,properties:{id:{get:function(){var id=this._attributes.id||(this._attributes.id=String.uniqueID());return this.cid||(this.cid=id),id}},urlRoot:{set:function(value){this.urlRoot=value,delete this._attributes.urlRoot},get:function(){var base=this.urlRoot||this.options.urlRoot||"no-urlRoot-set";return base.charAt(base.length-1)!="/"&&(base+="/"),base}}},options:{emulateREST:!1},initialize:function(obj,options){this.setupSync(),this.parent(obj,options)},sync:function(method,model){var options={};method=method&&methodMap[method]?methodMap[method]:methodMap.read,options.method=method;if(method==methodMap.create||method==methodMap.update)options.data=model||this.toJSON();return options.url=this.get("urlRoot")+this.get("id")+"/",this.request.setOptions(options),this.request[method](model),this},setupSync:function(){var self=this,rid=0,incrementRequestId=function(){rid++};return this.getRequestId=function(){return rid+1},this.request=new Request.JSON({link:"chain",url:this.get("urlRoot"),emulation:this.options.emulateREST,onRequest:incrementRequestId,onCancel:function(){this.removeEvents(syncPseudo+rid)},onSuccess:function(responseObj){responseObj=self.parse&&self.parse(responseObj),self.fireEvent(syncPseudo+rid,[responseObj]),self.fireEvent("sync",[responseObj,this.options.method,this.options.data])},onFailure:function(){self.fireEvent(syncPseudo+"error",[this.options.method,this.options.url,this.options.data])}}),Object.each(methodMap,function(requestMethod,protoMethod){self[protoMethod]=function(model){this.sync(protoMethod,model)}}),this},_throwAwaySyncEvent:function(eventName,callback){eventName=eventName||syncPseudo+this.getRequestId();var self=this,throwAway={};return throwAway[eventName]=function(responseObj){responseObj&&typeof responseObj=="object"&&(self.set(responseObj),callback&&callback.call(self,responseObj)),self.removeEvents(throwAway)},this.addEvents(throwAway)}.protect(),parse:function(resp){return resp},fetch:function(){return this._throwAwaySyncEvent(syncPseudo+this.getRequestId(),function(){this.fireEvent("fetch"),this.isNewModel=!1}),this.read(),this},save:function(key,value){var method=["update","create"][+this.isNew()];if(key){var ktype=typeOf(key),canSet=ktype=="object"||ktype=="string"&&typeof value!="undefined";canSet&&this._set.apply(this,arguments)}return this._throwAwaySyncEvent(syncPseudo+this.getRequestId(),function(){this.fireEvent("save"),this.fireEvent(method)}),this[method](),this.isNewModel=!1,this},destroy:function(){this._throwAwaySyncEvent(syncPseudo+this.getRequestId(),function(){this._attributes={},this.delete_(),this.fireEvent("destroy")})},isNew:function(){return typeof this.isNewModel=="undefined"&&(this.isNewModel=!0),this.isNewModel}}),typeof define=="function"&&define.amd?define("epitome-model-sync",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome;Epitome.Storage=function(){var hasNativeStorage=typeof exports.localStorage=="object"&&!!exports.localStorage.getItem,localStorage="localStorage",sessionStorage="sessionStorage",setStorage=function(storageMethod){var s,privateKey="epitome-"+storageMethod,storage={},storagePrefix="model";if(hasNativeStorage)try{storage=JSON.decode(exports[storageMethod].getItem(privateKey))||storage}catch(e){hasNativeStorage=!1}if(!hasNativeStorage)try{s=JSON.decode(exports.name),s&&typeof s=="object"&&s[privateKey]&&(storage=s[privateKey])}catch(e){serializeWindowName()}var Methods={store:function(model){model=model||this.toJSON(),setItem([storagePrefix,this.get("id")].join(":"),model),this.fireEvent("store",model)},eliminate:function(){return removeItem([storagePrefix,this.get("id")].join(":")),this.fireEvent("eliminate")},retrieve:function(){var model=getItem([storagePrefix,this.get("id")].join(":"))||null;return this.fireEvent("retrieve",model),model}},getItem=function(item){return storage[item]||null},setItem=function(item,value){storage=JSON.decode(exports[storageMethod].getItem(privateKey))||storage,storage[item]=value;if(hasNativeStorage)try{exports[storageMethod].setItem(privateKey,JSON.encode(storage))}catch(e){}else serializeWindowName();return this},removeItem=function(item){delete storage[item];if(hasNativeStorage)try{exports[storageMethod].setItem(privateKey,JSON.encode(storage))}catch(e){}else serializeWindowName()},serializeWindowName=function(){var obj={},s=JSON.decode(exports.name);obj[privateKey]=storage,exports.name=JSON.encode(Object.merge(obj,s))};return function(storageName){return storageName&&(storagePrefix=storageName),new Class(Object.clone(Methods))}};return{localStorage:setStorage(localStorage),sessionStorage:setStorage(sessionStorage)}}(),typeof define=="function"&&define.amd?define("epitome-storage",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome,methodMap=["forEach","each","invoke","filter","map","some","indexOf","contains","getRandom","getLast"];Function.extend({monitorModelEvents:function(listener,orig){var self=this;return orig=orig||this,!listener||!listener.fireEvent?this:function(type,args,delay){self.apply(orig,arguments),listener.getModelByCID(orig.cid)&&listener.fireEvent(type,Array.flatten([orig,args]),delay)}}});var Collection=Epitome.Collection=new Class({Implements:[Options,Events],model:Epitome.Model,_models:[],initialize:function(models,options){return this.setOptions(options),models&&this.setUp(models),this.id=this.options.id||String.uniqueID(),this.fireEvent("ready")},setUp:function(models){return models=Array.from(models),Array.each(models,this.addModel.bind(this)),this.addEvent("destroy",this.removeModel.bind(this)),this},addModel:function(model,replace){var exists;return typeOf(model)=="object"&&!instanceOf(model,this.model)&&(model=new this.model(model)),model.cid=model.cid||model.get("id")||String.uniqueID(),exists=this.getModelByCID(model.cid),exists&&replace!==!0?this.fireEvent("add:error",model):(exists&&replace===!0&&(this._models[this._models.indexOf(model)]=model),model.fireEvent=Function.monitorModelEvents.apply(model.fireEvent,[this,model]),this._models.push(model),model.collections.include(this),this.length=this._models.length,this.fireEvent("add",[model,model.cid]).fireEvent("reset",[model,model.cid]))},removeModel:function(models){var self=this;return models=Array.from(models),Array.each(models,function(model){model.collections.erase(self),model.collections.length||delete model.fireEvent,Array.erase(self._models,model),self.length=self._models.length,self.fireEvent("remove",[model,model.cid])}),this.fireEvent("reset",[models])},get:function(what){return this[what]},getModelByCID:function(cid){var last=null;return this.some(function(el){return el.cid==cid&&(last=el)}),last},getModelById:function(id){var last=null;return this.some(function(el){return el.get("id")==id&&(last=el)}),last},getModel:function(index){return this._models[index]},toJSON:function(){var getJSON=function(model){return model.toJSON()};return Array.map(this._models,getJSON)},empty:function(){return this.removeModel(this._models),this.fireEvent("empty")},sort:function(how){if(!how)return this._models.sort(),this.fireEvent("sort");if(typeof how=="function")return this.model.sort(how),this.fireEvent("sort");var type="asc",pseudos=how.split(":"),key=pseudos[0],c=function(a,b){return a<b?-1:a>b?1:0};return pseudos[1]&&(type=pseudos[1]),this._models.sort(function(a,b){var ak=a.get(key),bk=b.get(key),cm=c(ak,bk),map={asc:cm,desc:-cm};return typeof map[type]=="undefined"&&(type="asc"),map[type]}),this.fireEvent("sort")},reverse:function(){return Array.reverse(this._models),this.fireEvent("sort")}});Array.each(methodMap,function(method){Collection.implement(method,function(){return Array.prototype[method].apply(this._models,arguments)})}),typeof define=="function"&&define.amd?define("epitome-collection",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome-collection"):exports.Epitome,noUrl="no-urlRoot-set",eventPseudo="fetch:";Epitome.Collection.Sync=new Class({Extends:Epitome.Collection,options:{urlRoot:noUrl},initialize:function(models,options){this.setupSync(),this.parent(models,options)},setupSync:function(){var self=this,rid=0,incrementRequestId=function(){rid++};return this.getRequestId=function(){return rid+1},this.request=new Request.JSON({link:"chain",url:this.options.urlRoot,emulation:this.options.emulateREST,onRequest:incrementRequestId,onCancel:function(){this.removeEvents(eventPseudo+rid)},onSuccess:function(responseObj){responseObj=self.parse&&self.parse(responseObj),self.fireEvent(eventPseudo+rid,[[responseObj]])},onFailure:function(){self.fireEvent(eventPseudo+"error",[this.options.method,this.options.url,this.options.data])}}),this},parse:function(resp){return resp},fetch:function(refresh){return this._throwAwayEvent(function(models){refresh?(this.empty(),Array.each(models,this.addModel.bind(this))):this.processModels(models),this.fireEvent("fetch",[models])}),this.request.get(),this},processModels:function(models){var self=this;Array.each(models,function(model){var exists=model.id&&self.getModelById(model.id);exists?exists.set(model):self.addModel(model)})},_throwAwayEvent:function(callback){var eventName=eventPseudo+this.getRequestId(),self=this,throwAway={};if(!callback||typeof callback!="function")return;return throwAway[eventName]=function(responseObj){callback.apply(self,responseObj),self.removeEvents(throwAway)},this.addEvents(throwAway)}.protect()}),typeof define=="function"&&define.amd?define("epitome-collection-sync",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome;Epitome.Template=new Class({options:{evaluate:/<%([\s\S]+?)%>/g,normal:/<%=([\s\S]+?)%>/g,noMatch:/.^/,escaper:/\\|'|\r|\n|\t|\u2028|\u2029/g,unescaper:/\\(\\|'|r|n|t|u2028|u2029)/g},Implements:[Options],initialize:function(options){this.setOptions(options);var unescaper=this.options.unescaper,escapes=this.escapes={"\\":"\\","'":"'",r:"\r",n:"\n",t:" ",u2028:"\u2028",u2029:"\u2029"};return Object.each(escapes,function(value,key){this[value]=key},escapes),this.unescape=function(code){return code.replace(unescaper,function(match,escape){return escapes[escape]})},this},template:function(str,data){var o=this.options,escapes=this.escapes,unescape=this.unescape,noMatch=o.noMatch,escaper=o.escaper,template,source=["var __p=[],print=function(){__p.push.apply(__p,arguments);};","with(obj||{}){__p.push('",str.replace(escaper,function(match){return"\\"+escapes[match]}).replace(o.normal||noMatch,function(match,code){return"',\nobj['"+unescape(code)+"'],\n'"}).replace(o.evaluate||noMatch,function(match,code){return"');\n"+unescape(code)+"\n;__p.push('"}),"');\n}\nreturn __p.join('');"].join(""),render=new Function("obj","_",source);return data?render(data):(template=function(data){return render.call(this,data)},template.source="function(obj){\n"+source+"\n}",template)}}),typeof define=="function"&&define.amd?define("epitome-template",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome-template"):exports.Epitome;Epitome.View=new Class({Implements:[Options,Events],element:null,collection:null,model:null,options:{template:"",events:{}},initialize:function(options){return options&&options.collection&&(this.setCollection(options.collection),delete options.collection),options&&options.model&&(this.setModel(options.model),delete options.model),this.setOptions(options),this.options.element&&(this.setElement(this.options.element,this.options.events),delete this.options.element),this.fireEvent("ready")},setElement:function(el,events){return this.element&&this.detachEvents()&&this.destroy(),this.element=document.id(el),events&&this.attachEvents(events),this},setCollection:function(collection){var self=this,eventProxy=function(type){return function(){self.fireEvent(type+":collection",arguments)}};return instanceOf(collection,Epitome.Collection)&&(this.collection=collection,this.collection.addEvents({change:eventProxy("change"),fetch:eventProxy("fetch"),add:eventProxy("add"),remove:eventProxy("remove"),sort:eventProxy("sort"),reset:eventProxy("reset")})),this},setModel:function(model){var self=this,eventProxy=function(type){return function(){self.fireEvent(type+":model",arguments)}};return instanceOf(model,Epitome.Model)&&(this.model=model,this.model.addEvents({change:eventProxy("change"),destroy:eventProxy("destroy"),empty:eventProxy("empty")})),this},attachEvents:function(events){var self=this;return Object.each(events,function(method,type){self.element.addEvent(type,function(e){self.fireEvent(method,arguments)})}),this.element.store("attachedEvents",events),this},detachEvents:function(){var events=this.element.retrieve("attachedEvents");return events&&this.element.removeEvents(events).eliminate("attachedEvents"),this},template:function(data,template){template=template||this.options.template;var compiler=this.Template||(this.Template=new Epitome.Template);return compiler.template(template,data)},render:function(){return this.fireEvent("render")},empty:function(soft){return soft?this.element.empty():this.element.set("html",""),this.fireEvent("empty")},dispose:function(){return this.element.dispose(),this.fireEvent("dispose")},destroy:function(){return this.element.destroy(),this.fireEvent("destroy")}}),typeof define=="function"&&define.amd?define("epitome-view",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this),function(exports){var Epitome=typeof require=="function"?require("./epitome"):exports.Epitome,hc="hashchange",hcSupported="on"+hc in window,eventHosts=[window,document],timer,getQueryString=function(queryString){var result={},re=/([^&=]+)=([^&]*)/g,m;while(m=re.exec(queryString))result[decodeURIComponent(m[1])]=decodeURIComponent(m[2]);return result};Element.Events.hashchange={onAdd:function(){var hash=location.hash,check=function(){if(hash==location.hash)return;hash=location.hash,eventHosts.invoke("fireEvent",hc,hash.indexOf("#")==0?hash.substr(1):hash)};hcSupported&&(window.onhashchange=check)||(timer=check.periodical(100))},onRemove:function(){hcSupported&&(window.onhashchange=null)||clearInterval(timer)}},Epitome.Router=new Class({Implements:[Options,Events],options:{triggerOnLoad:!0},routes:{},boundEvents:{},initialize:function(options){var self=this;this.setOptions(options),this.options.routes&&(this.routes=this.options.routes),window.addEvent(hc,function(e){var hash=location.hash,path=hash.split("?")[0],query=hash.split("?")[1]||"",notfound=!0,route;for(route in self.routes){var keys=[],regex=self.normalize(route,keys,!0,!1),found=regex.exec(path),routeEvent=!1;if(found){notfound=!1,self.req=found[0];var args=found.slice(1),param={};Array.each(args,function(a,i){typeof keys[i]!="undefined"&&(param[keys[i].name]=a)}),self.route=route,self.param=param||{},self.query=query&&getQueryString(query),routeEvent=self.routes[route],self.fireEvent("before",routeEvent),routeEvent&&self.$events[routeEvent]?(self.fireEvent(routeEvent+":before"),self.fireEvent(routeEvent,Object.values(self.param))):self.fireEvent("error",["Route",routeEvent,"is undefined"].join(" ")),self.fireEvent("after",routeEvent),routeEvent&&self.fireEvent(routeEvent+":after");break}}notfound&&self.fireEvent("undefined")}),this.fireEvent("ready"),this.options.triggerOnLoad&&window.fireEvent(hc)},navigate:function(route,trigger){location.hash==route&&trigger?window.fireEvent(hc):location.hash=route},normalize:function(path,keys,sensitive,strict){return path instanceof RegExp?path:(path=path.concat(strict?"":"/?").replace(/\/\(/g,"(?:/").replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g,function(_,slash,format,key,capture,optional){return keys.push({name:key,optional:!!optional}),slash=slash||"",[optional?"":slash,"(?:",optional?slash:"",(format||"")+(capture||format&&"([^/.]+?)"||"([^/]+?)")+")",optional||""].join("")}).replace(/([\/.])/g,"\\$1").replace(/\*/g,"(.*)"),new RegExp("^"+path+"$",sensitive?"":"i"))},addRoute:function(obj){return!obj||!obj.route||!obj.id||!obj.events?this.fireEvent("error","Please include route, id and events in the argument object when adding a route"):obj.id.length?this.routes[obj.route]?this.fireEvent("error",'Route "{route}" or id "{id}" already exists, aborting'.substitute(obj)):(this.routes[obj.route]=obj.id,this.addEvents(this.boundEvents[obj.route]=obj.events),this.fireEvent("route:add",obj)):this.fireEvent("error","Route id cannot be empty, aborting")},removeRoute:function(route){return!route||!this.routes[route]||!this.boundEvents[route]?this.fireEvent("error","Could not find route or route is not removable"):(this.removeEvents(this.boundEvents[route]),delete this.routes[route],delete this.boundEvents[route],this.fireEvent("route:remove",route))}}),typeof define=="function"&&define.amd?define("epitome-router",[],function(){return Epitome}):typeof module=="object"?module.exports=Epitome:exports.Epitome=Epitome}(this)
\ No newline at end of file
(function(window) {
'use strict';
var App = window.App;
// Your starting point. Enjoy the ride!
var todos = new App.TodoCollection(null, {
// a consistent collection if is needed if you want to use storage for a collection.
id: 'todos'
});
// populate from storage if available
todos.setUp(todos.retrieve());
// instantiate the todo list view
App.todoView = new App.TodoView({
// bind to the collection and its events and model events
collection: todos,
// encapsulating element to bind to
element: document.id('todo-list'),
// template to use
template: document.id('item-template').get('text')
});
// the main view is for the footer/stats/controls
App.mainView = new App.MainView({
// also bound to the same collection but with a different output logic.
collection: todos,
// encapsulating element to bind to
element: document.id('todoapp'),
// stats template from DOM
template: document.id('stats-template').get('text')
});
// the pseudo controller via Epitome.Router
App.router = new Epitome.Router({
routes: {
'': 'init',
'#!/': 'applyFilter',
'#!/:filter': 'applyFilter'
},
onInit: function() {
// we want to always have a state
this.navigate('#!/');
},
onApplyFilter: function(filter) {
// the filter is being used by the todo collection and view.
// when false, the whole collection is being passed.
todos.filterType = filter || false;
// render as per current filter
App.todoView.render();
// fix up the links quickie.
var self = this;
document.getElements('#filters li a').each(function(link) {
link.set('class', link.get('href') == self.req ? 'selected' : '');
});
}
});
})(window);
\ No newline at end of file
;(function(window) {
'use strict';
window.App = window.App || {};
// a collection that holds the todos
App.TodoCollection = new Class({
// normal collection or Collection.Sync
Extends: Epitome.Collection,
// enable storage methods, namespaced as collection.
Implements: Epitome.Storage.localStorage('collection'),
// base model class prototype
model: App.Todo,
todoFilter: function(model) {
// references the filterType which the controller sets
return this.filterType === false ? true : model.get('completed') == this.filterType;
}
});
}(window));
;(function(window) {
'use strict';
window.App = window.App || {};
// base structure for the todos themselves
App.Todo = new Class({
Extends: Epitome.Model,
options: {
defaults: {
completed: 'active',
title: ''
}
}
});
// a collection that holds the todos
App.TodoCollection = new Class({
Extends: Epitome.Collection,
Implements: Epitome.Storage.sessionStorage('collection'),
model: App.Todo
});
}(window));
This source diff could not be displayed because it is too large. You can view the blob instead.
;(function(window) {
'use strict';
window.App = window.App || {};
App.TodoView = new Class({
// a view abstraction bound to collection that displays the current view list based upon known data.
// normal view
Extends: Epitome.View,
// not API, but a wrapper property
tagName: 'li',
options: {
// added to group when editing
editingClass: 'editing',
// mask to bind to
input: 'input.edit',
// eavesdrop on these events
events: {
'blur:relay(input.edit)': 'update',
'click:relay(input.toggle)': 'statusChange',
'keypress:relay(input.edit)': 'handleKeypress',
'click:relay(button.destroy)': 'removeItem',
'dblclick:relay(li)': 'editing'
},
// define actual event handlers
onReady: function() {
// initial view
this.render();
},
// when collection changes, save the data to storage and re-render
"onChange:collection": function(model) {
this.collection.store();
this.render();
},
// when models get removed, re-render
"onRemove:collection": function(model) {
this.collection.store();
this.render();
},
// when sort is applied, re-render
"onSort:collection": function() {
this.collection.store();
this.render();
},
// when a new model is added, re-render
"onAdd:collection": function(model) {
this.collection.store();
this.render();
},
// handler for the edit event
onEditing: function(e, el) {
e && e.stop && e.stop();
el.addClass(this.options.editingClass);
el.getElement(this.options.input).focus();
},
// fired when editing ends
onUpdate: function(e, el) {
var p = el.getParent('li').removeClass(this.options.editingClass),
value = el.get('value').trim();
if (!value.length) {
// the render method stores the model into the element, get it and remove
this.collection.removeModel(p.retrieve('model'));
return;
}
p.retrieve('model').set('title', value);
},
// handler for clicks on the checkboxes
onStatusChange: function(e, el) {
var p = el.getParent('li'),
done = !!el.get('checked') ? 'completed' : 'active';
p.retrieve('model').set('completed', done);
},
// when the X is pressed, drop the model
onRemoveItem: function(e, el) {
e && e.stop && e.stop();
// the render method stores the model into the element, get it and remove
this.collection.removeModel(el.getParent('li').retrieve('model'));
}
},
render: function() {
// main render method, will also fire onRender
var todos = new Elements(),
self = this;
// empty the container.
this.empty();
// the route controller works with the todoFilter to help determine what we render.
this.collection.filter(this.collection.todoFilter.bind(this.collection)).each(function(model) {
var obj = model.toJSON(),
li = new Element(self.tagName).toggleClass('completed', obj.completed == 'completed').store('model', model);
// help the template to avoid slower logic in the template layer
obj.completedCheckbox = obj.completed == 'completed' ? 'checked="checked"' : '';
// compile template and store resulting element in our Elements collection
todos.push(li.set('html', self.template(obj)));
});
// inject the elements collection into the container element
this.element.adopt(todos);
// propagate the render event.
this.parent();
return this;
}
});
}(window));
\ No newline at end of file
;(function(window) {
'use strict';
window.App = window.App || {};
App.MainView = new Class({
// main view (presenter) encapsulating the app itself.
Extends: Epitome.View,
options: {
// eavesdrop on these events
events: {
'change:relay(#new-todo)': 'addTodo',
'keypress:relay(#new-todo)': 'handleKeypress',
'click:relay(#toggle-all)': 'toggleAll',
'click:relay(#clear-completed)': 'clearCompleted',
'click:relay(#filters.a)': 'setFilters'
},
// pass on some options for later func
newTodo: 'new-todo',
footer: 'footer',
filters: '#filters li a',
toggleAll: 'toggle-all',
onToggleAll: function(e, el) {
// all todos will change their models to the new completed value
var state = el.get('checked') ? 'completed' : 'active';
this.collection.each(function(model) {
model.set('completed', state);
});
},
onHandleKeypress: function(e, el) {
// on enter, submit.
if (e.key == 'enter')
this.addTodo();
// om esc, reset
if (e.key == 'esc')
this.newTodo.set('value', '').blur();
},
onClearCompleted: function() {
// because removing a model re-indexes so we don't get a sparse array, cannot apply that in a normal loop.
var toRemove = this.collection.filter(function(model) {
return model.get('completed') == 'completed';
});
// removeModel actually supports a single model or an array of models as arguments.
this.collection.removeModel(toRemove);
this.render();
},
onAddTodo: function() {
// go to method
this.addTodo();
},
'onChange:collection': function() {
// also, re-render on change of collection
this.render();
},
'onAdd:collection': function() {
// when adding, re-render.
this.render();
}
},
initialize: function(options) {
// call default view constructor.
this.parent(options);
// store some pointers to static elements
this.newTodo = document.id(this.options.newTodo);
this.footer = document.id(this.options.footer);
this.toggleAll = document.id(this.options.toggleAll);
// draw it.
this.render();
},
addTodo: function() {
// adding a new model when data exists
var val = this.newTodo.get('value').trim();
if (val.length) {
this.collection.addModel({
title: val,
completed: 'active'
});
}
// clear the input
this.newTodo.set('value', '');
},
render: function() {
// main method to output everything. well. the footer anyway.
// work out what we have remaining and what is complete
var remaining = 0,
completed = this.collection.filter(function(el) {
var status = el.get('completed') == 'completed';
if (!status)
remaining++;
return status;
}).length;
// output footer
this.footer.set('html', this.template({
completed: completed,
remaining: remaining
}));
// auto-correct the toggle-all checkbox with the new stats.
this.toggleAll.set('checked', this.collection.length ? !remaining : false);
}
});
}(window));
\ No newline at end of file
# Template • [TodoMVC](http://todomvc.com)
## Getting Started
Read the [App Specification](https://github.com/addyosmani/todomvc/wiki/App-Specification) before touching the template.
## Need help?
Feel free to [contact us](https://github.com/sindresorhus) if you have any questions or need help with the template.
## Credit
Created by [Sindre Sorhus](http://sindresorhus.com)
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment