Evan You 13 лет назад
Родитель
Сommit
cf1732bea2
7 измененных файлов с 214 добавлено и 151 удалено
  1. 1 1
      TODO.md
  2. 1 0
      component.json
  3. 18 3
      dev.html
  4. 77 0
      src/directive.js
  5. 37 27
      src/directives.js
  6. 12 0
      src/filters.js
  7. 68 120
      src/main.js

+ 1 - 1
TODO.md

@@ -1,6 +1,6 @@
 - repeat directive by watching an array
 - repeat directive by watching an array
 - make Seeds compositable
 - make Seeds compositable
 - parse textNodes
 - parse textNodes
-- formatter arguments
+- [OK] formatter arguments
 - Seed.extend()
 - Seed.extend()
 - options to pass in templates to Seed.create()
 - options to pass in templates to Seed.create()

+ 1 - 0
component.json

@@ -7,6 +7,7 @@
   "main": "src/main.js",
   "main": "src/main.js",
   "scripts": [
   "scripts": [
     "src/main.js",
     "src/main.js",
+    "src/directive.js",
     "src/directives.js",
     "src/directives.js",
     "src/filters.js"
     "src/filters.js"
   ]
   ]

+ 18 - 3
dev.html

@@ -4,13 +4,18 @@
 		<title>title</title>
 		<title>title</title>
 		<meta charset="utf-8">
 		<meta charset="utf-8">
 		<script src="dist/seed.js"></script>
 		<script src="dist/seed.js"></script>
+		<style type="text/css">
+			.red {
+				color: red;
+			}
+		</style>
 	</head>
 	</head>
 	<body>
 	<body>
-		<div id="test" sd-on-click="changeMessage | .button">
+		<div id="test" sd-on-click="changeMessage | delegate .button">
             <p sd-text="msg.wow | capitalize" sd-on-click="remove"></p>
             <p sd-text="msg.wow | capitalize" sd-on-click="remove"></p>
             <p sd-text="msg.wow | uppercase" class="button"></p>
             <p sd-text="msg.wow | uppercase" class="button"></p>
             <p sd-class-red="error" sd-text="hello"></p>
             <p sd-class-red="error" sd-text="hello"></p>
-            <div sd-repeat="todos">
+            <div sd-each="todos">
             	<span sd-text="text"></span>
             	<span sd-text="text"></span>
             </div>
             </div>
         </div>
         </div>
@@ -27,7 +32,17 @@
 			        },
 			        },
 			        remove: function () {
 			        remove: function () {
 			            app.destroy()
 			            app.destroy()
-			        }
+			        },
+			        todos: [
+			        	{
+			        		title: 'make this shit work',
+			        		done: false
+			        	},
+			        	{
+			        		title: 'make this shit kinda work',
+			        		done: true
+			        	}
+			        ]
 			    }
 			    }
 			})
 			})
 		</script>
 		</script>

+ 77 - 0
src/directive.js

@@ -0,0 +1,77 @@
+var Directives = require('./directives'),
+    Filters    = require('./filters')
+
+var KEY_RE = /^[^\|]+/,
+    FILTERS_RE = /\|[^\|]+/g
+
+function Directive (def, attr, arg, key) {
+
+    if (typeof def === 'function') {
+        this._update = def
+    } else {
+        for (var prop in def) {
+            if (prop === 'update') {
+                this['_update'] = def.update
+                continue
+            }
+            this[prop] = def[prop]
+        }
+    }
+
+    this.attr = attr
+    this.arg  = arg
+    this.key  = 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+/)
+            return {
+                apply: Filters[tokens[0]],
+                args: tokens.length > 1 ? tokens.slice(1) : null
+            }
+        })
+    }
+}
+
+Directive.prototype.update = function (value) {
+    // apply filters
+    if (this.filters) {
+        value = this.applyFilters(value)
+    }
+    this._update(value)
+}
+
+Directive.prototype.applyFilters = function (value) {
+    var filtered = value
+    this.filters.forEach(function (filter) {
+        filtered = filter.apply(filtered, filter.args)
+    })
+    return filtered
+}
+
+module.exports = {
+
+    // make sure the directive and value is valid
+    parse: function (attr, prefix) {
+        
+        if (attr.name.indexOf(prefix) === -1) return null
+
+        var noprefix = attr.name.slice(prefix.length + 1),
+            argIndex = noprefix.indexOf('-'),
+            arg = argIndex === -1
+                ? null
+                : noprefix.slice(argIndex + 1),
+            name = arg
+                ? noprefix.slice(0, argIndex)
+                : noprefix,
+            def = Directives[name]
+
+        var key = attr.value.match(KEY_RE)
+
+        return def && key
+            ? new Directive(def, attr, arg, key[0].trim())
+            : null
+    }
+}

+ 37 - 27
src/directives.js

