From 3a47769a7e00249cf943b47a2f96ec7a5ed5951e Mon Sep 17 00:00:00 2001 From: Jeremy Ashkenas Date: Mon, 25 Oct 2010 15:33:33 -0400 Subject: [PATCH] Nice improvements to the rendering ... more templates, less javascript. --- Rakefile | 2 +- docs/backbone.html | 2 +- docs/todos.html | 3 +- examples/todos/index.html | 31 ++++++---- examples/todos/todos.css | 14 ++--- examples/todos/todos.js | 84 +++++++++++++--------------- test/vendor/backbone.localStorage.js | 53 +++++++++--------- 7 files changed, 95 insertions(+), 94 deletions(-) diff --git a/Rakefile b/Rakefile index 2b2223791..8754882d7 100644 --- a/Rakefile +++ b/Rakefile @@ -9,7 +9,7 @@ end desc "build the docco documentation" task :doc do - system "docco backbone.js" + system "docco backbone.js examples/todos/todos.js" end desc "run JavaScriptLint on the source" diff --git a/docs/backbone.html b/docs/backbone.html index 5f53fe7c2..648ede874 100644 --- a/docs/backbone.html +++ b/docs/backbone.html @@ -1,4 +1,4 @@ - backbone.js

backbone.js

(c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
+      backbone.js           

backbone.js

(c) 2010 Jeremy Ashkenas, DocumentCloud Inc.
 Backbone may be freely distributed under the MIT license.
 For all details and documentation:
 http://documentcloud.github.com/backbone
diff --git a/docs/todos.html b/docs/todos.html
index df9d142a6..970d3de16 100644
--- a/docs/todos.html
+++ b/docs/todos.html
@@ -1,4 +1,4 @@
-      todos.js           

todos.js

$(function(){

Todo

  window.Todo = Backbone.Model.extend({
+      todos.js           

todos.js

$(function(){

Todo

  window.Todo = Backbone.Model.extend({
 
     parse: function(resp) {
       return resp.model;
@@ -77,6 +77,7 @@
     changed: function(e) {
       if (e.keyCode == 13) {
         this.model.save({content: this.updateInput.val()});
+        this.$('.todo').removeClass("editing");
       }
     },
 
diff --git a/examples/todos/index.html b/examples/todos/index.html
index c30273a57..a6beed27f 100644
--- a/examples/todos/index.html
+++ b/examples/todos/index.html
@@ -30,15 +30,7 @@ 

Todos

    -
    - - 0 - items left. - - - Clear completed - -
    +
    @@ -55,10 +47,10 @@

    Todos

    Jérôme Gravel-Niquet - + + diff --git a/examples/todos/todos.css b/examples/todos/todos.css index fabc3c3fb..ff29efefd 100644 --- a/examples/todos/todos.css +++ b/examples/todos/todos.css @@ -128,14 +128,14 @@ body { margin-top: 10px; } /* line 56 */ -#todo-list .todo { +#todo-list li { padding: 15px 20px 15px 0; position: relative; font-size: 24px; border-bottom: 1px solid #cccccc; } -#todoapp .content ul#todo-list .todo:after { +#todoapp .content ul#todo-list li:after { content: "\0020"; display: block; height: 0; @@ -144,7 +144,7 @@ body { visibility: hidden; } /* line 62 */ -#todo-list .editing { +#todo-list li.editing { padding: 0; border-bottom: 0; } @@ -157,14 +157,14 @@ body { } /* line 65 */ #todoapp .content ul#todo-list .editing input { - width: 466px; + width: 444px; font-size: 24px; font-family: inherit; margin: 0; - line-height: 1.4em; + line-height: 1.6em; border: 0; outline: none; - padding: 7px 7px 8px; + padding: 10px 7px 0px 27px; 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; @@ -215,7 +215,6 @@ body { } /* line 102 */ #todoapp .content #todo-stats .todo-count { - display: none; float: left; } /* line 105 */ @@ -226,7 +225,6 @@ body { /* line 108 */ #todoapp .content #todo-stats .todo-clear { float: right; - display: none; } /* line 111 */ #todoapp .content #todo-stats .todo-clear a { diff --git a/examples/todos/todos.js b/examples/todos/todos.js index 15d1218d0..0004887f0 100644 --- a/examples/todos/todos.js +++ b/examples/todos/todos.js @@ -9,6 +9,11 @@ $(function(){ htmlId: function() { return "todo-" + this.id; + }, + + remove: function() { + this.destroy(); + $(this.view.el).remove(); } }); @@ -21,7 +26,7 @@ $(function(){ // Returns all done todos. done: function() { - return this.select(function(todo){ + return this.filter(function(todo){ return todo.get('done'); }); }, @@ -37,6 +42,10 @@ $(function(){ parse: function(resp) { return resp.models; + }, + + pluralize: function(count) { + return count == 1 ? 'item' : 'items'; } }); @@ -45,14 +54,14 @@ $(function(){ window.TodoView = Backbone.View.extend({ - tagName: "li", + tagName: "li", - template: _.template($('#item-template').html()), + template: _.template($('#item-template').html()), events: { - "click input[type=checkbox]": "markAsDone", + "click .check" : "toggleDone", "dblclick div.todo-content" : "edit", - "click span.todo-destroy" : "destroy", + "click span.todo-destroy" : "remove", "keypress .todo-input" : "changed" }, @@ -60,6 +69,7 @@ $(function(){ _.bindAll(this, 'render'); this.model.bind('change', this.render); this.el.id = this.model.htmlId(); + this.model.view = this; }, render: function() { @@ -74,28 +84,23 @@ $(function(){ this.$('.todo-input').val(content); }, - markAsDone: function() { - this.model.save({ done: !this.model.get("done") }); + toggleDone: function() { + this.model.save({done: !this.model.get("done")}); }, edit: function() { - this.$('.todo').addClass("editing"); - this.updateInput = this.$("input[type=text]"); + $(this.el).addClass("editing"); }, changed: function(e) { if (e.keyCode == 13) { - this.model.save({content: this.updateInput.val()}); + this.model.save({content: this.$(".todo-input").val()}); + $(this.el).removeClass("editing"); } }, - destroy: function() { - var thisView = this; - this.model.destroy({ - success: function(){ - $(thisView.el).remove(); - } - }); + remove: function() { + this.model.remove(); } }); @@ -104,43 +109,38 @@ $(function(){ el: $("#todoapp"), + template: _.template($('#stats-template').html()), + events: { - "keypress input#new-todo": "createIfEnter", - "keyup input#new-todo": "showTooltip", - "click span.todo-clear": "clearCompleted" + "keypress #new-todo": "createIfEnter", + "keyup #new-todo": "showTooltip", + "click .todo-clear a": "clearCompleted" }, initialize: function() { - _.bindAll(this, 'addTodo', 'clearCompleted', 'showTooltip', 'createIfEnter', 'analyzeTodos'); - - Todos.bind('add', this.addTodo); + _.bindAll(this, 'addTodo', 'clearCompleted', 'showTooltip', 'createIfEnter', 'render'); this.list = $("#todo-list"); this.newInput = $("#new-todo"); this.tooltip = this.$(".ui-tooltip-top"); + Todos.bind('add', this.addTodo); + Todos.bind('all', this.render); + Todos.fetch({ success: _.bind(function(coll) { _.each(coll.models, this.addTodo); }, this) }); - - this.analyzeTodos(); - - Todos.bind("add", this.analyzeTodos); - Todos.bind("remove", this.analyzeTodos); - Todos.bind("change", this.analyzeTodos); }, - analyzeTodos: function() { - var doneCount = Todos.done().length; - var todoCount = Todos.length; - var totalCount = todoCount - doneCount; - - this.$(".number").text(totalCount); - this.$(".word").text(totalCount == 1 ? 'item' : 'items'); - this.$("span.todo-count").css({display: todoCount > 0 ? "inline" : "none"}); - this.$("span.todo-clear").css({display: doneCount > 0 ? "inline" : "none"}); + render: function() { + var done = Todos.done().length; + this.$('#todo-stats').html(this.template({ + done: done, + total: Todos.length, + remaining: Todos.length - done + })); }, addTodo: function(todo) { @@ -174,12 +174,8 @@ $(function(){ }, clearCompleted: function() { - thisView = this; - _.each(Todos.done(), function(todo){ - todo.destroy({success: function(todo){ - thisView.$("#todo-"+todo.id).remove(); - }}); - }); + _.each(Todos.done(), function(todo){ todo.remove(); }); + return false; } }); diff --git a/test/vendor/backbone.localStorage.js b/test/vendor/backbone.localStorage.js index 135becc32..ce8af5078 100644 --- a/test/vendor/backbone.localStorage.js +++ b/test/vendor/backbone.localStorage.js @@ -9,18 +9,18 @@ function guid() { Storage.prototype.setObject = function(key, value) { this.setItem(key, JSON.stringify(value)); -} +}; Storage.prototype.getObject = function(key) { return this.getItem(key) && JSON.parse(this.getItem(key)); -} +}; var Store = function(name) { this.name = name; }; _.extend(Store.prototype, { - + create: function(model) { this.data = localStorage.getObject(this.name); @@ -29,14 +29,14 @@ _.extend(Store.prototype, { } if (!model.id) model.attributes.id = guid(); - + this.data.push(model); localStorage.setObject(this.name, this.data); return {model: model, status: "success"}; }, - + update: function(model) { var newData = []; var succeeded = false; @@ -50,21 +50,21 @@ _.extend(Store.prototype, { newData = _.map(this.data, function(i) { if (i.id == model.id) { succeeded = true; - return model + return model; } else { - return i + return i; } }); - + if (!succeeded) { - this.create(model) + this.create(model); } else { localStorage.setObject(this.name, newData); } return {model: model, status: "success"}; }, - + find: function(model) { var record; @@ -87,17 +87,17 @@ _.extend(Store.prototype, { return {error: "Record Not Found.", status: "error"}; } }, - + findAll: function() { this.data = localStorage.getObject(this.name); - + if (!this.data) { this.data = []; } - + return {models: this.data, status: "success"}; }, - + destroy: function(model) { var newData = []; var recordKey; @@ -116,7 +116,7 @@ _.extend(Store.prototype, { _.breakLoop(); } }); - + if (succeeded) this.data.splice(recordKey, 1); localStorage.setObject(this.name, this.data); @@ -124,27 +124,24 @@ _.extend(Store.prototype, { if (succeeded) { return {model: model, status: "success"}; } else { - return {error: "Record Not Found.", status: "error"} + return {error: "Record Not Found.", status: "error"}; } } - + }); Backbone.sync = function(method, model, success, error) { - + var resp; var store = model.localStore ? model.localStore : model.collection.localStore; - - if (method === "read") { - resp = model.id ? store.find(model) : store.findAll(); - } else if (method === "create") { - resp = store.create(model); - } else if (method === "update") { - resp = store.update(model); - } else if (method === "delete") { - resp = store.destroy(model); + + switch (method) { + case "read": resp = model.id ? store.find(model) : store.findAll(); break; + case "create": resp = store.create(model); break; + case "update": resp = store.update(model); break; + case "delete": resp = store.destroy(model); break; } - + if (resp.status == "success") { success(resp); } else if (resp.status == "error" && error) {