Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Initial port to TodoMVC and DoneJS

  • Loading branch information...
commit 29521abfa0f95e19962d1fdee941d73cb9a20608 1 parent ee7eb33
David Luecke daffl authored
1  .gitignore
... ... @@ -0,0 +1 @@
  1 +.idea
6 .gitmodules
... ... @@ -1,9 +1,9 @@
1   -[submodule "jquery"]
2   - path = jquery
3   - url = git://github.com/jupiterjs/jquerymx.git
4 1 [submodule "funcunit"]
5 2 path = funcunit
6 3 url = git://github.com/jupiterjs/funcunit.git
7 4 [submodule "steal"]
8 5 path = steal
9 6 url = git://github.com/jupiterjs/steal.git
  7 +[submodule "can"]
  8 + path = can
  9 + url = git@github.com:jupiterjs/canjs.git
1  can
... ... @@ -0,0 +1 @@
  1 +Subproject commit 3259501518e8f1afef6257b63811c185e0080fba
1  jquery
... ... @@ -1 +0,0 @@
1   -Subproject commit 9ce150fa768eb0fe42257f54d9e9dddd420a8c61
52 todo/index.html
... ... @@ -1,52 +0,0 @@
1   -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2   - "http://www.w3.org/TR/html4/strict.dtd">
3   -<html lang="en">
4   - <head>
5   - <link rel="stylesheet" type='text/css' href='production.css' />
6   - <title>todo</title>
7   - </head>
8   - <body>
9   - <div id='todos'>
10   - <h1>Todos</h1>
11   - <input type='text' class='create'/>
12   - <ul id='list'></ul>
13   - <div id='todo-stats'>
14   -
15   - </div>
16   - </div>
17   -
18   - <script type='text/ejs' id='todosEJS'>
19   - <% for(var i =0; i < this.length ; i++){ %>
20   - <li <%= this[i]%>>
21   - <%== $.View('todoEJS',this[i] ) %>
22   - </li>
23   - <% } %>
24   - </script>
25   - <script type='text/ejs' id='todoEJS'>
26   - <input type='checkbox' name='complete'
27   - <%= this.complete ? "checked" : "" %>>
28   - <span class=''><%= (this.text || "empty todo ...")%></span>
29   - <span class='destroy'></span>
30   - </script>
31   - <script type='text/ejs' id='statsEJS'>
32   - <% if (total) { %>
33   - <span class="todo-count">
34   - <span class="number"><%= remaining %></span>
35   - <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
36   - </span>
37   - <% } %>
38   - <% if (completed) { %>
39   - <span class="todo-clear">
40   - <a href="#">
41   - Clear <span class="number-done"><%= completed %></span>
42   - completed <span class="word-done"><%= completed == 1 ? 'item' : 'items' %></span>
43   - </a>
44   - </span>
45   - <% } %>
46   -
47   - </script>
48   - <script type='text/javascript'
49   - src='../steal/steal.production.js?todo'>
50   - </script>
51   - </body>
52   -</html>
97 todo/model.js
... ... @@ -0,0 +1,97 @@
  1 +steal('can/model').then(function() {
  2 +
  3 + // Basic Todo entry model
  4 + // { text: 'todo', complete: false }
  5 + can.Model('Todo', {
  6 +
  7 + // Implement local storage handling
  8 + localStore: function(cb){
  9 + var name = 'todos-canjs-jquery',
  10 + data = JSON.parse( window.localStorage[name] || (window.localStorage[name] = '[]') ),
  11 + res = cb.call(this, data);
  12 + if(res !== false){
  13 + can.each(data, function(i, todo) {
  14 + delete todo.editing;
  15 + });
  16 + window.localStorage[name] = JSON.stringify(data);
  17 + }
  18 + },
  19 +
  20 + findAll: function(params){
  21 + var def = new can.Deferred();
  22 + this.localStore(function(todos){
  23 + var instances = [],
  24 + self = this;
  25 + can.each(todos, function(i, todo) {
  26 + instances.push(new self(todo));
  27 + });
  28 + def.resolve({data: instances});
  29 + })
  30 + return def;
  31 + },
  32 +
  33 + destroy: function(id){
  34 + var def = new can.Deferred();
  35 + this.localStore(function(todos){
  36 + for (var i = 0; i < todos.length; i++) {
  37 + if (todos[i].id === id) {
  38 + todos.splice(i, 1);
  39 + break;
  40 + }
  41 + }
  42 + def.resolve({});
  43 + });
  44 + return def
  45 + },
  46 +
  47 + create: function(attrs){
  48 + var def = new can.Deferred();
  49 + this.localStore(function(todos){
  50 + attrs.id = attrs.id || parseInt(100000 *Math.random());
  51 + todos.push(attrs);
  52 + });
  53 + def.resolve({id : attrs.id});
  54 + return def
  55 + },
  56 +
  57 + update: function(id, attrs){
  58 + var def = new can.Deferred();
  59 + this.localStore(function(todos){
  60 + for (var i = 0; i < todos.length; i++) {
  61 + if (todos[i].id === id) {
  62 + var todo = todos[i];
  63 + break;
  64 + }
  65 + }
  66 + can.extend(todo, attrs);
  67 + });
  68 + def.resolve({});
  69 + return def
  70 + }
  71 +
  72 + },{});
  73 +
  74 + // List for Todos
  75 + can.Model.List('Todo.List', {
  76 +
  77 + completed: function() {
  78 + // Ensure this triggers on length change
  79 + this.attr('length');
  80 +
  81 + var completed = 0;
  82 + this.each(function(todo, i) {
  83 + completed += todo.attr('complete') ? 1 : 0
  84 + });
  85 + return completed;
  86 + },
  87 +
  88 + remaining: function() {
  89 + return this.attr('length') - this.completed();
  90 + },
  91 +
  92 + allComplete: function() {
  93 + return this.attr('length') === this.completed();
  94 + }
  95 + });
  96 +
  97 +});