@@ -1,43 +1,53 @@
 module.exports = {
 module.exports = {
-    text: function (el, value) {
-        el.textContent = value || ''
+
+    text: function (value) {
+        this.el.textContent = value || ''
     },
     },
-    show: function (el, value) {
-        el.style.display = value ? '' : 'none'
+
+    show: function (value) {
+        this.el.style.display = value ? '' : 'none'
     },
     },
-    class: function (el, value, classname) {
-        el.classList[value ? 'add' : 'remove'](classname)
+
+    class: function (value) {
+        this.el.classList[value ? 'add' : 'remove'](this.arg)
     },
     },
+
     on: {
     on: {
-        update: function (el, handler, event, directive) {
-            if (!directive.handlers) {
-                directive.handlers = {}
+        update: function (handler) {
+            var event = this.arg
+            if (!this.handlers) {
+                this.handlers = {}
             }
             }
-            var handlers = directive.handlers
+            var handlers = this.handlers
             if (handlers[event]) {
             if (handlers[event]) {
-                el.removeEventListener(event, handlers[event])
+                this.el.removeEventListener(event, handlers[event])
             }
             }
             if (handler) {
             if (handler) {
-                handler = handler.bind(el)
-                el.addEventListener(event, handler)
+                handler = handler.bind(this.el)
+                this.el.addEventListener(event, handler)
                 handlers[event] = handler
                 handlers[event] = handler
             }
             }
         },
         },
-        unbind: function (el, event, directive) {
-            if (directive.handlers) {
-                el.removeEventListener(event, directive.handlers[event])
-            }
-        },
-        customFilter: function (handler, selectors) {
-            return function (e) {
-                var match = selectors.every(function (selector) {
-                    return e.target.webkitMatchesSelector(selector)
-                })
-                if (match) handler.apply(this, arguments)
+        unbind: function () {
+            var event = this.arg
+            if (this.handlers) {
+                this.el.removeEventListener(event, this.handlers[event])
             }
             }
         }
         }
     },
     },
-    repeat: function () {
-        
+
+    each: {
+        update: function () {
+            // augmentArray(collection, this)
+            // console.log('collection updated')
+        }
+        // mutate: function (mutation) {
+            
+        // }
     }
     }
-}
+
+}
+
+// function augmentArray (collection, directive) {
+    
+// }

+ 12 - 0
src/filters.js

@@ -1,9 +1,21 @@
 module.exports = {
 module.exports = {
+
     capitalize: function (value) {
     capitalize: function (value) {
         value = value.toString()
         value = value.toString()
         return value.charAt(0).toUpperCase() + value.slice(1)
         return value.charAt(0).toUpperCase() + value.slice(1)
     },
     },
+
     uppercase: function (value) {
     uppercase: function (value) {
         return value.toUpperCase()
         return value.toUpperCase()
+    },
+
+    delegate: function (handler, selectors) {
+        return function (e) {
+            var match = selectors.every(function (selector) {
+                return e.target.webkitMatchesSelector(selector)
+            })
+            if (match) handler.apply(this, arguments)
+        }
     }
     }
+
 }
 }

+ 68 - 120
src/main.js

@@ -1,5 +1,5 @@
 var prefix      = 'sd',
 var prefix      = 'sd',
