/javascripts/todos.js

https://github.com/baali/localtodos · JavaScript · 203 lines · 152 code · 46 blank · 5 comment · 15 complexity · 68f11cce54fa278a938df0fb937b65df MD5 · raw file

  1. (function(){
  2. // Todo
  3. window.Todo = Backbone.Model.extend({
  4. toggle: function() {
  5. this.save({done: !this.get("done")});
  6. },
  7. // Remove this Todo from *localStorage*, deleting its view.
  8. clear: function() {
  9. this.destroy();
  10. $(this.view.el).dispose();
  11. }
  12. });
  13. // Todo List
  14. window.TodoList = Backbone.Collection.extend({
  15. model: Todo,
  16. localStorage: new Store("todos"),
  17. // Returns all done todos.
  18. done: function() {
  19. return this.filter(function(todo){
  20. return todo.get('done');
  21. });
  22. },
  23. nextOrder: function() {
  24. if (!this.length) return 1;
  25. return this.last().get('order') + 1;
  26. },
  27. comparator: function(todo) {
  28. return todo.get('order');
  29. },
  30. pluralize: function(count) {
  31. return count == 1 ? 'item' : 'items';
  32. }
  33. });
  34. window.Todos = new TodoList;
  35. window.TodoView = Backbone.View.extend({
  36. tagName: "li",
  37. className: "todo",
  38. template: _.template("<input type='checkbox' class='todo-check' /><div class='todo-content'></div><span class='todo-destroy'></span><input type='text' class='todo-input' />"),
  39. events: {
  40. "click .todo-check" : "toggleDone",
  41. "dblclick .todo-content" : "edit",
  42. "click .todo-destroy" : "clear",
  43. "keypress .todo-input" : "updateOnEnter"
  44. },
  45. initialize: function() {
  46. _.bindAll(this, 'render', 'close');
  47. this.model.bind('change', this.render);
  48. this.model.view = this;
  49. },
  50. render: function() {
  51. $(this.el).set('html', this.template(this.model.toJSON()));
  52. $(this.el).setProperty("id", "todo-"+this.model.id);
  53. this.setContent();
  54. sortableTodos.addItems(this.el);
  55. return this;
  56. },
  57. setContent: function() {
  58. var content = this.model.get('content');
  59. this.$('.todo-content').set("html", content);
  60. this.$('.todo-input').setProperty("value", content);
  61. if (this.model.get('done')) {
  62. this.$(".todo-check").setProperty("checked", "checked");
  63. $(this.el).addClass("done");
  64. } else {
  65. this.$(".todo-check").removeProperty("checked");
  66. $(this.el).removeClass("done");
  67. }
  68. this.input = this.$(".todo-input");
  69. this.input.addEvent('blur', this.close);
  70. },
  71. toggleDone: function() {
  72. this.model.toggle();
  73. },
  74. edit: function() {
  75. $(this.el).addClass("editing");
  76. //this.input.fireEvent("focus");
  77. this.input.focus();
  78. },
  79. close: function() {
  80. this.model.save({content: this.input.getProperty("value")});
  81. $(this.el).removeClass("editing");
  82. },
  83. updateOnEnter: function(e) {
  84. if (e.code == 13) this.close();
  85. },
  86. clear: function() {
  87. this.model.clear();
  88. }
  89. });
  90. var sortableTodos = new Sortables("todo-list", {
  91. constrain: true,
  92. clone: true,
  93. handle: ".todo-content",
  94. onComplete: function(ele){
  95. sortableTodos.serialize(false, function(element, index){
  96. todo = Todos.get(element.getProperty("id").replace("todo-", ""));
  97. todo.save({"order": index});
  98. });
  99. }
  100. });
  101. window.AppView = Backbone.View.extend({
  102. el: $("todoapp"),
  103. statsTemplate: _.template('<% if (total) { %><span class="todo-count"><span class="number"><%= remaining %></span><span class="word"> <%= remaining == 1 ? "item" : "items" %></span> left.</span><% } %><% if (done) { %><span class="todo-clear"><a href="#">Clear <span class="number-done"><%= done %> </span>completed <span class="word-done"><%= done == 1 ? "item" : "items" %></span></a></span><% } %>'),
  104. events: {
  105. "keypress #new-todo" : "createOnEnter",
  106. "keyup #new-todo" : "showTooltip",
  107. "click .todo-clear" : "clearCompleted"
  108. },
  109. initialize: function() {
  110. _.bindAll(this, 'addOne', 'addAll', 'render');
  111. this.input = this.$("#new-todo");
  112. Todos.bind('add', this.addOne);
  113. Todos.bind('refresh', this.addAll);
  114. Todos.bind('all', this.render);
  115. Todos.fetch();
  116. },
  117. render: function() {
  118. var done = Todos.done().length;
  119. this.$("#todo-stats").set("html",this.statsTemplate({
  120. done: done,
  121. total: Todos.length,
  122. remaining: Todos.length - done
  123. }));
  124. },
  125. addOne: function(todo) {
  126. var view = new TodoView({model: todo}).render().el;
  127. this.$("#todo-list").grab(view);
  128. sortableTodos.addItems(view);
  129. },
  130. addAll: function() {
  131. Todos.each(this.addOne);
  132. },
  133. createOnEnter: function(e) {
  134. if (e.code != 13) return;
  135. Todos.create({
  136. content: this.input.getProperty("value"),
  137. done: false
  138. });
  139. this.input.setProperty("value", "");
  140. },
  141. showTooltip: function(e) {
  142. var tooltip = this.$(".ui-tooltip-top");
  143. tooltip.fade("out");
  144. if (this.tooltipTimeout) clearTimeout(this.tooltipTimeout);
  145. if (this.input.getProperty("value") !== "" && this.input.getProperty("value") !== this.input.getProperty("placeholder")) {
  146. this.tooltipTimeout = setTimeout(function(){
  147. tooltip.fade("in");
  148. }, 1000);
  149. }
  150. },
  151. clearCompleted: function() {
  152. _.each(Todos.done(), function(todo){ todo.clear(); });
  153. return false;
  154. }
  155. });
  156. window.App = new AppView;
  157. }());