1  todo/production.css
... ... @@ -1 +0,0 @@
1   -body{font-family:verdana;background-image:url("http://javascriptmvc.com/jmvc/images/backgrounds/body.png");padding:0;margin:0}#todos{-moz-background-clip:border;-moz-background-origin:padding;-moz-background-size:auto auto;-moz-box-shadow:0 5px 6px 0 rgba(0,0,0,0.2);background-attachment:scroll;background-color:white;background-image:none;background-position:0 0;background-repeat:repeat;margin:0 auto 40px auto;padding:20px;width:480px}h1{text-align:center}.create,input.text{font-size:1.4em;width:100%}ul{margin:0;padding:0}.todo{list-style:none;border-bottom:solid 1px #ccc;padding:5px 1px;font-size:1.4em}#todo-stats{margin-top:20px}#list{margin-top:20px}
7 todo/production.js
0 additions, 7 deletions not shown
208 todo/styles/base.css
... ... @@ -0,0 +1,208 @@
  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 li .destroy {
  87 + display: none;
  88 + position: absolute;
  89 + top: 20px;
  90 + right: 10px;
  91 + cursor: pointer;
  92 + width: 20px;
  93 + height: 20px;
  94 + background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUBAMAAAB/pwA+AAAABGdBTUEAALGPC/xhBQAAACdQTFRFzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMzMAAAA////zMzMhnu0WAAAAAt0Uk5T5u3pqtV3jFQEKAC0bVelAAAAfUlEQVQI12NYtWpFsc8R865VqxhWrZpyBgg8QcylZ8AgCsjMgTCPrWJYfgYKqhjWwJgaDDVnzpw+c2bPmTPHGWzOnNm95/TuM2cOM/AARXfvBooeZAAp270bRCIz4QoOIGtDMqwJZoUEQzvCYrhzuhhWtUKYEahOX7UK6iEA3A6NUGwCTZIAAAAASUVORK5CYII=') no-repeat center center;
  95 +}
  96 +
  97 +#todo-list li:hover .destroy {
  98 + display: block;
  99 +}
  100 +
  101 +#todo-list li.editing {
  102 + border-bottom: none;
  103 + margin-top: -1px;
  104 + padding: 0;
  105 +}
  106 +
  107 +#todo-list li.editing:last-child {
  108 + margin-bottom: -1px;
  109 +}
  110 +
  111 +#todo-list li.editing .edit {
  112 + display: block;
  113 + width: 444px;
  114 + padding: 13px 15px 14px 20px;
  115 + margin: 0;
  116 +}
  117 +
  118 +#todo-list li.editing .view {
  119 + display: none;
  120 +}
  121 +
  122 +#todo-list li .view label {
  123 + word-break: break-word;
  124 +}
  125 +
  126 +#todo-list li .edit {
  127 + display: none;
  128 +}
  129 +
  130 +#todoapp footer {
  131 + display: none;
  132 + margin: 0 -20px -20px -20px;
  133 + overflow: hidden;
  134 + color: #555555;
  135 + background: #f4fce8;
  136 + border-top: 1px solid #ededed;
  137 + padding: 0 20px;
  138 + line-height: 37px;
  139 + -webkit-border-radius: 0 0 5px 5px;
  140 + -moz-border-radius: 0 0 5px 5px;
  141 + -ms-border-radius: 0 0 5px 5px;
  142 + -o-border-radius: 0 0 5px 5px;
  143 + border-radius: 0 0 5px 5px;
  144 +}
  145 +
  146 +#clear-completed {
  147 + display: none;
  148 + float: right;
  149 + line-height: 20px;
  150 + text-decoration: none;
  151 + background: rgba(0, 0, 0, 0.1);
  152 + color: #555555;
  153 + font-size: 11px;
  154 + margin-top: 8px;
  155 + margin-bottom: 8px;
  156 + padding: 0 10px 1px;
  157 + cursor: pointer;
  158 + -webkit-border-radius: 12px;
  159 + -moz-border-radius: 12px;
  160 + -ms-border-radius: 12px;
  161 + -o-border-radius: 12px;
  162 + border-radius: 12px;
  163 + -webkit-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  164 + -moz-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  165 + -ms-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  166 + -o-box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  167 + box-shadow: rgba(0, 0, 0, 0.2) 0 -1px 0 0;
  168 +}
  169 +
  170 +#clear-completed:hover {
  171 + background: rgba(0, 0, 0, 0.15);
  172 + -webkit-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  173 + -moz-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  174 + -ms-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  175 + -o-box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  176 + box-shadow: rgba(0, 0, 0, 0.3) 0 -1px 0 0;
  177 +}
  178 +
  179 +#clear-completed:active {
  180 + position: relative;
  181 + top: 1px;
  182 +}
  183 +
  184 +#todo-count span {
  185 + font-weight: bold;
  186 +}
  187 +
  188 +#instructions {
  189 + margin: 10px auto;
  190 + color: #777777;
  191 + text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
  192 + text-align: center;
  193 +}
  194 +
  195 +#instructions a {
  196 + color: #336699;
  197 +}
  198 +
  199 +#credits {
  200 + margin: 30px auto;
  201 + color: #999;
  202 + text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
  203 + text-align: center;
  204 +}
  205 +
  206 +#credits a {
  207 + color: #888;
  208 +}
