327 lines
9.0 KiB
JavaScript
327 lines
9.0 KiB
JavaScript
|
var keyMap = {}
|
|||
|
var keys = [
|
|||
|
'break,case,catch,continue,debugger,default,delete,do,else,false',
|
|||
|
'finally,for,function,if,in,instanceof,new,null,return,switch,this',
|
|||
|
'throw,true,try,typeof,var,void,while,with' /* 关键字*/,
|
|||
|
'abstract,boolean,byte,char,class,const,double,enum,export,extends',
|
|||
|
'final,float,goto,implements,import,int,interface,long,native',
|
|||
|
'package,private,protected,public,short,static,super,synchronized',
|
|||
|
'throws,transient,volatile' /*保留字*/,
|
|||
|
'arguments,let,yield,async,await,undefined'
|
|||
|
].join(',')
|
|||
|
keys.replace(/\w+/g, function(a) {
|
|||
|
keyMap[a] = true
|
|||
|
})
|
|||
|
|
|||
|
var ridentStart = /[a-z_$]/i
|
|||
|
var rwhiteSpace = /[\s\uFEFF\xA0]/
|
|||
|
function getIdent(input, lastIndex) {
|
|||
|
var result = []
|
|||
|
var subroutine = !!lastIndex
|
|||
|
lastIndex = lastIndex || 0
|
|||
|
//将表达式中的标识符抽取出来
|
|||
|
var state = 'unknown'
|
|||
|
var variable = ''
|
|||
|
for (var i = 0; i < input.length; i++) {
|
|||
|
var c = input.charAt(i)
|
|||
|
if (c === "'" || c === '"') {
|
|||
|
//字符串开始
|
|||
|
if (state === 'unknown') {
|
|||
|
state = c
|
|||
|
} else if (state === c) {
|
|||
|
//字符串结束
|
|||
|
state = 'unknown'
|
|||
|
}
|
|||
|
} else if (c === '\\') {
|
|||
|
if (state === "'" || state === '"') {
|
|||
|
i++
|
|||
|
}
|
|||
|
} else if (ridentStart.test(c)) {
|
|||
|
//碰到标识符
|
|||
|
if (state === 'unknown') {
|
|||
|
state = 'variable'
|
|||
|
variable = c
|
|||
|
} else if (state === 'maybePath') {
|
|||
|
variable = result.pop()
|
|||
|
variable += '.' + c
|
|||
|
state = 'variable'
|
|||
|
} else if (state === 'variable') {
|
|||
|
variable += c
|
|||
|
}
|
|||
|
} else if (/\w/.test(c)) {
|
|||
|
if (state === 'variable') {
|
|||
|
variable += c
|
|||
|
}
|
|||
|
} else if (c === '.') {
|
|||
|
if (state === 'variable') {
|
|||
|
if (variable) {
|
|||
|
result.push(variable)
|
|||
|
variable = ''
|
|||
|
state = 'maybePath'
|
|||
|
}
|
|||
|
}
|
|||
|
} else if (c === '[') {
|
|||
|
if (state === 'variable' || state === 'maybePath') {
|
|||
|
if (variable) {
|
|||
|
//如果前面存在变量,收集它
|
|||
|
result.push(variable)
|
|||
|
variable = ''
|
|||
|
}
|
|||
|
var lastLength = result.length
|
|||
|
var last = result[lastLength - 1]
|
|||
|
var innerResult = getIdent(input.slice(i), i)
|
|||
|
if (innerResult.length) {
|
|||
|
//如果括号中存在变量,那么这里添加通配符
|
|||
|
result[lastLength - 1] = last + '.*'
|
|||
|
result = innerResult.concat(result)
|
|||
|
} else {
|
|||
|
//如果括号中的东西是确定的,直接转换为其子属性
|
|||
|
var content = input.slice(i + 1, innerResult.i)
|
|||
|
try {
|
|||
|
var text = scpCompile(['return ' + content])()
|
|||
|
result[lastLength - 1] = last + '.' + text
|
|||
|
} catch (e) {}
|
|||
|
}
|
|||
|
state = 'maybePath' //]后面可能还接东西
|
|||
|
i = innerResult.i
|
|||
|
}
|
|||
|
} else if (c === ']') {
|
|||
|
if (subroutine) {
|
|||
|
result.i = i + lastIndex
|
|||
|
addVar(result, variable)
|
|||
|
return result
|
|||
|
}
|
|||
|
} else if (rwhiteSpace.test(c) && c !== '\r' && c !== '\n') {
|
|||
|
if (state === 'variable') {
|
|||
|
if (addVar(result, variable)) {
|
|||
|
state = 'maybePath' // aaa . bbb 这样的情况
|
|||
|
}
|
|||
|
variable = ''
|
|||
|
}
|
|||
|
} else {
|
|||
|
addVar(result, variable)
|
|||
|
state = 'unknown'
|
|||
|
variable = ''
|
|||
|
}
|
|||
|
}
|
|||
|
addVar(result, variable)
|
|||
|
return result
|
|||
|
}
|
|||
|
|
|||
|
function addVar(array, element) {
|
|||
|
if (element && !keyMap[element]) {
|
|||
|
array.push(element)
|
|||
|
return true
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function addAssign(vars, vmodel, name, binding) {
|
|||
|
var ret = []
|
|||
|
var prefix = ' = ' + name + '.'
|
|||
|
for (var i = vars.length, prop; (prop = vars[--i]); ) {
|
|||
|
var arr = prop.split('.')
|
|||
|
var first = arr[0]
|
|||
|
|
|||
|
if (vmodel.hasOwnProperty(first)) {
|
|||
|
// log(first, prop, prefix, vmodel)
|
|||
|
ret.push(first + prefix + first)
|
|||
|
binding.observers.push({
|
|||
|
v: vmodel,
|
|||
|
p: prop,
|
|||
|
type: Anot.type(vmodel[first])
|
|||
|
})
|
|||
|
vars.splice(i, 1)
|
|||
|
}
|
|||
|
}
|
|||
|
return ret
|
|||
|
}
|
|||
|
|
|||
|
var rproxy = /(proxy\-[a-z]+)\-[\-0-9a-f]+$/
|
|||
|
var variablePool = new Cache(218)
|
|||
|
//缓存求值函数,以便多次利用
|
|||
|
var evaluatorPool = new Cache(128)
|
|||
|
|
|||
|
function getVars(expr) {
|
|||
|
expr = expr.trim()
|
|||
|
var ret = variablePool.get(expr)
|
|||
|
if (ret) {
|
|||
|
return ret.concat()
|
|||
|
}
|
|||
|
var array = getIdent(expr)
|
|||
|
var uniq = {}
|
|||
|
var result = []
|
|||
|
for (var i = 0, el; (el = array[i++]); ) {
|
|||
|
if (!uniq[el]) {
|
|||
|
uniq[el] = 1
|
|||
|
result.push(el)
|
|||
|
}
|
|||
|
}
|
|||
|
return variablePool.put(expr, result).concat()
|
|||
|
}
|
|||
|
|
|||
|
function parseExpr(expr, vmodels, binding) {
|
|||
|
var filters = binding.filters
|
|||
|
if (typeof filters === 'string' && filters.trim() && !binding._filters) {
|
|||
|
binding._filters = parseFilter(filters.trim())
|
|||
|
}
|
|||
|
|
|||
|
var vars = getVars(expr)
|
|||
|
var expose = new Date() - 0
|
|||
|
var assigns = []
|
|||
|
var names = []
|
|||
|
var args = []
|
|||
|
binding.observers = []
|
|||
|
|
|||
|
for (var i = 0, sn = vmodels.length; i < sn; i++) {
|
|||
|
if (vars.length) {
|
|||
|
var name = 'vm' + expose + '_' + i
|
|||
|
names.push(name)
|
|||
|
args.push(vmodels[i])
|
|||
|
assigns.push.apply(assigns, addAssign(vars, vmodels[i], name, binding))
|
|||
|
}
|
|||
|
}
|
|||
|
binding.args = args
|
|||
|
var dataType = binding.type
|
|||
|
var exprId =
|
|||
|
vmodels.map(function(el) {
|
|||
|
return String(el.$id).replace(rproxy, '$1')
|
|||
|
}) +
|
|||
|
expr +
|
|||
|
dataType
|
|||
|
// log(expr, '---------------', assigns)
|
|||
|
var getter = evaluatorPool.get(exprId) //直接从缓存,免得重复生成
|
|||
|
if (getter) {
|
|||
|
if (dataType === 'duplex') {
|
|||
|
var setter = evaluatorPool.get(exprId + 'setter')
|
|||
|
binding.setter = setter.apply(setter, binding.args)
|
|||
|
}
|
|||
|
return (binding.getter = getter)
|
|||
|
}
|
|||
|
|
|||
|
// expr的字段不可枚举时,补上一个随机变量, 避免抛出异常
|
|||
|
if (!assigns.length) {
|
|||
|
assigns.push('fix' + expose)
|
|||
|
}
|
|||
|
|
|||
|
if (dataType === 'duplex') {
|
|||
|
var nameOne = {}
|
|||
|
assigns.forEach(function(a) {
|
|||
|
var arr = a.split('=')
|
|||
|
nameOne[arr[0].trim()] = arr[1].trim()
|
|||
|
})
|
|||
|
expr = expr.replace(/[\$\w]+/, function(a) {
|
|||
|
return nameOne[a] ? nameOne[a] : a
|
|||
|
})
|
|||
|
/* jshint ignore:start */
|
|||
|
var fn2 = scpCompile(
|
|||
|
names.concat(
|
|||
|
'"use strict";\n return function(vvv){' + expr + ' = vvv\n}\n'
|
|||
|
)
|
|||
|
)
|
|||
|
/* jshint ignore:end */
|
|||
|
evaluatorPool.put(exprId + 'setter', fn2)
|
|||
|
binding.setter = fn2.apply(fn2, binding.args)
|
|||
|
}
|
|||
|
|
|||
|
if (dataType === 'on') {
|
|||
|
//事件绑定
|
|||
|
if (expr.indexOf('(') === -1) {
|
|||
|
expr += '.call(' + names[names.length - 1] + ', $event)'
|
|||
|
} else {
|
|||
|
expr = expr.replace('(', '.call(' + names[names.length - 1] + ', ')
|
|||
|
}
|
|||
|
names.push('$event')
|
|||
|
expr = '\nreturn ' + expr + ';' //IE全家 Function("return ")出错,需要Function("return ;")
|
|||
|
var lastIndex = expr.lastIndexOf('\nreturn')
|
|||
|
var header = expr.slice(0, lastIndex)
|
|||
|
var footer = expr.slice(lastIndex)
|
|||
|
expr = header + '\n' + footer
|
|||
|
} else {
|
|||
|
// 对于非事件绑定的方法, 同样绑定到vm上
|
|||
|
binding.observers.forEach(function(it) {
|
|||
|
if (it.type === 'function') {
|
|||
|
// log(it, expr)
|
|||
|
var reg = new RegExp(it.p + '\\(([^)]*)\\)', 'g')
|
|||
|
expr = expr.replace(reg, function(s, m) {
|
|||
|
m = m.trim()
|
|||
|
return (
|
|||
|
it.p +
|
|||
|
'.call(' +
|
|||
|
names[names.length - 1] +
|
|||
|
(m ? ', ' + m : '') +
|
|||
|
')'
|
|||
|
)
|
|||
|
})
|
|||
|
}
|
|||
|
})
|
|||
|
expr = '\nreturn ' + expr + ';' //IE全家 Function("return ")出错,需要Function("return ;")
|
|||
|
}
|
|||
|
|
|||
|
/* jshint ignore:start */
|
|||
|
getter = scpCompile(
|
|||
|
names.concat(
|
|||
|
"'use strict';\ntry{\n var " +
|
|||
|
assigns.join(',\n ') +
|
|||
|
expr +
|
|||
|
'\n}catch(e){console.log(e)}'
|
|||
|
)
|
|||
|
)
|
|||
|
/* jshint ignore:end */
|
|||
|
|
|||
|
return evaluatorPool.put(exprId, getter)
|
|||
|
}
|
|||
|
|
|||
|
function normalizeExpr(code) {
|
|||
|
var hasExpr = rexpr.test(code) //比如:class="width{{w}}"的情况
|
|||
|
if (hasExpr) {
|
|||
|
var array = scanExpr(code)
|
|||
|
if (array.length === 1) {
|
|||
|
return array[0].expr
|
|||
|
}
|
|||
|
return array
|
|||
|
.map(function(el) {
|
|||
|
return el.type ? '(' + el.expr + ')' : quote(el.expr)
|
|||
|
})
|
|||
|
.join(' + ')
|
|||
|
} else {
|
|||
|
return code
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Anot.normalizeExpr = normalizeExpr
|
|||
|
Anot.parseExprProxy = parseExpr
|
|||
|
|
|||
|
var rthimRightParentheses = /\)\s*$/
|
|||
|
var rthimOtherParentheses = /\)\s*\|/g
|
|||
|
var rquoteFilterName = /\|\s*([$\w]+)/g
|
|||
|
var rpatchBracket = /"\s*\["/g
|
|||
|
var rthimLeftParentheses = /"\s*\(/g
|
|||
|
function parseFilter(filters) {
|
|||
|
filters =
|
|||
|
filters
|
|||
|
.replace(rthimRightParentheses, '') //处理最后的小括号
|
|||
|
.replace(rthimOtherParentheses, function() {
|
|||
|
//处理其他小括号
|
|||
|
return '],|'
|
|||
|
})
|
|||
|
.replace(rquoteFilterName, function(a, b) {
|
|||
|
//处理|及它后面的过滤器的名字
|
|||
|
return '[' + quote(b)
|
|||
|
})
|
|||
|
.replace(rpatchBracket, function() {
|
|||
|
return '"],["'
|
|||
|
})
|
|||
|
.replace(rthimLeftParentheses, function() {
|
|||
|
return '",'
|
|||
|
}) + ']'
|
|||
|
/* jshint ignore:start */
|
|||
|
return scpCompile(['return [' + filters + ']'])()
|
|||
|
/* jshint ignore:end */
|
|||
|
}
|
|||
|
|
|||
|
/*********************************************************************
|
|||
|
* 编译系统 *
|
|||
|
**********************************************************************/
|
|||
|
|
|||
|
var quote = JSON.stringify
|