Skip to content
This repository
Browse code

Added todos app example and 0.9.10 compat

  • Loading branch information...
commit 3c12cf6e54f9d92c65a771894d806dfb766f4b3c 1 parent 31fefb8
Jerome Gravel-Niquet authored January 27, 2013
1  CNAME
... ...
@@ -0,0 +1 @@
  1
+localtodos.com
BIN  destroy.png
82  index.html
... ...
@@ -0,0 +1,82 @@
  1
+<!DOCTYPE html>
  2
+<html lang="en">
  3
+
  4
+<head>
  5
+  <meta charset="utf-8">
  6
+  <title>Backbone.js Todos</title>
  7
+  <link rel="stylesheet" href="todos.css"/>
  8
+  <script type="text/javascript">
  9
+
  10
+    var _gaq = _gaq || [];
  11
+    _gaq.push(['_setAccount', 'UA-5201171-9']);
  12
+    _gaq.push(['_trackPageview']);
  13
+
  14
+    (function() {
  15
+      var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
  16
+      ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') +
  17
+  '.google-analytics.com/ga.js';
  18
+      var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  19
+    })();
  20
+
  21
+  </script>
  22
+</head>
  23
+
  24
+<body>
  25
+
  26
+  <div id="todoapp">
  27
+
  28
+    <header>
  29
+      <h1>Todos</h1>
  30
+      <input id="new-todo" type="text" placeholder="What needs to be done?">
  31
+    </header>
  32
+
  33
+    <section id="main">
  34
+      <input id="toggle-all" type="checkbox">
  35
+      <label for="toggle-all">Mark all as complete</label>
  36
+      <ul id="todo-list"></ul>
  37
+    </section>
  38
+
  39
+    <footer>
  40
+      <a id="clear-completed">Clear completed</a>
  41
+      <div id="todo-count"></div>
  42
+    </footer>
  43
+
  44
+  </div>
  45
+
  46
+  <div id="instructions">
  47
+    Double-click to edit a todo.
  48
+  </div>
  49
+
  50
+  <div id="credits">
  51
+    Created by
  52
+    <br />
  53
+    <a href="http://jgn.me/">J&eacute;r&ocirc;me Gravel-Niquet</a>.
  54
+    <br />Rewritten by: <a href="http://addyosmani.github.com/todomvc">TodoMVC</a>.
  55
+  </div>
  56
+
  57
+  <script src="spec/support/jquery.js"></script>
  58
+  <script src="spec/support/underscore.js"></script>
  59
+  <script src="spec/support/backbone.js"></script>
  60
+  <script src="backbone.localStorage.js"></script>
  61
+  <script src="todos.js"></script>
  62
+
  63
+  <!-- Templates -->
  64
+
  65
+  <script type="text/template" id="item-template">
  66
+    <div class="view">
  67
+      <input class="toggle" type="checkbox" <%= done ? 'checked="checked"' : '' %> />
  68
+      <label><%- title %></label>
  69
+      <a class="destroy"></a>
  70
+    </div>
  71
+    <input class="edit" type="text" value="<%- title %>" />
  72
+  </script>
  73
+
  74
+  <script type="text/template" id="stats-template">
  75
+    <% if (done) { %>
  76
+      <a id="clear-completed">Clear <%= done %> completed <%= done == 1 ? 'item' : 'items' %></a>
  77
+    <% } %>
  78
+    <div class="todo-count"><b><%= remaining %></b> <%= remaining == 1 ? 'item' : 'items' %> left</div>
  79
+  </script>
  80
+
  81
+  </body>
  82
+</html>
50  spec/localStorage_spec.js
@@ -308,56 +308,6 @@ describe("Without Backbone.localStorage", function(){
308 308
 
309 309
 });
310 310
 
