Evan You 13 лет назад
Родитель
Сommit
3149839736
7 измененных файлов с 132 добавлено и 89 удалено
  1. 34 22
      dev.html
  2. 1 2
      src/config.js
  3. 14 7
      src/directive.js
  4. 30 18
      src/directives.js
  5. 5 5
      src/filters.js
  6. 0 8
      src/main.js
  7. 48 27
      src/seed.js

+ 34 - 22
dev.html

@@ -8,6 +8,9 @@
 			.red {
 				color: red;
 			}
+            .todo.done {
+                text-decoration: line-through;
+            }
 		</style>
 	</head>
 	<body>
@@ -17,42 +20,51 @@
             <p sd-text="total | money"></p>
             <p sd-class-red="error" sd-text="hello"></p>
             <ul sd-show="todos">
-            	<li sd-each-todo="todos" sd-text="todo.text"></li>
+            	<li sd-each-todo="todos">
+                    <span class="todo" sd-text="todo.title" sd-class-done="todo.done"></span>   
+                </li>
             </ul>
         </div>
 		<script>
+
 			var Seed = require('seed')
 
 			Seed.filter('money', function (value) {
 			    return '$' + value.toFixed(2)
 			})
 
-			// define a seed
-			var Todos = Seed.extend({
-		        changeMessage: function () {
-		            this.scope['msg.wow'] = 'hola'
-		        },
-		        remove: function () {
-		            this.destroy()
-		        }
-		    })
+            var list = [
+                {
+                    title: 'make this shit kinda work',
+                    done: true
+                },
+                {
+                    title: 'make this shit work',
+                    done: false
+                },
+                {
+                    title: 'more features!!!',
+                    done: false
+                }
+            ]
+
+            var s = Date.now()
 
