Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge pull request #235 from DimitarChristoff/labs-epitome

initial labs for epitome
  • Loading branch information...
commit 90bb5cdcc50f285f5217dad93cf9b1399d692844 2 parents 0952622 + f9445c3
@addyosmani addyosmani authored
View
1  labs/architecture-examples/epitome/css/app.css
@@ -0,0 +1 @@
+/* base.css overrides */
View
84 labs/architecture-examples/epitome/index.html
@@ -0,0 +1,84 @@
+<!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>
View
1  labs/architecture-examples/epitome/js/Epitome-min.js
@@ -0,0 +1 @@
+(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)
View
73 labs/architecture-examples/epitome/js/app.js
@@ -0,0 +1,73 @@
+(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);
View
26 labs/architecture-examples/epitome/js/collections/todo-collection.js
@@ -0,0 +1,26 @@
+;(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));
View
32 labs/architecture-examples/epitome/js/models/todo-model.js
@@ -0,0 +1,32 @@
+;(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));
View
5,977 labs/architecture-examples/epitome/js/mootools-core.js
5,977 additions, 0 deletions not shown
View
127 labs/architecture-examples/epitome/js/views/todo-list.js
@@ -0,0 +1,127 @@
+;(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));
View
129 labs/architecture-examples/epitome/js/views/todo-main.js
@@ -0,0 +1,129 @@
+;(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));
View
17 labs/architecture-examples/epitome/readme.md
@@ -0,0 +1,17 @@
+# 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)
Please sign in to comment.
Something went wrong with that request. Please try again.