|
|
@@ -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
|
|
|
},
|