311  
-describe("Backbone backwards compatibility", function(){
312  
-
313  
-  describe("Should result in an exception when in backbone is 0.9.9", function(){
314  
-    
315  
-      var Model = Backbone.Model.extend();
316  
-
317  
-      var Collection = Backbone.Collection.extend({
318  
-        model: Model,
319  
-        localStorage: new Backbone.LocalStorage("collectionStore")
320  
-      });
321  
-
322  
-      var collection = new Collection();
323  
-      var cacheLocalSync = Backbone.localSync;
324  
-      
325  
-      before(function() {
326  
-          Backbone.VERSION = "0.9.9";
327  
-      })
328  
-      
329  
-      it("result in an exception if fetch is called", function() {
330  
-          assert.throw(collection.fetch, /(has no method \'sync\'|undefined)/ );
331  
-      })
332  
-      
333  
-      it("should call with one parameter if in 0.9.9", function() {
334  
-            
335  
-            Backbone.localSync = function(method, model, options) {
336  
-                
337  
-                options.success = function() {
338  
-                    assert.equal(arguments.length,1, "Should only be called with one argument in backbone 0.9.9")
339  
-                }
340  
-                
341  
-                cacheLocalSync(method, model, options)
342  
-            }
343  
-         
344  
-        collection.fetch()
345  
-         
346  
-         
347  
-      })
348  
-      
349  
-      after(function() {
350  
-          Backbone.VERSION = "0.9.10";
351  
-          Backbone.localSync = cacheLocalSync;
352  
-      })      
353  
-
354  
-  });
355  
-  
356  
-  
357  
-
358  
-
359  
-});
360  
-
361 311
 
362 312
 // For some reason this is not ran when viewed in a browser
363 313
 // but it is ran when using `mocha-phantomjs`.
211  todos.css
... ...
@@ -0,0 +1,211 @@
  1
+html,
  2
+body {
  3
+	margin: 0;
  4
+	padding: 0;
  5
+}
  6
+
  7
+body {
  8
+	font: 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
  9
+	line-height: 1.4em;
  10
+	background: #eeeeee;
  11
+	color: #333333;
  12
+	width: 520px;
  13
+	margin: 0 auto;
  14
+	-webkit-font-smoothing: antialiased;
  15
+}
  16
+
  17
+#todoapp {
  18
+	background: #fff;
  19
+	padding: 20px;
  20
+	margin-bottom: 40px;
  21
+	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
  22
+	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
  23
+	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
  24
+	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
  25
+	box-shadow: rgba(0, 0, 0, 0.2) 0 2px 6px 0;
  26
+	-webkit-border-radius: 0 0 5px 5px;
  27
+	-moz-border-radius: 0 0 5px 5px;
  28
+	-ms-border-radius: 0 0 5px 5px;
  29
+	-o-border-radius: 0 0 5px 5px;
  30
+	border-radius: 0 0 5px 5px;
  31
+}
  32
+
  33
+#todoapp h1 {
  34
+	font-size: 36px;
  35
+	font-weight: bold;
  36
+	text-align: center;
  37
+	padding: 0 0 10px 0;
  38
+}
  39
+
  40
+#todoapp input[type="text"] {
  41
+	width: 466px;
  42
+	font-size: 24px;
  43
+	font-family: inherit;
  44
+	line-height: 1.4em;
  45
+	border: 0;
  46
+	outline: none;
  47
+	padding: 6px;
  48
+	border: 1px solid #999999;
  49
+	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
  50
+	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
  51
+	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
  52
+	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
  53
+	box-shadow: rgba(0, 0, 0, 0.2) 0 1px 2px 0 inset;
  54
+}
  55
+
  56
+#todoapp input::-webkit-input-placeholder {
  57
+	font-style: italic;
  58
+}
  59
+
  60
+#main {
  61
+	display: none;
  62
+}
  63
+
  64
+#todo-list {
  65
+	margin: 10px 0;
  66
+	padding: 0;
  67
+	list-style: none;
  68
+}
  69
+
  70
+#todo-list li {
  71
+	padding: 18px 20px 18px 0;
  72
+	position: relative;
  73
+	font-size: 24px;
  74
+	border-bottom: 1px solid #cccccc;
  75
+}
  76
+
  77
+#todo-list li:last-child {
  78
+	border-bottom: none;
  79
+}
  80
+
  81
+#todo-list li.done label {
  82
+	color: #777777;
  83
+	text-decoration: line-through;
  84
+}
  85
+
  86
+#todo-list .destroy {
  87
+	position: absolute;
  88
+	right: 5px;
  89
+	top: 20px;
  90
+	display: none;
  91
+	cursor: pointer;
  92
+	width: 20px;
  93