5 todo/styles/todo.css
... ... @@ -0,0 +1,5 @@
  1 +#main.show,
  2 +#stats.show,
  3 +#stats.show #clear-completed {
  4 + display: block;
  5 +}
43 todo/todo.css
... ... @@ -1,43 +0,0 @@
1   -body {
2   - font-family: verdana;
3   - background-image: url("http://javascriptmvc.com/jmvc/images/backgrounds/body.png");
4   - padding: 0px; margin: 0px;
5   -}
6   -#todos {
7   - -moz-background-clip: border;
8   - -moz-background-origin: padding;
9   - -moz-background-size: auto auto;
10   - -moz-box-shadow: 0 5px 6px 0 rgba(0, 0, 0, 0.2);
11   - background-attachment: scroll;
12   - background-color: white;
13   - background-image: none;
14   - background-position: 0 0;
15   - background-repeat: repeat;
16   - margin: 0 auto 40px auto;
17   - padding: 20px;
18   - width: 480px;
19   -}
20   -
21   -h1{
22   - text-align: center;
23   -}
24   -.create, input.text {
25   - font-size: 1.4em;
26   - width: 100%;
27   -}
28   -
29   -ul {
30   - margin: 0px;padding: 0px;
31   -}
32   -.todo {
33   - list-style: none;
34   - border-bottom: solid 1px #CCCCCC;
35   - padding: 5px 1px;
36   - font-size: 1.4em;
37   -}
38   -#todo-stats {
39   - margin-top: 20px;
40   -}
41   -#list {
42   - margin-top: 20px;
43   -}
72 todo/todo.html 100644 → 100755
... ... @@ -1,51 +1,25 @@
1   -<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
2   - "http://www.w3.org/TR/html4/strict.dtd">
  1 +<!doctype html>
