Commit 60406854 authored by Sindre Sorhus's avatar Sindre Sorhus

Knockout: Convert back to tab indentation

parent 0318c185
<!DOCTYPE html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible"> <meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible">
<title>Knockout.js • TodoMVC</title> <title>Knockout.js • TodoMVC</title>
<link href="../../assets/base.css" rel="stylesheet"> <link href="../../assets/base.css" rel="stylesheet">
<!--[if IE]> <!--[if IE]>
<script src="../../assets/ie.js"></script> <script src="../../assets/ie.js"></script>
<![endif]--> <![endif]-->
</head> </head>
<body> <body>
<section id="todoapp"> <section id="todoapp">
<header id="header"> <header id="header">
<h1>todos</h1> <h1>todos</h1>
<input id="new-todo" autofocus data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?" type="text"> <input id="new-todo" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?" autofocus>
</header> </header>
<section id="main" data-bind="visible: todos().length"> <section id="main" data-bind="visible: todos().length">
<input id="toggle-all" data-bind="checked: allCompleted" type="checkbox"> <input id="toggle-all" data-bind="checked: allCompleted" type="checkbox">
<label for="toggle-all">Mark all as complete</label> <label for="toggle-all">Mark all as complete</label>
<ul id="todo-list" data-bind="foreach: filteredTodos"> <ul id="todo-list" data-bind="foreach: filteredTodos">
<li data-bind="css: { completed: completed, editing: editing }"> <li data-bind="css: { completed: completed, editing: editing }">
<div class="view" data-bind="event: { dblclick: $root.editItem }"> <div class="view" data-bind="event: { dblclick: $root.editItem }">
<input class="toggle" data-bind="checked: completed" type="checkbox"> <input class="toggle" data-bind="checked: completed" type="checkbox">
<label data-bind="text: title"></label> <label data-bind="text: title"></label>
<button class="destroy" data-bind="click: $root.remove"></button> <button class="destroy" data-bind="click: $root.remove"></button>
</div> </div>
<input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selectAndFocus: editing, event: { blur: $root.stopEditing }"> <input class="edit" data-bind="value: title, valueUpdate: 'afterkeydown', enterKey: $root.stopEditing, selectAndFocus: editing, event: { blur: $root.stopEditing }">
</li> </li>
</ul> </ul>
</section> </section>
<footer id="footer" data-bind="visible: completedCount() || remainingCount()"> <footer id="footer" data-bind="visible: completedCount() || remainingCount()">
<span id="todo-count"> <span id="todo-count">
<strong data-bind="text: remainingCount">1</strong> <strong data-bind="text: remainingCount">0</strong>
<span data-bind="text: getLabel( remainingCount )"></span> left <span data-bind="text: getLabel( remainingCount )"></span> left
</span> </span>
<ul id="filters"> <ul id="filters">
<li> <li>
<a data-bind="css: { selected: showMode() == 'all' }" href="#/all">All</a> <a data-bind="css: { selected: showMode() == 'all' }" href="#/all">All</a>
</li> </li>
<li> <li>
<a data-bind="css: { selected: showMode() == 'active' }" href="#/active">Active</a> <a data-bind="css: { selected: showMode() == 'active' }" href="#/active">Active</a>
</li> </li>
<li> <li>
<a data-bind="css: { selected: showMode() == 'completed' }" href="#/completed">Completed</a> <a data-bind="css: { selected: showMode() == 'completed' }" href="#/completed">Completed</a>
</li> </li>
</ul> </ul>
<button id="clear-completed" data-bind="visible: completedCount, click: removeCompleted">Clear completed (<span data-bind="text: completedCount"></span>)</button> <button id="clear-completed" data-bind="visible: completedCount, click: removeCompleted">
</footer> Clear completed (<span data-bind="text: completedCount"></span>)
</section> </button>
<footer id="info"> </footer>
<span data-bind="visible: todos().length">Double-click to edit a todo.</span> </section>
<p>Original Knockout version from <a href="https://github.com/ashish01/knockoutjs-todos">Ashish Sharma</a></p> <footer id="info">
<p>Rewritten to use Knockout 2.0 and standard template by <a href="http://knockmeout.net">Ryan Niemeyer</a></p> <span data-bind="visible: todos().length">Double-click to edit a todo</span>
<p>Patches/fixes for cross-browser compat: <a href="http://twitter.com/addyosmani">Addy Osmani</a></p> <p>Original Knockout version from <a href="https://github.com/ashish01/knockoutjs-todos">Ashish Sharma</a></p>
</footer> <p>Rewritten to use Knockout 2.0 and standard template by <a href="http://knockmeout.net">Ryan Niemeyer</a></p>
<script src="../../assets/base.js"></script> <p>Patches/fixes for cross-browser compat: <a href="http://twitter.com/addyosmani">Addy Osmani</a></p>
<script src="js/lib/knockout.min.js"></script> </footer>
<!--Crossroad.js --> <script src="../../assets/base.js"></script>
<script src="../../assets/signals.min.js"></script> <script src="js/lib/knockout.min.js"></script>
<script src="../../assets/crossroads.min.js"></script> <!--Crossroad.js -->
<!-- / --> <script src="../../assets/signals.min.js"></script>
<script src="js/app.js"></script> <script src="../../assets/crossroads.min.js"></script>
</body> <!-- / -->
<script src="js/app.js"></script>
</body>
</html> </html>
\ No newline at end of file
(function() { (function() {
'use strict'; 'use strict';
var ENTER_KEY = 13; var ENTER_KEY = 13;
// trim polyfill // trim polyfill
if(!String.prototype.trim) { if(!String.prototype.trim) {
String.prototype.trim = function() { String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, ''); return this.replace(/^\s+|\s+$/g, '');
}; };
} }
// a custom binding to handle the enter key (could go in a separate library) // a custom binding to handle the enter key (could go in a separate library)
ko.bindingHandlers.enterKey = { ko.bindingHandlers.enterKey = {
init: function(element, valueAccessor, allBindingsAccessor, data) { init: function(element, valueAccessor, allBindingsAccessor, data) {
var wrappedHandler, newValueAccessor; var wrappedHandler, newValueAccessor;
// wrap the handler with a check for the enter key // wrap the handler with a check for the enter key
wrappedHandler = function(data, event) { wrappedHandler = function(data, event) {
if(event.keyCode === ENTER_KEY) { if(event.keyCode === ENTER_KEY) {
valueAccessor().call(this, data, event); valueAccessor().call(this, data, event);
} }
}; };
// create a valueAccessor with the options that we would want to pass to the event binding // create a valueAccessor with the options that we would want to pass to the event binding
newValueAccessor = function() { newValueAccessor = function() {
return { return {
keyup: wrappedHandler keyup: wrappedHandler
}; };
}; };
// call the real event binding's init function // call the real event binding's init function
ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data); ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data);
} }
}; };
// wrapper to hasfocus that also selects text and applies focus async // wrapper to hasfocus that also selects text and applies focus async
ko.bindingHandlers.selectAndFocus = { ko.bindingHandlers.selectAndFocus = {
init: function(element, valueAccessor, allBindingsAccessor) { init: function(element, valueAccessor, allBindingsAccessor) {
ko.bindingHandlers.hasfocus.init(element, valueAccessor, allBindingsAccessor); ko.bindingHandlers.hasfocus.init(element, valueAccessor, allBindingsAccessor);
ko.utils.registerEventHandler(element, 'focus', function() { ko.utils.registerEventHandler(element, 'focus', function() {
element.select(); element.select();
}); });
}, },
update: function(element, valueAccessor) { update: function(element, valueAccessor) {
ko.utils.unwrapObservable(valueAccessor()); // for dependency ko.utils.unwrapObservable(valueAccessor()); // for dependency
// ensure that element is visible before trying to focus // ensure that element is visible before trying to focus
setTimeout(function() { setTimeout(function() {
ko.bindingHandlers.hasfocus.update(element, valueAccessor); ko.bindingHandlers.hasfocus.update(element, valueAccessor);
}, 0); }, 0);
} }
}; };
// represent a single todo item // represent a single todo item
var Todo = function(title, completed) { var Todo = function(title, completed) {
this.title = ko.observable(title); this.title = ko.observable(title);
this.completed = ko.observable(completed); this.completed = ko.observable(completed);
this.editing = ko.observable(false); this.editing = ko.observable(false);
}; };
// our main view model // our main view model
var ViewModel = function(todos) { var ViewModel = function(todos) {
var self = this; var self = this;
// map array of passed in todos to an observableArray of Todo objects // map array of passed in todos to an observableArray of Todo objects
self.todos = ko.observableArray(ko.utils.arrayMap(todos, function(todo) { self.todos = ko.observableArray(ko.utils.arrayMap(todos, function(todo) {
return new Todo(todo.title, todo.completed); return new Todo(todo.title, todo.completed);
})); }));
// store the new todo value being entered // store the new todo value being entered
self.current = ko.observable(); self.current = ko.observable();
self.showMode = ko.observable('all'); self.showMode = ko.observable('all');
self.filteredTodos = ko.computed(function() { self.filteredTodos = ko.computed(function() {
switch(self.showMode()) { switch(self.showMode()) {
case 'active': case 'active':
return self.todos().filter(function(todo) { return !todo.completed(); }); return self.todos().filter(function(todo) { return !todo.completed(); });
case 'completed': case 'completed':
return self.todos().filter(function(todo) { return todo.completed(); }); return self.todos().filter(function(todo) { return todo.completed(); });
default: default:
return self.todos(); return self.todos();
} }
}); });
// add a new todo, when enter key is pressed // add a new todo, when enter key is pressed
self.add = function() { self.add = function() {
var current = self.current().trim(); var current = self.current().trim();
if(current) { if(current) {
self.todos.push(new Todo(current)); self.todos.push(new Todo(current));
self.current(''); self.current('');
} }
}; };
// remove a single todo // remove a single todo
self.remove = function(todo) { self.remove = function(todo) {
self.todos.remove(todo); self.todos.remove(todo);
}; };
// remove all completed todos // remove all completed todos
self.removeCompleted = function() { self.removeCompleted = function() {
self.todos.remove(function(todo) { self.todos.remove(function(todo) {
return todo.completed(); return todo.completed();
}); });
}; };
// edit an item // edit an item
self.editItem = function(item) { self.editItem = function(item) {
item.editing(true); item.editing(true);
}; };
// stop editing an item. Remove the item, if it is now empty // stop editing an item. Remove the item, if it is now empty
self.stopEditing = function(item) { self.stopEditing = function(item) {
item.editing(false); item.editing(false);
if(!item.title().trim()) { if(!item.title().trim()) {
self.remove(item); self.remove(item);
} }
}; };
// count of all completed todos // count of all completed todos
self.completedCount = ko.computed(function() { self.completedCount = ko.computed(function() {
return ko.utils.arrayFilter(self.todos(), function(todo) { return ko.utils.arrayFilter(self.todos(), function(todo) {
return todo.completed(); return todo.completed();
}).length; }).length;
}); });
// count of todos that are not complete // count of todos that are not complete
self.remainingCount = ko.computed(function() { self.remainingCount = ko.computed(function() {
return self.todos().length - self.completedCount(); return self.todos().length - self.completedCount();
}); });
// writeable computed observable to handle marking all complete/incomplete // writeable computed observable to handle marking all complete/incomplete
self.allCompleted = ko.computed({ self.allCompleted = ko.computed({
//always return true/false based on the done flag of all todos //always return true/false based on the done flag of all todos
read: function() { read: function() {
return !self.remainingCount(); return !self.remainingCount();
}, },
// set all todos to the written value (true/false) // set all todos to the written value (true/false)
write: function(newValue) { write: function(newValue) {
ko.utils.arrayForEach(self.todos(), function(todo) { ko.utils.arrayForEach(self.todos(), function(todo) {
// set even if value is the same, as subscribers are not notified in that case // set even if value is the same, as subscribers are not notified in that case
todo.completed(newValue); todo.completed(newValue);
}); });
} }
}); });
// helper function to keep expressions out of markup // helper function to keep expressions out of markup
self.getLabel = function(count) { self.getLabel = function(count) {
return ko.utils.unwrapObservable(count) === 1 ? 'item' : 'items'; return ko.utils.unwrapObservable(count) === 1 ? 'item' : 'items';
}; };
// internal computed observable that fires whenever anything changes in our todos // internal computed observable that fires whenever anything changes in our todos
ko.computed(function() { ko.computed(function() {
// store a clean copy to local storage, which also creates a dependency on the observableArray and all observables in each item // store a clean copy to local storage, which also creates a dependency on the observableArray and all observables in each item
localStorage.setItem('todos-knockout', ko.toJSON(self.todos)); localStorage.setItem('todos-knockout', ko.toJSON(self.todos));
}).extend({ }).extend({
throttle: 500 throttle: 500
}); // save at most twice per second }); // save at most twice per second
}; };
// check local storage for todos // check local storage for todos
var todos = ko.utils.parseJson(localStorage.getItem('todos-knockout')); var todos = ko.utils.parseJson(localStorage.getItem('todos-knockout'));
// bind a new instance of our view model to the page // bind a new instance of our view model to the page
var viewModel = new ViewModel(todos || []) var viewModel = new ViewModel(todos || [])
ko.applyBindings(viewModel); ko.applyBindings(viewModel);
//setup crossroads //setup crossroads
crossroads.addRoute('all', function() { viewModel.showMode('all'); }); crossroads.addRoute('all', function() { viewModel.showMode('all'); });
crossroads.addRoute('active', function() { viewModel.showMode('active'); }); crossroads.addRoute('active', function() { viewModel.showMode('active'); });
crossroads.addRoute('completed', function() { viewModel.showMode('completed'); }); crossroads.addRoute('completed', function() { viewModel.showMode('completed'); });
window.onhashchange = function() { window.onhashchange = function() {
crossroads.parse(location.hash.replace("#", "")); crossroads.parse(location.hash.replace("#", ""));
}; };
crossroads.parse(location.hash.replace("#", "")); crossroads.parse(location.hash.replace("#", ""));
})(); })();
\ 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