<!doctype html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Document</title> <style> html, body { margin: 0; padding: 0; } body { font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif; line-height: 1.4em; background: #eeeeee; color: #333333; width: 520px; margin: 0 auto; -webkit-font-smoothing: antialiased; } #todoapp { background: #fff; padding: 20px; margin-bottom: 40px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0; -ms-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; -webkit-border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -ms-border-radius: 0 0 5px 5px; -o-border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px; } #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; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset; -ms-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; } #main { display: none; } #todo-list { margin: 10px 0; padding: 0; list-style: none; } #todo-list li { padding: 18px 20px 18px 0; position: relative; font-size: 24px; border-bottom: 1px solid #cccccc; } #todo-list li:last-child { border-bottom: none; } #todo-list li.done label { color: #777777; text-decoration: line-through; } #todo-list .destroy { position: absolute; right: 5px; top: 20px; display: none; cursor: pointer; width: 20px; height: 20px; background: orange; border-radius: 10px; } #todo-list li:hover .destroy { display: block; } #todo-list .destroy:hover { background-position: 0 -20px; } #todo-list li.editing { border-bottom: none; margin-top: -1px; padding: 0; } #todo-list li.editing:last-child { margin-bottom: -1px; } #todo-list li.editing .edit { display: block; width: 444px; padding: 13px 15px 14px 20px; margin: 0; } #todo-list li.editing .view { display: none; } #todo-list li .view label { word-break: break-word; } #todo-list li .edit { display: none; } #todoapp footer { display: none; margin: 0 -20px -20px -20px; overflow: hidden; color: #555555; background: #f4fce8; border-top: 1px solid #ededed; padding: 0 20px; line-height: 37px; -webkit-border-radius: 0 0 5px 5px; -moz-border-radius: 0 0 5px 5px; -ms-border-radius: 0 0 5px 5px; -o-border-radius: 0 0 5px 5px; border-radius: 0 0 5px 5px; } #clear-completed { 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; cursor: pointer; -webkit-border-radius: 12px; -moz-border-radius: 12px; -ms-border-radius: 12px; -o-border-radius: 12px; border-radius: 12px; -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0; -ms-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; } #clear-completed:hover { background: rgba(0, 0, 0, 0.15); -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0; -ms-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; } #clear-completed:active { position: relative; top: 1px; } #todo-count span { font-weight: bold; } #instructions { margin: 10px auto; color: #777777; text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; text-align: center; } #instructions a { color: #336699; } #credits { margin: 30px auto; color: #999; text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0; text-align: center; } #credits a { color: #888; } </style> </head> <body> <div id="todoapp"> <header> <h1>Todos</h1> <input id="new-todo" type="text" placeholder="What needs to be done?"> </header> <section id="main"> <input id="toggle-all" type="checkbox"> <label for="toggle-all">Mark all as complete</label> <ul id="todo-list"></ul> </section> <footer> <a id="clear-completed">Clear completed</a> <div id="todo-count"></div> </footer> </div> <div id="instructions"> Double-click to edit a todo. </div> <!-- Templates --> <script type="text/template" id="item-template"> <div class="view"> <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> /> <label><%- title %></label> <a class="destroy"></a> </div> <input class="edit" type="text" value="<%- title %>" /> </script> <script type="text/template" id="stats-template"> <% if (done) { %> <a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a> <% } %> <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div> </script> <script src="lib/json2.js"></script> <script src="lib/jquery-latest.js"></script> <script src="lib/underscore.js"></script> <script src="lib/backbone.js"></script> <script src="lib/backbone.localStorage.js"></script> <script src="js/demo20.js"></script> </body> </html>
$(function(){ var Todo = Backbone.Model.extend({ defaults: function() { return { title: "empty todo...", order: Todos.nextOrder(), done: false }; }, initialize: function() { if (!this.get("title")) { this.set({"title": this.defaults().title}); } }, toggle: function() { this.save({done: !this.get("done")}); } }); var TodoList = Backbone.Collection.extend({ model: Todo, localStorage: new Backbone.LocalStorage("todos-backbone"), done: function() { return this.filter(function(todo){ return todo.get('done'); }); }, remaining: function() { return this.without.apply(this, this.done()); }, nextOrder: function() { if (!this.length) return 1; return this.last().get('order') + 1; }, comparator: function(todo) { return todo.get('order'); } }); var Todos = new TodoList; var TodoView = Backbone.View.extend({ tagName: "li", template: _.template($('#item-template').html()), events: { "click .toggle" : "toggleDone", "dblclick .view" : "edit", "click a.destroy" : "clear", "keypress .edit" : "updateOnEnter", "blur .edit" : "close" }, initialize: function() { this.listenTo(this.model, 'change', this.render); this.listenTo(this.model, 'destroy', this.remove); }, render: function() { this.$el.html(this.template(this.model.toJSON())); this.$el.toggleClass('done', this.model.get('done')); this.input = this.$('.edit'); return this; }, toggleDone: function() { this.model.toggle(); }, edit: function() { this.$el.addClass("editing"); this.input.focus(); }, close: function() { var value = this.input.val(); if (!value) { this.clear(); } else { this.model.save({title: value}); this.$el.removeClass("editing"); } }, updateOnEnter: function(e) { if (e.keyCode == 13) this.close(); }, clear: function() { this.model.destroy(); } }); var AppView = Backbone.View.extend({ el: $("#todoapp"), statsTemplate: _.template($('#stats-template').html()), events: { "keypress #new-todo": "createOnEnter", "click #clear-completed": "clearCompleted", "click #toggle-all": "toggleAllComplete" }, initialize: function() { this.input = this.$("#new-todo"); this.allCheckbox = this.$("#toggle-all")[0]; this.listenTo(Todos, 'add', this.addOne); this.listenTo(Todos, 'reset', this.addAll); this.listenTo(Todos, 'all', this.render); this.footer = this.$('footer'); this.main = $('#main'); Todos.fetch(); }, render: function() { var done = Todos.done().length; var remaining = Todos.remaining().length; if (Todos.length) { this.main.show(); this.footer.show(); this.footer.html(this.statsTemplate({done: done, remaining: remaining})); } else { this.main.hide(); this.footer.hide(); } this.allCheckbox.checked = !remaining; }, addOne: function(todo) { var view = new TodoView({model: todo}); this.$("#todo-list").append(view.render().el); }, addAll: function() { Todos.each(this.addOne, this); }, createOnEnter: function(e) { if (e.keyCode != 13) return; if (!this.input.val()) return; Todos.create({title: this.input.val()}); this.input.val(''); }, clearCompleted: function() { _.invoke(Todos.done(), 'destroy'); return false; }, toggleAllComplete: function () { var done = this.allCheckbox.checked; Todos.each(function (todo) { todo.save({'done': done}); }); } }); var App = new AppView; });