"] //div可以不用闭合
}
+
tagHooks.optgroup = tagHooks.option
tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead
tagHooks.th = tagHooks.td
-
+//处理SVG
+ tagHooks.circle = tagHooks.ellipse = tagHooks.line = tagHooks.path =
+ tagHooks.polygon = tagHooks.polyline = tagHooks.rect = tagHooks.text
var script = DOC.createElement("script")
avalon.parseHTML = function(html) {
if (typeof html !== "string") {
@@ -1624,12 +1693,28 @@
el.parentNode.removeChild(el)
}
}
+ for (els = wrapper.all, i = 0; el = els[i++]; ) {//fix VML
+ if (isVML(el)) {
+ fixVML(el)
+ }
+ }
}
while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上!
fragment.appendChild(firstChild)
}
return fragment
}
+ function isVML(src) {
+ var nodeName = src.nodeName
+ return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === ""
+ }
+ function fixVML(node) {
+ if (node.currentStyle.behavior !== "url(#default#VML)") {
+ node.style.behavior = "url(#default#VML)"
+ node.style.display = "inline-block"
+ node.style.zoom = 1 //hasLayout
+ }
+ }
avalon.innerHTML = function(node, html) {
if (!W3C && (!rcreate.test(html) && !rnest.test(html))) {
try {
@@ -1642,13 +1727,16 @@
this.clearHTML(node).appendChild(a)
}
avalon.clearHTML = function(node) {
- expelFromSanctuary(node)
+ node.textContent = ""
+ while (node.firstChild) {
+ node.removeChild(node.firstChild)
+ }
return node
}
/*********************************************************************
- * 自定义事件系统 *
+ * 事件管理器 *
**********************************************************************/
- var Events = {
+ var EventManager = {
$watch: function(type, callback) {
if (typeof callback === "function") {
var callbacks = this.$events[type]
@@ -1682,7 +1770,7 @@
},
$fire: function(type) {
var special
- if (/^(\w+)!(\w+)$/.test(type)) {
+ if (/^(\w+)!(\S+)$/.test(type)) {
special = RegExp.$1
type = RegExp.$2
}
@@ -1691,111 +1779,151 @@
var all = events.$all || []
var args = aslice.call(arguments, 1)
for (var i = 0, callback; callback = callbacks[i++]; ) {
- callback.apply(this, args)
+ if (isFunction(callback))
+ callback.apply(this, args)
}
for (var i = 0, callback; callback = all[i++]; ) {
- callback.apply(this, arguments)
+ if (isFunction(callback))
+ callback.apply(this, arguments)
}
- var element = events.element
+ var element = events.expr && findNode(events.expr)
if (element) {
var detail = [type].concat(args)
- if (special === "up") {
- if (W3C) {
- W3CFire(element, "dataavailable", detail)
- } else {
- var event = document.createEventObject()
- event.detail = detail
- element.fireEvent("ondataavailable", event)
- }
- } else if (special === "down") {
- var alls = []
+ var alls = []
+ if (special === "up" || special === "down" || special === "all") {
for (var i in avalon.vmodels) {
var v = avalon.vmodels[i]
- if (v && v.$events && v.$events.element) {
- var node = v.$events.element;
- if (avalon.contains(element, node) && element != node) {
- alls.push(v)
+ if (v && v.$events && v.$events.expr) {
+ if (v !== this) {
+ var node = findNode(v.$events.expr)
+ if (!node) {
+ continue
+ }
+ var ok = special === "all" ? 1 : //全局广播
+ special === "down" ? element.contains(node) : //向下捕获
+ node.contains(element)//向上冒泡
+ if (ok) {
+ alls.push([node, v])
+ }
}
}
}
- alls.forEach(function(v) {
- v.$fire.apply(v, detail)
+ var nodes = DOC.getElementsByTagName("*")//实现节点排序
+ alls.sort(function(a, b) {
+ return Array.prototype.indexOf.call(nodes, a[0]) - Array.prototype.indexOf.call(nodes, b[0])
})
- } else if (special === "all") {
- for (var i in avalon.vmodels) {
- var v = avalon.vmodels[i]
- if (v !== this) {
- v.$fire.apply(v, detail)
- }
+ if (special === "up") {
+ alls.reverse()
}
+ alls.forEach(function(v) {
+ v[1].$fire.apply(v[1], detail)
+ })
}
}
}
}
+ var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
+ var findNode = DOC.querySelector ? function(str) {
+ return DOC.querySelector(str)
+ } : function(str) {
+ var match = str.match(ravalon)
+ var all = DOC.getElementsByTagName(match[1])
+ for (var i = 0, el; el = all[i++]; ) {
+ if (el.getAttribute(match[2]) === match[3]) {
+ return el
+ }
+ }
+ }
/*********************************************************************
* 依赖调度系统 *
**********************************************************************/
-
- function registerSubscriber(data, val) {
+ var ronduplex = /^(duplex|on)$/
+ function registerSubscriber(data) {
Registry[expose] = data //暴光此函数,方便collectSubscribers收集
avalon.openComputedCollect = true
var fn = data.evaluator
if (fn) { //如果是求值函数
- if (data.type === "duplex") {
- data.handler()
- } else {
- try {
- var c = data.type === "on" ? data : fn.apply(0, data.args)
- data.handler(c, data.element, data)
- } catch (e) {
- delete data.evaluator
- if (data.nodeType === 3) {
- if (kernel.commentInterpolate) {
- data.element.replaceChild(DOC.createComment(data.value), data.node)
- } else {
- data.node.data = openTag + data.value + closeTag
- }
+ try {
+ var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args)
+ data.handler(c, data.element, data)
+ } catch (e) {
+ log("warning:exception throwed in [registerSubscriber] " + e)
+ delete data.evaluator
+ var node = data.element
+ if (node.nodeType === 3) {
+ var parent = node.parentNode
+ if (kernel.commentInterpolate) {
+ parent.replaceChild(DOC.createComment(data.value), node)
+ } else {
+ node.data = openTag + data.value + closeTag
}
- log("warning:evaluator of [" + data.value + "] throws error!")
}
}
- } else { //如果是计算属性的accessor
- data()
}
avalon.openComputedCollect = false
delete Registry[expose]
}
- function collectSubscribers(accessor) { //收集依赖于这个访问器的订阅者
- if (Registry[expose]) {
- var list = accessor[subscribers]
- list && avalon.Array.ensure(list, Registry[expose]) //只有数组不存在此元素才push进去
+ function collectSubscribers(list) { //收集依赖于这个访问器的订阅者
+ var data = Registry[expose]
+ if (list && data && avalon.Array.ensure(list, data) && data.element) { //只有数组不存在此元素才push进去
+ $$subscribers.push({
+ data: data, list: list
+ })
}
}
-
- function notifySubscribers(accessor) { //通知依赖于这个访问器的订阅者更新自身
- var list = accessor[subscribers]
+ var $$subscribers = [], $startIndex = 0, $maxIndex = 200
+ function removeSubscribers() {
+ for (var i = $startIndex, n = $startIndex + $maxIndex; i < n; i++) {
+ var obj = $$subscribers[i]
+ if (!obj) {
+ break
+ }
+ var data = obj.data
+ var el = data.element
+ var remove = el === null ? 1 : (el.nodeType === 1 ? typeof el.sourceIndex === "number" ?
+ el.sourceIndex === 0 : !root.contains(el) : !avalon.contains(root, el))
+ if (remove) { //如果它没有在DOM树
+ $$subscribers.splice(i, 1)
+ avalon.Array.remove(obj.list, data)
+ //log("debug: remove " + data.type)
+ if (data.type === "if" && data.template && data.template.parentNode === head) {
+ head.removeChild(data.template)
+ }
+ for (var key in data) {
+ data[key] = null
+ }
+ obj.data = obj.list = null
+ i--
+ n--
+ }
+ }
+ obj = $$subscribers[i]
+ if (obj) {
+ $startIndex = n
+ } else {
+ $startIndex = 0
+ }
+ }
+ var beginTime = new Date(), removeID
+ function notifySubscribers(list) { //通知依赖于这个访问器的订阅者更新自身
+ var currentTime = new Date()
+ clearTimeout(removeID)
+ if (currentTime - beginTime > 333) {
+ removeSubscribers()
+ beginTime = new Date()
+ } else {
+ removeID = setTimeout(removeSubscribers, 333)
+ }
if (list && list.length) {
var args = aslice.call(arguments, 1)
for (var i = list.length, fn; fn = list[--i]; ) {
- var el = fn.element,
- remove
- if (el && !avalon.contains(ifSanctuary, el)) {
- if (typeof el.sourceIndex == "number") { //IE6-IE11
- remove = el.sourceIndex === 0
- } else {
- remove = !avalon.contains(root, el)
- }
- }
- if (remove) { //如果它没有在DOM树
- list.splice(i, 1)
- log("debug: remove " + fn.name)
- } else if (typeof fn === "function") {
- fn.apply(0, args) //强制重新计算自身
- } else if (fn.getter) {
+ var el = fn.element
+ if (fn.$repeat) {
fn.handler.apply(fn, args) //处理监控数组的方法
- } else {
- fn.handler(fn.evaluator.apply(0, fn.args || []), el, fn)
+ } else if (fn.element && fn.type !== "on") {//事件绑定只能由用户触发,不能由程序触发
+ var fun = fn.evaluator || noop
+ fn.handler(fun.apply(0, fn.args || []), el, fn)
}
}
}
@@ -1833,13 +1961,13 @@
function scanTag(elem, vmodels, node) {
//扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
//--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
- var a = elem.getAttribute(prefix + "skip")
+ var a = elem.getAttribute("ms-skip")
//#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形
if (!elem.getAttributeNode) {
return log("warning " + elem.tagName + " no getAttributeNode method")
}
- var b = elem.getAttributeNode(prefix + "important")
- var c = elem.getAttributeNode(prefix + "controller")
+ var b = elem.getAttributeNode("ms-important")
+ var c = elem.getAttributeNode("ms-controller")
if (typeof a === "string") {
return
} else if (node = b || c) {
@@ -1849,36 +1977,40 @@
}
//ms-important不包含父VM,ms-controller相反
vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
+ var name = node.name
+ elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则
+ elem.setAttribute("avalonctrl", node.value)
+ newVmodel.$events.expr = elem.tagName + '[avalonctrl="' + node.value + '"]'
+ avalon(elem).removeClass(name)
- elem.removeAttribute(node.name) //removeAttributeNode不会刷新[ms-controller]样式规则
- newVmodel.$events.element = elem
- avalon.bind(elem, "dataavailable", function(e) {
- if (typeof e.detail === "object" && elem !== e.target) {
- newVmodel.$fire.apply(newVmodel, e.detail)
- }
- })
- avalon(elem).removeClass(node.name)
}
-
scanAttr(elem, vmodels) //扫描特性节点
}
- function scanNodes(parent, vmodels) {
+ function scanNodeList(parent, vmodels) {
var node = parent.firstChild
while (node) {
var nextNode = node.nextSibling
- var nodeType = node.nodeType
- if (nodeType === 1) {
- scanTag(node, vmodels) //扫描元素节点
- } else if (nodeType === 3 && rexpr.test(node.data)) {
- scanText(node, vmodels) //扫描文本节点
- } else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
- scanText(node, vmodels) //扫描注释节点
- }
+ scanNode(node, node.nodeType, vmodels)
node = nextNode
}
}
+ function scanNodeArray(nodes, vmodels) {
+ for (var i = 0, node; node = nodes[i++]; ) {
+ scanNode(node, node.nodeType, vmodels)
+ }
+ }
+ function scanNode(node, nodeType, vmodels) {
+ if (nodeType === 1) {
+ scanTag(node, vmodels) //扫描元素节点
+ } else if (nodeType === 3 && rexpr.test(node.data)) {
+ scanText(node, vmodels) //扫描文本节点
+ } else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
+ scanText(node, vmodels) //扫描注释节点
+ }
+ }
+
function scanText(textNode, vmodels) {
var bindings = []
if (textNode.nodeType === 8) {
@@ -1902,15 +2034,14 @@
var filters = token.filters
var binding = {
type: "text",
- node: node,
- nodeType: 3,
+ element: node,
value: token.value,
filters: filters
}
if (filters && filters.indexOf("html") !== -1) {
avalon.Array.remove(filters, "html")
binding.type = "html"
- binding.replaceNodes = [node]
+ binding.group = 1
if (!filters.length) {
delete bindings.filters
}
@@ -1929,14 +2060,17 @@
var priorityMap = {
"if": 10,
"repeat": 90,
+ "data": 100,
"widget": 110,
"each": 1400,
"with": 1500,
"duplex": 2000,
"on": 3000
}
- var ons = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scroll,submit")
-
+ var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
+ function bindingSorter(a, b) {
+ return a.priority - b.priority
+ }
function scanAttr(elem, vmodels) {
//防止setAttribute, removeAttribute时 attributes自动被同步,导致for循环出错
var attributes = getAttributes ? getAttributes(elem) : avalon.slice(elem.attributes)
@@ -1952,7 +2086,7 @@
var value = attr.value
var name = attr.name
msData[name] = value
- if (ons[type]) {
+ if (events[type]) {
param = type
type = "on"
} else if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
@@ -1991,9 +2125,7 @@
}
}
}
- bindings.sort(function(a, b) {
- return a.priority - b.priority
- })
+ bindings.sort(bindingSorter)
if (msData["ms-checked"] && msData["ms-duplex"]) {
log("warning!一个元素上不能同时定义ms-checked与ms-duplex")
}
@@ -2007,19 +2139,11 @@
default:
executeBindings(bindings, vmodels)
if (!stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) {
- scanNodes(elem, vmodels) //扫描子孙元素
+ scanNodeList(elem, vmodels) //扫描子孙元素
}
break;
}
- if (elem.patchRepeat) {
- elem.patchRepeat()
- try {
- elem.patchRepeat = ""
- elem.removeAttribute("patchRepeat")
- } catch (e) {
- }
- }
}
//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支,
@@ -2035,7 +2159,7 @@
// window.onload = function() {
// var body = document.body
// for (var i = 0, el; el = body.children[i++]; ) {
- // console.log(el.outerHTML)
+ // avalon.log(el.outerHTML)
// }
// }
//依次输出
@@ -2074,7 +2198,7 @@
for (var i = 0, data; data = bindings[i++]; ) {
data.vmodels = vmodels
bindingHandlers[data.type](data, vmodels)
- if (data.evaluator && data.name) { //移除数据绑定,防止被二次解析
+ if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
//chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
data.element.removeAttribute(data.name)
}
@@ -2146,16 +2270,18 @@
/*********************************************************************
* 编译系统 *
**********************************************************************/
-
var keywords =
// 关键字
- "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"
+ "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"
-
+ + ",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"
// ECMA 5 - use strict
+ ",arguments,let,yield"
-
+ ",undefined"
var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g
var rsplit = /[^\w$]+/g
@@ -2175,48 +2301,40 @@
.replace(rnumber, "")
.replace(rcomma, "")
.split(/^$|,+/)
- var vars = [],
- unique = {}
- for (var i = 0; i < match.length; ++i) {
- var variable = match[i]
- if (!unique[variable]) {
- unique[variable] = vars.push(variable)
- }
- }
- return cacheVars(key, vars)
+ return cacheVars(key, uniqSet(match))
}
-
- //添加赋值语句
-
- function addAssign(vars, scope, name, duplex) {
+ /*添加赋值语句*/
+ function addAssign(vars, scope, name, data) {
var ret = [],
prefix = " = " + name + "."
for (var i = vars.length, prop; prop = vars[--i]; ) {
- if (scope.hasOwnProperty && scope.hasOwnProperty(prop)) { //IE6下节点没有hasOwnProperty
+ if (scope.hasOwnProperty(prop)) {
ret.push(prop + prefix + prop)
- if (duplex === "duplex") {
+ if (data.type === "duplex") {
vars.get = name + "." + prop
}
vars.splice(i, 1)
}
}
return ret
+
}
- function uniqVmodels(arr) {
- var uniq = {}
- return arr.filter(function(el) {
- if (!uniq[el.$id]) {
- uniq[el.$id] = 1
- return true
+ function uniqSet(array) {
+ var ret = [], unique = {}
+ for (var i = 0; i < array.length; i++) {
+ var el = array[i]
+ var id = el && typeof el.$id === "string" ? el.$id : el
+ if (!unique[id]) {
+ unique[id] = ret.push(el)
}
- })
+ }
+ return ret
}
- //缓存求值函数,以便多次利用
+
function createCache(maxLength) {
var keys = []
-
function cache(key, value) {
if (keys.push(key) > maxLength) {
delete cache[keys.shift()]
@@ -2225,14 +2343,15 @@
}
return cache;
}
- var cacheExprs = createCache(256)
+ //缓存求值函数,以便多次利用
+ var cacheExprs = createCache(128)
//取得求值函数及其传参
var rduplex = /\w\[.*\]|\w\.\w/
var rproxy = /(\$proxy\$[a-z]+)\d+$/
function parseExpr(code, scopes, data) {
var dataType = data.type
- var filters = dataType == "html" || dataType === "text" ? data.filters : ""
+ var filters = data.filters ? data.filters.join("") : ""
var exprId = scopes.map(function(el) {
return el.$id.replace(rproxy, "$1")
}) + code + dataType + filters
@@ -2242,13 +2361,13 @@
args = [],
prefix = ""
//args 是一个对象数组, names 是将要生成的求值函数的参数
- scopes = uniqVmodels(scopes)
+ scopes = uniqSet(scopes)
for (var i = 0, sn = scopes.length; i < sn; i++) {
if (vars.length) {
var name = "vm" + expose + "_" + i
names.push(name)
args.push(scopes[i])
- assigns.push.apply(assigns, addAssign(vars, scopes[i], name, dataType))
+ assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data))
}
}
if (!assigns.length && dataType === "duplex") {
@@ -2304,10 +2423,12 @@
}
return
} else if (dataType === "on") { //事件绑定
- code = code.replace("(", ".call(this,")
- if (data.hasArgs === "$event") {
- names.push("$event")
+ if (code.indexOf("(") === -1) {
+ code += ".call(this, $event)"
+ } else {
+ code = code.replace("(", ".call(this,")
}
+ names.push("$event")
code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
var lastIndex = code.lastIndexOf("\nreturn")
var header = code.slice(0, lastIndex)
@@ -2334,7 +2455,7 @@
'\r': '\\r',
'"': '\\"',
'\\': '\\\\'
- };
+ }
var quote = window.JSON && JSON.stringify || function(str) {
return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) {
var c = meta[a];
@@ -2365,30 +2486,6 @@
/*********************************************************************
* 绑定处理系统 *
**********************************************************************/
- var cacheDisplay = oneObject("a,abbr,b,span,strong,em,font,i,kbd", "inline")
- avalon.mix(cacheDisplay, oneObject("div,h1,h2,h3,h4,h5,h6,section,p", "block"))
-
- function parseDisplay(nodeName, val) {
- //用于取得此类标签的默认display值
- nodeName = nodeName.toLowerCase()
- if (!cacheDisplay[nodeName]) {
- var node = DOC.createElement(nodeName)
- root.appendChild(node)
- if (W3C) {
- val = getComputedStyle(node, null).display
- } else {
- val = node.currentStyle.display
- }
- root.removeChild(node)
- cacheDisplay[nodeName] = val
- }
- return cacheDisplay[nodeName]
- }
- avalon.parseDisplay = parseDisplay
- var supportDisplay = (function(td) {
- return W3C ? getComputedStyle(td, null).display === "table-cell" : true
- })(DOC.createElement("td"))
-
var propMap = {//属性名映射
"accept-charset": "acceptCharset",
"char": "ch",
@@ -2397,13 +2494,14 @@
"for": "htmlFor",
"http-equiv": "httpEquiv"
}
- var anomaly = "accessKey,allowTransparency,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan,contentEditable,"
- + "dateTime,defaultChecked,defaultSelected,defaultValue,frameBorder,isMap,longDesc,maxLength,marginWidth,marginHeight,"
- + "noHref,noResize,noShade,readOnly,rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
+
+ var anomaly = "accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan,"
+ + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight,"
+ + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
anomaly.replace(rword, function(name) {
propMap[name.toLowerCase()] = name
})
- var rdash = /\(([^)]*)\)/
+
var cssText = ""
head.insertBefore(avalon.parseHTML(cssText), head.firstChild) //避免IE6 base标签BUG
var rnoscripts = /
(?:[\s\S]+?)<\/noscript>/img
@@ -2423,16 +2521,16 @@
}
}
var cacheTmpls = avalon.templateCache = {}
- var ifSanctuary = DOC.createElement("div")
- ifSanctuary.innerHTML = "a"
- try {
- ifSanctuary.contains(ifSanctuary.firstChild)
- avalon.contains = function(a, b) {
- return a.contains(b)
- }
- } catch (e) {
- avalon.contains = fixContains
- }
+
+ avalon.contains = fixContains
+
+ var bools = "autofocus,autoplay,async,allowTransparency,checked,controls,declare,disabled,defer,defaultChecked,defaultSelected" +
+ "contentEditable,isMap,loop,multiple,noHref,noResize,noShade,open,readOnly,selected"
+ var boolMap = {}
+ bools.replace(rword, function(name) {
+ boolMap[name.toLowerCase()] = name
+ })
+
//这里的函数每当VM发生改变后,都会被执行(操作方为notifySubscribers)
var bindingExecutors = avalon.bindingExecutors = {
"attr": function(val, elem, data) {
@@ -2444,17 +2542,32 @@
// ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc
// ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名
// ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性
+ if (boolMap[attrName]) {
+ var bool = boolMap[attrName]
+ if (typeof elem[bool] === "boolean") {
+ return elem[bool] = !!val
+ }
+ }
var toRemove = (val === false) || (val === null) || (val === void 0)
+ if (!W3C && propMap[attrName]) {//旧式IE下需要进行名字映射
+ attrName = propMap[attrName]
+ var isInnate = true
+ }
if (toRemove) {
- elem.removeAttribute(attrName)
- } else if (!W3C) {
- attrName = propMap[attrName] || attrName
- if (toRemove) {
- elem.removeAttribute(attrName)
- } else {
- elem[attrName] = val
+ return elem.removeAttribute(attrName)
+ }
+ if (window.VBArray && !isInnate) {//IE下需要区分固有属性与自定义属性
+ if (isVML(elem)) {
+ isInnate = true
+ } else if (!rsvg.test(elem)) {
+ var attrs = elem.attributes || {}
+ var attr = attrs[attrName]
+ isInnate = attr ? attr.expando === false : attr === null
}
- } else if (!toRemove) {
+ }
+ if (isInnate) {
+ elem[attrName] = val
+ } else {
elem.setAttribute(attrName, val)
}
} else if (method === "include" && val) {
@@ -2467,14 +2580,16 @@
text = loaded.apply(elem, [text].concat(vmodels))
}
avalon.innerHTML(elem, text)
- scanNodes(elem, vmodels)
+ scanNodeList(elem, vmodels)
rendered && checkScan(elem, function() {
rendered.call(elem)
})
}
if (data.param === "src") {
if (cacheTmpls[val]) {
- scanTemplate(cacheTmpls[val])
+ avalon.nextTick(function() {
+ scanTemplate(cacheTmpls[val])
+ })
} else {
var xhr = getXHR()
xhr.onreadystatechange = function() {
@@ -2495,7 +2610,7 @@
} else {
//IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+)
//http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/
- var el = val && val.nodeType == 1 ? val : DOC.getElementById(val)
+ var el = val && val.nodeType === 1 ? val : DOC.getElementById(val)
if (el) {
if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML
var xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以
@@ -2523,44 +2638,51 @@
val = val.replace(/&/g, "&") //处理IE67自动转义的问题
}
elem[method] = val
+ if (window.chrome && elem.tagName === "EMBED") {
+ var parent = elem.parentNode//#525 chrome1-37下embed标签动态设置src不能发生请求
+ var comment = document.createComment("ms-src")
+ parent.replaceChild(comment, elem)
+ parent.replaceChild(elem, comment)
+ }
}
},
"class": function(val, elem, data) {
var $elem = avalon(elem),
method = data.type
- if (method === "class" && data.param) { //如果是旧风格
- $elem.toggleClass(data.param, !!val)
+ if (method === "class" && data.oldStyle) { //如果是旧风格
+ $elem.toggleClass(data.oldStyle, !!val)
} else {
- var toggle = data._evaluator ? !!data._evaluator.apply(elem, data._args) : true
- var className = data._class || val
+ //如果存在冒号就有求值函数
+ data.toggleClass = data._evaluator ? !!data._evaluator.apply(elem, data._args) : true
+ data.newClass = data.immobileClass || val
+ if (data.oldClass && data.newClass !== data.oldClass) {
+ $elem.removeClass(data.oldClass)
+ }
+ data.oldClass = data.newClass
switch (method) {
case "class":
- if (toggle && data.oldClass) {
- $elem.removeClass(data.oldClass)
- }
- $elem.toggleClass(className, toggle)
- data.oldClass = className
- break;
+ $elem.toggleClass(data.newClass, data.toggleClass)
+ break
case "hover":
case "active":
- if (!data.init) { //确保只绑定一次
- if (method === "hover") { //在移出移入时切换类名
- var event1 = "mouseenter",
- event2 = "mouseleave"
- } else { //在聚焦失焦中切换类名
+ if (!data.hasBindEvent) { //确保只绑定一次
+ var activate = "mouseenter" //在移出移入时切换类名
+ var abandon = "mouseleave"
+ if (method === "active") {//在聚焦失焦中切换类名
elem.tabIndex = elem.tabIndex || -1
- event1 = "mousedown", event2 = "mouseup"
+ activate = "mousedown"
+ abandon = "mouseup"
$elem.bind("mouseleave", function() {
- toggle && $elem.removeClass(className)
+ data.toggleClass && $elem.removeClass(data.newClass)
})
}
- $elem.bind(event1, function() {
- toggle && $elem.addClass(className)
+ $elem.bind(activate, function() {
+ data.toggleClass && $elem.addClass(data.newClass)
})
- $elem.bind(event2, function() {
- toggle && $elem.removeClass(className)
+ $elem.bind(abandon, function() {
+ data.toggleClass && $elem.removeClass(data.newClass)
})
- data.init = 1
+ data.hasBindEvent = true
}
break;
}
@@ -2577,44 +2699,38 @@
"repeat": function(method, pos, el) {
if (method) {
var data = this
- var group = data.group
- var pp = data.startRepeat && data.startRepeat.parentNode
- if (pp) { //fix #300 #307
- data.parent = pp
- }
- var parent = data.parent
+ var parent = data.element.parentNode
var proxies = data.proxies
var transation = hyperspace.cloneNode(false)
+
if (method === "del" || method === "move") {
- var locatedNode = getLocatedNode(parent, data, pos)
+ var locatedNode = locateFragment(data, pos)
}
+ var group = data.group
switch (method) {
case "add": //在pos位置后添加el数组(pos为数字,el为数组)
var arr = el
- var last = data.getter().length - 1
- var spans = []
- var lastFn = {}
+ var last = data.$repeat.length - 1
+ var fragments = []
for (var i = 0, n = arr.length; i < n; i++) {
var ii = i + pos
var proxy = getEachProxy(ii, arr[i], data, last)
proxies.splice(ii, 0, proxy)
- lastFn = shimController(data, transation, spans, proxy)
+ shimController(data, transation, proxy, fragments)
}
- locatedNode = getLocatedNode(parent, data, pos)
- lastFn.node = locatedNode
- lastFn.parent = parent
+ locatedNode = locateFragment(data, pos)
parent.insertBefore(transation, locatedNode)
- for (var i = 0, node; node = spans[i++]; ) {
- scanTag(node, data.vmodels)
+ for (var i = 0, fragment; fragment = fragments[i++]; ) {
+ scanNodeArray(fragment.nodes, fragment.vmodels)
+ fragment.nodes = fragment.vmodels = null
}
- spans = null
+ calculateFragmentGroup(data)
break
case "del": //将pos后的el个元素删掉(pos, el都是数字)
var removed = proxies.splice(pos, el)
- for (var i = 0, proxy; proxy = removed[i++]; ) {
- recycleEachProxy(proxy)
- }
- expelFromSanctuary(removeView(locatedNode, group, el))
+ var transation = removeFragment(locatedNode, group, el)
+ avalon.clearHTML(transation)
+ recycleEachProxies(removed)
break
case "index": //将proxies中的第pos个起的所有元素重新索引(pos为数字,el用作循环变量)
var last = proxies.length - 1
@@ -2625,27 +2741,27 @@
}
break
case "clear":
- if (data.startRepeat) {
+ var size = "proxySize" in data ? data.proxySize : proxies.length
+ if (size) {
+ var n = size * group, k = 0
while (true) {
- var node = data.startRepeat.nextSibling
- if (node && node !== data.endRepeat) {
- transation.appendChild(node)
+ var nextNode = data.element.nextSibling
+ if (nextNode && k < n) {
+ parent.removeChild(nextNode)
+ k++
} else {
break
}
}
- } else {
- transation = parent
+ recycleEachProxies(proxies)
}
- expelFromSanctuary(transation)
- proxies.length = 0
break
case "move": //将proxies中的第pos个元素移动el位置上(pos, el都是数字)
var t = proxies.splice(pos, 1)[0]
if (t) {
proxies.splice(el, 0, t)
- transation = removeView(locatedNode, group)
- locatedNode = getLocatedNode(parent, data, el)
+ transation = removeFragment(locatedNode, group)
+ locatedNode = locateFragment(data, el)
parent.insertBefore(transation, locatedNode)
}
break
@@ -2657,45 +2773,48 @@
break
case "append": //将pos的键值对从el中取出(pos为一个普通对象,el为预先生成好的代理VM对象池)
var pool = el
- var callback = getBindingCallback(data.callbackElement, "data-with-sorted", data.vmodels)
var keys = []
- var spans = []
- var lastFn = {}
+ var fragments = []
for (var key in pos) { //得到所有键名
if (pos.hasOwnProperty(key) && key !== "hasOwnProperty") {
keys.push(key)
}
}
- if (callback) { //如果有回调,则让它们排序
- var keys2 = callback.call(parent, keys)
+ if (data.sortedCallback) { //如果有回调,则让它们排序
+ var keys2 = data.sortedCallback.call(parent, keys)
if (keys2 && Array.isArray(keys2) && keys2.length) {
keys = keys2
}
}
for (var i = 0, key; key = keys[i++]; ) {
if (key !== "hasOwnProperty") {
- lastFn = shimController(data, transation, spans, pool[key])
+ shimController(data, transation, pool[key], fragments)
}
}
- lastFn.parent = parent
- lastFn.node = data.endRepeat || null
- parent.insertBefore(transation, lastFn.node)
- for (var i = 0, el; el = spans[i++]; ) {
- scanTag(el, data.vmodels)
+ data.proxySize = keys.length
+ parent.insertBefore(transation, data.element.nextSibling)
+ for (var i = 0, fragment; fragment = fragments[i++]; ) {
+ scanNodeArray(fragment.nodes, fragment.vmodels)
+ fragment.nodes = fragment.vmodels = null
}
- spans = null
+ calculateFragmentGroup(data)
break
}
- iteratorCallback.call(data, arguments)
+ var callback = data.renderedCallback || noop, args = arguments
+ checkScan(parent, function() {
+ callback.apply(parent, args)
+ if (parent.oldValue && parent.tagName === "SELECT" && method === "index") {//fix #503
+ avalon(parent).val(parent.oldValue.split(","))
+ }
+ })
}
},
"html": function(val, elem, data) {
val = val == null ? "" : val
- if (!elem) {
- elem = data.element = data.node.parentNode
- }
- if (data.replaceNodes) {
+ var parent = "group" in data ? elem.parentNode : elem
+ if ("group" in data) {
var fragment, nodes
+ //将值转换为文档碎片,原值可以为元素节点,文档碎片,NodeList,字符串
if (val.nodeType === 11) {
fragment = val
} else if (val.nodeType === 1 || val.item) {
@@ -2707,62 +2826,57 @@
} else {
fragment = avalon.parseHTML(val)
}
- var replaceNodes = avalon.slice(fragment.childNodes)
- elem.insertBefore(fragment, data.replaceNodes[0] || null) //fix IE6-8 insertBefore的第2个参数只能为节点或null
- for (var i = 0, node; node = data.replaceNodes[i++]; ) {
- elem.removeChild(node)
+ nodes = avalon.slice(fragment.childNodes)
+ if (nodes.length === 0) {
+ var comment = DOC.createComment("ms-html")
+ fragment.appendChild(comment)
+ nodes = [comment]
+ }
+ parent.insertBefore(fragment, elem) //fix IE6-8 insertBefore的第2个参数只能为节点或null
+ var length = data.group
+ while (elem) {
+ var nextNode = elem.nextSibling
+ parent.removeChild(elem)
+ length--
+ if (length === 0 || nextNode === null)
+ break
+ elem = nextNode
}
- data.replaceNodes = replaceNodes
+ data.element = nodes[0]
+ data.group = nodes.length
} else {
- avalon.innerHTML(elem, val)
+ avalon.innerHTML(parent, val)
}
avalon.nextTick(function() {
- scanNodes(elem, data.vmodels)
+ scanNodeList(parent, data.vmodels)
})
},
"if": function(val, elem, data) {
- var placehoder = data.placehoder
if (val) { //插回DOM树
- if (!data.msInDocument) {
- data.msInDocument = true
- try {
- placehoder.parentNode.replaceChild(elem, placehoder)
- } catch (e) {
- log("debug: ms-if " + e.message)
- }
+ if (elem.nodeType === 8) {
+ elem.parentNode.replaceChild(data.template, elem)
+ elem = data.element = data.template
}
- if (rbind.test(elem.outerHTML.replace(rlt, "<").replace(rgt, ">"))) {
+ if (elem.getAttribute(data.name)) {
+ elem.removeAttribute(data.name)
scanAttr(elem, data.vmodels)
}
- } else { //移出DOM树,放进ifSanctuary DIV中,并用注释节点占据原位置
- if (data.msInDocument) {
- data.msInDocument = false
- try {
- elem.parentNode.replaceChild(placehoder, elem)
- } catch (e) {
- log("debug: ms-if: elem.parentNode= " + elem.parentNode)
- }
- placehoder.elem = elem
- ifSanctuary.appendChild(elem)
+ } else { //移出DOM树,并用注释节点占据原位置
+ if (elem.nodeType === 1) {
+ var node = data.element = DOC.createComment("ms-if")
+ elem.parentNode.replaceChild(node, elem)
+ data.template = elem //元素节点
+ head.appendChild(elem)
}
}
},
"on": function(callback, elem, data) {
- var fn = data.evaluator
- var args = data.args
- var vmodels = data.vmodels
- if (!data.hasArgs) {
- callback = function(e) {
- return fn.apply(0, args).call(this, e)
- }
- } else {
- callback = function(e) {
- return fn.apply(this, args.concat(e))
- }
+ data.type = "on"
+ callback = function(e) {
+ var fn = data.evaluator || noop
+ return fn.apply(this, data.args.concat(e))
}
- elem.$vmodel = vmodels[0]
- elem.$vmodels = vmodels
- var eventType = data.param = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
+ var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
if (eventType === "scan") {
callback.call(elem, {type: eventType})
} else if (typeof data.specialBind === "function") {
@@ -2777,20 +2891,15 @@
avalon.unbind(elem, eventType, removeFn)
}
}
- data.evaluator = data.handler = noop
},
- "text": function(val, elem, data) {
+ "text": function(val, elem) {
val = val == null ? "" : val //不在页面上显示undefined null
- var node = data.node
- if (data.nodeType === 3) { //绑定在文本节点上
+ if (elem.nodeType === 3) { //绑定在文本节点上
try {//IE对游离于DOM树外的节点赋值会报错
- node.data = val
+ elem.data = val
} catch (e) {
}
} else { //绑定在特性节点上
- if (!elem) {
- elem = data.element = node.parentNode
- }
if ("textContent" in elem) {
elem.textContent = val
} else {
@@ -2803,10 +2912,30 @@
},
"widget": noop
}
- var rwhitespace = /^\s+$/
+
+ var rdash = /\(([^)]*)\)/
+
+ function parseDisplay(nodeName, val) {
+ //用于取得此类标签的默认display值
+ var key = "_" + nodeName
+ if (!parseDisplay[key]) {
+ var node = DOC.createElement(nodeName)
+ root.appendChild(node)
+ if (W3C) {
+ val = getComputedStyle(node, null).display
+ } else {
+ val = node.currentStyle.display
+ }
+ root.removeChild(node)
+ parseDisplay[key] = val
+ }
+ return parseDisplay[key]
+ }
+
+ avalon.parseDisplay = parseDisplay
//这里的函数只会在第一次被扫描后被执行一次,并放进行对应VM属性的subscribers数组内(操作方为registerSubscriber)
var bindingHandlers = avalon.bindingHandlers = {
- //这是一个字符串属性绑定的范本, 方便你在title, alt, src, href, include, css添加插值表达式
+ //这是一个字符串属性绑定的范本, 方便你在title, alt, src, href, include, css添加插值表达式
//
"attr": function(data, vmodels) {
var text = data.value.trim(),
@@ -2850,10 +2979,11 @@
}
var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况
if (!hasExpr) {
- data._class = className
+ data.immobileClass = className
}
parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : null))
- } else if (data.type === "class") {
+ } else {
+ data.immobileClass = data.oldStyle = data.param
parseExprProxy(text, vmodels, data)
}
},
@@ -2869,6 +2999,20 @@
if (form && form.msValidate) {
form.msValidate(elem)
}
+ data.msType = data.param || ""
+ if (data.msType === "bool") {
+ data.msType = "boolean"
+ log("ms-duplex-bool已经更名为ms-duplex-boolean")
+ } else if (data.msType === "text") {
+ data.msType = "string"
+ log("ms-duplex-text已经更名为ms-duplex-string")
+ }
+ if (data.msType === "radio") {
+ log("ms-duplex-radio将在2.0废掉,请尽量不要用")
+ }
+ if (!/boolean|string|number/.test(data.msType)) {
+ data.msType = ""
+ }
data.bound = function(type, callback) {
if (elem.addEventListener) {
elem.addEventListener(type, callback, false)
@@ -2886,66 +3030,56 @@
}
},
"repeat": function(data, vmodels) {
- var type = data.type,
- list
+ var type = data.type
parseExpr(data.value, vmodels, data)
- if (type !== "repeat") {
- log("warning:建议使用ms-repeat代替ms-each, ms-with, ms-repeat只占用一个标签并且性能更好")
- }
- var elem = data.callbackElement = data.parent = data.element //用于判定当前元素是否位于DOM树
- data.getter = function() {
- return this.evaluator.apply(0, this.args || [])
- }
data.proxies = []
- var freturn = true
+ var freturn = false
try {
- list = data.getter()
- var xtype = avalon.type(list)
- if (xtype == "object" || xtype == "array") {
- freturn = false
+ var $repeat = data.$repeat = data.evaluator.apply(0, data.args || [])
+ var xtype = avalon.type($repeat)
+ if (xtype !== "object" && xtype !== "array") {
+ freturn = true
+ avalon.log("warning:" + data.value + "对应类型不正确")
}
} catch (e) {
+ freturn = true
+ avalon.log("warning:" + data.value + "编译出错")
}
- var template = hyperspace.cloneNode(false)
- if (type === "repeat") {
- var startRepeat = DOC.createComment("ms-repeat-start")
- var endRepeat = DOC.createComment("ms-repeat-end")
- data.element = data.parent = elem.parentNode
- data.startRepeat = startRepeat
- data.endRepeat = endRepeat
- elem.removeAttribute(data.name)
- data.parent.replaceChild(endRepeat, elem)
- data.parent.insertBefore(startRepeat, endRepeat)
- template.appendChild(elem)
+ var elem = data.element
+ elem.removeAttribute(data.name)
+ data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
+ data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
+
+ var comment = data.element = DOC.createComment("ms-repeat")
+ if (type === "each" || type === "with") {
+ data.template = elem.innerHTML.trim()
+ avalon.clearHTML(elem).appendChild(comment)
} else {
- var node
- while (node = elem.firstChild) {
- if (node.nodeType === 3 && rwhitespace.test(node.data)) {
- elem.removeChild(node)
- } else {
- template.appendChild(node)
- }
- }
+ data.template = elem.outerHTML.trim()
+ data.group = 1
+ elem.parentNode.replaceChild(comment, elem)
}
- data.template = template
- data.rollback = function() {
+
+ data.rollback = function() {//只用于list为对象的情况
bindingExecutors.repeat.call(data, "clear")
- var endRepeat = data.endRepeat
- var parent = data.parent
- parent.insertBefore(data.template, endRepeat || null)
- if (endRepeat) {
- parent.removeChild(endRepeat)
- parent.removeChild(data.startRepeat)
- data.element = data.callbackElement
- }
+ var elem = data.element
+ var parentNode = elem.parentNode
+ var content = avalon.parseHTML(data.template)
+ var target = content.firstChild
+ parentNode.replaceChild(content, elem)
+ target = data.element = data.type === "repeat" ? target : parentNode
+ data.group = null
+ target.setAttribute(data.name, data.value)
}
var arr = data.value.split(".") || []
if (arr.length > 1) {
arr.pop()
var n = arr[0]
for (var i = 0, v; v = vmodels[i++]; ) {
- if (v && v.hasOwnProperty(n) && v[n][subscribers]) {
- v[n][subscribers].push(data)
+ if (v && v.hasOwnProperty(n)) {
+ var events = v[n].$events
+ events[subscribers] = events[subscribers] || []
+ events[subscribers].push(data)
break
}
}
@@ -2953,12 +3087,12 @@
if (freturn) {
return
}
- data.callbackName = "data-" + type + "-rendered"
+
data.handler = bindingExecutors.repeat
data.$outer = {}
var check0 = "$key",
check1 = "$val"
- if (Array.isArray(list)) {
+ if (Array.isArray($repeat)) {
check0 = "$first"
check1 = "$last"
}
@@ -2968,65 +3102,66 @@
break
}
}
- node = template.firstChild
- data.fastRepeat = !!node && node.nodeType === 1 && template.lastChild === node && !node.attributes["ms-controller"] && !node.attributes["ms-important"]
- list[subscribers] && list[subscribers].push(data)
- if (!Array.isArray(list) && type !== "each") {
- var pool = withProxyPool[list.$id]
+ var $list = ($repeat.$events || {})[subscribers]
+ if ($list && avalon.Array.ensure($list, data)) {
+ $$subscribers.push({
+ data: data, list: $list
+ })
+ }
+ if (!Array.isArray($repeat) && type !== "each") {
+ var pool = withProxyPool[$repeat.$id]
if (!pool) {
withProxyCount++
- pool = withProxyPool[list.$id] = {}
- for (var key in list) {
- if (list.hasOwnProperty(key) && key !== "hasOwnProperty") {
+ pool = withProxyPool[$repeat.$id] = {}
+ for (var key in $repeat) {
+ if ($repeat.hasOwnProperty(key) && key !== "hasOwnProperty") {
(function(k, v) {
pool[k] = createWithProxy(k, v, {})
pool[k].$watch("$val", function(val) {
- list[k] = val //#303
+ $repeat[k] = val //#303
})
- })(key, list[key])
+ })(key, $repeat[key])
}
}
}
- data.handler("append", list, pool)
+ data.handler("append", $repeat, pool)
} else {
- data.handler("add", 0, list)
+ data.handler("add", 0, $repeat)
}
},
"html": function(data, vmodels) {
parseExprProxy(data.value, vmodels, data)
},
- "if": function(data, vmodels) {
- var elem = data.element
- elem.removeAttribute(data.name)
- if (!data.placehoder) {
- data.msInDocument = data.placehoder = DOC.createComment("ms-if")
- }
- data.vmodels = vmodels
- parseExprProxy(data.value, vmodels, data)
-
- },
"on": function(data, vmodels) {
- var value = data.value,
- four = "$event"
+ var value = data.value
+ var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
+ if (typeof bindingHandlers.on[eventType + "Hook"] === "function") {
+ bindingHandlers.on[eventType + "Hook"](data)
+ }
if (value.indexOf("(") > 0 && value.indexOf(")") > -1) {
var matched = (value.match(rdash) || ["", ""])[1].trim()
if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理
- four = void 0
value = value.replace(rdash, "")
}
- } else {
- four = void 0
}
- data.hasArgs = four
- parseExprProxy(value, vmodels, data, four)
+ parseExprProxy(value, vmodels, data)
},
"visible": function(data, vmodels) {
- var elem = data.element
- if (!supportDisplay && !root.contains(elem)) { //fuck firfox 全家!
- var display = parseDisplay(elem.tagName)
- }
- display = display || avalon(elem).css("display")
- data.display = display === "none" ? parseDisplay(elem.tagName) : display
+ var elem = avalon(data.element)
+ var display = elem.css("display")
+ if (display === "none") {
+ var style = elem[0].style
+ var has = /visibility/i.test(style.cssText)
+ var visible = elem.css("visibility")
+ style.display = ""
+ style.visibility = "hidden"
+ display = elem.css("display")
+ if (display === "none") {
+ display = parseDisplay(elem[0].nodeName)
+ }
+ style.visibility = has ? visible : ""
+ }
+ data.display = display
parseExprProxy(data.value, vmodels, data)
},
"widget": function(data, vmodels) {
@@ -3066,22 +3201,20 @@
vmodel.$init()
}
if (vmodel.hasOwnProperty("$remove")) {
- var offTree = function() {
- vmodel.$remove()
- elem.msData = {}
- delete VMODELS[vmodel.$id]
+ function offTree() {
+ if (!elem.msRetain && !root.contains(elem)) {
+ vmodel.$remove()
+ elem.msData = {}
+ delete VMODELS[vmodel.$id]
+ return false
+ }
}
- if (supportMutationEvents) {
- elem.addEventListener("DOMNodeRemoved", function(e) {
- if (e.target === this && !this.msRetain &&
- //#441 chrome浏览器对文本域进行Ctrl+V操作,会触发DOMNodeRemoved事件
- (window.chrome ? (this.tagName === "INPUT" ? e.relatedNode.nodeType === 1 : 1) : 1)) {
- offTree()
- }
+ if (window.chrome) {
+ elem.addEventListener("DOMNodeRemovedFromDocument", function() {
+ setTimeout(offTree)
})
} else {
- elem.offTree = offTree
- launchImpl(elem)
+ avalon.tick(offTree)
}
}
} else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels
@@ -3089,9 +3222,6 @@
}
}
}
-
- var supportMutationEvents = W3C && DOC.implementation.hasFeature("MutationEvents", "2.0")
-
//============================ class preperty binding =======================
"hover,active".replace(rword, function(method) {
bindingHandlers[method] = bindingHandlers["class"]
@@ -3099,7 +3229,7 @@
"with,each".replace(rword, function(name) {
bindingHandlers[name] = bindingHandlers.repeat
})
- bindingHandlers.data = bindingHandlers.text = bindingHandlers.html
+ bindingHandlers["if"] = bindingHandlers.data = bindingHandlers.text = bindingHandlers.html
//============================= string preperty binding =======================
//与href绑定器 用法差不多的其他字符串属性的绑定器
//建议不要直接在src属性上修改,这样会发出无效的请求,请使用ms-src
@@ -3113,96 +3243,123 @@
//如果一个input标签添加了model绑定。那么它对应的字段将与元素的value连结在一起
//字段变,value就变;value变,字段也跟着变。默认是绑定input事件,
duplexBinding.INPUT = function(element, evaluator, data) {
- var fixType = data.param,
- type = element.type,
+ var type = element.type,
bound = data.bound,
$elem = avalon(element),
firstTigger = false,
- composing = false,
- callback = function(value) {
- firstTigger = true
- data.changed.call(this, value)
- },
- compositionStart = function() {
- composing = true
- },
- compositionEnd = function() {
- composing = false
- },
- //当value变化时改变model的值
- updateVModel = function() {
- if (composing)
- return
- var val = element.oldValue = element.value
- if ($elem.data("duplex-observe") !== false) {
- evaluator(val)
- callback.call(element, val)
- }
+ composing = false
+ function callback(value) {
+ firstTigger = true
+ data.changed.call(this, value)
+ }
+ function compositionStart() {
+ composing = true
+ }
+ function compositionEnd() {
+ composing = false
+ }
+ //当value变化时改变model的值
+ function updateVModel() {
+ if (composing)//处理中文输入法在minlengh下引发的BUG
+ return
+ var val = element.oldValue = element.value //防止递归调用形成死循环
+ var typedVal = getTypedValue(data, val) //尝式转换为正确的格式
+ if ($elem.data("duplex-observe") !== false) {
+ evaluator(typedVal)
+ callback.call(element, typedVal)
+ if ($elem.data("duplex-focus")) {
+ avalon.nextTick(function() {
+ element.focus()
+ })
}
+ }
+ }
//当model变化时,它就会改变value的值
data.handler = function() {
var val = evaluator()
+ val = val == null ? "" : val + ""
if (val !== element.value) {
- element.value = val + ""
+ element.value = val
}
}
- if (type === "checkbox" && fixType === "radio") {
+ if (type === "checkbox" && data.param === "radio") {
type = "radio"
}
if (type === "radio") {
- data.handler = function() {
- //IE6是通过defaultChecked来实现打勾效果
- element.defaultChecked = (element.checked = /bool|text/.test(fixType) ? evaluator() + "" === element.value : !!evaluator())
- }
+ var IE6 = !window.XMLHttpRequest
updateVModel = function() {
if ($elem.data("duplex-observe") !== false) {
var val = element.value
- if (fixType === "text") {
- evaluator(val)
- } else if (fixType === "bool") {
- val = val === "true"
- evaluator(val)
- } else {
- val = !element.defaultChecked
- evaluator(val)
- element.checked = val
- }
- callback.call(element, val)
+ var typedValue = data.msType ? getTypedValue(data, val) : !element.oldValue
+ evaluator(typedValue)
+ callback.call(element, typedValue)
}
}
- bound(fixType ? "click" : "mousedown", updateVModel)
+ data.handler = function() {
+ var val = evaluator()
+ var checked = data.msType ? val + "" === element.value : !!val
+ element.oldValue = checked
+ if (IE6) {
+ setTimeout(function() {
+ //IE8 checkbox, radio是使用defaultChecked控制选中状态,
+ //并且要先设置defaultChecked后设置checked
+ //并且必须设置延迟
+ element.defaultChecked = checked
+ element.checked = checked
+ }, 100)
+ } else {
+ element.checked = checked
+ }
+ }
+ bound(IE6 ? "mouseup" : "click", updateVModel)
} else if (type === "checkbox") {
updateVModel = function() {
if ($elem.data("duplex-observe") !== false) {
var method = element.checked ? "ensure" : "remove"
var array = evaluator()
- if (Array.isArray(array)) {
- avalon.Array[method](array, element.value)
- } else {
- avalon.error("ms-duplex位于checkbox时要求对应一个数组")
+ if (!Array.isArray(array)) {
+ log("ms-duplex应用于checkbox上要对应一个数组")
+ array = [array]
}
+ var typedValue = getTypedValue(data, element.value)
+ avalon.Array[method](array, typedValue)
callback.call(element, array)
}
}
+
data.handler = function() {
var array = [].concat(evaluator()) //强制转换为数组
- element.checked = array.indexOf(element.value) >= 0
+ element.checked = array.indexOf(getTypedValue(data, element.value)) >= 0
}
bound(W3C ? "change" : "click", updateVModel)
} else {
var event = element.attributes["data-duplex-event"] || element.attributes["data-event"] || {}
+ if (element.attributes["data-event"]) {
+ log("data-event指令已经废弃,请改用data-duplex-event")
+ }
event = event.value
if (event === "change") {
bound("change", updateVModel)
} else {
- if (W3C && DOC.documentMode !== 9) { //IE10+, W3C
+ if (W3C) { //IE9+, W3C
bound("input", updateVModel)
bound("compositionstart", compositionStart)
bound("compositionend", compositionEnd)
+ if ("onselectionchange"in DOC) {//fix IE9 http://www.matts411.com/post/internet-explorer-9-oninput/
+ function selectionchange(e) {
+ if (e.type === "focus") {
+ DOC.addEventListener("selectionchange", updateVModel, false);
+ } else {
+ DOC.removeEventListener("selectionchange", updateVModel, false);
+ }
+ }
+ bound("focus", selectionchange)
+ bound("blur", selectionchange)
+ }
} else {
var events = ["keyup", "paste", "cut", "change"]
@@ -3228,12 +3385,16 @@
})
}
}
-
}
}
- element.onTree = onTree
- launch(element)
element.oldValue = element.value
+ launch(function() {
+ if (avalon.contains(root, element)) {
+ onTree.call(element)
+ } else if (!element.msRetain) {
+ return false
+ }
+ })
registerSubscriber(data)
var timer = setTimeout(function() {
if (!firstTigger) {
@@ -3242,6 +3403,18 @@
clearTimeout(timer)
}, 31)
}
+
+ function getTypedValue(data, val) {
+ switch (data.msType) {
+ case "boolean":
+ return val === "true"
+ case "number":
+ return isFinite(val) || val === "" ? parseFloat(val) || 0 : val
+ default:
+ return val
+ }
+ }
+
var TimerID, ribbon = [],
launch = noop
function W3CFire(el, name, detail) {
@@ -3252,6 +3425,7 @@
}
el.dispatchEvent(event)
}
+
function onTree() { //disabled状态下改动不触发input事件
if (!this.disabled && this.oldValue !== this.value) {
if (W3C) {
@@ -3262,13 +3436,16 @@
}
}
+ avalon.tick = function(fn) {
+ if (ribbon.push(fn) === 1) {
+ TimerID = setInterval(ticker, 60)
+ }
+ }
+
function ticker() {
for (var n = ribbon.length - 1; n >= 0; n--) {
var el = ribbon[n]
- if (avalon.contains(root, el)) {
- el.onTree && el.onTree()
- } else if (!el.msRetain) {
- el.offTree && el.offTree()
+ if (el() === false) {
ribbon.splice(n, 1)
}
}
@@ -3276,11 +3453,6 @@
clearInterval(TimerID)
}
}
- function launchImpl(el) {
- if (ribbon.push(el) === 1) {
- TimerID = setInterval(ticker, 30)
- }
- }
function newSetter(newValue) {
oldSetter.call(this, newValue)
@@ -3296,29 +3468,44 @@
set: newSetter
})
} catch (e) {
- launch = launchImpl
+ launch = avalon.tick
}
duplexBinding.SELECT = function(element, evaluator, data) {
var $elem = avalon(element)
-
function updateVModel() {
if ($elem.data("duplex-observe") !== false) {
var val = $elem.val() //字符串或字符串数组
+ if (Array.isArray(val)) {
+ val = val.map(function(v) {
+ return getTypedValue(data, v)
+ })
+ } else {
+ val = getTypedValue(data, val)
+ }
if (val + "" !== element.oldValue) {
evaluator(val)
- element.oldValue = val + ""
}
data.changed.call(element, val)
}
}
data.handler = function() {
- var curValue = evaluator()
- curValue = curValue && curValue.$model || curValue
- curValue = Array.isArray(curValue) ? curValue.map(String) : curValue + ""
- if (curValue + "" !== element.oldValue) {
- $elem.val(curValue)
- element.oldValue = curValue + ""
+ var val = evaluator()
+ val = val && val.$model || val
+ //必须变成字符串后才能比较
+ if (Array.isArray(val)) {
+ if (!element.multiple) {
+ log("ms-duplex在