+	height: 20px;
  94
+	background: url(destroy.png) no-repeat;
  95
+}
  96
+
  97
+#todo-list li:hover .destroy {
  98
+  	display: block;
  99
+}
  100
+
  101
+#todo-list .destroy:hover {
  102
+  	background-position: 0 -20px;
  103
+}
  104
+
  105
+#todo-list li.editing {
  106
+	border-bottom: none;
  107
+	margin-top: -1px;
  108
+	padding: 0;
  109
+}
  110
+
  111
+#todo-list li.editing:last-child {
  112
+	margin-bottom: -1px;
  113
+}
  114
+
  115
+#todo-list li.editing .edit {
  116
+	display: block;
  117
+	width: 444px;
  118
+	padding: 13px 15px 14px 20px;
  119
+	margin: 0;
  120
+}
  121
+
  122
+#todo-list li.editing .view {
  123
+	display: none;
  124
+}
  125
+
  126
+#todo-list li .view label {
  127
+	word-break: break-word;
  128
+}
  129
+
  130
+#todo-list li .edit {
  131
+	display: none;
  132
+}
  133
+
  134
+#todoapp footer {
  135
+	display: none;
  136
+	margin: 0 -20px -20px -20px;
  137
+	overflow: hidden;
  138
+	color: #555555;
  139
+	background: #f4fce8;
  140
+	border-top: 1px solid #ededed;
  141
+	padding: 0 20px;
  142
+	line-height: 37px;
  143
+	-webkit-border-radius: 0 0 5px 5px;
  144
+	-moz-border-radius: 0 0 5px 5px;
  145
+	-ms-border-radius: 0 0 5px 5px;
  146
+	-o-border-radius: 0 0 5px 5px;
  147
+	border-radius: 0 0 5px 5px;
  148
+}
  149
+
  150
+#clear-completed {
  151
+	float: right;
  152
+	line-height: 20px;
  153
+	text-decoration: none;
  154
+	background: rgba(0, 0, 0, 0.1);
  155
+	color: #555555;
  156
+	font-size: 11px;
  157
+	margin-top: 8px;
  158
+	margin-bottom: 8px;
  159
+	padding: 0 10px 1px;
  160
+	cursor: pointer;
  161
+	-webkit-border-radius: 12px;
  162
+	-moz-border-radius: 12px;
  163
+	-ms-border-radius: 12px;
  164
+	-o-border-radius: 12px;
  165
+	border-radius: 12px;
  166
+	-webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  167
+	-moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  168
+	-ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  169
+	-o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  170
+	box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  171
+}
  172
+
  173
+#clear-completed:hover {
  174
+	background: rgba(0, 0, 0, 0.15);
  175
+	-webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  176
+	-moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  177
+	-ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  178
+	-o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  179
+	box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  180
+}
  181
+
  182
+#clear-completed:active {
  183
+	position: relative;
  184
+	top: 1px;
  185
+}
  186
+
  187
+#todo-count span {
  188
+	font-weight: bold;
  189
+}
  190
+
  191
+#instructions {
  192
+	margin: 10px auto;
  193
+	color: #777777;
  194
+	text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
  195
+	text-align: center;
  196
+}
  197
+
  198
+#instructions a {
  199
+	color: #336699;
  200
+}
  201
+
  202
+#credits {
  203
+	margin: 30px auto;
  204
+	color: #999;
  205
+	text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
  206
+	text-align: center;
  207
+}
  208
+
  209
+#credits a {
  210
+	color: #888;
  211
+}
243  todos.js
... ...
@@ -0,0 +1,243 @@
  1
+// An example Backbone application contributed by
  2
+// [Jérôme Gravel-Niquet](http://jgn.me/). This demo uses a simple
  3
+// [LocalStorage adapter](backbone-localstorage.html)
  4
+// to persist Backbone models within your browser.
  5
+
  6
+// Load the application once the DOM is ready, using `jQuery.ready`:
  7
