TodoMVC - Knockout.js

https://github.com/addyosmani/todomvc/tree/master/architecture-examples/knockoutjs

by nkerkin

HTML

<script src="http://cloud.github.com/downloads/SteveSanderson/knockout/knockout-2.0.0.js"></script>
<script src="https://raw.github.com/appendto/amplify/master/store/amplify.store.js"></script>
<script src="https://github.com/douglascrockford/JSON-js/blob/master/json2.js"></script>
<div id="todoapp">
        <div class="title">
            <h1>Todos</h1>
        </div>
        <div class="content">
            <div id="create-todo">
                <input id="new-todo" data-bind="value: current, valueUpdate: 'afterkeydown', enterKey: add" placeholder="What needs to be done?" />
                <span class="ui-tooltip-top" data-bind="visible: showTooltip" style="display: none;">Press Enter to save this task</span>
            </div>
            <div id="todos">
                <div data-bind="visible: todos().length">
                    <input id="check-all" class="check" type="checkbox" data-bind="checked: allCompleted" />
                    <label for="check-all">Mark all as complete</label>
                </div>
                <ul id="todo-list" data-bind="foreach: todos">
                    <li data-bind="css: { editing: editing }">
                        <div class="todo" data-bind="css: { done : done }">
                            <div class="display">
                                <input class="check" type="checkbox" data-bind="checked: done" />
                                <div class="todo-content" data-bind="text: content, event: { dblclick: edit }" style="cursor: pointer;"></div>
                                <span class="todo-destroy" data-bind="click: $root.remove"></span>
                            </div>
                            <div class="edit">
                                <input class="todo-input" data-bind="value: content, valueUpdate: 'afterkeydown', enterKey: stopEditing, event: { blur: stopEditing }"/>
                            </div>
                        </div>
                    </li>
                </ul>
       ...

CSS

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, font, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td {
    margin: 0;
    padding: 0;
    border: 0;
    outline: 0;
    font-weight: inherit;
    font-style: inherit;
    font-size: 100%;
    font-family: inherit;
    vertical-align: baseline;
}
body {
    line-height: 1;
    color: black;
    background: white;
}
ol, ul {
    list-style: none;
}
a img {
    border: none;
}

html {
    background: #eeeeee;
}
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;
    background: white;
    padding: 20px;
    -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
    -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
    -o-box-shadow: rgba(0, 0, 0, 0.2) 0 5px 6px 0;
    box-shadow: rgba(0, 0, 0, 0.2) 0 5px 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 h1 {
    font-size: 36px;
    font-weight: bold;
    text-align: center;
    padding: 20px 0 30px 0;
    line-height: 1;
}

#create-todo {
    position: relative;
}
#create-todo input {
    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;
}
#create-todo input::-webkit-input-placeholder {
    font-style: italic;
}
#create-todo span {
   ...

JavaScript

(function() {
    //a custom binding to handle the enter key (could go in a separate library)
    ko.bindingHandlers.enterKey = {
        init: function(element, valueAccessor, allBindingsAccessor, data) {
            var wrappedHandler, newValueAccessor;

            //wrap the handler with a check for the enter key
            wrappedHandler = function(data, event) {
                if (event.keyCode === 13) {
                    valueAccessor().call(this, data, event);
                }
            };

            //create a valueAccessor with the options that we would want to pass to the event binding
            newValueAccessor = function() {
                return {
                    keyup: wrappedHandler
                };
            };

            //call the real event binding's init function
            ko.bindingHandlers.event.init(element, newValueAccessor, allBindingsAccessor, data);
        }
    };


    //represent a single todo item
    var Todo = function(content, done) {
        this.content = ko.observable(content);
        this.done = ko.observable(done);
        this.editing = ko.observable(false);
    };

    //can place methods on prototype, as there can be many todos
    ko.utils.extend(Todo.prototype, {
        edit: function() {
            this.editing(true);
        },
        stopEditing: function() {
            this.editing(false);
        }
    });


    //our main view model
    var ViewModel = function(todos) {
        var self = this;
        //map array of passed in todos to an observableArray of Todo objects
        self.todos = ko.observableArray(ko.utils.arrayMap(todos, function(todo) {
            return new Todo(todo.content, todo.done);
        }));

        //store the new todo value being entered
        self.current = ko.observable();

        //add a new todo, when enter key is pressed
        self.add = function(data, event) {
            var newTodo = new Todo(self.current());
           ...