-			var todos = new Todos('#test', {
-                total     : 1000,
+			var todos = new Seed('#test', {
+                total     : Math.random() * 100000,
                 'msg.wow' : 'wow',
                 hello     : 'hello',
-                todos     : [
-                    {
-                        title: 'make this shit work',
-                        done: false
-                    },
-                    {
-                        title: 'make this shit kinda work',
-                        done: true
-                    }
-                ]
+                todos     : list,
+                changeMessage: function () {
+                    this.scope['msg.wow'] = 'hola'
+                },
+                remove: function () {
+                    this.destroy()
+                }
             })
 
+            console.log(Date.now() - s + 'ms')
+
 		</script>
 	</body>
 </html>

+ 1 - 2
src/config.js

@@ -1,4 +1,3 @@
 module.exports = {
-    prefix: 'sd',
-    selector: null
+    prefix: 'sd'
 }

+ 14 - 7
src/directive.js

@@ -2,8 +2,10 @@ var config     = require('./config'),
     Directives = require('./directives'),
     Filters    = require('./filters')
 
-var KEY_RE = /^[^\|]+/,
-    FILTERS_RE = /\|[^\|]+/g
+var KEY_RE          = /^[^\|]+/,
+    FILTERS_RE      = /\|[^\|]+/g,
+    FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
+    QUOTE_RE        = /'/g
 
 function Directive (def, attr, arg, key) {
 
@@ -26,12 +28,17 @@ function Directive (def, attr, arg, key) {
     var filters = attr.value.match(FILTERS_RE)
     if (filters) {
         this.filters = filters.map(function (filter) {
-            // TODO test performance against regex
-            var tokens = filter.replace('|', '').trim().split(/\s+/)
+            var tokens = filter.slice(1)
+                .match(FILTER_TOKEN_RE)
+                .map(function (token) {
+                    return token.replace(QUOTE_RE, '').trim()
+                })
             return {
-                name: tokens[0],
-                apply: Filters[tokens[0]],
-                args: tokens.length > 1 ? tokens.slice(1) : null
+                name  : tokens[0],
+                apply : Filters[tokens[0]],
+                args  : tokens.length > 1
+                        ? tokens.slice(1)
+                        : null
             }
         })
     }

+ 30 - 18
src/directives.js

@@ -26,6 +26,7 @@ module.exports = {
                 this.el.removeEventListener(event, handlers[event])
             }
             if (handler) {
+                handler = handler.bind(this.seed)
                 this.el.addEventListener(event, handler)
                 handlers[event] = handler
             }
@@ -39,30 +40,41 @@ module.exports = {
     },
 
     each: {
+        bind: function () {
+            this.el['sd-block'] = true
+            this.prefixRE = new RegExp('^' + this.arg + '.')
+            var ctn = this.container = this.el.parentNode
+            this.marker = document.createComment('sd-each-' + this.arg + '-marker')
+            ctn.insertBefore(this.marker, this.el)
+            ctn.removeChild(this.el)
+            this.childSeeds = []
+        },
         update: function (collection) {
+            if (this.childSeeds.length) {
+                this.childSeeds.forEach(function (child) {
+                    child.destroy()
+                })
+                this.childSeeds = []
+            }
             watchArray(collection, this.mutate.bind(this))
-            // for each in array
-            //   - create a Seed element using the el's outerHTML and raw data object
-            //   - replace the raw object with new Seed's scope object
+            var self = this
+            collection.forEach(function (item, i) {
+                self.childSeeds.push(self.buildItem(item, i, collection))
+            })
         },
         mutate: function (mutation) {
             console.log(mutation)
-            console.log(this)
+        },
+        buildItem: function (data, index, collection) {
+            var node = this.el.cloneNode(true),
+                spore = new Seed(node, data, {
+                    eachPrefixRE: this.prefixRE,
+                    parentScope: this.seed.scope
+                })
+            this.container.insertBefore(node, this.marker)
+            collection[index] = spore.scope
+            return spore
         }
     }
 
-}
-
-var push = [].push,
-    slice = [].slice
-
-function augmentArray (collection, directive) {
-    collection.push = function (element) {
-        push.call(this, arguments)
-        directive.mutate({
-            event: 'push',
-            elements: slice.call(arguments),
-            collection: collection
-        })
-    }
 }

+ 5 - 5
src/filters.js

@@ -9,12 +9,12 @@ module.exports = {
         return value.toUpperCase()
     },
 
-    delegate: function (handler, selectors) {
+    delegate: function (handler, args) {
+        var selector = args[0]
         return function (e) {
-            var match = selectors.every(function (selector) {
-                return e.target.webkitMatchesSelector(selector)
-            })
-            if (match) handler.apply(this, arguments)
+            if (e.target.webkitMatchesSelector(selector)) {
+                handler.apply(this, arguments)
+            }
         }
     }
 

+ 0 - 8
src/main.js

@@ -3,14 +3,7 @@ var config     = require('./config'),
     directives = require('./directives'),
     filters    = require('./filters')
 
-function buildSelector () {
-    config.selector = Object.keys(directives).map(function (directive) {
-        return '[' + config.prefix + '-' + directive + ']'
-    }).join()
-}
-
 Seed.config = config
-buildSelector()
 
 Seed.extend = function (opts) {
     var Spore = function () {
@@ -32,7 +25,6 @@ Seed.extend = function (opts) {
 
 Seed.directive = function (name, fn) {
     directives[name] = fn
-    buildSelector()
 }
 
 Seed.filter = function (name, fn) {

+ 48 - 27
src/seed.js

@@ -1,21 +1,21 @@
 var config      = require('./config'),
-    Directive   = require('./directive'),
-    Directives  = require('./directives'),
-    Filters     = require('./filters')
+    Directive   = require('./directive')
 
-function Seed (el, data) {
+var map  = Array.prototype.map,
+    each = Array.prototype.forEach
+
+function Seed (el, data, options) {
 
     if (typeof el === 'string') {
         el = document.querySelector(el)
     }
 
     this.el         = el
-    this._bindings  = {}
     this.scope      = {}
+    this._bindings  = {}
+    this._options   = options || {}
 
     // process nodes for directives
-    var els  = el.querySelectorAll(config.selector)
-    ;[].forEach.call(els, this._compileNode.bind(this))
     this._compileNode(el)
 
     // initialize all variables by invoking setters
@@ -27,28 +27,58 @@ function Seed (el, data) {
 
 Seed.prototype._compileNode = function (node) {
     var self = this
-    cloneAttributes(node.attributes).forEach(function (attr) {
-        var directive = Directive.parse(attr)
-        if (directive) {
-            self._bind(node, directive)
-        }
-    })
+
+    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,
+                value: attr.value
+            }
+        })
+        attrs.forEach(function (attr) {
+            var directive = Directive.parse(attr)
+            if (directive) {
+                self._bind(node, directive)
+            }
+        })
+    }
+
+    if (!node['sd-block'] && node.childNodes.length) {
+        each.call(node.childNodes, function (child) {
+            self._compileNode(child)
+        })
+    }
+}
+
+Seed.prototype._compileTextNode = function (node) {
+    
 }
 
 Seed.prototype._bind = function (node, directive) {
 
+    directive.seed = this
     directive.el = node
+
     node.removeAttribute(directive.attr.name)
 
-    var key      = directive.key,
-        binding  = this._bindings[key] || this._createBinding(key)
+    var key = directive.key,
+        epr = this._options.eachPrefixRE
+    if (epr) {
+        key = key.replace(epr, '')
+    }
+
+    var binding  = this._bindings[key] || this._createBinding(key)
 
     // add directive to this binding
     binding.directives.push(directive)
 
     // invoke bind hook if exists
     if (directive.bind) {
-        directive.bind(node, binding.value)
+        directive.bind.call(directive, binding.value)
     }
 
 }
@@ -89,8 +119,9 @@ Seed.prototype.dump = function () {
 Seed.prototype.destroy = function () {
     for (var key in this._bindings) {
         this._bindings[key].directives.forEach(unbind)
+        delete this._bindings[key]
     }
-    this.el.parentNode.remove(this.el)
+    this.el.parentNode.removeChild(this.el)
     function unbind (directive) {
         if (directive.unbind) {
             directive.unbind()
@@ -98,14 +129,4 @@ Seed.prototype.destroy = function () {
     }
 }
 
-// clone attributes so they don't change
-function cloneAttributes (attributes) {
-    return [].map.call(attributes, function (attr) {
-        return {
-            name: attr.name,
-            value: attr.value
-        }
-    })
-}
-
 module.exports = Seed