+$(function(){
  8
+
  9
+  // Todo Model
  10
+  // ----------
  11
+
  12
+  // Our basic **Todo** model has `title`, `order`, and `done` attributes.
  13
+  var Todo = Backbone.Model.extend({
  14
+
  15
+    // Default attributes for the todo item.
  16
+    defaults: function() {
  17
+      return {
  18
+        title: "empty todo...",
  19
+        order: Todos.nextOrder(),
  20
+        done: false
  21
+      };
  22
+    },
  23
+
  24
+    // Ensure that each todo created has `title`.
  25
+    initialize: function() {
  26
+      if (!this.get("title")) {
  27
+        this.set({"title": this.defaults().title});
  28
+      }
  29
+    },
  30
+
  31
+    // Toggle the `done` state of this todo item.
  32
+    toggle: function() {
  33
+      this.save({done: !this.get("done")});
  34
+    }
  35
+
  36
+  });
  37
+
  38
+  // Todo Collection
  39
+  // ---------------
  40
+
  41
+  // The collection of todos is backed by *localStorage* instead of a remote
  42
+  // server.
  43
+  var TodoList = Backbone.Collection.extend({
  44
+
  45
+    // Reference to this collection's model.
  46
+    model: Todo,
  47
+
  48
+    // Save all of the todo items under the `"todos-backbone"` namespace.
  49
+    localStorage: new Backbone.LocalStorage("todos-backbone"),
  50
+
  51
+    // Filter down the list of all todo items that are finished.
  52
+    done: function() {
  53
+      return this.filter(function(todo){ return todo.get('done'); });
  54
+    },
  55
+
  56
+    // Filter down the list to only todo items that are still not finished.
  57
+    remaining: function() {
  58
+      return this.without.apply(this, this.done());
  59
+    },
  60
+
  61
+    // We keep the Todos in sequential order, despite being saved by unordered
  62
+    // GUID in the database. This generates the next order number for new items.
  63
+    nextOrder: function() {
  64
+      if (!this.length) return 1;
  65
+      return this.last().get('order') + 1;
  66
+    },
  67
+
  68
+    // Todos are sorted by their original insertion order.
  69
+    comparator: function(todo) {
  70
+      return todo.get('order');
  71
+    }
  72
+
  73
+  });
  74
+
  75
+  // Create our global collection of **Todos**.
  76
+  var Todos = new TodoList;
  77
+
  78
+  // Todo Item View
  79
+  // --------------
  80
+
  81
+  // The DOM element for a todo item...
  82
+  var TodoView = Backbone.View.extend({
  83
+
  84
+    //... is a list tag.
  85
+    tagName:  "li",
  86
+
  87
+    // Cache the template function for a single item.
  88
+    template: _.template($('#item-template').html()),
  89
+
  90
+    // The DOM events specific to an item.
  91
+    events: {
  92
+      "click .toggle"   : "toggleDone",
  93
+      "dblclick .view"  : "edit",
  94
+      "click a.destroy" : "clear",
  95
+      "keypress .edit"  : "updateOnEnter",
  96
+      "blur .edit"      : "close"
  97
+    },
  98
+
  99
+    // The TodoView listens for changes to its model, re-rendering. Since there's
  100
+    // a one-to-one correspondence between a **Todo** and a **TodoView** in this
  101
+    // app, we set a direct reference on the model for convenience.
  102
+    initialize: function() {
  103
+      this.listenTo(this.model, 'change', this.render);
  104
+      this.listenTo(this.model, 'destroy', this.remove);
  105
+    },
  106
+
  107
+    // Re-render the titles of the todo item.
  108
+    render: function() {
  109
+      this.$el.html(this.template(this.model.toJSON()));
  110
+      this.$el.toggleClass('done', this.model.get('done'));
  111
+      this.input = this.$('.edit');
  112
+      return this;
  113
+    },
  114
+
  115
+    // Toggle the `"done"` state of the model.
  116
+    toggleDone: function() {
  117
+      this.model.toggle();
  118
+    },
  119
+
  120
+    // Switch this view into `"editing"` mode, displaying the input field.
  121
+    edit: function() {
  122
+      this.$el.addClass("editing");
  123
+      this.input.focus();
  124
+    },
  125
+
  126
+    // Close the `"editing"` mode, saving changes to the todo.
  127
+    close: function() {
  128
+      var value = this.input.val();
  129
+      if (!value) {
  130
+        this.clear();
  131
+      } else {
  132
+        this.model.save({title: value});
  133
+        this.$el.removeClass("editing");
  134
+      }
  135
+    },
  136
+
  137
+    // If you hit `enter`, we're through editing the item.
  138
+    updateOnEnter: function(e) {
  139
+      if (e.keyCode == 13) this.close();
  140
+    },
  141
+
  142
+    // Remove the item, destroy the model.
  143
+    clear: function() {
  144
+      this.model.destroy();
  145
+    }
  146
+
  147
+  });
  148
+
  149
+  // The Application
  150
+  // ---------------
  151
+
  152
+  // Our overall **AppView** is the top-level piece of UI.
  153
+  var AppView = Backbone.View.extend({
  154
+
  155
+    // Instead of generating a new element, bind to the existing skeleton of
  156
+    // the App already present in the HTML.
  157
+    el: $("#todoapp"),
  158
+
  159
+    // Our template for the line of statistics at the bottom of the app.
  160
+    statsTemplate: _.template($('#stats-template').html()),
  161
+
  162
+    // Delegated events for creating new items, and clearing completed ones.
  163
+    events: {
  164
+      "keypress #new-todo":  "createOnEnter",
  165
+      "click #clear-completed": "clearCompleted",
  166
+      "click #toggle-all": "toggleAllComplete"
  167
+    },
  168
+
  169
+    // At initialization we bind to the relevant events on the `Todos`
  170
+    // collection, when items are added or changed. Kick things off by
  171
+    // loading any preexisting todos that might be saved in *localStorage*.
  172
+    initialize: function() {
  173
+
  174
+      this.input = this.$("#new-todo");
  175
+      this.allCheckbox = this.$("#toggle-all")[0];
  176
+
  177
+      this.listenTo(Todos, 'add', this.addOne);
  178
+      this.listenTo(Todos, 'reset', this.addAll);
  179
+      this.listenTo(Todos, 'all', this.render);
  180
+
  181
+      this.footer = this.$('footer');
  182
+      this.main = $('#main');
  183
+
  184
+      Todos.fetch();
  185
+    },
  186
+
  187
+    // Re-rendering the App just means refreshing the statistics -- the rest
  188
+    // of the app doesn't change.
  189
+    render: function() {
  190
+      var done = Todos.done().length;
  191
+      var remaining = Todos.remaining().length;
  192
+
  193
+      if (Todos.length) {
  194
+        this.main.show();
  195
+        this.footer.show();
  196
+        this.footer.html(this.statsTemplate({done: done, remaining: remaining}));
  197
+      } else {
  198
+        this.main.hide();
  199
+        this.footer.hide();
  200
+      }
  201
+
  202
+      this.allCheckbox.checked = !remaining;
  203
+    },
  204
+
  205
+    // Add a single todo item to the list by creating a view for it, and
  206
+    // appending its element to the `<ul>`.
  207
+    addOne: function(todo) {
  208
+      var view = new TodoView({model: todo});
  209
+      this.$("#todo-list").append(view.render().el);
  210
+    },
  211
+
  212
+    // Add all items in the **Todos** collection at once.
  213
+    addAll: function() {
  214
+      Todos.each(this.addOne, this);
  215
+    },
  216
+
  217
+    // If you hit return in the main input field, create new **Todo** model,
  218
+    // persisting it to *localStorage*.
  219
+    createOnEnter: function(e) {
  220
+      if (e.keyCode != 13) return;
  221
+      if (!this.input.val()) return;
  222
+
  223
+      Todos.create({title: this.input.val()});
  224
+      this.input.val('');
  225
+    },
  226
+
  227
+    // Clear all done todo items, destroying their models.
  228
+    clearCompleted: function() {
  229
+      _.invoke(Todos.done(), 'destroy');
  230
+      return false;
  231
+    },
  232
+
  233
+    toggleAllComplete: function () {
  234
+      var done = this.allCheckbox.checked;
  235
+      Todos.each(function (todo) { todo.save({'done': done}); });
  236
+    }
  237
+
  238
+  });
  239
+
  240
+  // Finally, we kick things off by creating the **App**.
  241
+  var App = new AppView;
  242
+
  243
+});

0 notes on commit 3c12cf6

Please sign in to comment.
Something went wrong with that request. Please try again.