3 2 <html lang="en">
4   - <head>
5   - <title>todo</title>
6   - </head>
7   - <body>
8   - <div id='todos'>
9   - <h1>Todos</h1>
10   - <input type='text' class='create'/>
11   - <ul id='list'></ul>
12   - <div id='todo-stats'>
13   -
14   - </div>
15   - </div>
16   -
17   - <script type='text/ejs' id='todosEJS'>
18   - <% for(var i =0; i < this.length ; i++){ %>
19   - <li <%= this[i]%>>
20   - <%== $.View('todoEJS',this[i] ) %>
21   - </li>
22   - <% } %>
23   - </script>
24   - <script type='text/ejs' id='todoEJS'>
25   - <input type='checkbox' name='complete'
26   - <%= this.complete ? "checked" : "" %>>
27   - <span class=''><%= (this.text || "empty todo ...")%></span>
28   - <span class='destroy'></span>
29   - </script>
30   - <script type='text/ejs' id='statsEJS'>
31   - <% if (total) { %>
32   - <span class="todo-count">
33   - <span class="number"><%= remaining %></span>
34   - <span class="word"><%= remaining == 1 ? 'item' : 'items' %></span> left.
35   - </span>
36   - <% } %>
37   - <% if (completed) { %>
38   - <span class="todo-clear">
39   - <a href="#">
40   - Clear <span class="number-done"><%= completed %></span>
41   - completed <span class="word-done"><%= completed == 1 ? 'item' : 'items' %></span>
42   - </a>
43   - </span>
44   - <% } %>
45   -
46   - </script>
47   - <script type='text/javascript'
48   - src='../steal/steal.js?todo,development'>
49   - </script>
50   - </body>
  3 +<head>
  4 + <meta charset="utf-8">
  5 + <title>Todo - DoneJS with jQuery</title>
  6 + <!--[if lt IE 9]>
  7 + <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
  8 + <![endif]-->
  9 +</head>
  10 +<body>
  11 + <div id="todoapp">
  12 + <header>
  13 + <h1>Todos</h1>
  14 + <input id="new-todo" type="text" placeholder="What needs to be done?">
  15 + </header>
  16 + </div>
  17 + <div id="instructions">
  18 + Double-click to edit a todo.
  19 + </div>
  20 + <div id="credits">
  21 + Created by <a href="http://bitovi.com/">Bitovi</a>.
  22 + </div>
  23 + <script src="../steal/steal.js?todo,development"></script>
  24 +</body>
