|
|
@@ -17,24 +17,28 @@ var uid = 0
|
|
|
* @param {Node} el
|
|
|
* @param {Vue} vm
|
|
|
* @param {Object} descriptor
|
|
|
- * - {String} arg
|
|
|
* - {String} expression
|
|
|
- * - {Array<Object>} filters
|
|
|
+ * - {String} [arg]
|
|
|
+ * - {Array<Object>} [filters]
|
|
|
+ * @param {Object|Function} definition
|
|
|
+ * - {Function} update
|
|
|
+ * - {Function} [bind]
|
|
|
+ * - {Function} [unbind]
|
|
|
+ * - {Boolean} [literal]
|
|
|
+ * - {Boolean} [twoway]
|
|
|
+ * - {Array} [params]
|
|
|
* @constructor
|
|
|
*/
|
|
|
|
|
|
-function Directive (type, el, vm, descriptor) {
|
|
|
+function Directive (type, el, vm, descriptor, definition) {
|
|
|
// public
|
|
|
this.type = type
|
|
|
this.el = el
|
|
|
this.vm = vm
|
|
|
+ this.value = undefined
|
|
|
this.arg = descriptor.arg
|
|
|
- this.expression = descriptor.expression
|
|
|
+ this.expression = descriptor.expression.trim()
|
|
|
this.filters = descriptor.filters
|
|
|
- this.value = undefined
|
|
|
-
|
|
|
- // TODO
|
|
|
- // mixin type definition
|
|
|
|
|
|
// private
|
|
|
this._id = ++uid
|
|
|
@@ -43,18 +47,82 @@ function Directive (type, el, vm, descriptor) {
|
|
|
this._deps = Object.create(null)
|
|
|
this._newDeps = Object.create(null)
|
|
|
|
|
|
- // TODO
|
|
|
- // test for simple path vs. expression
|
|
|
- this._getter = expParser.parse(this.expression)
|
|
|
- this._setter = this._getter.setter
|
|
|
+ // init definition
|
|
|
+ this._initDef(definition)
|
|
|
+
|
|
|
+ if (this.expression && !this.isLiteral) {
|
|
|
+ // TODO
|
|
|
+ // test for simple path vs. expression
|
|
|
+ this._getter = expParser.parse(this.expression, this.twoway)
|
|
|
+ this._setter = this._getter.setter
|
|
|
+
|
|
|
+ // init filters
|
|
|
+ this._initFilters()
|
|
|
+ // init dependencies
|
|
|
+ this._initDeps()
|
|
|
+ // init methods that need to be context-bound
|
|
|
+ this._initBoundMethods()
|
|
|
+ // update for the first time
|
|
|
+ this._realUpdate(true)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+var p = Directive.prototype
|
|
|
+
|
|
|
+/**
|
|
|
+ * Initialize read and write filters
|
|
|
+ */
|
|
|
|
|
|
+p._initFilters = function () {
|
|
|
+ if (!this.filters) {
|
|
|
+ return
|
|
|
+ }
|
|
|
var self = this
|
|
|
+ var vm = this.vm
|
|
|
+ var registry = vm.$options.filters
|
|
|
+ this.filters.forEach(function (f) {
|
|
|
+ var def = registry[f.name]
|
|
|
+ var args = f.args
|
|
|
+ var read, write
|
|
|
+ if (typeof def === 'function') {
|
|
|
+ read = def
|
|
|
+ } else {
|
|
|
+ read = def.read
|
|
|
+ write = def.write
|
|
|
+ }
|
|
|
+ if (read) {
|
|
|
+ if (!self._readFilters) {
|
|
|
+ self._readFilters = []
|
|
|
+ }
|
|
|
+ self._readFilters.push(function (value) {
|
|
|
+ return args
|
|
|
+ ? read.apply(vm, [value].concat(args))
|
|
|
+ : read.call(vm, value)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ if (write) {
|
|
|
+ if (!self._writeFilters) {
|
|
|
+ self._writeFilters = []
|
|
|
+ }
|
|
|
+ self._writeFilters.push(function (value) {
|
|
|
+ return args
|
|
|
+ ? write.apply(vm, [value, self.value].concat(args))
|
|
|
+ : write.call(vm, value, self.value)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Add root level path as a dependency.
|
|
|
+ * this is specifically for the case where the expression
|
|
|
+ * references a non-existing root level path, and later
|
|
|
+ * that path is created with `vm.$add`.
|
|
|
+ * e.g. "a && a.b"
|
|
|
+ */
|
|
|
|
|
|
- // add root level path as a dependency.
|
|
|
- // this is specifically for the case where the expression
|
|
|
- // references a non-existing root level path, and later
|
|
|
- // that path is created with `vm.$add`.
|
|
|
- // e.g. "a && a.b"
|
|
|
+p._initDeps = function () {
|
|
|
+ var self = this
|
|
|
var paths = this._getter.paths
|
|
|
paths.forEach(function (path) {
|
|
|
if (path.indexOf('.') < 0 && path.indexOf('[') < 0) {
|
|
|
@@ -62,6 +130,37 @@ function Directive (type, el, vm, descriptor) {
|
|
|
}
|
|
|
})
|
|
|
this._deps = this._newDeps
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Initialize the directive instance's definition.
|
|
|
+ */
|
|
|
+
|
|
|
+p._initDef = function (definition) {
|
|
|
+ _.extend(this, definition)
|
|
|
+ // init params
|
|
|
+ var el = this.el
|
|
|
+ var attrs = this.paramAttributes
|
|
|
+ if (attrs) {
|
|
|
+ var params = this.params = {}
|
|
|
+ attrs.forEach(function (p) {
|
|
|
+ params[p] = el.getAttribute(p)
|
|
|
+ el.removeAttribute(p)
|
|
|
+ })
|
|
|
+ }
|
|
|
+ // call bind hook
|
|
|
+ if (this.bind) {
|
|
|
+ this.bind()
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * Initialize a few methods that need to be context-bound
|
|
|
+ * so we don't have to create them ad-hoc everytime
|
|
|
+ */
|
|
|
+
|
|
|
+p._initBoundMethods = function () {
|
|
|
+ var self = this
|
|
|
|
|
|
/**
|
|
|
* Unlock function used in .set()
|
|
|
@@ -89,17 +188,13 @@ function Directive (type, el, vm, descriptor) {
|
|
|
init
|
|
|
) {
|
|
|
self.value = value
|
|
|
- // TODO call definition update
|
|
|
- console.log('updated! new value: ' + value)
|
|
|
+ if (self.update) {
|
|
|
+ self.update(value)
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- // update for the first time
|
|
|
- this._realUpdate(true)
|
|
|
}
|
|
|
|
|
|
-var p = Directive.prototype
|
|
|
-
|
|
|
/**
|
|
|
* Add a binding dependency to this directive.
|
|
|
*
|
|
|
@@ -128,7 +223,7 @@ p._addDep = function (path) {
|
|
|
p.get = function () {
|
|
|
this._beforeGet()
|
|
|
var value = this._getter.call(this.vm, this.vm.$scope)
|
|
|
- if (this.filters) {
|
|
|
+ if (this._readFilters) {
|
|
|
value = this._applyFilters(value, -1)
|
|
|
}
|
|
|
this._afterGet()
|
|
|
@@ -145,7 +240,7 @@ p.get = function () {
|
|
|
p.set = function (value) {
|
|
|
if (this._setter) {
|
|
|
this._locked = true
|
|
|
- if (this.filters) {
|
|
|
+ if (this._writeFilters) {
|
|
|
value = this._applyFilters(value, 1)
|
|
|
}
|
|
|
this._setter.call(this.vm, this.vm.$scope, value)
|
|
|
@@ -194,13 +289,13 @@ p._update = function () {
|
|
|
*/
|
|
|
|
|
|
p._applyFilters = function (value, direction) {
|
|
|
- if (direction < 0) {
|
|
|
- // TODO read
|
|
|
- return value
|
|
|
- } else {
|
|
|
- // TODO write
|
|
|
- return value
|
|
|
+ var filters = direction > 0
|
|
|
+ ? this._writeFilters
|
|
|
+ : this._readFilters
|
|
|
+ for (var i = 0, l = filters.length; i < l; i++) {
|
|
|
+ value = filters[i](value)
|
|
|
}
|
|
|
+ return value
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -208,6 +303,7 @@ p._applyFilters = function (value, direction) {
|
|
|
*/
|
|
|
|
|
|
p._teardown = function () {
|
|
|
+ if (this.unbind) this.unbind()
|
|
|
this._unbound = true
|
|
|
for (var p in this._deps) {
|
|
|
this._deps[p]._removeSub(this)
|