Procházet zdrojové kódy

todo demo kinda works

Evan You před 12 roky
rodič
revize
a6b72570e2
8 změnil soubory, kde provedl 160 přidání a 112 odebrání
  1. 3 3
      component.json
  2. 75 54
      dev.html
  3. 5 4
      src/directive-parser.js
  4. 38 10
      src/directives.js
  5. 2 2
      src/main.js
  6. 37 39
      src/seed.js
  7. 0 0
      src/textnode-parser.js
  8. 0 0
      src/watch-array.js

+ 3 - 3
component.json

@@ -9,11 +9,11 @@
     "src/main.js",
     "src/config.js",
     "src/seed.js",
-    "src/binding.js",
-    "src/textNodeParser.js",
+    "src/directive-parser.js",
+    "src/textnode-parser.js",
     "src/directives.js",
     "src/filters.js",
     "src/controllers.js",
-    "src/watchArray.js"
+    "src/watch-array.js"
   ]
 }

+ 75 - 54
dev.html

@@ -8,86 +8,107 @@
 			.red {
 				color: red;
 			}
-            .todo.done {
+            .done {
                 text-decoration: line-through;
+            }
+            #app.all .all {
+                font-weight: bold;
+            }
+            #app.remaining .todo.done {
+                display: none;
+            }
+            #app.remaining .remaining {
+                font-weight: bold;
+            }
+            #app.completed .todo:not(.done) {
+                display: none;
+            }
+            #app.completed .completed {
+                font-weight: bold;
             }
 		</style>
 	</head>
 	<body>
-		<div id="app" sd-controller="TodoList" sd-on="click:changeMessage | delegate .button">
-            <p sd-text="msg | capitalize"></p>
-            <p sd-text="msg | uppercase"></p>
-            <p sd-on="click:remove">bye</p>
-            <p sd-text="total | money"></p>
-            <p class="button">Change Message</p>
-            <p sd-class="red:error" sd-show="error">Error</p>
+		<div id="app" class="all" sd-controller="TodoList">
+            <div>
+                <input placeholder="What needs to be done?" sd-on="change:addTodo">
+            </div>
             <ul sd-show="todos">
-            	<li class="todo"
-                    sd-controller="Todo"
-                    sd-each="todo:todos"
-                    sd-class="done:todo.done"
-                    sd-on="click:todo.toggle"
-                    sd-text="todo.title"
-                ></li>
+            	<li class="todo" sd-each="todo:todos" sd-class="done:todo.done">
+                    <input type="checkbox" sd-checked="todo.done" sd-on="change:toggleTodo">
+                    <span sd-text="todo.text"></span>
+                    <a sd-on="click:removeTodo">X</a>
+                </li>
             </ul>
+            <div id="footer">
+                Remaining: <span sd-text="remaining"></span><br>
+                <a class="all" sd-on="click:filter">Show All</a> |
+                <a class="remaining" sd-on="click:filter">Show Remaining</a> |
+                <a class="completed" sd-on="click:filter">Show Completed</a>
+            </div>
         </div>
 		<script>
 
-			var Seed = require('seed')
-
-			Seed.filter('money', function (value) {
-			    return value
-                    ? '$' + value.toFixed(2)
-                    : ''
-			})
-
-            Seed.controller('TodoList', function (scope, seed) {
-                seed.on('toggle', function (todo) {
-                    console.log(todo)
-                })
-                scope.changeMessage = function () {
-                    scope.msg = (Math.random() * 100).toFixed(2) + '% awesomeness'
-                }
-                scope.remove = function () {
-                    seed.destroy()
-                }
-            })
-
-            Seed.controller('Todo', function (scope, seed) {
-                scope.toggle = function () {
-                    scope.done = !scope.done
-                    seed.parentSeed.emit('toggle', seed)
-                }
-            })
-
-            var s = Date.now()
-
             var data = {
-                msg: 'hello!',
-                total: 9999,
-                error: true,
                 todos: [
                     {
-                        title: 'hello!',
-                        done: true
+                        text: '1!',
+                        done: false
                     },
                     {
-                        title: 'hello!!',
+                        text: '2!',
                         done: false
                     },
                     {
-                        title: 'hello!!!',
-                        done: false
+                        text: '3!',
+                        done: true
                     }
                 ]
             }
 