51 25 </html>
323 todo/todo.js 100644 → 100755
... ... @@ -1,248 +1,99 @@
1   -// Load what we need
2   -steal('jquery/model/list',
3   - 'jquery/controller',
4   - 'jquery/view/ejs',
5   - 'jquery/lang/json',
6   - './todo.css',
7   - function($){
  1 +steal('can/control', 'can/view/ejs')
  2 + .then('./model.js',
  3 + './styles/base.css',
  4 + './styles/todo.css')
  5 + .then(function() {
8 6
9   -/**
10   - * A todo model for CRUDing todos.
11   - */
12   -$.Model('Todo',{
13   - /**
14   - * Gets JSON data from localStorage. Any changes that
15   - * get made in cb get written back to localStorage.
16   - *
17   - * This is unimportant for understanding JavaScriptMVC!
18   - */
19   - localStore: function(cb){
20   - var name = this.shortName,
21   - data = $.evalJSON( window.localStorage[name] || (window.localStorage[name] = "{}") ),
22   - res = cb.call(this, data);
23   - if(res !== false){
24   - window.localStorage[name] = $.toJSON(data);
25   - }
26   - },
27   - /**
28   - * Gets todos from localStorage.
29   - *
30   - * Todo.findAll({}, success(todos))
31   - */
32   - findAll: function(params , success){
33   - this.localStore(function(todos){
34   - var instances = [];
35   - for(var id in todos){
36   - instances.push( new this( todos[id]) )
  7 + can.Control('Todos', {
  8 +
  9 + // Initialize the Todos list
  10 + init : function(){
  11 + // Render the Todos
  12 + this.element.append(can.view('//todo/views/todo.ejs', {
  13 + todos: this.options.todos
  14 + }));
  15 +
  16 + // Clear the new todo field
  17 + $('#new-todo').val('').focus();
  18 + },
  19 +
  20 + // Listen for when a new Todo has been entered
  21 + '#new-todo keyup' : function(el, ev){
  22 + if(ev.keyCode == 13){
  23 + new Todo({
  24 + text : el.val(),
  25 + complete : false
  26 + }).save(function() {
  27 + el.val('');
  28 + });
37 29 }
38   - success && success(instances)
39   - })
40   - },
41   - /**
42   - * Destroys a single todo by id
43   - *
44   - * Todo.destroy(1, success())
45   - */
46   - destroy: function(id, success){
47   - this.List.destroy([id], success)
48   - },
49   - /**
50   - * Creates a todo with the provided attrs. This allows:
51   - *
52   - * new Todo({text: 'hello'}).save( success(todo) );
53   - */
54   - create: function(attrs, success){
55   - this.localStore(function(todos){
56   - attrs.id = attrs.id || parseInt(100000 *Math.random())
57   - todos[attrs.id] = attrs;
58   - });
59   - success({id : attrs.id})
60   - },
61   - /**
62   - * Updates a todo by id with the provided attrs. This allows:
63   - *
64   - * todo.update({text: 'world'}, success(todos) )
65   - */
66   - update: function(id, attrs, success){
67   - this.localStore(function(todos){
68   - var todo = todos[id];
69   - $.extend(todo, attrs);
70   - });
71   - success({});
72   - }
73   -
74   -},{});
  30 + },
  31 +
  32 + // Handle a newly created Todo
  33 + '{Todo} created' : function(list, ev, item){
  34 + this.options.todos.push(item);
  35 + },
75 36
76   -/**
77   - * Helper methods on collections of todos. But lists can also use their model's
78   - * methods. Ex:
79   - *
80   - * var todos = [new Todo({id: 5}) , new Todo({id: 6})],
81   - * list = new Todo.List(todos);
82   - *
83   - * list.destroyAll() -> calls Todo.destroyAll with [5,6].
84   - */
85   -$.Model.List('Todo.List',
86   -{
87   - /**
88   - * Destroys a list of todos by id from localStorage
89   - *
90   - * Todo.destroy([1,2], success())
91   - */
92   - destroy : function(ids, success, error){
93   - this.namespace.localStore(function(todos){
94   - $.each(ids, function(){
95   - delete todos[this]
  37 + // Listen for editing a Todo
  38 + '.todo dblclick' : function(el, ev) {
  39 + el.data('todo').attr('editing', true).save(function() {
  40 + el.children('.edit').focus().select();
96 41 });
97   - });
98   - success();
99   - }
100   -},
101   -{
102   -
103   - /**
104   - * Return a new Todo.List of only complete items
105   - */
106   - completed : function(){
107   - return this.grep(function(item){
108   - return item.complete === true;
109   - })
110   - }
111   -});
  42 + },
112 43
  44 + // Update a todo
  45 + updateTodo: function(el) {
  46 + el.closest('.todo').data('todo')
  47 + .attr({
  48 + editing: false,
  49 + text: el.val()
  50 + }).save();
  51 + },
113 52
114   -/**
115   - * A Todos widget created like
116   - *
117   - * $("#todos").todos({ list: new Todo.List() });
118   - *
119   - * It listens on changes to the list and items in the list with the following actions:
120   - *
121   - * - "{list} add" - todos being added to the list
122   - * - "{list} remove" - todos being removed from the list
123   - * - "{list} update" - todos being updated in the list
124   - *
125   - */
126   -$.Controller('Todos',{
127   -
128   - // sets up the widget
129   - init : function(){
130   -
131   - // empties the create input element
132   - this.find(".create").val("")[0].focus();
133   -
134   - // fills this list of items (creates add events on the list)
135   - this.options.list.findAll();
136   - },
137   -
138   - // adds existing and created to the list
139   - "{list} add" : function(list, ev, items){
140   -
141   - // uses the todosEJS template (in todo.html) to render a list of items
142   - // then adds those items to #list
143   - this.find('#list').append("todosEJS",items)
144   -
145   - // calls a helper to update the stats info
146   - this.updateStats();
147   - },
148   -
149   - // Creating a todo --------------
150   -
151   - // listens for key events and creates a new todo
152   - ".create keyup" : function(el, ev){
153   -
154   - if(ev.keyCode == 13){
155   - new Todo({
156   - text : el.val(),
157   - complete : false
158   - }).save(this.callback('created'));
159   -
160   - el.val("");
161   - }
162   - },
163   -
164   - // When a todo is created, add it to this list
165   - "created" : function(todo){
166   - this.options.list.push(todo); //triggers 'add' on the list
167   - },
168   -
169   - // Destroying a todo --------------
170   -
171   - // the clear button is clicked
172   - ".todo-clear click" : function(){
173   - // gets completed todos in the list, destroys them
174   - this.options.list.completed()
175   - .destroy();
  53 + // Listen for an edited Todo
  54 + '.todo .edit keyup' : function(el, ev){
  55 + if(ev.keyCode == 13){
  56 + this.updateTodo(el);
  57 + }
  58 + },
  59 + '.todo .edit focusout' : function(el, ev) {
  60 + this.updateTodo(el);
  61 + },
176 62
177   - },
178   -
179   - // When a todo's destroy button is clicked.
180   - ".todo .destroy click" : function(el){
181   - el.closest('.todo').model().destroy();
182   - },
183   -
184   - // when an item is removed from the list ...
185   - "{list} remove" : function(list, ev, items){
186   -
187   - // get the elements in the list and remove them
188   - items.elements(this.element).slideUp(function(){
189   - $(this).remove();
190   - });
191   -
192   - this.updateStats();
193   - },
194   -
195   -
196   - // Updating a todo --------------
197   -
198   - // when the checkbox changes, update the model
199   - ".todo [name=complete] change" : function(el, ev){
200   - var todo = el.closest('.todo').model().update({
201   - complete : el.is(':checked')
202   - });
203   - },
204   -
205   - // switch to edit mode
206   - ".todo dblclick" : function(el){
207   - var input = $("<input name='text' class='text'/>").val(el.model().text)
208   - el.html(input);
209   - input[0].focus();
210   - },
211   -
212   - // update the todo's text on blur
213   - ".todo [name=text] focusout" : function(el, ev){
214   -
215   - var todo = el.closest('.todo').model().update({
216   - text : el.val()
217   - });
218   - },
219   -
220   - // when an item is updated
221   - "{list} updated" : function(list, ev, item){
222   - item.elements().html("todoEJS", item);
223   - this.updateStats();
224   - //update completed
225   - },
226   -
227   - // a helper that updates the stats
228   - updateStats : function(){
229   - var list = this.options.list,
230   - completed = list.completed().length;
231   - $("#todo-stats").html("statsEJS",{
232   - completed : completed,
233   - total : list.length,
234   - remaining : list.length - completed
235   - })
236   - }
237   -})
  63 + // Listen for the toggled completion of a Todo
  64 + '.todo .toggle click' : function(el, ev) {
  65 + el.closest('.todo').data('todo')
  66 + .attr('complete', el.is(':checked'))
  67 + .save();
  68 + },
  69 +
  70 + // Listen for a removed Todo
  71 + '.todo .destroy click' : function(el){
  72 + el.closest('.todo').data('todo').destroy();
  73 + },
238 74
  75 + // Listen for toggle all completed Todos
  76 + '#toggle-all click' : function(el, ev) {
  77 + var toggle = el.prop('checked');
  78 + can.each(this.options.todos, function(i, todo) {
  79 + todo.attr('complete', toggle).save();
  80 + });
  81 + },
239 82
  83 + // Listen for removing all completed Todos
  84 + '#clear-completed click' : function() {
  85 + for (var i = this.options.todos.length - 1, todo; i > -1 && (todo = this.options.todos[i]); i--) {
  86 + todo.attr('complete') && todo.destroy();
  87 + }
  88 + }
240 89
241   -$(function(){
242   -
243   - // create a todos widget with a list
244   - $("#todos").todos({list : new Todo.List()});
245   -})
  90 + })
246 91
  92 + // Initialize the app
  93 + Todo.findAll({}, function(todos) {
  94 + new Todos('#todoapp', {
  95 + todos: todos
  96 + });
  97 + });
247 98
248   -});
  99 +});
25 todo/views/todo.ejs
... ... @@ -0,0 +1,25 @@
  1 +<section id="main" class="<%= todos.attr("length") > 0 ? "show" : "" %>">
  2 + <input id="toggle-all" type="checkbox" <%= todos.allComplete() ? "checked" : "" %>>
  3 + <label for="toggle-all">Mark all as complete</label>
  4 + <ul id="todo-list">
  5 + <% list(todos, function( todo ) { %>
  6 + <li class="todo
  7 + <%= todo.attr("complete") ? "done" : "" %>
  8 + <%= todo.attr("editing") ? "editing" : "" %>"
  9 + <%= (el)-> el.data('todo', todo) %>>
  10 + <div class="view">
  11 + <input class="toggle" type="checkbox" <%= todo.attr("complete") ? "checked" : "" %>>
  12 + <label><%= todo.attr("text") %></label>
  13 + <a class="destroy"></a>
  14 + </div>
  15 + <input class="edit" type="text" value="<%= todo.attr("text") %>">
  16 + </li>
  17 + <% }) %>
  18 + </ul>
  19 +</section>
  20 +<footer id="stats" class="<%= todos.attr("length") > 0 ? "show" : "" %>">
  21 + <a id="clear-completed">Clear <%= todos.completed() %>
  22 + completed item<%= todos.completed() == 1 ? "" : "s" %></a>
  23 + <div id="todo-count"><span><%= todos.remaining() %></span>
  24 + item<%= todos.remaining() == 1 ? "" : "s" %> left</div>
  25 +</footer>

0 comments on commit 29521ab

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