223 lines
5.9 KiB
JavaScript
223 lines
5.9 KiB
JavaScript
import { Anot, platform, isObject } from '../seed/core'
|
|
import { $$skipArray } from './reserved'
|
|
import { Mutation } from './Mutation'
|
|
import { Computed } from './Computed'
|
|
|
|
/**
|
|
* 这里放置ViewModel模块的共用方法
|
|
* Anot.define: 全框架最重要的方法,生成用户VM
|
|
* IProxy, 基本用户数据产生的一个数据对象,基于$model与vmodel之间的形态
|
|
* modelFactory: 生成用户VM
|
|
* canHijack: 判定此属性是否该被劫持,加入数据监听与分发的的逻辑
|
|
* createProxy: listFactory与modelFactory的封装
|
|
* createAccessor: 实现数据监听与分发的重要对象
|
|
* itemFactory: ms-for循环中产生的代理VM的生成工厂
|
|
* fuseFactory: 两个ms-controller间产生的代理VM的生成工厂
|
|
*/
|
|
|
|
Anot.define = function(definition) {
|
|
var $id = definition.$id
|
|
if (!$id) {
|
|
console.error('vm.$id must be specified')
|
|
}
|
|
if (Anot.vmodels[$id]) {
|
|
console.warn('error:[' + $id + '] had defined!')
|
|
}
|
|
var vm = platform.modelFactory(definition)
|
|
return (Anot.vmodels[$id] = vm)
|
|
}
|
|
|
|
/**
|
|
* Anot改用Proxy来创建VM,因此
|
|
*/
|
|
|
|
export function IProxy(definition, dd) {
|
|
Anot.mix(this, definition)
|
|
Anot.mix(this, $$skipArray)
|
|
this.$hashcode = Anot.makeHashCode('$')
|
|
this.$id = this.$id || this.$hashcode
|
|
this.$events = {
|
|
__dep__: dd || new Mutation(this.$id)
|
|
}
|
|
|
|
delete this.$mutations
|
|
this.$accessors = {}
|
|
this.$computed = {}
|
|
this.$track = ''
|
|
|
|
if (dd === void 0) {
|
|
this.$watch = platform.watchFactory(this.$events)
|
|
this.$fire = platform.fireFactory(this.$events)
|
|
} else {
|
|
delete this.$watch
|
|
delete this.$fire
|
|
}
|
|
}
|
|
|
|
platform.modelFactory = function modelFactory(definition, dd) {
|
|
var $computed = definition.$computed || {}
|
|
delete definition.$computed
|
|
var core = new IProxy(definition, dd)
|
|
var $accessors = core.$accessors
|
|
var keys = []
|
|
|
|
platform.hideProperty(core, '$mutations', {})
|
|
|
|
for (let key in definition) {
|
|
if (key in $$skipArray) continue
|
|
var val = definition[key]
|
|
keys.push(key)
|
|
if (canHijack(key, val)) {
|
|
$accessors[key] = createAccessor(key, val)
|
|
}
|
|
}
|
|
for (let key in $computed) {
|
|
if (key in $$skipArray) continue
|
|
var val = $computed[key]
|
|
if (typeof val === 'function') {
|
|
val = {
|
|
get: val
|
|
}
|
|
}
|
|
if (val && val.get) {
|
|
val.getter = val.get
|
|
val.setter = val.set
|
|
Anot.Array.ensure(keys, key)
|
|
$accessors[key] = createAccessor(key, val, true)
|
|
}
|
|
}
|
|
//将系统API以unenumerable形式加入vm,
|
|
//添加用户的其他不可监听属性或方法
|
|
//重写$track
|
|
//并在IE6-8中增添加不存在的hasOwnPropert方法
|
|
var vm = platform.createViewModel(core, $accessors, core)
|
|
platform.afterCreate(vm, core, keys, !dd)
|
|
return vm
|
|
}
|
|
var $proxyItemBackdoorMap = {}
|
|
|
|
export function canHijack(key, val, $proxyItemBackdoor) {
|
|
if (key in $$skipArray) return false
|
|
if (key.charAt(0) === '$') {
|
|
if ($proxyItemBackdoor) {
|
|
if (!$proxyItemBackdoorMap[key]) {
|
|
$proxyItemBackdoorMap[key] = 1
|
|
console.warn(`ms-for中的变量${key}不再建议以$为前缀`)
|
|
}
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
if (val == null) {
|
|
console.warn('定义vmodel时' + key + '的属性值不能为null undefine')
|
|
return true
|
|
}
|
|
if (/error|date|function|regexp/.test(Anot.type(val))) {
|
|
return false
|
|
}
|
|
return !(val && val.nodeName && val.nodeType)
|
|
}
|
|
|
|
export function createProxy(target, dd) {
|
|
if (target && target.$events) {
|
|
return target
|
|
}
|
|
var vm
|
|
if (Array.isArray(target)) {
|
|
vm = platform.listFactory(target, false, dd)
|
|
} else if (isObject(target)) {
|
|
vm = platform.modelFactory(target, dd)
|
|
}
|
|
return vm
|
|
}
|
|
|
|
platform.createProxy = createProxy
|
|
|
|
platform.itemFactory = function itemFactory(before, after) {
|
|
var keyMap = before.$model
|
|
var core = new IProxy(keyMap)
|
|
var state = Object.assign(core.$accessors, before.$accessors) //防止互相污染
|
|
var data = after.data
|
|
//core是包含系统属性的对象
|
|
//keyMap是不包含系统属性的对象, keys
|
|
for (var key in data) {
|
|
var val = (keyMap[key] = core[key] = data[key])
|
|
state[key] = createAccessor(key, val)
|
|
}
|
|
var keys = Object.keys(keyMap)
|
|
var vm = platform.createViewModel(core, state, core)
|
|
platform.afterCreate(vm, core, keys)
|
|
return vm
|
|
}
|
|
|
|
function createAccessor(key, val, isComputed) {
|
|
var mutation = null
|
|
var Accessor = isComputed ? Computed : Mutation
|
|
return {
|
|
get: function Getter() {
|
|
if (!mutation) {
|
|
mutation = new Accessor(key, val, this)
|
|
}
|
|
return mutation.get()
|
|
},
|
|
set: function Setter(newValue) {
|
|
if (!mutation) {
|
|
mutation = new Accessor(key, val, this)
|
|
}
|
|
mutation.set(newValue)
|
|
},
|
|
enumerable: true,
|
|
configurable: true
|
|
}
|
|
}
|
|
|
|
platform.fuseFactory = function fuseFactory(before, after) {
|
|
var keyMap = Anot.mix(before.$model, after.$model)
|
|
var core = new IProxy(
|
|
Anot.mix(keyMap, {
|
|
$id: before.$id + after.$id
|
|
})
|
|
)
|
|
var state = Anot.mix(core.$accessors, before.$accessors, after.$accessors) //防止互相污染
|
|
|
|
var keys = Object.keys(keyMap)
|
|
//将系统API以unenumerable形式加入vm,并在IE6-8中添加hasOwnPropert方法
|
|
var vm = platform.createViewModel(core, state, core)
|
|
platform.afterCreate(vm, core, keys, false)
|
|
return vm
|
|
}
|
|
|
|
function toJson(val) {
|
|
var xtype = Anot.type(val)
|
|
if (xtype === 'array') {
|
|
var array = []
|
|
for (var i = 0; i < val.length; i++) {
|
|
array[i] = toJson(val[i])
|
|
}
|
|
return array
|
|
} else if (xtype === 'object') {
|
|
if (typeof val.$track === 'string') {
|
|
var obj = {}
|
|
var arr = val.$track.match(/[^☥]+/g) || []
|
|
arr.forEach(function(i) {
|
|
var value = val[i]
|
|
obj[i] = value && value.$events ? toJson(value) : value
|
|
})
|
|
return obj
|
|
}
|
|
}
|
|
return val
|
|
}
|
|
|
|
var modelAccessor = {
|
|
get: function() {
|
|
return toJson(this)
|
|
},
|
|
set: Anot.noop,
|
|
enumerable: false,
|
|
configurable: true
|
|
}
|
|
|
|
platform.toJson = toJson
|
|
platform.modelAccessor = modelAccessor
|