Commit 5690847f authored by weepy's avatar weepy Committed by Sindre Sorhus

Add new app o_O

parent 894a653d
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>o_O • TodoMVC</title>
<link rel="stylesheet" href="../../../assets/base.css">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>Todos</h1>
<input id="new-todo" data-bind="value: current; enterKey: add" placeholder="What needs to be done?">
</header>
<section id="main" data-bind="visible: todos.count">
<div>
<input id="toggle-all" type="checkbox" data-bind="value: allCompleted;">
<label for="toggle-all">Mark all as complete</label>
</div>
<ul id="todo-list" data-bind="foreach: todos">
<li data-bind="class: klass; visible; dblclick: startEditing">
<div class="view">
<input class="toggle" type="checkbox" data-bind="value: completed">
<label data-bind="text: title"></label>
<button class="destroy" data-bind="click: remove"></button>
</div>
<input class="edit" data-bind="value: title; enterKey: stopEditing; blur: stopEditing" />
</li>
</ul>
</section>
<footer id="footer" data-bind="visible: todos.count">
<span id="todo-count">
<strong data-bind="text: remainingCount"></strong>
<span class="word" data-bind="text: pluralize('item', remainingCount())"></span> left
</span>
<ul id="filters" >
<li><a href="#/">All</a></li>
<li><a href="#/active">Active</a></li>
<li><a href="#/completed">Completed</a></li>
</ul>
<button id="clear-completed" data-bind="click: removeCompleted; visible: completedCount">
Clear completed
(<span data-bind='text: completedCount'></span>)
</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Created by <a href="http://weepy.github.com">weepy (Jonah Fox)</a></p>
</footer>
<script src="../../../assets/jquery.min.js"></script>
<script src="js/lib/o_O.js" type="text/javascript"></script>
<script src="js/lib/o_O.router.js" type="text/javascript"></script>
<script src="js/app.js"></script>
</body>
</html>
//a custom binding to handle the enter key
o_O.bindings.enterKey = function( func, $el ) {
var ENTER_KEY = 13;
var context = this
$el.keyup(function(e) {
if( e.keyCode === ENTER_KEY )
func.call(context);
})
}
o_O.bindingTypes.enterKey = 'outbound'
// represents a single todo item
var Todo = o_O.model.extend({
title: '',
completed: false
},
{
initialize: function() {
this.editing = o_O(false)
},
startEditing: function() {
this.editing( true )
var self = this
setTimeout(function() {
$(self.el).parent().find('input.edit').focus().select()
}, 0)
},
stopEditing: function() {
var text = $.trim( this.title() )
text
? this.title( text )
: this.remove()
this.editing( false )
},
remove: function() {
todoapp.todos.remove( this )
},
visible: function() {
var filter = todoapp.filter(),
completed = this.completed()
return filter == '' || (filter == 'completed' && completed) || (filter == 'active' && !completed)
},
klass: function() {
if(this.editing())
return 'editing'
if(this.completed())
return 'completed'
else
return ''
}
}
);
// main application
var TodoApp = o_O.model.extend({
current: "",
completedCount: 0,
filter: ''
}, {
initialize: function() {
var self = this
self.todos = o_O.array( this.todos() )
this.todos.on('set:completed set:title add remove', function() {
var completed = self.todos.filter(function(todo) {
return todo.completed()
})
self.completedCount( completed.length )
self.persist()
})
this.remainingCount = o_O(function() {
return self.todos.count() - self.completedCount()
})
// writeable computed observable
// handles marking all complete/incomplete
// or retrieving if this is true
this.allCompleted = o_O(function(v) {
if(arguments.length == 0) {
return self.remainingCount() == 0
}
self.todos.each(function(todo) {
todo.completed( v )
})
return v
})
},
add: function() {
var text = $.trim( this.current() );
if( text ) {
this.todos.unshift( Todo({title: text}) );
this.current( "" )
}
},
removeCompleted: function () {
this.todos.remove( function(todo) {
return todo.completed()
})
return false
},
persist: function() {
localStorage[ 'todos-o_O' ] = JSON.stringify( this.todos.toJSON() )
},
// adds an `s` where necessary
pluralize: function( word, count ) {
return word + (count === 1 ? "" : "s");
}
}
);
function main() {
// load todos
var todos = []
try {
todos = JSON.parse( localStorage['todos-o_O'] );
}
catch(e) { }
// create models
for( var i=0; i < todos.length; i++ )
todos[ i ] = Todo.create( todos[i] )
// create app
window.todoapp = TodoApp( {todos: todos} )
// bind to DOM element
todoapp.bind('#todoapp')
// setup Routing
o_O.router()
.add('*filter', function(filt) {
todoapp.filter(filt)
$( '#filters a' )
.removeClass( 'selected' )
.filter( "[href='#/" + filt + "']" )
.addClass( 'selected' )
})
.start()
}
// kick it off
main();
This diff is collapsed.
/*
* Router
* ======
*
* Very simple router using #hashes
* designed to work with o_O
*/
;(function() {
var namedParam = /:\w+/g,
splatParam = /\*\w+/g,
escapeRegExp = /[-[\]{}()+?.,\\^$|#\s]/g,
routeStripper = /^[#\/]/;
function regexRoute(str) {
str = str.replace(escapeRegExp, "\\$&")
.replace(namedParam, "([^\/]+)")
.replace(splatParam, "(.*?)")
return new RegExp('^' + str + '$')
}
o_O.router = o_O.model.extend({}, {
initialize: function() {
this.routes = []
},
add: function(param, handler) {
if(param === 404) {
this.routing_404 = {
handler: handler
}
}
else {
this.routes.push({
regex: regexRoute(param),
handler: handler
})
}
return this
},
runRouting: function(hash) {
var routes = []
for(var i = 0; i < this.routes.length; i++) {
var route = this.routes[i];
if(route.regex.test(hash))
routes.push(route)
}
for(var i = 0; i < routes.length; i++) {
var params = route.regex.exec(hash).slice(1)
this.runRoute(routes[i], params)
}
if(routes.length == 0 && this.routing_404)
this.runRoute(this.routing_404, [])
},
runRoute: function(route, params) {
if(typeof route.handler == 'string') {
params.unshift(route.handler)
this.emit.apply(this, params)
}
else
route.handler.apply(null, params)
},
getHash: function() {
var match = window.location.href.match(/#(.*)$/);
return match ? match[1] : '';
},
go: function() {
this.location = this.getHash().replace(routeStripper, '');
this.runRouting(this.location)
},
start: function() {
var self = this;
this.go()
$(window).bind('hashchange', function() {
self.go()
})
},
redirect: function(url, changeUrlHash) {
if(changeUrlHash === false)
this.runRouting(url)
else {
var hash = "#!" + url
document.location.hash == hash
? this.go()
: document.location.hash = hash
}
}
})
o_O.extend(o_O.router.prototype, o_O.Events)
}).call(this)
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