فهرست منبع

kinda work now except for scope nesting

Evan You 13 سال پیش
والد
کامیت
62b75d4983
7فایلهای تغییر یافته به همراه113 افزوده شده و 93 حذف شده
  1. 0 1
      TODO.md
  2. 34 31
      dev.html
  3. 0 1
      src/binding.js
  4. 7 16
      src/directives.js
  5. 1 2
      src/filters.js
  6. 2 7
      src/main.js
  7. 69 35
      src/seed.js

+ 0 - 1
TODO.md

@@ -1,4 +1,3 @@
-- fix event delegation
 - improve arrayWatcher
 - make Seeds compositable
 - parse textNodes

+ 34 - 31
dev.html

@@ -14,18 +14,19 @@
 		</style>
 	</head>
 	<body>
-		<div id="app" sd-controller="TodoList">
-            <p sd-text="msg | capitalize" sd-on="click:changeMessage"></p>
+		<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>
             <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-on="click:changeMessage, click:todo.toggle"
                     sd-text="todo.title"
                 ></li>
             </ul>
@@ -40,44 +41,46 @@
                     : ''
 			})
 
-            Seed.controller('TodoList', {
-                changeMessage: function () {
-                    this.scope.msg = 'It works!'
-                },
-                remove: function () {
-                    this.destroy()
+            Seed.controller('TodoList', function (scope, seed) {
+                scope.changeMessage = function () {
+                    scope.msg = 'It works!'
+                }
+                scope.remove = function () {
+                    seed.destroy()
                 }
             })
 
-            Seed.controller('Todo', {
-                toggle: function () {
-                    this.scope.done = !scope.done
+            Seed.controller('Todo', function (scope) {
+                scope.toggle = function () {
+                    scope.done = !scope.done
                 }
             })
 
             var s = Date.now()
 
+            var data = {
+                msg: 'hello!',
+                total: 9999,
+                error: true,
+                todos: [
+                    {
+                        title: 'hello!',
+                        done: true
+                    },
+                    {
+                        title: 'hello!!',
+                        done: false
+                    },
+                    {
+                        title: 'hello!!!',
+                        done: false
+                    }
+                ]
+            }
+
 			var app = Seed.bootstrap({
                 el: '#app',
-                data: {
-                    msg: 'hello!',
-                    total: 9999,
-                    error: true,
-                    todos: [
-                        {
-                            title: 'hello!',
-                            done: true
-                        },
-                        {
-                            title: 'hello!!',
-                            done: false
-                        },
-                        {
-                            title: 'hello!!!',
-                            done: false
-                        }
-                    ]
-                }
+                data: data
             })
 
             console.log(Date.now() - s + 'ms')

+ 0 - 1
src/binding.js

@@ -22,7 +22,6 @@ function Binding (directiveName, expression) {
             }
         }
     }
-    this.directiveName = directiveName
 
     var rawKey   = expression.match(KEY_RE)[0], // guarded in parse
         argMatch = rawKey.match(ARG_RE)

+ 7 - 16
src/directives.js

@@ -1,5 +1,4 @@
 var config = require('./config'),
-    controllers = require('./controllers'),
     watchArray = require('./watchArray')
 
 module.exports = {
@@ -19,17 +18,12 @@ module.exports = {
     on: {
         update: function (handler) {
             var event = this.arg
-            if (!this.handlers) {
-                this.handlers = {}
-            }
-            var handlers = this.handlers
-            if (handlers[event]) {
-                this.el.removeEventListener(event, handlers[event])
+            if (this.handler) {
+                this.el.removeEventListener(event, this.handler)
             }
             if (handler) {
-                handler = handler.bind(this.seed)
                 this.el.addEventListener(event, handler)
-                handlers[event] = handler
+                this.handler = handler
             }
         },
         unbind: function () {
@@ -42,7 +36,7 @@ module.exports = {
 
     each: {
         bind: function () {
-            this.el['sd-block'] = true
+            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')
@@ -68,13 +62,10 @@ module.exports = {
         },
         buildItem: function (data, index, collection) {
             var Seed = require('./seed'),
-                node = this.el.cloneNode(true),
-                ctrl = node.getAttribute(config.prefix + '-controller'),
-                Ctrl = ctrl ? controllers[ctrl] : Seed
-            if (ctrl) node.removeAttribute(config.prefix + '-controller')
-            var spore = new Ctrl(node, data, {
+                node = this.el.cloneNode(true)
+            var spore = new Seed(node, data, {
                     eachPrefixRE: this.prefixRE,
-                    parentScope: this.seed.scope
+                    parentSeed: this.seed
                 })
             this.container.insertBefore(node, this.marker)
             collection[index] = spore.scope

+ 1 - 2
src/filters.js

@@ -6,13 +6,12 @@ module.exports = {
     },
 
     uppercase: function (value) {
-        return value.toUpperCase()
+        return value.toString().toUpperCase()
     },
 
     delegate: function (handler, args) {
         var selector = args[0]
         return function (e) {
-            console.log('triggered')
             if (delegateCheck(e.target, e.currentTarget, selector)) {
                 handler.apply(this, arguments)
             }

+ 2 - 7
src/main.js

@@ -28,8 +28,7 @@ Seed.controller = function (id, extensions) {
     if (controllers[id]) {
         console.warn('controller "' + id + '" was already registered and has been overwritten.')
     }
-    var c = controllers[id] = Seed.extend(extensions)
-    return c
+    controllers[id] = extensions
 }
 
 Seed.bootstrap = function (seeds) {
@@ -41,11 +40,7 @@ Seed.bootstrap = function (seeds) {
             el = document.querySelector(el)
         }
         if (!el) console.warn('invalid element or selector: ' + seed.el)
-        var ctrlid = el.getAttribute(config.prefix + '-controller'),
-            Controller = ctrlid ? controllers[ctrlid] : Seed
-        if (!Controller) console.warn('controller ' + ctrlid + ' is not defined.')
-        if (ctrlid) el.removeAttribute(config.prefix + '-controller')
-        instances.push(new Controller(el, seed.data, seed.options))
+        instances.push(new Seed(el, seed.data, seed.options))
     })
     return instances.length > 1
         ? instances

+ 69 - 35
src/seed.js

@@ -1,11 +1,20 @@
-var config = require('./config'),
+var config        = require('./config'),
+    controllers   = require('./controllers'),
     bindingParser = require('./binding')
 
-var map    = Array.prototype.map,
-    each   = Array.prototype.forEach
+var map  = Array.prototype.map,
+    each = Array.prototype.forEach
+
+// lazy init
+var ctrlAttr,
+    eachAttr
 
 function Seed (el, data, options) {
 
+    // refresh
+    ctrlAttr = config.prefix + '-controller'
+    eachAttr = config.prefix + '-each'
+
     if (typeof el === 'string') {
         el = document.querySelector(el)
     }
@@ -20,47 +29,73 @@ function Seed (el, data, options) {
         dataCopy[key] = data[key]
     }
 
+    // if has controller
+    var ctrlID = el.getAttribute(ctrlAttr),
+        controller = null
+    if (ctrlID) {
+        controller = controllers[ctrlID]
+        el.removeAttribute(ctrlAttr)
+        if (!controller) throw new Error('controller ' + ctrlID + ' is not defined.')
+    }
+
     // process nodes for directives
-    this._compileNode(el)
+    this._compileNode(el, true)
+
+    // copy in methods from controller
+    if (controller) {
+        controller.call(null, this.scope, this)
+    }
 
     // initialize all variables by invoking setters
-    for (key in this._bindings) {
+    for (key in dataCopy) {
         this.scope[key] = dataCopy[key]
     }
 
 }
 
-Seed.prototype._compileNode = function (node) {
-    var self = this,
-        ctrl = config.prefix + '-controller'
+Seed.prototype._compileNode = function (node, root) {
+    var self = this
 
     if (node.nodeType === 3) {
         // text node
         self._compileTextNode(node)
     } else if (node.attributes && node.attributes.length) {
-        // clone attributes because the list can change
-        var attrs = map.call(node.attributes, function (attr) {
-            return {
-                name: attr.name,
-                expressions: attr.value.split(',')
+        var eachExp = node.getAttribute(eachAttr),
+            ctrlExp = node.getAttribute(ctrlAttr)
+        if (eachExp) {
+            // each block
+            var binding = bindingParser.parse(eachAttr, eachExp)
+            if (binding) {
+                self._bind(node, binding)
             }
-        })
-        attrs.forEach(function (attr) {
-            if (attr.name === ctrl) return
-            attr.expressions.forEach(function (exp) {
-                var binding = bindingParser.parse(attr.name, exp)
-                if (binding) {
-                    self._bind(node, binding)
+        } else if (!ctrlExp || root) { // skip nested controllers
+            // normal node
+            // 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) {
+                var valid = false
+                attr.expressions.forEach(function (exp) {
+                    var binding = bindingParser.parse(attr.name, exp)
+                    if (binding) {
+                        valid = true
+                        self._bind(node, binding)
+                    }
+                })
+                if (valid) node.removeAttribute(attr.name)
+            })
+            if (node.childNodes.length) {
+                each.call(node.childNodes, function (child) {
+                    self._compileNode(child)
+                })
+            }
+        }
     }
 
-    if (!node['sd-block'] && node.childNodes.length) {
-        each.call(node.childNodes, function (child) {
-            self._compileNode(child)
-        })
-    }
 }
 
 Seed.prototype._compileTextNode = function (node) {
@@ -72,19 +107,18 @@ Seed.prototype._bind = function (node, bindingInstance) {
     bindingInstance.seed = this
     bindingInstance.el = node
 
-    node.removeAttribute(config.prefix + '-' + bindingInstance.directiveName)
-
     var key = bindingInstance.key,
-        scope = this.scope,
         epr = this._options.eachPrefixRE,
-        isEach = epr && epr.test(key)
+        isEachKey = epr && epr.test(key),
+        seed = this
     // TODO make scope chain work on nested controllers
-    if (isEach) {
+    if (isEachKey) {
         key = key.replace(epr, '')
-        scope = this._options.parentScope
+    } else if (epr) {
+        seed = this._options.parentSeed
     }
 
-    var binding  = this._bindings[key] || this._createBinding(key, scope)
+    var binding = seed._bindings[key] || seed._createBinding(key)
 
     // add directive to this binding
     binding.instances.push(bindingInstance)
@@ -96,7 +130,7 @@ Seed.prototype._bind = function (node, bindingInstance) {
 
 }
 
-Seed.prototype._createBinding = function (key, scope) {
+Seed.prototype._createBinding = function (key) {
 
     var binding = {
         value: null,
@@ -106,7 +140,7 @@ Seed.prototype._createBinding = function (key, scope) {
     this._bindings[key] = binding
 
     // bind accessor triggers to scope
-    Object.defineProperty(scope, key, {
+    Object.defineProperty(this.scope, key, {
         get: function () {
             return binding.value
         },