-			var app = Seed.bootstrap({
+			var Seed = require('seed')
+
+            Seed.plant('TodoList', function (scope, seed) {
+
+                scope.remaining = scope.todos.reduce(function (count, todo) {
+                    return count + (todo.done ? 0 : 1)
+                }, 0)
+
+                scope.addTodo = function (e) {
+                    var text = e.el.value
+                    if (text) {
+                        e.el.value = ''
+                        scope.todos.push({
+                            text: text,
+                            done: false
+                        })
+                        scope.remaining++
+                    }
+                }
+
+                scope.removeTodo = function (e) {
+                    var i = e.seed.eachIndex
+                    scope.todos.splice(i, 1)
+                }
+
+                scope.toggleTodo = function (e) {
+                    scope.remaining += e.seed.scope.done ? -1 : 1
+                }
+
+                scope.filter = function (e) {
+                    var filter = e.el.className
+                    seed.el.className = filter
+                }
+            })
+
+            var now = Date.now()
+
+			var app = Seed.sprout({
                 el: '#app',
                 data: data
             })
 
-            console.log(Date.now() - s + 'ms')
+            console.log(Date.now() - now)
 
 		</script>
 	</body>

+ 5 - 4
src/binding.js → src/directive-parser.js

@@ -3,12 +3,13 @@ var config     = require('./config'),
     filters    = require('./filters')
 
 var KEY_RE          = /^[^\|]+/,
+    LOCAL_KEY_RE    = /\.[^.]+$/,
     ARG_RE          = /([^:]+):(.+)$/,
     FILTERS_RE      = /\|[^\|]+/g,
     FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
     QUOTE_RE        = /'/g
 
-function Binding (directiveName, expression) {
+function Directive (directiveName, expression) {
 
     var directive = directives[directiveName]
     if (typeof directive === 'function') {
@@ -55,7 +56,7 @@ function Binding (directiveName, expression) {
     }
 }
 
-Binding.prototype.update = function (value) {
+Directive.prototype.update = function (value) {
     // apply filters
     if (this.filters) {
         value = this.applyFilters(value)
@@ -63,7 +64,7 @@ Binding.prototype.update = function (value) {
     this._update(value)
 }
 
-Binding.prototype.applyFilters = function (value) {
+Directive.prototype.applyFilters = function (value) {
     var filtered = value
     this.filters.forEach(function (filter) {
         if (!filter.apply) throw new Error('Unknown filter: ' + filter.name)
@@ -88,7 +89,7 @@ module.exports = {
         if (!valid) console.warn('invalid directive expression: ' + expression)
 
         return dir && valid
-            ? new Binding(dirname, expression)
+            ? new Directive(dirname, expression)
             : null
     }
 }

+ 38 - 10
src/directives.js

@@ -1,10 +1,11 @@
 var config = require('./config'),
-    watchArray = require('./watchArray')
+    watchArray = require('./watch-array')
 
 module.exports = {
 
     text: function (value) {
-        this.el.textContent = value || ''
+        this.el.textContent = value === null ?
+            '' : value.toString()
     },
 
     show: function (value) {
@@ -15,21 +16,47 @@ module.exports = {
         this.el.classList[value ? 'add' : 'remove'](this.arg)
     },
 
+    checked: {
+        bind: function () {
+            var el = this.el,
+                self = this
+            this.change = function () {
+                self.seed.scope[self.key] = el.checked
+            }
+            el.addEventListener('change', this.change)
+        },
+        update: function (value) {
+            this.el.checked = value
+        },
+        unbind: function () {
+            this.el.removeEventListener('change', this.change)
+        }
+    },
+
     on: {
         update: function (handler) {
-            var event = this.arg
+            var self  = this,
+                event = this.arg
             if (this.handler) {
                 this.el.removeEventListener(event, this.handler)
             }
             if (handler) {
-                this.el.addEventListener(event, handler)
-                this.handler = handler
+                var proxy = function (e) {
+                    handler({
+                        el            : e.currentTarget,
+                        originalEvent : e,
+                        directive     : self,
+                        seed          : self.seed
+                    })
+                }
+                this.el.addEventListener(event, proxy)
+                this.handler = proxy
             }
         },
         unbind: function () {
             var event = this.arg
             if (this.handlers) {
-                this.el.removeEventListener(event, this.handlers[event])
+                this.el.removeEventListener(event, this.handler)
             }
         }
     },
@@ -37,9 +64,8 @@ module.exports = {
     each: {
         bind: function () {
             this.el.removeAttribute(config.prefix + '-each')
-            this.prefixRE = new RegExp('^' + this.arg + '.')
             var ctn = this.container = this.el.parentNode
-            this.marker = document.createComment('sd-each-' + this.arg + '-marker')
+            this.marker = document.createComment('sd-each-' + this.arg)
             ctn.insertBefore(this.marker, this.el)
             ctn.removeChild(this.el)
             this.childSeeds = []
@@ -64,8 +90,10 @@ module.exports = {
             var Seed = require('./seed'),
                 node = this.el.cloneNode(true)
             var spore = new Seed(node, data, {
-                    eachPrefixRE: this.prefixRE,
-                    parentSeed: this.seed
+                    eachPrefix: this.arg,
+                    parentSeed: this.seed,
+                    eachIndex: index,
+                    eachCollection: collection
                 })
             this.container.insertBefore(node, this.marker)
             collection[index] = spore.scope

+ 2 - 2
src/main.js

@@ -58,7 +58,7 @@ Seed.filter = function (name, fn) {
 }
 
 // alias for an alternative API
-Seed.evolve = Seed.controller
-Seed.plant  = Seed.bootstrap
+Seed.plant  = Seed.controller
+Seed.sprout = Seed.bootstrap
 
 module.exports = Seed

+ 37 - 39
src/seed.js

@@ -1,10 +1,9 @@
-var Emitter       = require('emitter'),
-    config        = require('./config'),
-    controllers   = require('./controllers'),
-    bindingParser = require('./binding')
+var Emitter         = require('emitter'),
+    config          = require('./config'),
+    controllers     = require('./controllers'),
+    DirectiveParser = require('./directive-parser')
 
-var map  = Array.prototype.map,
-    each = Array.prototype.forEach
+var slice = Array.prototype.slice
 
 // lazy init
 var ctrlAttr,
@@ -20,13 +19,15 @@ function Seed (el, data, options) {
         el = document.querySelector(el)
     }
 
-    this.el         = el
-    this.scope      = data
-    this._bindings  = {}
+    el.seed        = this
+    this.el        = el
+    this.scope     = data
+    this._bindings = {}
 
     if (options) {
         this.parentSeed = options.parentSeed
-        this.scopeNameRE = options.eachPrefixRE
+        this.eachPrefixRE = new RegExp('^' + options.eachPrefix + '.')
+        this.eachIndex = options.eachIndex
     }
 
     var key
@@ -60,7 +61,7 @@ function Seed (el, data, options) {
 
     // copy in methods from controller
     if (controller) {
-        controller.call(null, this.scope, this)
+        controller.call(this, this.scope, this)
     }
 }
 
@@ -73,14 +74,14 @@ Seed.prototype._compileNode = function (node, root) {
 
         self._compileTextNode(node)
 
-    } else if (node.attributes && node.attributes.length) {
+    } else {
 
         var eachExp = node.getAttribute(eachAttr),
             ctrlExp = node.getAttribute(ctrlAttr)
 
         if (eachExp) { // each block
 
-            var binding = bindingParser.parse(eachAttr, eachExp)
+            var binding = DirectiveParser.parse(eachAttr, eachExp)
             if (binding) {
                 self._bind(node, binding)
                 // need to set each block now so it can inherit
@@ -94,25 +95,12 @@ Seed.prototype._compileNode = function (node, root) {
 
             // TODO need to be clever here!
 
-        } else { // normal node (non-controller)
+        } else if (node.attributes && node.attributes.length) { // normal node (non-controller)
 
-            if (node.childNodes.length) {
-                each.call(node.childNodes, function (child) {
-                    self._compileNode(child)
-                })
-            }
-
-            // clone attributes because the list can change
-            var attrs = map.call(node.attributes, function (attr) {
-                return {
-                    name: attr.name,
-                    expressions: attr.value.split(',')
-                }
-            })
-            attrs.forEach(function (attr) {
+            slice.call(node.attributes).forEach(function (attr) {
                 var valid = false
-                attr.expressions.forEach(function (exp) {
-                    var binding = bindingParser.parse(attr.name, exp)
+                attr.value.split(',').forEach(function (exp) {
+                    var binding = DirectiveParser.parse(attr.name, exp)
                     if (binding) {
                         valid = true
                         self._bind(node, binding)
@@ -121,6 +109,14 @@ Seed.prototype._compileNode = function (node, root) {
                 if (valid) node.removeAttribute(attr.name)
             })
         }
+
+        if (!eachExp && !ctrlExp) {
+            if (node.childNodes.length) {
+                slice.call(node.childNodes).forEach(function (child, i) {
+                    self._compileNode(child)       
+                })
+            }
+        }
     }
 }
 
@@ -128,13 +124,13 @@ Seed.prototype._compileTextNode = function (node) {
     return node
 }
 
-Seed.prototype._bind = function (node, bindingInstance) {
+Seed.prototype._bind = function (node, directive) {
 
-    bindingInstance.seed = this
-    bindingInstance.el = node
+    directive.el   = node
+    directive.seed = this
 
-    var key = bindingInstance.key,
-        snr = this.scopeNameRE,
+    var key = directive.key,
+        snr = this.eachPrefixRE,
         isEachKey = snr && snr.test(key),
         scopeOwner = this
     // TODO make scope chain work on nested controllers
@@ -144,14 +140,16 @@ Seed.prototype._bind = function (node, bindingInstance) {
         scopeOwner = this.parentSeed
     }
 
+    directive.key = key
+
     var binding = scopeOwner._bindings[key] || scopeOwner._createBinding(key)
 
     // add directive to this binding
-    binding.instances.push(bindingInstance)
+    binding.instances.push(directive)
 
     // invoke bind hook if exists
-    if (bindingInstance.bind) {
-        bindingInstance.bind(binding.value)
+    if (directive.bind) {
+        directive.bind(binding.value)
     }
 
 }
@@ -192,7 +190,7 @@ Seed.prototype.dump = function () {
 Seed.prototype.destroy = function () {
     for (var key in this._bindings) {
         this._bindings[key].instances.forEach(unbind)
-        ;delete this._bindings[key]
+        delete this._bindings[key]
     }
     this.el.parentNode.removeChild(this.el)
     function unbind (instance) {

+ 0 - 0
src/textNodeParser.js → src/textnode-parser.js


+ 0 - 0
src/watchArray.js → src/watch-array.js