380 lines
11 KiB
JavaScript
380 lines
11 KiB
JavaScript
/*********************************************************************
|
|
* 扫描系统 *
|
|
**********************************************************************/
|
|
|
|
//http://www.w3.org/TR/html5/syntax.html#void-elements
|
|
var stopScan = oneObject(
|
|
'area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea'.toUpperCase()
|
|
)
|
|
|
|
function isRef(el) {
|
|
return el.hasAttribute('ref') ? el.getAttribute('ref') : null
|
|
}
|
|
|
|
function checkScan(elem, callback, innerHTML) {
|
|
var id = setTimeout(function() {
|
|
var currHTML = elem.innerHTML
|
|
clearTimeout(id)
|
|
if (currHTML === innerHTML) {
|
|
callback()
|
|
} else {
|
|
checkScan(elem, callback, currHTML)
|
|
}
|
|
})
|
|
}
|
|
|
|
function getBindingCallback(elem, name, vmodels) {
|
|
var callback = elem.getAttribute(name)
|
|
if (callback) {
|
|
for (var i = 0, vm; (vm = vmodels[i++]); ) {
|
|
if (vm.hasOwnProperty(callback) && typeof vm[callback] === 'function') {
|
|
return vm[callback]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function executeBindings(bindings, vmodels) {
|
|
for (var i = 0, binding; (binding = bindings[i++]); ) {
|
|
binding.vmodels = vmodels
|
|
directives[binding.type].init(binding)
|
|
|
|
Anot.injectBinding(binding)
|
|
if (binding.getter && binding.element.nodeType === 1) {
|
|
//移除数据绑定,防止被二次解析
|
|
//chrome使用removeAttributeNode移除不存在的特性节点时会报错
|
|
binding.element.removeAttribute(binding.name)
|
|
}
|
|
}
|
|
bindings.length = 0
|
|
}
|
|
|
|
var roneTime = /^\s*::/
|
|
var rmsAttr = /^:(\w+)-?(.*)|@(.*)/
|
|
|
|
var events = oneObject(
|
|
'animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit'
|
|
)
|
|
var obsoleteAttrs = oneObject(
|
|
'value,title,alt,checked,selected,disabled,readonly,loading,enabled,href,src'
|
|
)
|
|
function bindingSorter(a, b) {
|
|
return a.priority - b.priority
|
|
}
|
|
|
|
var rnoCollect = /^(:\S+|data-\S+|on[a-z]+|style|class)$/
|
|
var filterTypes = ['html', 'text', 'attr', 'data']
|
|
|
|
function scanAttr(elem, vmodels, match) {
|
|
var scanNode = true
|
|
if (vmodels.length) {
|
|
var attributes = elem.attributes
|
|
var bindings = []
|
|
var uniq = {}
|
|
for (var i = 0, attr; (attr = attributes[i++]); ) {
|
|
var name = attr.name
|
|
if (uniq[name]) {
|
|
//IE8下:for BUG
|
|
continue
|
|
}
|
|
uniq[name] = 1
|
|
if (attr.specified) {
|
|
if ((match = name.match(rmsAttr))) {
|
|
//如果是以指定前缀命名的
|
|
var type = match[1]
|
|
var param = match[2] || ''
|
|
var eparam = match[3] || '' // 事件绑定的简写
|
|
var value = attr.value
|
|
if (obsoleteAttrs[type]) {
|
|
param = type
|
|
type = 'attr'
|
|
}
|
|
if (eparam) {
|
|
param = eparam
|
|
type = 'on'
|
|
}
|
|
if (directives[type]) {
|
|
var newValue = value.replace(roneTime, '')
|
|
var oneTime = value !== newValue
|
|
var binding = {
|
|
type: type,
|
|
param: param,
|
|
element: elem,
|
|
name: name,
|
|
expr: newValue,
|
|
oneTime: oneTime,
|
|
uuid: '_' + ++bindingID,
|
|
priority:
|
|
(directives[type].priority || type.charCodeAt(0) * 10) +
|
|
(Number(param.replace(/\D/g, '')) || 0)
|
|
}
|
|
// 如果指令允许使用过滤器
|
|
if (filterTypes.includes(type)) {
|
|
var filters = getToken(value).filters
|
|
binding.expr = binding.expr.replace(filters, '')
|
|
binding.filters = filters
|
|
.replace(rhasHtml, function() {
|
|
binding.type = 'html'
|
|
binding.group = 1
|
|
return ''
|
|
})
|
|
.trim() // jshint ignore:line
|
|
} else if (type === 'duplex') {
|
|
var hasDuplex = name
|
|
} else if (name === ':if-loop') {
|
|
binding.priority += 100
|
|
} else if (name === ':attr-value') {
|
|
var hasAttrValue = name
|
|
}
|
|
bindings.push(binding)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (bindings.length) {
|
|
bindings.sort(bindingSorter)
|
|
|
|
if (hasDuplex && hasAttrValue && elem.type === 'text') {
|
|
log('warning!一个控件不能同时定义:attr-value与' + hasDuplex)
|
|
}
|
|
|
|
for (i = 0; (binding = bindings[i]); i++) {
|
|
type = binding.type
|
|
if (rnoscanAttrBinding.test(type)) {
|
|
return executeBindings(bindings.slice(0, i + 1), vmodels)
|
|
} else if (scanNode) {
|
|
scanNode = !rnoscanNodeBinding.test(type)
|
|
}
|
|
}
|
|
executeBindings(bindings, vmodels)
|
|
}
|
|
}
|
|
if (scanNode && !stopScan[elem.tagName]) {
|
|
scanNodeList(elem, vmodels) //扫描子孙元素
|
|
}
|
|
}
|
|
|
|
var rnoscanAttrBinding = /^if|for$/
|
|
var rnoscanNodeBinding = /^html|include$/
|
|
|
|
function scanNodeList(elem, vmodels) {
|
|
var nodes = Anot.slice(elem.childNodes)
|
|
scanNodeArray(nodes, vmodels)
|
|
}
|
|
|
|
function scanNodeArray(nodes, vmodels) {
|
|
function _delay_component(name) {
|
|
setTimeout(function() {
|
|
Anot.component(name)
|
|
})
|
|
}
|
|
for (var i = 0, node; (node = nodes[i++]); ) {
|
|
switch (node.nodeType) {
|
|
case 1:
|
|
var elem = node
|
|
if (elem.parentNode && elem.parentNode.nodeType === 1) {
|
|
// 非组件才检查 ref属性
|
|
var ref = isRef(elem)
|
|
if (ref && vmodels.length) {
|
|
vmodels[0].$refs[ref] = elem
|
|
}
|
|
}
|
|
|
|
scanTag(node, vmodels) //扫描元素节点
|
|
|
|
if (node.msHasEvent) {
|
|
Anot.fireDom(node, 'datasetchanged', {
|
|
bubble: node.msHasEvent
|
|
})
|
|
}
|
|
|
|
break
|
|
case 3:
|
|
if (rexpr.test(node.nodeValue)) {
|
|
scanText(node, vmodels, i) //扫描文本节点
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
function scanTag(elem, vmodels) {
|
|
//扫描顺序 skip(0) --> anot(1) --> :if(10) --> :for(90)
|
|
//--> :if-loop(110) --> :attr(970) ...--> :duplex(2000)垫后
|
|
var skip = elem.getAttribute('skip')
|
|
var node = elem.getAttributeNode('anot')
|
|
var vm = vmodels.concat()
|
|
if (typeof skip === 'string') {
|
|
return
|
|
} else if (node) {
|
|
var newVmodel = Anot.vmodels[node.value]
|
|
var attrs = aslice.call(elem.attributes, 0)
|
|
|
|
if (!newVmodel) {
|
|
return
|
|
}
|
|
|
|
vm = [newVmodel]
|
|
|
|
elem.removeAttribute(node.name) //removeAttributeNode不会刷新xx[anot]样式规则
|
|
// 挂载VM对象到相应的元素上
|
|
elem.__VM__ = newVmodel
|
|
hideProperty(newVmodel, '$elem', elem)
|
|
|
|
if (vmodels.length) {
|
|
newVmodel.$up = vmodels[0]
|
|
vmodels[0].$children.push(newVmodel)
|
|
var props = {}
|
|
attrs.forEach(function(attr) {
|
|
if (/^:/.test(attr.name)) {
|
|
var name = attr.name.match(rmsAttr)[1]
|
|
var value = null
|
|
if (!name || Anot.directives[name] || events[name]) {
|
|
return
|
|
}
|
|
try {
|
|
value = parseExpr(attr.value, vmodels, {}).apply(0, vmodels)
|
|
value = toJson(value)
|
|
elem.removeAttribute(attr.name)
|
|
props[name] = value
|
|
} catch (error) {
|
|
log(
|
|
'Props parse faild on (%s[class=%s]),',
|
|
elem.nodeName,
|
|
elem.className,
|
|
attr,
|
|
error + ''
|
|
)
|
|
}
|
|
}
|
|
})
|
|
// 一旦设定了 props的类型, 就必须传入正确的值
|
|
for (var k in newVmodel.props) {
|
|
if (newVmodel.props[k] && newVmodel.props[k].type === 'PropsTypes') {
|
|
if (newVmodel.props[k].check(props[k])) {
|
|
newVmodel.props[k] = props[k]
|
|
delete props[k]
|
|
} else {
|
|
console.error(
|
|
new TypeError(
|
|
'props.' +
|
|
k +
|
|
' needs [' +
|
|
newVmodel.props[k].checkType +
|
|
'], but [' +
|
|
newVmodel.props[k].result +
|
|
'] given.'
|
|
)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
Object.assign(newVmodel.props, props)
|
|
props = undefined
|
|
}
|
|
}
|
|
scanAttr(elem, vm) //扫描特性节点
|
|
|
|
if (newVmodel) {
|
|
setTimeout(function() {
|
|
if (typeof newVmodel.$mounted === 'function') {
|
|
newVmodel.$mounted()
|
|
}
|
|
delete newVmodel.$mounted
|
|
})
|
|
}
|
|
}
|
|
var rhasHtml = /\|\s*html(?:\b|$)/,
|
|
r11a = /\|\|/g,
|
|
rlt = /</g,
|
|
rgt = />/g,
|
|
rstringLiteral = /(['"])(\\\1|.)+?\1/g,
|
|
rline = /\r?\n/g
|
|
function getToken(value) {
|
|
if (value.indexOf('|') > 0) {
|
|
var scapegoat = value.replace(rstringLiteral, function(_) {
|
|
return Array(_.length + 1).join('1') // jshint ignore:line
|
|
})
|
|
var index = scapegoat.replace(r11a, '\u1122\u3344').indexOf('|') //干掉所有短路或
|
|
if (index > -1) {
|
|
return {
|
|
type: 'text',
|
|
filters: value.slice(index).trim(),
|
|
expr: value.slice(0, index)
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
type: 'text',
|
|
expr: value,
|
|
filters: ''
|
|
}
|
|
}
|
|
|
|
function scanExpr(str) {
|
|
var tokens = [],
|
|
value,
|
|
start = 0,
|
|
stop
|
|
do {
|
|
stop = str.indexOf(openTag, start)
|
|
if (stop === -1) {
|
|
break
|
|
}
|
|
value = str.slice(start, stop)
|
|
if (value) {
|
|
// {{ 左边的文本
|
|
tokens.push({
|
|
expr: value
|
|
})
|
|
}
|
|
start = stop + openTag.length
|
|
stop = str.indexOf(closeTag, start)
|
|
if (stop === -1) {
|
|
break
|
|
}
|
|
value = str.slice(start, stop)
|
|
if (value) {
|
|
//处理{{ }}插值表达式
|
|
tokens.push(getToken(value.replace(rline, '')))
|
|
}
|
|
start = stop + closeTag.length
|
|
} while (1)
|
|
value = str.slice(start)
|
|
if (value) {
|
|
//}} 右边的文本
|
|
tokens.push({
|
|
expr: value
|
|
})
|
|
}
|
|
return tokens
|
|
}
|
|
|
|
function scanText(textNode, vmodels, index) {
|
|
var bindings = [],
|
|
tokens = scanExpr(textNode.data)
|
|
if (tokens.length) {
|
|
for (var i = 0, token; (token = tokens[i++]); ) {
|
|
var node = DOC.createTextNode(token.expr) //将文本转换为文本节点,并替换原来的文本节点
|
|
if (token.type) {
|
|
token.expr = token.expr.replace(roneTime, function() {
|
|
token.oneTime = true
|
|
return ''
|
|
}) // jshint ignore:line
|
|
token.element = node
|
|
token.filters = token.filters.replace(rhasHtml, function() {
|
|
token.type = 'html'
|
|
return ''
|
|
}) // jshint ignore:line
|
|
token.pos = index * 1000 + i
|
|
bindings.push(token) //收集带有插值表达式的文本
|
|
}
|
|
anotFragment.appendChild(node)
|
|
}
|
|
textNode.parentNode.replaceChild(anotFragment, textNode)
|
|
if (bindings.length) {
|
|
executeBindings(bindings, vmodels)
|
|
}
|
|
}
|
|
}
|