silk-gui
Version:
GUI for developers and Node OS
214 lines (194 loc) • 4.28 kB
JavaScript
var _ = require('../util')
var Observer = require('../observer')
var Dep = require('../observer/dep')
/**
* Setup the scope of an instance, which contains:
* - observed data
* - computed properties
* - user methods
* - meta properties
*/
exports._initScope = function () {
this._initData()
this._initComputed()
this._initMethods()
this._initMeta()
}
/**
* Initialize the data.
*/
exports._initData = function () {
// proxy data on instance
var data = this._data
var keys = Object.keys(data)
var i = keys.length
var key
while (i--) {
key = keys[i]
if (!_.isReserved(key)) {
this._proxy(key)
}
}
// observe data
Observer.create(data).addVm(this)
}
/**
* Swap the isntance's $data. Called in $data's setter.
*
* @param {Object} newData
*/
exports._setData = function (newData) {
newData = newData || {}
var oldData = this._data
this._data = newData
var keys, key, i
// unproxy keys not present in new data
keys = Object.keys(oldData)
i = keys.length
while (i--) {
key = keys[i]
if (!_.isReserved(key) && !(key in newData)) {
this._unproxy(key)
}
}
// proxy keys not already proxied,
// and trigger change for changed values
keys = Object.keys(newData)
i = keys.length
while (i--) {
key = keys[i]
if (!this.hasOwnProperty(key) && !_.isReserved(key)) {
// new property
this._proxy(key)
}
}
oldData.__ob__.removeVm(this)
Observer.create(newData).addVm(this)
this._digest()
}
/**
* Proxy a property, so that
* vm.prop === vm._data.prop
*
* @param {String} key
*/
exports._proxy = function (key) {
// need to store ref to self here
// because these getter/setters might
// be called by child instances!
var self = this
Object.defineProperty(self, key, {
configurable: true,
enumerable: true,
get: function proxyGetter () {
return self._data[key]
},
set: function proxySetter (val) {
self._data[key] = val
}
})
}
/**
* Unproxy a property.
*
* @param {String} key
*/
exports._unproxy = function (key) {
delete this[key]
}
/**
* Force update on every watcher in scope.
*/
exports._digest = function () {
var i = this._watcherList.length
while (i--) {
this._watcherList[i].update()
}
var children = this._children
i = children.length
while (i--) {
var child = children[i]
if (child.$options.inherit) {
child._digest()
}
}
}
/**
* Setup computed properties. They are essentially
* special getter/setters
*/
function noop () {}
exports._initComputed = function () {
var computed = this.$options.computed
if (computed) {
for (var key in computed) {
var userDef = computed[key]
var def = {
enumerable: true,
configurable: true
}
if (typeof userDef === 'function') {
def.get = _.bind(userDef, this)
def.set = noop
} else {
def.get = userDef.get
? _.bind(userDef.get, this)
: noop
def.set = userDef.set
? _.bind(userDef.set, this)
: noop
}
Object.defineProperty(this, key, def)
}
}
}
/**
* Setup instance methods. Methods must be bound to the
* instance since they might be called by children
* inheriting them.
*/
exports._initMethods = function () {
var methods = this.$options.methods
if (methods) {
for (var key in methods) {
this[key] = _.bind(methods[key], this)
}
}
}
/**
* Initialize meta information like $index, $key & $value.
*/
exports._initMeta = function () {
var metas = this.$options._meta
if (metas) {
for (var key in metas) {
this._defineMeta(key, metas[key])
}
}
}
/**
* Define a meta property, e.g $index, $key, $value
* which only exists on the vm instance but not in $data.
*
* @param {String} key
* @param {*} value
*/
exports._defineMeta = function (key, value) {
var dep = new Dep()
Object.defineProperty(this, key, {
enumerable: true,
configurable: true,
get: function metaGetter () {
if (Observer.target) {
Observer.target.addDep(dep)
}
return value
},
set: function metaSetter (val) {
if (val !== value) {
value = val
dep.notify()
}
}
})
}