Commit 32fdff67 authored by Addy Osmani's avatar Addy Osmani

Merge pull request #26 from sindresorhus/master

Adding jQuery-only todo example. This will be more clearly made available in TodoMVC 0.3 along with the other new examples coming soon.
parents b304ba29 67233f91
html, body {
margin: 0;
padding: 0;
}
body {
font-family: "Helvetica Neue", helvetica, arial, sans-serif;
font-size: 14px;
line-height: 1.4em;
background: #eeeeee;
color: #333333;
}
#todoapp {
width: 480px;
margin: 0 auto 40px auto;
background: white;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#todoapp {
padding: 20px;
}
#todoapp h1 {
font-size: 36px;
font-weight: bold;
text-align: center;
padding: 0 0 10px 0;
}
#todoapp input[type="text"] {
width: 466px;
font-size: 24px;
font-family: inherit;
line-height: 1.4em;
border: 0;
outline: none;
padding: 6px;
border: 1px solid #999999;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
}
#todoapp input::-webkit-input-placeholder {
font-style: italic;
}
#todoapp .items {
margin: 10px 0;
padding: 0;
list-style: none;
}
#todoapp .item {
padding: 15px 20px 15px 0;
position: relative;
font-size: 24px;
border-bottom: 1px solid #cccccc;
}
#todoapp .item.done span {
color: #777777;
text-decoration: line-through;
}
#todoapp .item .destroy {
position: absolute;
right: 10px;
top: 16px;
display: none;
cursor: pointer;
width: 20px;
height: 20px;
background: url(../img/destroy.png) no-repeat center center;
}
#todoapp .item:hover .destroy {
display: block;
}
#todoapp .item .edit { display: none; }
#todoapp .item.editing .edit { display: block; }
#todoapp .item.editing .view { display: none; }
#todoapp footer {
display: none;
margin: 20px -20px -20px -20px;
overflow: hidden;
color: #555555;
background: #f4fce8;
border-top: 1px solid #ededed;
padding: 0 20px;
line-height: 36px;
-moz-border-radius: 0 0 5px 5px;
-o-border-radius: 0 0 5px 5px;
-webkit-border-radius: 0 0 5px 5px;
border-radius: 0 0 5px 5px;
}
#todoapp .clear {
display: none;
float: right;
line-height: 20px;
text-decoration: none;
background: rgba(0, 0, 0, 0.1);
color: #555555;
font-size: 11px;
margin-top: 8px;
margin-bottom:8px;
padding: 0 10px 1px;
-moz-border-radius: 12px;
-webkit-border-radius: 12px;
-o-border-radius: 12px;
border-radius: 12px;
-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
cursor: pointer;
}
#todoapp .clear:hover {
background: rgba(0, 0, 0, 0.15);
-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
-o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
}
#todoapp .clear:active {
position: relative;
top: 1px;
}
#todoapp .count span {
font-weight: bold;
}
#instructions {
width: 520px;
margin: 10px auto;
color: #777777;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#instructions a {
color: #336699;
}
#credits {
width: 520px;
margin: 30px auto;
color: #999;
text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
text-align: center;
}
#credits a {
color: #888;
}
\ No newline at end of file
<!doctype html>
<html>
<head>
<title>jQuery</title>
<link rel="stylesheet" href="css/app.css">
<script src="js/json2.js"></script>
<script src="js/jquery.min.js"></script>
<script src="js/jquery.tmpl.js"></script>
<script src="js/store.js"></script>
<script src="js/app.js"></script>
<script type="text/x-jquery-tmpl" id="todo-template">
<li class="item {{if done}}done{{/if}}" data-id="${id}">
<div class="view" title="Double click to edit...">
<input type="checkbox" {{if done}}checked="checked"{{/if}}>
<span>${title}</span>
<a class="destroy"></a>
</div>
<div class="edit">
<input type="text" value="${title}">
</div>
</li>
</script>
</head>
<body>
<div id="todoapp">
<h1>Todos</h1>
<form>
<input type="text" placeholder="What needs to be done?">
</form>
<ul class="items"></ul>
<footer>
<a class="clear">Clear completed</a>
<div class="count">
<span class="countVal"></span> items left
</div>
</footer>
</div>
<div id='instructions'>
Double-click to edit a todo.
</div>
<div id="credits">
Created by <a href="http://sindresorhus.com">Sindre Sorhus</a>.
</div>
</body>
</html>
\ No newline at end of file
// Array Remove - By John Resig (MIT Licensed)
Array.prototype.remove = function(from, to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
/*
By Sindre Sorhus
sindresorhus.com
*/
jQuery(function($){
var Utils = {
// https://gist.github.com/823878
uuid: function() {
var uuid = "", i, random;
for ( i = 0; i < 32; i++ ) {
random = Math.random() * 16 | 0;
if ( i == 8 || i == 12 || i == 16 || i == 20 ) {
uuid += "-";
}
uuid += (i == 12 ? 4 : (i == 16 ? (random & 3 | 8) : random)).toString(16);
}
return uuid;
}
};
var App = {
init: function() {
this.element = $('#todoapp');
this.todoList = $('.items');
// localStorage support
this.store = new Store('todoapp');
this.todos = this.store.get('todos') || [];
this.bindEvents();
this.render();
},
render: function() {
var template = $('#todo-template').tmpl( this.todos );
this.todoList.html( template );
this.renderActiveTodoCount();
this.store.set('todos', this.todos);
// Only show the footer when there are at least one todo.
$('footer').toggle( !!this.todos.length );
// Only show the clear button when there are done todos.
$('.clear').toggle( !!( this.todos.length - this.renderActiveTodoCount() ) );
},
bindEvents: function() {
var elem = this.element,
list = this.todoList;
elem.on('click', '.clear', this.destroyDone);
elem.on('submit', 'form', this.create);
list.on('change', 'input[type="checkbox"]', this.toggle);
list.on('dblclick', '.view', this.edit);
list.on('keypress', 'input[type="text"]', this.blurOnEnter);
list.on('blur', 'input[type="text"]', this.update);
list.on('click', '.destroy', this.destroy);
},
renderActiveTodoCount: function() {
var count = 0;
$.each(this.todos, function(i, val) {
if ( !val.done ) {
count++;
}
});
$('.countVal').text( count );
return count;
},
destroyDone: function() {
// Reverse loop; since we are dynamically removing items from the todos array
for ( var i = App.todos.length; i--; ) {
if ( App.todos[i].done ) {
App.todos.remove(i);
}
}
App.render();
},
// Accepts an element from inside the ".item" div, and returns the corresponding todo in the todos array.
getTodo: function(elem, callback) {
var id = $(elem).closest('.item').data('id');
$.each(this.todos, function(i, val) {
if ( val.id === id ) {
callback.apply(App, arguments);
return false;
}
});
},
create: function(e) {
e.preventDefault();
var $input = $(this).find('input'),
inputVal = $input.val();
if ( !inputVal ) {
return;
}
App.todos.push({
title: inputVal,
id: Utils.uuid(),
done: false
});
$input.val('');
App.render();
},
toggle: function() {
App.getTodo(this, function(i, val) {
val.done = !val.done;
});
App.render();
},
edit: function() {
$(this).closest('.item').addClass('editing').find('.edit input').focus();
},
blurOnEnter: function(e) {
if ( e.keyCode === 13 ) {
e.target.blur();
}
},
update: function() {
var newVal = $(this).removeClass('editing').val();
App.getTodo(this, function(i) {
this.todos[i].title = newVal;
});
App.render();
},
destroy: function() {
App.getTodo(this, function(i) {
this.todos.remove(i);
this.render();
});
}
};
window.TodoApp = App.init();
});
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
//
// Copyright (c) 2011 Frank Kohlhepp
// https://github.com/frankkohlhepp/store-js
// License: MIT-license
//
(function () {
var Store = this.Store = function (name, defaults, watcherSpeed) {
var that = this;
this.name = name;
this.listeners = {};
// Set defaults
if (defaults) {
for (var key in defaults) {
if (defaults.hasOwnProperty(key) && this.get(key) === undefined) {
this.set(key, defaults[key]);
}
}
}
// Fake events
var fireEvent = function (name, value) {
([name, "*"]).each(function (selector) {
if (that.listeners[selector]) {
that.listeners[selector].each(function (callback) {
callback(value, name, that.name);
});
}
});
};
var oldObj = this.toObject();
var standby = function () { watcher(true); };
var watcher = function (skipCheck) {
if (Object.keys(that.listeners).length !== 0) {
var newObj = that.toObject();
if (!skipCheck) {
for (var key in newObj) {
if (newObj.hasOwnProperty(key) && newObj[key] !== oldObj[key]) {
fireEvent(key, newObj[key]);
}
}
for (var key in oldObj) {
if (oldObj.hasOwnProperty(key) && !newObj.hasOwnProperty(key)) {
fireEvent(key, newObj[key]);
}
}
}
oldObj = newObj;
setTimeout(watcher, (watcherSpeed || 300));
} else {
setTimeout(standby, 1000);
}
};
standby();
};
Store.__proto__ = function Empty() {};
Store.__proto__.clear = function () {
localStorage.clear();
};
Store.prototype.get = function (name) {
var value = localStorage.getItem("store." + this.name + "." + name);
if (value === null) { return; }
try { return JSON.parse(value); } catch (e) { return null; }
};
Store.prototype.set = function (name, value) {
if (value === undefined) {
this.remove(name);
} else {
if (typeof value === "function") {
value = null;
} else {
try {
value = JSON.stringify(value);
} catch (e) {
value = null;
}
}
localStorage.setItem("store." + this.name + "." + name, value);
}
return this;
};
Store.prototype.remove = function (name) {
localStorage.removeItem("store." + this.name + "." + name);
return this;
};
Store.prototype.removeAll = function () {
var name = "store." + this.name + ".";
for (var i = (localStorage.length - 1); i >= 0; i--) {
if (localStorage.key(i).substring(0, name.length) === name) {
localStorage.removeItem(localStorage.key(i));
}
}
return this;
};
Store.prototype.toObject = function () {
var values = {};
var name = "store." + this.name + ".";
for (var i = (localStorage.length - 1); i >= 0; i--) {
if (localStorage.key(i).substring(0, name.length) === name) {
var key = localStorage.key(i).substring(name.length);
var value = this.get(key);
if (value !== undefined) { values[key] = value; }
}
}
return values;
};
Store.prototype.fromObject = function (values, merge) {
if (!merge) { this.removeAll(); }
for (var key in values) {
if (values.hasOwnProperty(key)) {
this.set(key, values[key]);
}
}
return this;
};
Store.prototype.addEvent = function (selector, callback) {
if (!this.listeners[selector]) { this.listeners[selector] = []; }
this.listeners[selector].push(callback);
return this;
};
Store.prototype.removeEvent = function (selector, callback) {
for (var i = (this.listeners[selector].length - 1); i >= 0; i--) {
if (this.listeners[selector][i] === callback) { this.listeners[selector].splice(i, 1); }
}
if (this.listeners[selector].length === 0) { delete this.listeners[selector]; }
return 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