-    Filters     = require('./filters'),
+    Directive   = require('./directive'),
     Directives  = require('./directives'),
     Directives  = require('./directives'),
     selector    = Object.keys(Directives).map(function (d) {
     selector    = Object.keys(Directives).map(function (d) {
         return '[' + prefix + '-' + d + ']'
         return '[' + prefix + '-' + d + ']'
@@ -11,163 +11,111 @@ function Seed (opts) {
         root = this.el = document.getElementById(opts.id),
         root = this.el = document.getElementById(opts.id),
         els  = root.querySelectorAll(selector)
         els  = root.querySelectorAll(selector)
 
 
-    var bindings = self._bindings = {} // internal real data
-    self.scope = {} // external interface
+    self.bindings = {}
+    self.scope = {}
 
 
     // process nodes for directives
     // process nodes for directives
-    ;[].forEach.call(els, processNode)
-    processNode(root)
+    ;[].forEach.call(els, this.compileNode.bind(this))
+    this.compileNode(root)
 
 
     // initialize all variables by invoking setters
     // initialize all variables by invoking setters
-    for (var key in bindings) {
+    for (var key in self.bindings) {
         self.scope[key] = opts.scope[key]
         self.scope[key] = opts.scope[key]
     }
     }
 
 
-    function processNode (el) {
-        cloneAttributes(el.attributes).forEach(function (attr) {
-            var directive = parseDirective(attr)
-            if (directive) {
-                bindDirective(self, el, bindings, directive)
-            }
-        })
-    }
-}
-
-Seed.prototype.dump = function () {
-    var data = {}
-    for (var key in this._bindings) {
-        data[key] = this._bindings[key].value
-    }
-    return data
-}
-
-Seed.prototype.destroy = function () {
-    for (var key in this._bindings) {
-        this._bindings[key].directives.forEach(function (directive) {
-            if (directive.definition.unbind) {
-                directive.definition.unbind(
-                    directive.el,
-                    directive.argument,
-                    directive
-                )
-            }
-        })
-    }
-    this.el.parentNode.remove(this.el)
 }
 }
 
 
-// clone attributes so they don't change
-function cloneAttributes (attributes) {
-    return [].map.call(attributes, function (attr) {
-        return {
-            name: attr.name,
-            value: attr.value
+Seed.prototype.compileNode = function (node) {
+    var self = this
+    cloneAttributes(node.attributes).forEach(function (attr) {
+        var directive = Directive.parse(attr, prefix)
+        if (directive) {
+            self.bind(node, directive)
         }
         }
     })
     })
 }
 }
 
 
-function bindDirective (seed, el, bindings, directive) {
-    directive.el = el
-    el.removeAttribute(directive.attr.name)
-    var key = directive.key,
-        binding = bindings[key]
-    if (!binding) {
-        bindings[key] = binding = {
-            value: undefined,
-            directives: []
-        }
-    }
+Seed.prototype.bind = function (node, directive) {
+
+    directive.el = node
+    node.removeAttribute(directive.attr.name)
+
+    var key      = directive.key,
+        binding  = this.bindings[key] || this.createBinding(key)
+
+    // add directive to this binding
     binding.directives.push(directive)
     binding.directives.push(directive)
+
     // invoke bind hook if exists
     // invoke bind hook if exists
     if (directive.bind) {
     if (directive.bind) {
-        directive.bind(el, binding.value)
-    }
-    if (!seed.scope.hasOwnProperty(key)) {
-        bindAccessors(seed, key, binding)
+        directive.bind(node, binding.value)
     }
     }
+
 }
 }
 
 
-function bindAccessors (seed, key, binding) {
-    Object.defineProperty(seed.scope, key, {
+Seed.prototype.createBinding = function (key) {
+
+    var binding = {
+        value: undefined,
+        directives: []
+    }
+
+    this.bindings[key] = binding
+
+    // bind accessor triggers to scope
+    Object.defineProperty(this.scope, key, {
         get: function () {
         get: function () {
             return binding.value
             return binding.value
         },
         },
         set: function (value) {
         set: function (value) {
             binding.value = value
             binding.value = value
             binding.directives.forEach(function (directive) {
             binding.directives.forEach(function (directive) {
-                var filteredValue = value && directive.filters
-                    ? applyFilters(value, directive)
-                    : value
-                directive.update(
-                    directive.el,
-                    filteredValue,
-                    directive.argument,
-                    directive,
-                    seed
-                )
+                directive.update(value)
             })
             })
         }
         }
     })
     })
+
+    return binding
 }
 }
 
 
-function parseDirective (attr) {
-
-    if (attr.name.indexOf(prefix) === -1) return
-
-    // parse directive name and argument
-    var noprefix = attr.name.slice(prefix.length + 1),
-        argIndex = noprefix.indexOf('-'),
-        dirname  = argIndex === -1
-            ? noprefix
-            : noprefix.slice(0, argIndex),
-        def = Directives[dirname],
-        arg = argIndex === -1
-            ? null
-            : noprefix.slice(argIndex + 1)
-
-    // parse scope variable key and pipe filters
-    var exp = attr.value,
-        pipeIndex = exp.indexOf('|'),
-        key = pipeIndex === -1
-            ? exp.trim()
-            : exp.slice(0, pipeIndex).trim(),
-        filters = pipeIndex === -1
-            ? null
-            : exp.slice(pipeIndex + 1).split('|').map(function (filter) {
-                return filter.trim()
-            })
+Seed.prototype.dump = function () {
+    var data = {}
+    for (var key in this._bindings) {
+        data[key] = this._bindings[key].value
+    }
+    return data
+}
 
 
-    return def
-        ? {
-            attr: attr,
-            key: key,
-            filters: filters,
-            definition: def,
-            argument: arg,
-            update: typeof def === 'function'
-                ? def
-                : def.update
+Seed.prototype.destroy = function () {
+    for (var key in this._bindings) {
+        this._bindings[key].directives.forEach(unbind)
+    }
+    this.el.parentNode.remove(this.el)
+    function unbind (directive) {
+        if (directive.unbind) {
+            directive.unbind()
         }
         }
-        : null
+    }
 }
 }
 
 
-function applyFilters (value, directive) {
-    if (directive.definition.customFilter) {
-        return directive.definition.customFilter(value, directive.filters)
-    } else {
-        directive.filters.forEach(function (filter) {
-            if (Filters[filter]) {
-                value = Filters[filter](value)
-            }
-        })
-        return value
-    }
+// 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 = {
 module.exports = {
     create: function (opts) {
     create: function (opts) {
         return new Seed(opts)
         return new Seed(opts)
     },
     },
-    filters: Filters,
-    directives: Directives
+    directive: function () {
+        // create dir
+    },
+    filter: function () {
+        // create filter
+    }
 }
 }