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
This diff is collapsed.
(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 diff is collapsed.
;(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