Commit fb3b2b08 authored by tosh shimayama's avatar tosh shimayama Committed by Sindre Sorhus

Closes #129: todo application made with Agilityjs.. Fixes #115

parent dea3bd5f
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Agility.js • TodoMVC</title>
<link rel="stylesheet" href="../../assets/base.css">
</head>
<body>
<section id="todoapp">
<header id="header">
<h1>todos</h1>
<input id="new-todo" type="text" data-bind="newtitle" placeholder="What needs to be done?" autofocus>
</header>
<section id="main" data-bind="class = mainStyle">
<input id="toggle-all" type="checkbox">
<label for="toggle-all">Mark all as completed</label>
<ul id="todo-list">
<li>
<div class="view">
<input class="toggle" type="checkbox" data-bind="completed">
<label data-bind="title"></label>
<button class="destroy"></button>
</div>
<input class="edit" type="text" data-bind="title">
</li>
</ul>
</section>
<footer id="footer" data-bind="class = mainStyle">
<span id="todo-count"><strong data-bind='todoCount'></strong> item<span data-bind='pluralizer'></span> 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>
<button id="clear-completed" data-bind="class = clearBtnStyle">Clear completed (<span data-bind="completedCount"></span>)</button>
</footer>
</section>
<footer id="info">
<p>Double-click to edit a todo</p>
<p>Template by <a href="http://sindresorhus.com">Sindre Sorhus</a></p>
<p>Created by <a href="http://github.com/tshm/todomvc/">Tosh Shimayama</a></p>
</footer>
<script src="../../assets/jquery.min.js"></script>
<script src="js/lib/agility.min.js"></script>
<script src="js/localstorage.js"></script>
<script src="js/app.js"></script>
</body>
</html>
(function( $, $$ ) {
'use strict';
var ENTER_KEY = 13;
// Hack of taking out html elements from DOM so that agility's view can use it.
// We need 'outerhtml' also, as agilityjs will append DOM, so removing it.
var drawHtml = function( selector ) {
return $(selector).remove().wrap('<div>').parent().html();
};
// Simple Two layer composition:
// individual 'todoitem' and 'app'lication which holds multiple todoitems.
$(function() {
// todo item
var todoitem = $$({
model: {
title: '',
completed: false
},
view: {
format: drawHtml('#todo-list li'),
style: '.hidden { display: none }'
},
controller: {
'change:completed': function() {
this.view.$().toggleClass( 'completed', this.model.get('completed') );
app.updateStatus();
},
'dblclick .view': function() {
this.view.$().addClass('editing');
this.view.$('.edit').select();
},
'click .destroy': function() {
this.destroy();
},
'create': function() {
this.view.$().toggleClass( 'completed', this.model.get('completed') );
},
'change': function() {
this.save();
},
'destroy': function() {
this.erase();
},
'change:title': function() {
this.view.$().removeClass('editing');
var title = this.model.get('title').trim();
if ( title ) {
this.model.set({
title: title
});
} else {
this.destroy();
}
}
}
}).persist( $$.adapter.localStorage, {
collection: 'todos-agilityjs'
});
// The main application which holds todo items.
var app = $$({
model: {
todoCount: '0',
pluralizer: '',
completedCount: '0',
newtitle: '',
mainStyle: '',
clearBtnStyle: ''
},
view: {
format: drawHtml('#todoapp'),
style: '.hidden { display: none }'
},
controller: {
'remove': function() {
this.updateStatus();
},
'append': function() {
this.updateStatus();
},
'keyup #new-todo': function( event ) {
var title = $('#new-todo').val().trim();
if ( event.which === ENTER_KEY && title ) {
var item = $$( todoitem, {
title: title
}).save();
this.append( item, '#todo-list' );
event.target.value = ''; // clear input field
}
},
'click #toggle-all': function() {
var ischecked = this.view.$('#toggle-all').prop('checked');
this.each(function( id, item ) {
item.model.set({
completed: ischecked
});
});
},
'click #clear-completed': function() {
this.each(function( id, item ) {
if ( item.model.get('completed') ) {
item.destroy();
}
});
}
},
// utility functions
updateStatus: function() {
// update counts
var count = this.size(),
completedCount = 0;
this.each(function( id, item ) {
if ( item.model.get('completed') ) {
completedCount++;
}
});
this.model.set({
todoCount: count - completedCount + '',
pluralizer: (count > 1 ? 's' : ''),
completedCount: completedCount + '',
mainStyle: (count === 0 ? 'hidden' : ''),
clearBtnStyle: (completedCount === 0 ? 'hidden' : '')
});
// update toggle-all checked status
$('#toggle-all').prop( 'checked', completedCount === count );
},
// filter handler
filters: {
'#/': function( item ) {
return true;
},
'#/active': function( item ) {
return !item.model.get('completed');
},
'#/completed': function( item ) {
return item.model.get('completed');
}
},
applyFilter: function( hash ) {
var isVisible = this.filters[hash];
this.each(function( id, item ) {
item.view.$().toggleClass( 'hidden', !isVisible( item ) );
});
}
}).persist();
$$.document.prepend( app );
// load from localStorage
app.gather( todoitem, 'append', '#todo-list' ).updateStatus();
// manual routing (not supported by agilityjs)
$(window).on( 'hashchange', function() {
var hash = location.hash;
app.applyFilter( hash );
$('#filters a').each(function() {
if ( hash === $(this).attr('href') ) {
$(this).addClass('selected');
} else {
$(this).removeClass('selected');
}
});
} );
if ( location.hash ) {
$(window).trigger('hashchange');
}
});
})( window.jQuery, window.agility );
This diff is collapsed.
// custom agilityjs adapter for localstorage
(function( $$, undefined ) {
'use strict';
$$.adapter.localStorage = function( _params ) {
var storageKey = (this._data.persist.baseUrl || '') + this._data.persist.collection,
storageStr = localStorage[storageKey],
items = (storageStr ? JSON.parse( storageStr ) : {});
//
if ( _params.type === 'GET' ) {
if ( _params.id !== undefined ) { // normal get
if ( typeof items[_params.id] === 'object' ) {
_params.success( items[_params.id] );
} else {
_params.error();
}
} else { // gather call
_params.success( items );
}
} else if ( _params.type === 'DELETE' ) {
delete items[_params.id];
localStorage[storageKey] = JSON.stringify( items );
} else if ( _params.type === 'PUT' || _params.type === 'POST' ) {
if ( _params.id === undefined ) {
_params.id = (new Date()).getTime();
_params.data.id = _params.id;
}
items[_params.id] = _params.data;
localStorage[storageKey] = JSON.stringify( items );
} else {
_params.error();
}
_params.complete();
};
})( window.agility );
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