avalon-rails 0.0.3 → 1.3.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +6 -0
- data/lib/avalon-rails/version.rb +1 -1
- data/vendor/assets/javascripts/avalon.js +57 -52
- data/vendor/assets/javascripts/avalon.min.js +101 -101
- data/vendor/assets/javascripts/avalon.mobile.js +127 -76
- data/vendor/assets/javascripts/avalon.mobile.min.js +34 -35
- data/vendor/assets/javascripts/avalon.mobile.shim.js +4370 -0
- data/vendor/assets/javascripts/avalon.modern.js +58 -48
- data/vendor/assets/javascripts/avalon.modern.min.js +10 -11
- data/vendor/assets/javascripts/avalon.modern.shim.js +4091 -0
- data/vendor/assets/javascripts/avalon.shim.js +4761 -0
- metadata +5 -2
@@ -0,0 +1,4761 @@
|
|
1
|
+
/*==================================================
|
2
|
+
Copyright (c) 2013-2015 司徒正美 and other contributors
|
3
|
+
http://www.cnblogs.com/rubylouvre/
|
4
|
+
https://github.com/RubyLouvre
|
5
|
+
http://weibo.com/jslouvre/
|
6
|
+
|
7
|
+
Released under the MIT license
|
8
|
+
avalon.shim.js(去掉加载器与domReady) 1.391 build in 2015.1.31
|
9
|
+
___
|
10
|
+
support IE6+ and other browsers
|
11
|
+
==================================================*/
|
12
|
+
(function(global, factory) {
|
13
|
+
|
14
|
+
if (typeof module === "object" && typeof module.exports === "object") {
|
15
|
+
// For CommonJS and CommonJS-like environments where a proper `window`
|
16
|
+
// is present, execute the factory and get avalon.
|
17
|
+
// For environments that do not have a `window` with a `document`
|
18
|
+
// (such as Node.js), expose a factory as module.exports.
|
19
|
+
// This accentuates the need for the creation of a real `window`.
|
20
|
+
// e.g. var avalon = require("avalon")(window);
|
21
|
+
module.exports = global.document ? factory(global, true) : function(w) {
|
22
|
+
if (!w.document) {
|
23
|
+
throw new Error("Avalon requires a window with a document")
|
24
|
+
}
|
25
|
+
return factory(w)
|
26
|
+
}
|
27
|
+
} else {
|
28
|
+
factory(global)
|
29
|
+
}
|
30
|
+
|
31
|
+
// Pass this if window is not defined yet
|
32
|
+
}(typeof window !== "undefined" ? window : this, function(window, noGlobal){
|
33
|
+
|
34
|
+
/*********************************************************************
|
35
|
+
* 全局变量及方法 *
|
36
|
+
**********************************************************************/
|
37
|
+
var expose = new Date - 0
|
38
|
+
//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function
|
39
|
+
var DOC = window.document
|
40
|
+
var head = DOC.getElementsByTagName("head")[0] //HEAD元素
|
41
|
+
var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG
|
42
|
+
ifGroup.innerHTML = "X<style id='avalonStyle'>.avalonHide{ display: none!important }</style>"
|
43
|
+
ifGroup.setAttribute("ms-skip", "1")
|
44
|
+
var rnative = /\[native code\]/ //判定是否原生函数
|
45
|
+
function log() {
|
46
|
+
if (window.console && avalon.config.debug) {
|
47
|
+
// http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log
|
48
|
+
Function.apply.call(console.log, console, arguments)
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
|
53
|
+
var subscribers = "$" + expose
|
54
|
+
var otherRequire = window.require
|
55
|
+
var otherDefine = window.define
|
56
|
+
var stopRepeatAssign = false
|
57
|
+
var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach
|
58
|
+
var rcomplexType = /^(?:object|array)$/
|
59
|
+
var rsvg = /^\[object SVG\w*Element\]$/
|
60
|
+
var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/
|
61
|
+
var oproto = Object.prototype
|
62
|
+
var ohasOwn = oproto.hasOwnProperty
|
63
|
+
var serialize = oproto.toString
|
64
|
+
var ap = Array.prototype
|
65
|
+
var aslice = ap.slice
|
66
|
+
var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖
|
67
|
+
var W3C = window.dispatchEvent
|
68
|
+
var root = DOC.documentElement
|
69
|
+
var hyperspace = DOC.createDocumentFragment()
|
70
|
+
var cinerator = DOC.createElement("div")
|
71
|
+
var class2type = {}
|
72
|
+
"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function(name) {
|
73
|
+
class2type["[object " + name + "]"] = name.toLowerCase()
|
74
|
+
})
|
75
|
+
|
76
|
+
|
77
|
+
function noop() {
|
78
|
+
}
|
79
|
+
|
80
|
+
|
81
|
+
function oneObject(array, val) {
|
82
|
+
if (typeof array === "string") {
|
83
|
+
array = array.match(rword) || []
|
84
|
+
}
|
85
|
+
var result = {},
|
86
|
+
value = val !== void 0 ? val : 1
|
87
|
+
for (var i = 0, n = array.length; i < n; i++) {
|
88
|
+
result[array[i]] = value
|
89
|
+
}
|
90
|
+
return result
|
91
|
+
}
|
92
|
+
|
93
|
+
function createCache(maxLength) {
|
94
|
+
var keys = []
|
95
|
+
function cache(key, value) {
|
96
|
+
if (keys.push(key) > maxLength) {
|
97
|
+
delete cache[keys.shift()]
|
98
|
+
}
|
99
|
+
return cache[key] = value;
|
100
|
+
}
|
101
|
+
return cache;
|
102
|
+
}
|
103
|
+
//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
104
|
+
var generateID = function(prefix) {
|
105
|
+
prefix = prefix || "avalon"
|
106
|
+
return (prefix + Math.random() + Math.random()).replace(/0\./g, "")
|
107
|
+
}
|
108
|
+
function IE() {
|
109
|
+
if (window.VBArray) {
|
110
|
+
var mode = document.documentMode
|
111
|
+
return mode ? mode : window.XMLHttpRequest ? 7 : 6
|
112
|
+
} else {
|
113
|
+
return 0
|
114
|
+
}
|
115
|
+
}
|
116
|
+
var IEVersion = IE()
|
117
|
+
/*********************************************************************
|
118
|
+
* avalon的静态方法定义区 *
|
119
|
+
**********************************************************************/
|
120
|
+
avalon = function(el) { //创建jQuery式的无new 实例化结构
|
121
|
+
return new avalon.init(el)
|
122
|
+
}
|
123
|
+
|
124
|
+
avalon.init = function(el) {
|
125
|
+
this[0] = this.element = el
|
126
|
+
}
|
127
|
+
avalon.fn = avalon.prototype = avalon.init.prototype
|
128
|
+
|
129
|
+
avalon.type = function(obj) { //取得目标的类型
|
130
|
+
if (obj == null) {
|
131
|
+
return String(obj)
|
132
|
+
}
|
133
|
+
// 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function
|
134
|
+
return typeof obj === "object" || typeof obj === "function" ?
|
135
|
+
class2type[serialize.call(obj)] || "object" :
|
136
|
+
typeof obj
|
137
|
+
}
|
138
|
+
|
139
|
+
var isFunction = typeof alert === "object" ? function(fn) {
|
140
|
+
try {
|
141
|
+
return /^\s*\bfunction\b/.test(fn + "")
|
142
|
+
} catch (e) {
|
143
|
+
return false
|
144
|
+
}
|
145
|
+
} : function(fn) {
|
146
|
+
return serialize.call(fn) == "[object Function]"
|
147
|
+
}
|
148
|
+
avalon.isFunction = isFunction
|
149
|
+
|
150
|
+
avalon.isWindow = function(obj) {
|
151
|
+
if (!obj)
|
152
|
+
return false
|
153
|
+
// 利用IE678 window == document为true,document == window竟然为false的神奇特性
|
154
|
+
// 标准浏览器及IE9,IE10等使用 正则检测
|
155
|
+
return obj == obj.document && obj.document != obj
|
156
|
+
}
|
157
|
+
|
158
|
+
function isWindow(obj) {
|
159
|
+
return rwindow.test(serialize.call(obj))
|
160
|
+
}
|
161
|
+
if (isWindow(window)) {
|
162
|
+
avalon.isWindow = isWindow
|
163
|
+
}
|
164
|
+
var enu
|
165
|
+
for (enu in avalon({})) {
|
166
|
+
break
|
167
|
+
}
|
168
|
+
var enumerateBUG = enu !== "0" //IE6下为true, 其他为false
|
169
|
+
/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/
|
170
|
+
avalon.isPlainObject = function(obj, key) {
|
171
|
+
if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) {
|
172
|
+
return false;
|
173
|
+
}
|
174
|
+
try { //IE内置对象没有constructor
|
175
|
+
if (obj.constructor && !ohasOwn.call(obj, "constructor") && !ohasOwn.call(obj.constructor.prototype, "isPrototypeOf")) {
|
176
|
+
return false;
|
177
|
+
}
|
178
|
+
} catch (e) { //IE8 9会在这里抛错
|
179
|
+
return false;
|
180
|
+
}
|
181
|
+
if (enumerateBUG) {
|
182
|
+
for (key in obj) {
|
183
|
+
return ohasOwn.call(obj, key)
|
184
|
+
}
|
185
|
+
}
|
186
|
+
for (key in obj) {
|
187
|
+
}
|
188
|
+
return key === void 0 || ohasOwn.call(obj, key);
|
189
|
+
}
|
190
|
+
if (rnative.test(Object.getPrototypeOf)) {
|
191
|
+
avalon.isPlainObject = function(obj) {
|
192
|
+
// 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过
|
193
|
+
return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto
|
194
|
+
}
|
195
|
+
}
|
196
|
+
//与jQuery.extend方法,可用于浅拷贝,深拷贝
|
197
|
+
avalon.mix = avalon.fn.mix = function() {
|
198
|
+
var options, name, src, copy, copyIsArray, clone,
|
199
|
+
target = arguments[0] || {},
|
200
|
+
i = 1,
|
201
|
+
length = arguments.length,
|
202
|
+
deep = false
|
203
|
+
|
204
|
+
// 如果第一个参数为布尔,判定是否深拷贝
|
205
|
+
if (typeof target === "boolean") {
|
206
|
+
deep = target
|
207
|
+
target = arguments[1] || {}
|
208
|
+
i++
|
209
|
+
}
|
210
|
+
|
211
|
+
//确保接受方为一个复杂的数据类型
|
212
|
+
if (typeof target !== "object" && !isFunction(target)) {
|
213
|
+
target = {}
|
214
|
+
}
|
215
|
+
|
216
|
+
//如果只有一个参数,那么新成员添加于mix所在的对象上
|
217
|
+
if (i === length) {
|
218
|
+
target = this
|
219
|
+
i--
|
220
|
+
}
|
221
|
+
|
222
|
+
for (; i < length; i++) {
|
223
|
+
//只处理非空参数
|
224
|
+
if ((options = arguments[i]) != null) {
|
225
|
+
for (name in options) {
|
226
|
+
src = target[name]
|
227
|
+
try {
|
228
|
+
copy = options[name] //当options为VBS对象时报错
|
229
|
+
} catch (e) {
|
230
|
+
continue
|
231
|
+
}
|
232
|
+
|
233
|
+
// 防止环引用
|
234
|
+
if (target === copy) {
|
235
|
+
continue
|
236
|
+
}
|
237
|
+
if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) {
|
238
|
+
|
239
|
+
if (copyIsArray) {
|
240
|
+
copyIsArray = false
|
241
|
+
clone = src && Array.isArray(src) ? src : []
|
242
|
+
|
243
|
+
} else {
|
244
|
+
clone = src && avalon.isPlainObject(src) ? src : {}
|
245
|
+
}
|
246
|
+
|
247
|
+
target[name] = avalon.mix(deep, clone, copy)
|
248
|
+
} else if (copy !== void 0) {
|
249
|
+
target[name] = copy
|
250
|
+
}
|
251
|
+
}
|
252
|
+
}
|
253
|
+
}
|
254
|
+
return target
|
255
|
+
}
|
256
|
+
|
257
|
+
function _number(a, len) { //用于模拟slice, splice的效果
|
258
|
+
a = Math.floor(a) || 0
|
259
|
+
return a < 0 ? Math.max(len + a, 0) : Math.min(a, len);
|
260
|
+
}
|
261
|
+
avalon.mix({
|
262
|
+
rword: rword,
|
263
|
+
subscribers: subscribers,
|
264
|
+
version: 1.391,
|
265
|
+
ui: {},
|
266
|
+
log: log,
|
267
|
+
slice: W3C ? function(nodes, start, end) {
|
268
|
+
return aslice.call(nodes, start, end)
|
269
|
+
} : function(nodes, start, end) {
|
270
|
+
var ret = []
|
271
|
+
var len = nodes.length
|
272
|
+
if (end === void 0)
|
273
|
+
end = len
|
274
|
+
if (typeof end === "number" && isFinite(end)) {
|
275
|
+
start = _number(start, len)
|
276
|
+
end = _number(end, len)
|
277
|
+
for (var i = start; i < end; ++i) {
|
278
|
+
ret[i - start] = nodes[i]
|
279
|
+
}
|
280
|
+
}
|
281
|
+
return ret
|
282
|
+
},
|
283
|
+
noop: noop,
|
284
|
+
/*如果不用Error对象封装一下,str在控制台下可能会乱码*/
|
285
|
+
error: function(str, e) {
|
286
|
+
throw new (e || Error)(str)
|
287
|
+
},
|
288
|
+
/*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/
|
289
|
+
oneObject: oneObject,
|
290
|
+
/* avalon.range(10)
|
291
|
+
=> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
292
|
+
avalon.range(1, 11)
|
293
|
+
=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
|
294
|
+
avalon.range(0, 30, 5)
|
295
|
+
=> [0, 5, 10, 15, 20, 25]
|
296
|
+
avalon.range(0, -10, -1)
|
297
|
+
=> [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
|
298
|
+
avalon.range(0)
|
299
|
+
=> []*/
|
300
|
+
range: function(start, end, step) { // 用于生成整数数组
|
301
|
+
step || (step = 1)
|
302
|
+
if (end == null) {
|
303
|
+
end = start || 0
|
304
|
+
start = 0
|
305
|
+
}
|
306
|
+
var index = -1,
|
307
|
+
length = Math.max(0, Math.ceil((end - start) / step)),
|
308
|
+
result = Array(length)
|
309
|
+
while (++index < length) {
|
310
|
+
result[index] = start
|
311
|
+
start += step
|
312
|
+
}
|
313
|
+
return result
|
314
|
+
},
|
315
|
+
eventHooks: {},
|
316
|
+
/*绑定事件*/
|
317
|
+
bind: function(el, type, fn, phase) {
|
318
|
+
var hooks = avalon.eventHooks
|
319
|
+
var hook = hooks[type]
|
320
|
+
if (typeof hook === "object") {
|
321
|
+
type = hook.type
|
322
|
+
if (hook.deel) {
|
323
|
+
fn = hook.deel(el, fn)
|
324
|
+
}
|
325
|
+
}
|
326
|
+
var callback = W3C ? fn : function(e) {
|
327
|
+
fn.call(el, fixEvent(e));
|
328
|
+
}
|
329
|
+
if (W3C) {
|
330
|
+
el.addEventListener(type, callback, !!phase)
|
331
|
+
} else {
|
332
|
+
el.attachEvent("on" + type, callback)
|
333
|
+
}
|
334
|
+
return callback
|
335
|
+
},
|
336
|
+
/*卸载事件*/
|
337
|
+
unbind: function(el, type, fn, phase) {
|
338
|
+
var hooks = avalon.eventHooks
|
339
|
+
var hook = hooks[type]
|
340
|
+
var callback = fn || noop
|
341
|
+
if (typeof hook === "object") {
|
342
|
+
type = hook.type
|
343
|
+
}
|
344
|
+
if (W3C) {
|
345
|
+
el.removeEventListener(type, callback, !!phase)
|
346
|
+
} else {
|
347
|
+
el.detachEvent("on" + type, callback)
|
348
|
+
}
|
349
|
+
},
|
350
|
+
/*读写删除元素节点的样式*/
|
351
|
+
css: function(node, name, value) {
|
352
|
+
if (node instanceof avalon) {
|
353
|
+
node = node[0]
|
354
|
+
}
|
355
|
+
var prop = /[_-]/.test(name) ? camelize(name) : name
|
356
|
+
name = avalon.cssName(prop) || prop
|
357
|
+
if (value === void 0 || typeof value === "boolean") { //获取样式
|
358
|
+
var fn = cssHooks[prop + ":get"] || cssHooks["@:get"]
|
359
|
+
if (name === "background") {
|
360
|
+
name = "backgroundColor"
|
361
|
+
}
|
362
|
+
var val = fn(node, name)
|
363
|
+
return value === true ? parseFloat(val) || 0 : val
|
364
|
+
} else if (value === "") { //请除样式
|
365
|
+
node.style[name] = ""
|
366
|
+
} else { //设置样式
|
367
|
+
if (value == null || value !== value) {
|
368
|
+
return
|
369
|
+
}
|
370
|
+
if (isFinite(value) && !avalon.cssNumber[prop]) {
|
371
|
+
value += "px"
|
372
|
+
}
|
373
|
+
fn = cssHooks[prop + ":set"] || cssHooks["@:set"]
|
374
|
+
fn(node, name, value)
|
375
|
+
}
|
376
|
+
},
|
377
|
+
/*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/
|
378
|
+
each: function(obj, fn) {
|
379
|
+
if (obj) { //排除null, undefined
|
380
|
+
var i = 0
|
381
|
+
if (isArrayLike(obj)) {
|
382
|
+
for (var n = obj.length; i < n; i++) {
|
383
|
+
if (fn(i, obj[i]) === false)
|
384
|
+
break
|
385
|
+
}
|
386
|
+
} else {
|
387
|
+
for (i in obj) {
|
388
|
+
if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) {
|
389
|
+
break
|
390
|
+
}
|
391
|
+
}
|
392
|
+
}
|
393
|
+
}
|
394
|
+
},
|
395
|
+
//收集元素的data-{{prefix}}-*属性,并转换为对象
|
396
|
+
getWidgetData: function(elem, prefix) {
|
397
|
+
var raw = avalon(elem).data()
|
398
|
+
var result = {}
|
399
|
+
for (var i in raw) {
|
400
|
+
if (i.indexOf(prefix) === 0) {
|
401
|
+
result[i.replace(prefix, "").replace(/\w/, function(a) {
|
402
|
+
return a.toLowerCase()
|
403
|
+
})] = raw[i]
|
404
|
+
}
|
405
|
+
}
|
406
|
+
return result
|
407
|
+
},
|
408
|
+
Array: {
|
409
|
+
/*只有当前数组不存在此元素时只添加它*/
|
410
|
+
ensure: function(target, item) {
|
411
|
+
if (target.indexOf(item) === -1) {
|
412
|
+
return target.push(item)
|
413
|
+
}
|
414
|
+
},
|
415
|
+
/*移除数组中指定位置的元素,返回布尔表示成功与否*/
|
416
|
+
removeAt: function(target, index) {
|
417
|
+
return !!target.splice(index, 1).length
|
418
|
+
},
|
419
|
+
/*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/
|
420
|
+
remove: function(target, item) {
|
421
|
+
var index = target.indexOf(item)
|
422
|
+
if (~index)
|
423
|
+
return avalon.Array.removeAt(target, index)
|
424
|
+
return false
|
425
|
+
}
|
426
|
+
}
|
427
|
+
})
|
428
|
+
|
429
|
+
var bindingHandlers = avalon.bindingHandlers = {}
|
430
|
+
var bindingExecutors = avalon.bindingExecutors = {}
|
431
|
+
|
432
|
+
/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/
|
433
|
+
function isArrayLike(obj) {
|
434
|
+
if (!obj)
|
435
|
+
return false
|
436
|
+
var n = obj.length
|
437
|
+
if (n === (n >>> 0)) { //检测length属性是否为非负整数
|
438
|
+
var type = serialize.call(obj).slice(8, -1)
|
439
|
+
if (/(?:regexp|string|function|window|global)$/i.test(type))
|
440
|
+
return false
|
441
|
+
if (type === "Array")
|
442
|
+
return true
|
443
|
+
try {
|
444
|
+
if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象
|
445
|
+
return /^\s?function/.test(obj.item || obj.callee)
|
446
|
+
}
|
447
|
+
return true
|
448
|
+
} catch (e) { //IE的NodeList直接抛错
|
449
|
+
return !obj.eval //IE6-8 window
|
450
|
+
}
|
451
|
+
}
|
452
|
+
return false
|
453
|
+
}
|
454
|
+
/*视浏览器情况采用最快的异步回调(在avalon.ready里,还有一个分支,用于处理IE6-9)*/
|
455
|
+
avalon.nextTick = window.setImmediate ? setImmediate.bind(window) : function(callback) {
|
456
|
+
setTimeout(callback, 0) //IE10-11 or W3C
|
457
|
+
}
|
458
|
+
|
459
|
+
/*********************************************************************
|
460
|
+
* javascript 底层补丁 *
|
461
|
+
**********************************************************************/
|
462
|
+
if (!"司徒正美".trim) {
|
463
|
+
var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g
|
464
|
+
String.prototype.trim = function() {
|
465
|
+
return this.replace(rtrim, "")
|
466
|
+
}
|
467
|
+
}
|
468
|
+
var hasDontEnumBug = !({
|
469
|
+
'toString': null
|
470
|
+
}).propertyIsEnumerable('toString'),
|
471
|
+
hasProtoEnumBug = (function() {
|
472
|
+
}).propertyIsEnumerable('prototype'),
|
473
|
+
dontEnums = [
|
474
|
+
"toString",
|
475
|
+
"toLocaleString",
|
476
|
+
"valueOf",
|
477
|
+
"hasOwnProperty",
|
478
|
+
"isPrototypeOf",
|
479
|
+
"propertyIsEnumerable",
|
480
|
+
"constructor"
|
481
|
+
],
|
482
|
+
dontEnumsLength = dontEnums.length;
|
483
|
+
if (!Object.keys) {
|
484
|
+
Object.keys = function(object) { //ecma262v5 15.2.3.14
|
485
|
+
var theKeys = [];
|
486
|
+
var skipProto = hasProtoEnumBug && typeof object === "function"
|
487
|
+
if (typeof object === "string" || (object && object.callee)) {
|
488
|
+
for (var i = 0; i < object.length; ++i) {
|
489
|
+
theKeys.push(String(i))
|
490
|
+
}
|
491
|
+
} else {
|
492
|
+
for (var name in object) {
|
493
|
+
if (!(skipProto && name === "prototype") && ohasOwn.call(object, name)) {
|
494
|
+
theKeys.push(String(name))
|
495
|
+
}
|
496
|
+
}
|
497
|
+
}
|
498
|
+
|
499
|
+
if (hasDontEnumBug) {
|
500
|
+
var ctor = object.constructor,
|
501
|
+
skipConstructor = ctor && ctor.prototype === object;
|
502
|
+
for (var j = 0; j < dontEnumsLength; j++) {
|
503
|
+
var dontEnum = dontEnums[j]
|
504
|
+
if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) {
|
505
|
+
theKeys.push(dontEnum)
|
506
|
+
}
|
507
|
+
}
|
508
|
+
}
|
509
|
+
return theKeys
|
510
|
+
}
|
511
|
+
}
|
512
|
+
if (!Array.isArray) {
|
513
|
+
Array.isArray = function(a) {
|
514
|
+
return serialize.call(a) === "[object Array]"
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
518
|
+
if (!noop.bind) {
|
519
|
+
Function.prototype.bind = function(scope) {
|
520
|
+
if (arguments.length < 2 && scope === void 0)
|
521
|
+
return this
|
522
|
+
var fn = this,
|
523
|
+
argv = arguments
|
524
|
+
return function() {
|
525
|
+
var args = [],
|
526
|
+
i
|
527
|
+
for (i = 1; i < argv.length; i++)
|
528
|
+
args.push(argv[i])
|
529
|
+
for (i = 0; i < arguments.length; i++)
|
530
|
+
args.push(arguments[i])
|
531
|
+
return fn.apply(scope, args)
|
532
|
+
}
|
533
|
+
}
|
534
|
+
}
|
535
|
+
|
536
|
+
function iterator(vars, body, ret) {
|
537
|
+
var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret
|
538
|
+
return Function("fn,scope", fun)
|
539
|
+
}
|
540
|
+
if (!rnative.test([].map)) {
|
541
|
+
avalon.mix(ap, {
|
542
|
+
//定位操作,返回数组中第一个等于给定参数的元素的索引值。
|
543
|
+
indexOf: function(item, index) {
|
544
|
+
var n = this.length,
|
545
|
+
i = ~~index
|
546
|
+
if (i < 0)
|
547
|
+
i += n
|
548
|
+
for (; i < n; i++)
|
549
|
+
if (this[i] === item)
|
550
|
+
return i
|
551
|
+
return -1
|
552
|
+
},
|
553
|
+
//定位操作,同上,不过是从后遍历。
|
554
|
+
lastIndexOf: function(item, index) {
|
555
|
+
var n = this.length,
|
556
|
+
i = index == null ? n - 1 : index
|
557
|
+
if (i < 0)
|
558
|
+
i = Math.max(0, n + i)
|
559
|
+
for (; i >= 0; i--)
|
560
|
+
if (this[i] === item)
|
561
|
+
return i
|
562
|
+
return -1
|
563
|
+
},
|
564
|
+
//迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。
|
565
|
+
forEach: iterator("", '_', ""),
|
566
|
+
//迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组
|
567
|
+
filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'),
|
568
|
+
//收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。
|
569
|
+
map: iterator('r=[],', 'r[i]=_', 'return r'),
|
570
|
+
//只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。
|
571
|
+
some: iterator("", 'if(_)return true', 'return false'),
|
572
|
+
//只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。
|
573
|
+
every: iterator("", 'if(!_)return false', 'return true')
|
574
|
+
})
|
575
|
+
}
|
576
|
+
/*********************************************************************
|
577
|
+
* DOM 底层补丁 *
|
578
|
+
**********************************************************************/
|
579
|
+
|
580
|
+
function fixContains(root, el) {
|
581
|
+
try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错
|
582
|
+
while ((el = el.parentNode))
|
583
|
+
if (el === root)
|
584
|
+
return true;
|
585
|
+
return false
|
586
|
+
} catch (e) {
|
587
|
+
return false
|
588
|
+
}
|
589
|
+
}
|
590
|
+
avalon.contains = fixContains
|
591
|
+
//safari5+是把contains方法放在Element.prototype上而不是Node.prototype
|
592
|
+
if (!root.contains) {
|
593
|
+
Node.prototype.contains = function(arg) {
|
594
|
+
return !!(this.compareDocumentPosition(arg) & 16)
|
595
|
+
}
|
596
|
+
}
|
597
|
+
//IE6-11的文档对象没有contains
|
598
|
+
if (!DOC.contains) {
|
599
|
+
DOC.contains = function(b) {
|
600
|
+
return fixContains(DOC, b)
|
601
|
+
}
|
602
|
+
}
|
603
|
+
|
604
|
+
function outerHTML() {
|
605
|
+
return new XMLSerializer().serializeToString(this)
|
606
|
+
}
|
607
|
+
|
608
|
+
|
609
|
+
if (window.SVGElement) {
|
610
|
+
var svgns = "http://www.w3.org/2000/svg"
|
611
|
+
var svg = DOC.createElementNS(svgns, "svg")
|
612
|
+
svg.innerHTML = '<circle cx="50" cy="50" r="40" fill="red" />'
|
613
|
+
if (!rsvg.test(svg.firstChild)) { // #409
|
614
|
+
function enumerateNode(node, targetNode) {
|
615
|
+
if (node && node.childNodes) {
|
616
|
+
var nodes = node.childNodes
|
617
|
+
for (var i = 0, el; el = nodes[i++]; ) {
|
618
|
+
if (el.tagName) {
|
619
|
+
var svg = DOC.createElementNS(svgns,
|
620
|
+
el.tagName.toLowerCase())
|
621
|
+
ap.forEach.call(el.attributes, function(attr) {
|
622
|
+
svg.setAttribute(attr.name, attr.value) //复制属性
|
623
|
+
})
|
624
|
+
// 递归处理子节点
|
625
|
+
enumerateNode(el, svg)
|
626
|
+
targetNode.appendChild(svg)
|
627
|
+
}
|
628
|
+
}
|
629
|
+
}
|
630
|
+
}
|
631
|
+
Object.defineProperties(SVGElement.prototype, {
|
632
|
+
"outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性
|
633
|
+
enumerable: true,
|
634
|
+
configurable: true,
|
635
|
+
get: outerHTML,
|
636
|
+
set: function(html) {
|
637
|
+
var tagName = this.tagName.toLowerCase(),
|
638
|
+
par = this.parentNode,
|
639
|
+
frag = avalon.parseHTML(html)
|
640
|
+
// 操作的svg,直接插入
|
641
|
+
if (tagName === "svg") {
|
642
|
+
par.insertBefore(frag, this)
|
643
|
+
// svg节点的子节点类似
|
644
|
+
} else {
|
645
|
+
var newFrag = DOC.createDocumentFragment()
|
646
|
+
enumerateNode(frag, newFrag)
|
647
|
+
par.insertBefore(newFrag, this)
|
648
|
+
}
|
649
|
+
par.removeChild(this)
|
650
|
+
}
|
651
|
+
},
|
652
|
+
"innerHTML": {
|
653
|
+
enumerable: true,
|
654
|
+
configurable: true,
|
655
|
+
get: function() {
|
656
|
+
var s = this.outerHTML
|
657
|
+
var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i")
|
658
|
+
var rclose = new RegExp("<\/" + this.nodeName + ">$", "i")
|
659
|
+
return s.replace(ropen, "").replace(rclose, "")
|
660
|
+
},
|
661
|
+
set: function(html) {
|
662
|
+
if (avalon.clearHTML) {
|
663
|
+
avalon.clearHTML(this)
|
664
|
+
var frag = avalon.parseHTML(html)
|
665
|
+
enumerateNode(frag, this)
|
666
|
+
}
|
667
|
+
}
|
668
|
+
}
|
669
|
+
})
|
670
|
+
}
|
671
|
+
}
|
672
|
+
if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML
|
673
|
+
HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML);
|
674
|
+
}
|
675
|
+
|
676
|
+
//============================= event binding =======================
|
677
|
+
var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/
|
678
|
+
function fixEvent(event) {
|
679
|
+
var ret = {}
|
680
|
+
for (var i in event) {
|
681
|
+
ret[i] = event[i]
|
682
|
+
}
|
683
|
+
var target = ret.target = event.srcElement
|
684
|
+
if (event.type.indexOf("key") === 0) {
|
685
|
+
ret.which = event.charCode != null ? event.charCode : event.keyCode
|
686
|
+
} else if (rmouseEvent.test(event.type)) {
|
687
|
+
var doc = target.ownerDocument || DOC
|
688
|
+
var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement
|
689
|
+
ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0)
|
690
|
+
ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0)
|
691
|
+
ret.wheelDeltaY = ret.wheelDelta
|
692
|
+
ret.wheelDeltaX = 0
|
693
|
+
}
|
694
|
+
ret.timeStamp = new Date - 0
|
695
|
+
ret.originalEvent = event
|
696
|
+
ret.preventDefault = function() { //阻止默认行为
|
697
|
+
event.returnValue = false
|
698
|
+
}
|
699
|
+
ret.stopPropagation = function() { //阻止事件在DOM树中的传播
|
700
|
+
event.cancelBubble = true
|
701
|
+
}
|
702
|
+
return ret
|
703
|
+
}
|
704
|
+
|
705
|
+
var eventHooks = avalon.eventHooks
|
706
|
+
//针对firefox, chrome修正mouseenter, mouseleave
|
707
|
+
if (!("onmouseenter" in root)) {
|
708
|
+
avalon.each({
|
709
|
+
mouseenter: "mouseover",
|
710
|
+
mouseleave: "mouseout"
|
711
|
+
}, function(origType, fixType) {
|
712
|
+
eventHooks[origType] = {
|
713
|
+
type: fixType,
|
714
|
+
deel: function(elem, fn) {
|
715
|
+
return function(e) {
|
716
|
+
var t = e.relatedTarget
|
717
|
+
if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) {
|
718
|
+
delete e.type
|
719
|
+
e.type = origType
|
720
|
+
return fn.call(elem, e)
|
721
|
+
}
|
722
|
+
}
|
723
|
+
}
|
724
|
+
}
|
725
|
+
})
|
726
|
+
}
|
727
|
+
//针对IE9+, w3c修正animationend
|
728
|
+
avalon.each({
|
729
|
+
AnimationEvent: "animationend",
|
730
|
+
WebKitAnimationEvent: "webkitAnimationEnd"
|
731
|
+
}, function(construct, fixType) {
|
732
|
+
if (window[construct] && !eventHooks.animationend) {
|
733
|
+
eventHooks.animationend = {
|
734
|
+
type: fixType
|
735
|
+
}
|
736
|
+
}
|
737
|
+
})
|
738
|
+
//针对IE6-8修正input
|
739
|
+
if (!("oninput" in DOC.createElement("input"))) {
|
740
|
+
eventHooks.input = {
|
741
|
+
type: "propertychange",
|
742
|
+
deel: function(elem, fn) {
|
743
|
+
return function(e) {
|
744
|
+
if (e.propertyName === "value") {
|
745
|
+
e.type = "input"
|
746
|
+
return fn.call(elem, e)
|
747
|
+
}
|
748
|
+
}
|
749
|
+
}
|
750
|
+
}
|
751
|
+
}
|
752
|
+
if (DOC.onmousewheel === void 0) {
|
753
|
+
/* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120
|
754
|
+
firefox DOMMouseScroll detail 下3 上-3
|
755
|
+
firefox wheel detlaY 下3 上-3
|
756
|
+
IE9-11 wheel deltaY 下40 上-40
|
757
|
+
chrome wheel deltaY 下100 上-100 */
|
758
|
+
var fixWheelType = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll"
|
759
|
+
var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail"
|
760
|
+
eventHooks.mousewheel = {
|
761
|
+
type: fixWheelType,
|
762
|
+
deel: function(elem, fn) {
|
763
|
+
return function(e) {
|
764
|
+
e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120
|
765
|
+
e.wheelDeltaX = 0
|
766
|
+
if (Object.defineProperty) {
|
767
|
+
Object.defineProperty(e, "type", {
|
768
|
+
value: "mousewheel"
|
769
|
+
})
|
770
|
+
}
|
771
|
+
fn.call(elem, e)
|
772
|
+
}
|
773
|
+
}
|
774
|
+
}
|
775
|
+
}
|
776
|
+
|
777
|
+
|
778
|
+
/*********************************************************************
|
779
|
+
* 配置系统 *
|
780
|
+
**********************************************************************/
|
781
|
+
|
782
|
+
function kernel(settings) {
|
783
|
+
for (var p in settings) {
|
784
|
+
if (!ohasOwn.call(settings, p))
|
785
|
+
continue
|
786
|
+
var val = settings[p]
|
787
|
+
if (typeof kernel.plugins[p] === "function") {
|
788
|
+
kernel.plugins[p](val)
|
789
|
+
} else if (typeof kernel[p] === "object") {
|
790
|
+
avalon.mix(kernel[p], val)
|
791
|
+
} else {
|
792
|
+
kernel[p] = val
|
793
|
+
}
|
794
|
+
}
|
795
|
+
return this
|
796
|
+
}
|
797
|
+
var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g
|
798
|
+
|
799
|
+
function escapeRegExp(target) {
|
800
|
+
//http://stevenlevithan.com/regex/xregexp/
|
801
|
+
//将字符串安全格式化为正则表达式的源码
|
802
|
+
return (target + "").replace(rregexp, "\\$&")
|
803
|
+
}
|
804
|
+
var innerRequire = noop
|
805
|
+
var plugins = {
|
806
|
+
loader: function(builtin) {
|
807
|
+
window.define = builtin ? innerRequire.define : otherDefine
|
808
|
+
window.require = builtin ? innerRequire : otherRequire
|
809
|
+
},
|
810
|
+
interpolate: function(array) {
|
811
|
+
openTag = array[0]
|
812
|
+
closeTag = array[1]
|
813
|
+
if (openTag === closeTag) {
|
814
|
+
throw new SyntaxError("openTag!==closeTag")
|
815
|
+
} else if (array + "" === "<!--,-->") {
|
816
|
+
kernel.commentInterpolate = true
|
817
|
+
} else {
|
818
|
+
var test = openTag + "test" + closeTag
|
819
|
+
cinerator.innerHTML = test
|
820
|
+
if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) {
|
821
|
+
throw new SyntaxError("此定界符不合法")
|
822
|
+
}
|
823
|
+
cinerator.innerHTML = ""
|
824
|
+
}
|
825
|
+
var o = escapeRegExp(openTag),
|
826
|
+
c = escapeRegExp(closeTag)
|
827
|
+
rexpr = new RegExp(o + "(.*?)" + c)
|
828
|
+
rexprg = new RegExp(o + "(.*?)" + c, "g")
|
829
|
+
rbind = new RegExp(o + ".*?" + c + "|\\sms-")
|
830
|
+
}
|
831
|
+
}
|
832
|
+
|
833
|
+
kernel.debug = true
|
834
|
+
kernel.plugins = plugins
|
835
|
+
kernel.plugins['interpolate'](["{{", "}}"])
|
836
|
+
kernel.paths = {}
|
837
|
+
kernel.shim = {}
|
838
|
+
kernel.maxRepeatSize = 100
|
839
|
+
avalon.config = kernel
|
840
|
+
/*********************************************************************
|
841
|
+
* 事件总线 *
|
842
|
+
**********************************************************************/
|
843
|
+
var EventBus = {
|
844
|
+
$watch: function(type, callback) {
|
845
|
+
if (typeof callback === "function") {
|
846
|
+
var callbacks = this.$events[type]
|
847
|
+
if (callbacks) {
|
848
|
+
callbacks.push(callback)
|
849
|
+
} else {
|
850
|
+
this.$events[type] = [callback]
|
851
|
+
}
|
852
|
+
} else { //重新开始监听此VM的第一重简单属性的变动
|
853
|
+
this.$events = this.$watch.backup
|
854
|
+
}
|
855
|
+
return this
|
856
|
+
},
|
857
|
+
$unwatch: function(type, callback) {
|
858
|
+
var n = arguments.length
|
859
|
+
if (n === 0) { //让此VM的所有$watch回调无效化
|
860
|
+
this.$watch.backup = this.$events
|
861
|
+
this.$events = {}
|
862
|
+
} else if (n === 1) {
|
863
|
+
this.$events[type] = []
|
864
|
+
} else {
|
865
|
+
var callbacks = this.$events[type] || []
|
866
|
+
var i = callbacks.length
|
867
|
+
while (~--i < 0) {
|
868
|
+
if (callbacks[i] === callback) {
|
869
|
+
return callbacks.splice(i, 1)
|
870
|
+
}
|
871
|
+
}
|
872
|
+
}
|
873
|
+
return this
|
874
|
+
},
|
875
|
+
$fire: function(type) {
|
876
|
+
var special
|
877
|
+
if (/^(\w+)!(\S+)$/.test(type)) {
|
878
|
+
special = RegExp.$1
|
879
|
+
type = RegExp.$2
|
880
|
+
}
|
881
|
+
var events = this.$events
|
882
|
+
var args = aslice.call(arguments, 1)
|
883
|
+
var detail = [type].concat(args)
|
884
|
+
if (special === "all") {
|
885
|
+
for (var i in avalon.vmodels) {
|
886
|
+
var v = avalon.vmodels[i]
|
887
|
+
if (v !== this) {
|
888
|
+
v.$fire.apply(v, detail)
|
889
|
+
}
|
890
|
+
}
|
891
|
+
} else if (special === "up" || special === "down") {
|
892
|
+
var elements = events.expr ? findNodes(events.expr) : []
|
893
|
+
if (elements.length === 0)
|
894
|
+
return
|
895
|
+
for (var i in avalon.vmodels) {
|
896
|
+
var v = avalon.vmodels[i]
|
897
|
+
if (v !== this) {
|
898
|
+
if (v.$events.expr) {
|
899
|
+
var eventNodes = findNodes(v.$events.expr)
|
900
|
+
if (eventNodes.length === 0) {
|
901
|
+
continue
|
902
|
+
}
|
903
|
+
//循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识
|
904
|
+
Array.prototype.forEach.call(eventNodes, function(node) {
|
905
|
+
Array.prototype.forEach.call(elements, function(element) {
|
906
|
+
var ok = special === "down" ? element.contains(node) : //向下捕获
|
907
|
+
node.contains(element) //向上冒泡
|
908
|
+
|
909
|
+
if (ok) {
|
910
|
+
node._avalon = v //符合条件的加一个标识
|
911
|
+
}
|
912
|
+
});
|
913
|
+
})
|
914
|
+
}
|
915
|
+
}
|
916
|
+
}
|
917
|
+
var nodes = DOC.getElementsByTagName("*") //实现节点排序
|
918
|
+
var alls = []
|
919
|
+
Array.prototype.forEach.call(nodes, function(el) {
|
920
|
+
if (el._avalon) {
|
921
|
+
alls.push(el._avalon)
|
922
|
+
el._avalon = ""
|
923
|
+
el.removeAttribute("_avalon")
|
924
|
+
}
|
925
|
+
})
|
926
|
+
if (special === "up") {
|
927
|
+
alls.reverse()
|
928
|
+
}
|
929
|
+
for (var i = 0, el; el = alls[i++]; ) {
|
930
|
+
if (el.$fire.apply(el, detail) === false) {
|
931
|
+
break
|
932
|
+
}
|
933
|
+
}
|
934
|
+
} else {
|
935
|
+
var callbacks = events[type] || []
|
936
|
+
var all = events.$all || []
|
937
|
+
for (var i = 0, callback; callback = callbacks[i++]; ) {
|
938
|
+
if (isFunction(callback))
|
939
|
+
callback.apply(this, args)
|
940
|
+
}
|
941
|
+
for (var i = 0, callback; callback = all[i++]; ) {
|
942
|
+
if (isFunction(callback))
|
943
|
+
callback.apply(this, arguments)
|
944
|
+
}
|
945
|
+
}
|
946
|
+
}
|
947
|
+
}
|
948
|
+
|
949
|
+
var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
|
950
|
+
var findNodes = DOC.querySelectorAll ? function(str) {
|
951
|
+
return DOC.querySelectorAll(str)
|
952
|
+
} : function(str) {
|
953
|
+
var match = str.match(ravalon)
|
954
|
+
var all = DOC.getElementsByTagName(match[1])
|
955
|
+
var nodes = []
|
956
|
+
for (var i = 0, el; el = all[i++]; ) {
|
957
|
+
if (el.getAttribute(match[2]) === match[3]) {
|
958
|
+
nodes.push(el)
|
959
|
+
}
|
960
|
+
}
|
961
|
+
return nodes
|
962
|
+
}
|
963
|
+
/*********************************************************************
|
964
|
+
* modelFactory *
|
965
|
+
**********************************************************************/
|
966
|
+
//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM)
|
967
|
+
var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里
|
968
|
+
avalon.define = function(id, factory) {
|
969
|
+
var $id = id.$id || id
|
970
|
+
if (!$id) {
|
971
|
+
log("warning: vm必须指定$id")
|
972
|
+
}
|
973
|
+
if (VMODELS[$id]) {
|
974
|
+
log("warning: " + $id + " 已经存在于avalon.vmodels中")
|
975
|
+
}
|
976
|
+
if (typeof id === "object") {
|
977
|
+
var model = modelFactory(id)
|
978
|
+
} else {
|
979
|
+
var scope = {
|
980
|
+
$watch: noop
|
981
|
+
}
|
982
|
+
factory(scope) //得到所有定义
|
983
|
+
model = modelFactory(scope) //偷天换日,将scope换为model
|
984
|
+
stopRepeatAssign = true
|
985
|
+
factory(model)
|
986
|
+
stopRepeatAssign = false
|
987
|
+
}
|
988
|
+
model.$id = $id
|
989
|
+
return VMODELS[$id] = model
|
990
|
+
}
|
991
|
+
|
992
|
+
//一些不需要被监听的属性
|
993
|
+
var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray").match(rword)
|
994
|
+
|
995
|
+
function isObservable(name, value, $skipArray) {
|
996
|
+
if (isFunction(value) || value && value.nodeType) {
|
997
|
+
return false
|
998
|
+
}
|
999
|
+
if ($skipArray.indexOf(name) !== -1) {
|
1000
|
+
return false
|
1001
|
+
}
|
1002
|
+
if ($$skipArray.indexOf(name) !== -1) {
|
1003
|
+
return false
|
1004
|
+
}
|
1005
|
+
var $special = $skipArray.$special
|
1006
|
+
if (name && name.charAt(0) === "$" && !$special[name]) {
|
1007
|
+
return false
|
1008
|
+
}
|
1009
|
+
return true
|
1010
|
+
}
|
1011
|
+
//ms-with,ms-each, ms-repeat绑定生成的代理对象储存池
|
1012
|
+
var midway = {}
|
1013
|
+
function getNewValue(accessor, name, value, $vmodel) {
|
1014
|
+
switch (accessor.type) {
|
1015
|
+
case 0://计算属性
|
1016
|
+
var getter = accessor.get
|
1017
|
+
var setter = accessor.set
|
1018
|
+
if (isFunction(setter)) {
|
1019
|
+
var $events = $vmodel.$events
|
1020
|
+
var lock = $events[name]
|
1021
|
+
$events[name] = [] //清空回调,防止内部冒泡而触发多次$fire
|
1022
|
+
setter.call($vmodel, value)
|
1023
|
+
$events[name] = lock
|
1024
|
+
}
|
1025
|
+
return getter.call($vmodel) //同步$model
|
1026
|
+
case 1://监控属性
|
1027
|
+
return value
|
1028
|
+
case 2://对象属性(包括数组与哈希)
|
1029
|
+
if (value !== $vmodel.$model[name]) {
|
1030
|
+
var svmodel = accessor.svmodel = objectFactory($vmodel, name, value, accessor.valueType)
|
1031
|
+
value = svmodel.$model //同步$model
|
1032
|
+
var fn = midway[svmodel.$id]
|
1033
|
+
fn && fn() //同步视图
|
1034
|
+
}
|
1035
|
+
return value
|
1036
|
+
}
|
1037
|
+
}
|
1038
|
+
|
1039
|
+
var defineProperty = Object.defineProperty
|
1040
|
+
var canHideOwn = true
|
1041
|
+
//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8
|
1042
|
+
//标准浏览器使用__defineGetter__, __defineSetter__实现
|
1043
|
+
try {
|
1044
|
+
defineProperty({}, "_", {
|
1045
|
+
value: "x"
|
1046
|
+
})
|
1047
|
+
var defineProperties = Object.defineProperties
|
1048
|
+
} catch (e) {
|
1049
|
+
canHideOwn = false
|
1050
|
+
}
|
1051
|
+
function modelFactory(source, $special, $model) {
|
1052
|
+
if (Array.isArray(source)) {
|
1053
|
+
var arr = source.concat()
|
1054
|
+
source.length = 0
|
1055
|
+
var collection = Collection(source)
|
1056
|
+
collection.pushArray(arr)
|
1057
|
+
return collection
|
1058
|
+
}
|
1059
|
+
if (typeof source.nodeType === "number") {
|
1060
|
+
return source
|
1061
|
+
}
|
1062
|
+
if (source.$id && source.$events) { //fix IE6-8 createWithProxy $val: val引发的BUG
|
1063
|
+
return source
|
1064
|
+
}
|
1065
|
+
if (!Array.isArray(source.$skipArray)) {
|
1066
|
+
source.$skipArray = []
|
1067
|
+
}
|
1068
|
+
source.$skipArray.$special = $special || {} //强制要监听的属性
|
1069
|
+
var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤
|
1070
|
+
$model = $model || {} //vmodels.$model属性
|
1071
|
+
var $events = {} //vmodel.$events属性
|
1072
|
+
var watchedProperties = {} //监控属性
|
1073
|
+
var initCallbacks = [] //初始化才执行的函数
|
1074
|
+
for (var i in source) {
|
1075
|
+
(function(name, val) {
|
1076
|
+
$model[name] = val
|
1077
|
+
if (!isObservable(name, val, source.$skipArray)) {
|
1078
|
+
return //过滤所有非监控属性
|
1079
|
+
}
|
1080
|
+
//总共产生三种accessor
|
1081
|
+
$events[name] = []
|
1082
|
+
var valueType = avalon.type(val)
|
1083
|
+
var accessor = function(newValue) {
|
1084
|
+
var name = accessor._name
|
1085
|
+
var $vmodel = this
|
1086
|
+
var $model = $vmodel.$model
|
1087
|
+
var oldValue = $model[name]
|
1088
|
+
var $events = $vmodel.$events
|
1089
|
+
|
1090
|
+
if (arguments.length) {
|
1091
|
+
if (stopRepeatAssign) {
|
1092
|
+
return
|
1093
|
+
}
|
1094
|
+
//计算属性与对象属性需要重新计算newValue
|
1095
|
+
if (accessor.type !== 1) {
|
1096
|
+
newValue = getNewValue(accessor, name, newValue, $vmodel)
|
1097
|
+
if (!accessor.type)
|
1098
|
+
return
|
1099
|
+
}
|
1100
|
+
if (!isEqual(oldValue, newValue)) {
|
1101
|
+
$model[name] = newValue
|
1102
|
+
if ($events.$digest) {
|
1103
|
+
if (!accessor.pedding) {
|
1104
|
+
accessor.pedding = true
|
1105
|
+
setTimeout(function() {
|
1106
|
+
notifySubscribers($events[name]) //同步视图
|
1107
|
+
safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
|
1108
|
+
accessor.pedding = false
|
1109
|
+
})
|
1110
|
+
}
|
1111
|
+
} else {
|
1112
|
+
notifySubscribers($events[name]) //同步视图
|
1113
|
+
safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
|
1114
|
+
}
|
1115
|
+
}
|
1116
|
+
} else {
|
1117
|
+
if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性
|
1118
|
+
//计算属性不需要收集视图刷新函数,都是由其他监控属性代劳
|
1119
|
+
var newValue = accessor.get.call($vmodel)
|
1120
|
+
if (oldValue !== newValue) {
|
1121
|
+
$model[name] = newValue
|
1122
|
+
//这里不用同步视图
|
1123
|
+
if ($events.$digest) {
|
1124
|
+
if (!accessor.pedding) {
|
1125
|
+
accessor.pedding = true
|
1126
|
+
setTimeout(function() {
|
1127
|
+
safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
|
1128
|
+
accessor.pedding = false
|
1129
|
+
})
|
1130
|
+
}
|
1131
|
+
} else {
|
1132
|
+
safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
|
1133
|
+
}
|
1134
|
+
}
|
1135
|
+
return newValue
|
1136
|
+
} else {
|
1137
|
+
collectSubscribers($events[name]) //收集视图函数
|
1138
|
+
return accessor.svmodel || oldValue
|
1139
|
+
}
|
1140
|
+
}
|
1141
|
+
}
|
1142
|
+
//总共产生三种accessor
|
1143
|
+
if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) {
|
1144
|
+
//第1种为计算属性, 因变量,通过其他监控属性触发其改变
|
1145
|
+
accessor.set = val.set
|
1146
|
+
accessor.get = val.get
|
1147
|
+
accessor.type = 0
|
1148
|
+
initCallbacks.push(function() {
|
1149
|
+
var data = {
|
1150
|
+
evaluator: function() {
|
1151
|
+
data.type = new Date - 0
|
1152
|
+
data.element = null
|
1153
|
+
$model[name] = accessor.get.call($vmodel)
|
1154
|
+
},
|
1155
|
+
element: head,
|
1156
|
+
type: new Date - 0,
|
1157
|
+
handler: noop,
|
1158
|
+
args: []
|
1159
|
+
}
|
1160
|
+
Registry[expose] = data
|
1161
|
+
accessor.call($vmodel)
|
1162
|
+
delete Registry[expose]
|
1163
|
+
})
|
1164
|
+
} else if (rcomplexType.test(valueType)) {
|
1165
|
+
//第2种为对象属性,产生子VM与监控数组
|
1166
|
+
accessor.type = 2
|
1167
|
+
accessor.valueType = valueType
|
1168
|
+
initCallbacks.push(function() {
|
1169
|
+
var svmodel = modelFactory(val, 0, $model[name])
|
1170
|
+
accessor.svmodel = svmodel
|
1171
|
+
svmodel.$events[subscribers] = $events[name]
|
1172
|
+
})
|
1173
|
+
} else {
|
1174
|
+
accessor.type = 1
|
1175
|
+
//第3种为监控属性,对应简单的数据类型,自变量
|
1176
|
+
}
|
1177
|
+
accessor._name = name
|
1178
|
+
watchedProperties[name] = accessor
|
1179
|
+
})(i, source[i])
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
$$skipArray.forEach(function(name) {
|
1183
|
+
delete source[name]
|
1184
|
+
delete $model[name] //这些特殊属性不应该在$model中出现
|
1185
|
+
})
|
1186
|
+
|
1187
|
+
$vmodel = defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel
|
1188
|
+
for (var name in source) {
|
1189
|
+
if (!watchedProperties[name]) {
|
1190
|
+
$vmodel[name] = source[name]
|
1191
|
+
}
|
1192
|
+
}
|
1193
|
+
//添加$id, $model, $events, $watch, $unwatch, $fire
|
1194
|
+
$vmodel.$id = generateID()
|
1195
|
+
$vmodel.$model = $model
|
1196
|
+
$vmodel.$events = $events
|
1197
|
+
for (var i in EventBus) {
|
1198
|
+
var fn = EventBus[i]
|
1199
|
+
if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下
|
1200
|
+
fn = fn.bind($vmodel)
|
1201
|
+
}
|
1202
|
+
$vmodel[i] = fn
|
1203
|
+
}
|
1204
|
+
|
1205
|
+
if (canHideOwn) {
|
1206
|
+
Object.defineProperty($vmodel, "hasOwnProperty", {
|
1207
|
+
value: function(name) {
|
1208
|
+
return name in this.$model
|
1209
|
+
},
|
1210
|
+
writable: false,
|
1211
|
+
enumerable: false,
|
1212
|
+
configurable: true
|
1213
|
+
})
|
1214
|
+
|
1215
|
+
} else {
|
1216
|
+
$vmodel.hasOwnProperty = function(name) {
|
1217
|
+
return name in $vmodel.$model
|
1218
|
+
}
|
1219
|
+
}
|
1220
|
+
initCallbacks.forEach(function(cb) { //收集依赖
|
1221
|
+
cb()
|
1222
|
+
})
|
1223
|
+
return $vmodel
|
1224
|
+
}
|
1225
|
+
|
1226
|
+
//比较两个值是否相等
|
1227
|
+
var isEqual = Object.is || function(v1, v2) {
|
1228
|
+
if (v1 === 0 && v2 === 0) {
|
1229
|
+
return 1 / v1 === 1 / v2
|
1230
|
+
} else if (v1 !== v1) {
|
1231
|
+
return v2 !== v2
|
1232
|
+
} else {
|
1233
|
+
return v1 === v2
|
1234
|
+
}
|
1235
|
+
}
|
1236
|
+
|
1237
|
+
function safeFire(a, b, c, d) {
|
1238
|
+
if (a.$events) {
|
1239
|
+
EventBus.$fire.call(a, b, c, d)
|
1240
|
+
}
|
1241
|
+
}
|
1242
|
+
|
1243
|
+
var descriptorFactory = W3C ? function(obj) {
|
1244
|
+
var descriptors = {}
|
1245
|
+
for (var i in obj) {
|
1246
|
+
descriptors[i] = {
|
1247
|
+
get: obj[i],
|
1248
|
+
set: obj[i],
|
1249
|
+
enumerable: true,
|
1250
|
+
configurable: true
|
1251
|
+
}
|
1252
|
+
}
|
1253
|
+
return descriptors
|
1254
|
+
} : function(a) {
|
1255
|
+
return a
|
1256
|
+
}
|
1257
|
+
|
1258
|
+
|
1259
|
+
|
1260
|
+
//应用于第2种accessor
|
1261
|
+
function objectFactory(parent, name, value, valueType) {
|
1262
|
+
//a为原来的VM, b为新数组或新对象
|
1263
|
+
var son = parent[name]
|
1264
|
+
if (valueType === "array") {
|
1265
|
+
if (!Array.isArray(value) || son === value) {
|
1266
|
+
return son //fix https://github.com/RubyLouvre/avalon/issues/261
|
1267
|
+
}
|
1268
|
+
son._.$unwatch()
|
1269
|
+
son.clear()
|
1270
|
+
son._.$watch()
|
1271
|
+
son.pushArray(value.concat())
|
1272
|
+
return son
|
1273
|
+
} else {
|
1274
|
+
var iterators = parent.$events[name]
|
1275
|
+
var pool = son.$events.$withProxyPool
|
1276
|
+
if (pool) {
|
1277
|
+
recycleProxies(pool, "with")
|
1278
|
+
son.$events.$withProxyPool = null
|
1279
|
+
}
|
1280
|
+
var ret = modelFactory(value)
|
1281
|
+
ret.$events[subscribers] = iterators
|
1282
|
+
midway[ret.$id] = function(data) {
|
1283
|
+
while (data = iterators.shift()) {
|
1284
|
+
(function(el) {
|
1285
|
+
avalon.nextTick(function() {
|
1286
|
+
if (el.type) { //重新绑定
|
1287
|
+
el.rollback && el.rollback() //还原 ms-with ms-on
|
1288
|
+
bindingHandlers[el.type](el, el.vmodels)
|
1289
|
+
}
|
1290
|
+
})
|
1291
|
+
})(data)
|
1292
|
+
}
|
1293
|
+
delete midway[ret.$id]
|
1294
|
+
}
|
1295
|
+
return ret
|
1296
|
+
}
|
1297
|
+
}
|
1298
|
+
//===================修复浏览器对Object.defineProperties的支持=================
|
1299
|
+
if (!canHideOwn) {
|
1300
|
+
if ("__defineGetter__" in avalon) {
|
1301
|
+
defineProperty = function(obj, prop, desc) {
|
1302
|
+
if ('value' in desc) {
|
1303
|
+
obj[prop] = desc.value
|
1304
|
+
}
|
1305
|
+
if ("get" in desc) {
|
1306
|
+
obj.__defineGetter__(prop, desc.get)
|
1307
|
+
}
|
1308
|
+
if ('set' in desc) {
|
1309
|
+
obj.__defineSetter__(prop, desc.set)
|
1310
|
+
}
|
1311
|
+
return obj
|
1312
|
+
}
|
1313
|
+
defineProperties = function(obj, descs) {
|
1314
|
+
for (var prop in descs) {
|
1315
|
+
if (descs.hasOwnProperty(prop)) {
|
1316
|
+
defineProperty(obj, prop, descs[prop])
|
1317
|
+
}
|
1318
|
+
}
|
1319
|
+
return obj
|
1320
|
+
}
|
1321
|
+
}
|
1322
|
+
if (IEVersion) {
|
1323
|
+
window.execScript([
|
1324
|
+
"Function parseVB(code)",
|
1325
|
+
"\tExecuteGlobal(code)",
|
1326
|
+
"End Function",
|
1327
|
+
"Dim VBClassBodies",
|
1328
|
+
"Set VBClassBodies=CreateObject(\"Scripting.Dictionary\")",
|
1329
|
+
"Function findOrDefineVBClass(name,body)",
|
1330
|
+
"\tDim found",
|
1331
|
+
"\tfound=\"\"",
|
1332
|
+
"\tFor Each key in VBClassBodies",
|
1333
|
+
"\t\tIf body=VBClassBodies.Item(key) Then",
|
1334
|
+
"\t\t\tfound=key",
|
1335
|
+
"\t\t\tExit For",
|
1336
|
+
"\t\tEnd If",
|
1337
|
+
"\tnext",
|
1338
|
+
"\tIf found=\"\" Then",
|
1339
|
+
"\t\tparseVB(\"Class \" + name + body)",
|
1340
|
+
"\t\tVBClassBodies.Add name, body",
|
1341
|
+
"\t\tfound=name",
|
1342
|
+
"\tEnd If",
|
1343
|
+
"\tfindOrDefineVBClass=found",
|
1344
|
+
"End Function"
|
1345
|
+
].join("\n"), "VBScript")
|
1346
|
+
|
1347
|
+
function VBMediator(instance, accessors, name, value) {
|
1348
|
+
var accessor = accessors[name]
|
1349
|
+
if (arguments.length === 4) {
|
1350
|
+
accessor.call(instance, value)
|
1351
|
+
} else {
|
1352
|
+
return accessor.call(instance)
|
1353
|
+
}
|
1354
|
+
}
|
1355
|
+
defineProperties = function(name, accessors, properties) {
|
1356
|
+
var className = "VBClass" + setTimeout("1"),
|
1357
|
+
buffer = []
|
1358
|
+
buffer.push(
|
1359
|
+
"\r\n\tPrivate [__data__], [__proxy__]",
|
1360
|
+
"\tPublic Default Function [__const__](d, p)",
|
1361
|
+
"\t\tSet [__data__] = d: set [__proxy__] = p",
|
1362
|
+
"\t\tSet [__const__] = Me", //链式调用
|
1363
|
+
"\tEnd Function")
|
1364
|
+
//添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好
|
1365
|
+
for (name in properties) {
|
1366
|
+
if (!accessors.hasOwnProperty(name)) {
|
1367
|
+
buffer.push("\tPublic [" + name + "]")
|
1368
|
+
}
|
1369
|
+
}
|
1370
|
+
$$skipArray.forEach(function(name) {
|
1371
|
+
if (!accessors.hasOwnProperty(name)) {
|
1372
|
+
buffer.push("\tPublic [" + name + "]")
|
1373
|
+
}
|
1374
|
+
})
|
1375
|
+
buffer.push("\tPublic [" + 'hasOwnProperty' + "]")
|
1376
|
+
//添加访问器属性
|
1377
|
+
for (name in accessors) {
|
1378
|
+
buffer.push(
|
1379
|
+
//由于不知对方会传入什么,因此set, let都用上
|
1380
|
+
"\tPublic Property Let [" + name + "](val" + expose + ")", //setter
|
1381
|
+
"\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")",
|
1382
|
+
"\tEnd Property",
|
1383
|
+
"\tPublic Property Set [" + name + "](val" + expose + ")", //setter
|
1384
|
+
"\t\tCall [__proxy__](Me,[__data__], \"" + name + "\", val" + expose + ")",
|
1385
|
+
"\tEnd Property",
|
1386
|
+
"\tPublic Property Get [" + name + "]", //getter
|
1387
|
+
"\tOn Error Resume Next", //必须优先使用set语句,否则它会误将数组当字符串返回
|
1388
|
+
"\t\tSet[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")",
|
1389
|
+
"\tIf Err.Number <> 0 Then",
|
1390
|
+
"\t\t[" + name + "] = [__proxy__](Me,[__data__],\"" + name + "\")",
|
1391
|
+
"\tEnd If",
|
1392
|
+
"\tOn Error Goto 0",
|
1393
|
+
"\tEnd Property")
|
1394
|
+
|
1395
|
+
}
|
1396
|
+
|
1397
|
+
buffer.push("End Class")
|
1398
|
+
var code = buffer.join("\r\n"),
|
1399
|
+
realClassName = window['findOrDefineVBClass'](className, code) //如果该VB类已定义,返回类名。否则用className创建一个新类。
|
1400
|
+
if (realClassName === className) {
|
1401
|
+
window.parseVB([
|
1402
|
+
"Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数
|
1403
|
+
"\tDim o",
|
1404
|
+
"\tSet o = (New " + className + ")(a, b)",
|
1405
|
+
"\tSet " + className + "Factory = o",
|
1406
|
+
"End Function"
|
1407
|
+
].join("\r\n"))
|
1408
|
+
}
|
1409
|
+
// console.log(code)
|
1410
|
+
var ret = window[realClassName + "Factory"](accessors, VBMediator) //得到其产品
|
1411
|
+
return ret //得到其产品
|
1412
|
+
}
|
1413
|
+
}
|
1414
|
+
}
|
1415
|
+
|
1416
|
+
/*********************************************************************
|
1417
|
+
* 监控数组(与ms-each, ms-repeat配合使用) *
|
1418
|
+
**********************************************************************/
|
1419
|
+
|
1420
|
+
function Collection(model) {
|
1421
|
+
var array = []
|
1422
|
+
array.$id = generateID()
|
1423
|
+
array.$model = model //数据模型
|
1424
|
+
array.$events = {}
|
1425
|
+
array.$events[subscribers] = []
|
1426
|
+
array._ = modelFactory({
|
1427
|
+
length: model.length
|
1428
|
+
})
|
1429
|
+
array._.$watch("length", function(a, b) {
|
1430
|
+
array.$fire("length", a, b)
|
1431
|
+
})
|
1432
|
+
for (var i in EventBus) {
|
1433
|
+
array[i] = EventBus[i]
|
1434
|
+
}
|
1435
|
+
avalon.mix(array, CollectionPrototype)
|
1436
|
+
return array
|
1437
|
+
}
|
1438
|
+
|
1439
|
+
function mutateArray(method, pos, n, index, method2, pos2, n2) {
|
1440
|
+
var oldLen = this.length, loop = 2
|
1441
|
+
while (--loop) {
|
1442
|
+
switch (method) {
|
1443
|
+
case "add":
|
1444
|
+
var array = this.$model.slice(pos, pos + n).map(function(el) {
|
1445
|
+
if (rcomplexType.test(avalon.type(el))) {
|
1446
|
+
return el.$id ? el : modelFactory(el, 0, el)
|
1447
|
+
} else {
|
1448
|
+
return el
|
1449
|
+
}
|
1450
|
+
})
|
1451
|
+
_splice.apply(this, [pos, 0].concat(array))
|
1452
|
+
this._fire("add", pos, n)
|
1453
|
+
break
|
1454
|
+
case "del":
|
1455
|
+
var ret = this._splice(pos, n)
|
1456
|
+
this._fire("del", pos, n)
|
1457
|
+
break
|
1458
|
+
}
|
1459
|
+
if (method2) {
|
1460
|
+
method = method2
|
1461
|
+
pos = pos2
|
1462
|
+
n = n2
|
1463
|
+
loop = 2
|
1464
|
+
method2 = 0
|
1465
|
+
}
|
1466
|
+
}
|
1467
|
+
this._fire("index", index)
|
1468
|
+
if (this.length !== oldLen) {
|
1469
|
+
this._.length = this.length
|
1470
|
+
}
|
1471
|
+
return ret
|
1472
|
+
}
|
1473
|
+
|
1474
|
+
var _splice = ap.splice
|
1475
|
+
var CollectionPrototype = {
|
1476
|
+
_splice: _splice,
|
1477
|
+
_fire: function(method, a, b) {
|
1478
|
+
notifySubscribers(this.$events[subscribers], method, a, b)
|
1479
|
+
},
|
1480
|
+
size: function() { //取得数组长度,这个函数可以同步视图,length不能
|
1481
|
+
return this._.length
|
1482
|
+
},
|
1483
|
+
pushArray: function(array) {
|
1484
|
+
var m = array.length, n = this.length
|
1485
|
+
if (m) {
|
1486
|
+
ap.push.apply(this.$model, array)
|
1487
|
+
mutateArray.call(this, "add", n, m, n)
|
1488
|
+
}
|
1489
|
+
return m + n
|
1490
|
+
},
|
1491
|
+
push: function() {
|
1492
|
+
//http://jsperf.com/closure-with-arguments
|
1493
|
+
var array = []
|
1494
|
+
var i, n = arguments.length
|
1495
|
+
for (i = 0; i < n; i++) {
|
1496
|
+
array[i] = arguments[i]
|
1497
|
+
}
|
1498
|
+
return this.pushArray(arguments)
|
1499
|
+
},
|
1500
|
+
unshift: function() {
|
1501
|
+
var m = arguments.length, n = this.length
|
1502
|
+
if (m) {
|
1503
|
+
ap.unshift.apply(this.$model, arguments)
|
1504
|
+
mutateArray.call(this, "add", 0, m, 0)
|
1505
|
+
}
|
1506
|
+
return m + n //IE67的unshift不会返回长度
|
1507
|
+
},
|
1508
|
+
shift: function() {
|
1509
|
+
if (this.length) {
|
1510
|
+
var el = this.$model.shift()
|
1511
|
+
mutateArray.call(this, "del", 0, 1, 0)
|
1512
|
+
return el //返回被移除的元素
|
1513
|
+
}
|
1514
|
+
},
|
1515
|
+
pop: function() {
|
1516
|
+
var m = this.length
|
1517
|
+
if (m) {
|
1518
|
+
var el = this.$model.pop()
|
1519
|
+
mutateArray.call(this, "del", m - 1, 1, Math.max(0, m - 2))
|
1520
|
+
return el //返回被移除的元素
|
1521
|
+
}
|
1522
|
+
},
|
1523
|
+
splice: function(start) {
|
1524
|
+
var m = arguments.length, args = [], change
|
1525
|
+
var removed = _splice.apply(this.$model, arguments)
|
1526
|
+
if (removed.length) { //如果用户删掉了元素
|
1527
|
+
args.push("del", start, removed.length, 0)
|
1528
|
+
change = true
|
1529
|
+
}
|
1530
|
+
if (m > 2) { //如果用户添加了元素
|
1531
|
+
if (change) {
|
1532
|
+
args.splice(3, 1, 0, "add", start, m - 2)
|
1533
|
+
} else {
|
1534
|
+
args.push("add", start, m - 2, 0)
|
1535
|
+
}
|
1536
|
+
change = true
|
1537
|
+
}
|
1538
|
+
if (change) { //返回被移除的元素
|
1539
|
+
return mutateArray.apply(this, args)
|
1540
|
+
} else {
|
1541
|
+
return []
|
1542
|
+
}
|
1543
|
+
},
|
1544
|
+
contains: function(el) { //判定是否包含
|
1545
|
+
return this.indexOf(el) !== -1
|
1546
|
+
},
|
1547
|
+
remove: function(el) { //移除第一个等于给定值的元素
|
1548
|
+
return this.removeAt(this.indexOf(el))
|
1549
|
+
},
|
1550
|
+
removeAt: function(index) { //移除指定索引上的元素
|
1551
|
+
if (index >= 0) {
|
1552
|
+
this.$model.splice(index, 1)
|
1553
|
+
return mutateArray.call(this, "del", index, 1, 0)
|
1554
|
+
}
|
1555
|
+
return []
|
1556
|
+
},
|
1557
|
+
clear: function() {
|
1558
|
+
this.$model.length = this.length = this._.length = 0 //清空数组
|
1559
|
+
this._fire("clear", 0)
|
1560
|
+
return this
|
1561
|
+
},
|
1562
|
+
removeAll: function(all) { //移除N个元素
|
1563
|
+
if (Array.isArray(all)) {
|
1564
|
+
all.forEach(function(el) {
|
1565
|
+
this.remove(el)
|
1566
|
+
}, this)
|
1567
|
+
} else if (typeof all === "function") {
|
1568
|
+
for (var i = this.length - 1; i >= 0; i--) {
|
1569
|
+
var el = this[i]
|
1570
|
+
if (all(el, i)) {
|
1571
|
+
this.removeAt(i)
|
1572
|
+
}
|
1573
|
+
}
|
1574
|
+
} else {
|
1575
|
+
this.clear()
|
1576
|
+
}
|
1577
|
+
},
|
1578
|
+
ensure: function(el) {
|
1579
|
+
if (!this.contains(el)) { //只有不存在才push
|
1580
|
+
this.push(el)
|
1581
|
+
}
|
1582
|
+
return this
|
1583
|
+
},
|
1584
|
+
set: function(index, val) {
|
1585
|
+
if (index >= 0) {
|
1586
|
+
var valueType = avalon.type(val)
|
1587
|
+
if (val && val.$model) {
|
1588
|
+
val = val.$model
|
1589
|
+
}
|
1590
|
+
var target = this[index]
|
1591
|
+
if (valueType === "object") {
|
1592
|
+
for (var i in val) {
|
1593
|
+
if (target.hasOwnProperty(i)) {
|
1594
|
+
target[i] = val[i]
|
1595
|
+
}
|
1596
|
+
}
|
1597
|
+
} else if (valueType === "array") {
|
1598
|
+
target.clear().push.apply(target, val)
|
1599
|
+
} else if (target !== val) {
|
1600
|
+
this[index] = val
|
1601
|
+
this.$model[index] = val
|
1602
|
+
this._fire("set", index, val)
|
1603
|
+
}
|
1604
|
+
}
|
1605
|
+
return this
|
1606
|
+
}
|
1607
|
+
}
|
1608
|
+
|
1609
|
+
function sortByIndex(array, indexes) {
|
1610
|
+
var map = {};
|
1611
|
+
for (var i = 0, n = indexes.length; i < n; i++) {
|
1612
|
+
map[i] = array[i] // preserve
|
1613
|
+
var j = indexes[i]
|
1614
|
+
if (j in map) {
|
1615
|
+
array[i] = map[j]
|
1616
|
+
delete map[j]
|
1617
|
+
} else {
|
1618
|
+
array[i] = array[j]
|
1619
|
+
}
|
1620
|
+
}
|
1621
|
+
}
|
1622
|
+
|
1623
|
+
"sort,reverse".replace(rword, function(method) {
|
1624
|
+
CollectionPrototype[method] = function() {
|
1625
|
+
var newArray = this.$model//这是要排序的新数组
|
1626
|
+
var oldArray = newArray.concat() //保持原来状态的旧数组
|
1627
|
+
var mask = Math.random()
|
1628
|
+
var indexes = []
|
1629
|
+
var hasSort
|
1630
|
+
ap[method].apply(newArray, arguments) //排序
|
1631
|
+
for (var i = 0, n = oldArray.length; i < n; i++) {
|
1632
|
+
var neo = newArray[i]
|
1633
|
+
var old = oldArray[i]
|
1634
|
+
if (isEqual(neo, old)) {
|
1635
|
+
indexes.push(i)
|
1636
|
+
} else {
|
1637
|
+
var index = oldArray.indexOf(neo)
|
1638
|
+
indexes.push(index)//得到新数组的每个元素在旧数组对应的位置
|
1639
|
+
oldArray[index] = mask //屏蔽已经找过的元素
|
1640
|
+
hasSort = true
|
1641
|
+
}
|
1642
|
+
}
|
1643
|
+
if (hasSort) {
|
1644
|
+
sortByIndex(this, indexes)
|
1645
|
+
this._fire("move", indexes)
|
1646
|
+
this._fire("index", 0)
|
1647
|
+
}
|
1648
|
+
return this
|
1649
|
+
}
|
1650
|
+
})
|
1651
|
+
|
1652
|
+
/*********************************************************************
|
1653
|
+
* 依赖调度系统 *
|
1654
|
+
**********************************************************************/
|
1655
|
+
var ronduplex = /^(duplex|on)$/
|
1656
|
+
|
1657
|
+
function registerSubscriber(data) {
|
1658
|
+
Registry[expose] = data //暴光此函数,方便collectSubscribers收集
|
1659
|
+
avalon.openComputedCollect = true
|
1660
|
+
var fn = data.evaluator
|
1661
|
+
if (fn) { //如果是求值函数
|
1662
|
+
try {
|
1663
|
+
var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args)
|
1664
|
+
data.handler(c, data.element, data)
|
1665
|
+
} catch (e) {
|
1666
|
+
//log("warning:exception throwed in [registerSubscriber] " + e)
|
1667
|
+
delete data.evaluator
|
1668
|
+
var node = data.element
|
1669
|
+
if (node.nodeType === 3) {
|
1670
|
+
var parent = node.parentNode
|
1671
|
+
if (kernel.commentInterpolate) {
|
1672
|
+
parent.replaceChild(DOC.createComment(data.value), node)
|
1673
|
+
} else {
|
1674
|
+
node.data = openTag + data.value + closeTag
|
1675
|
+
}
|
1676
|
+
}
|
1677
|
+
}
|
1678
|
+
}
|
1679
|
+
avalon.openComputedCollect = false
|
1680
|
+
delete Registry[expose]
|
1681
|
+
}
|
1682
|
+
|
1683
|
+
function collectSubscribers(list) { //收集依赖于这个访问器的订阅者
|
1684
|
+
var data = Registry[expose]
|
1685
|
+
if (list && data && avalon.Array.ensure(list, data) && data.element) { //只有数组不存在此元素才push进去
|
1686
|
+
addSubscribers(data, list)
|
1687
|
+
}
|
1688
|
+
}
|
1689
|
+
|
1690
|
+
|
1691
|
+
function addSubscribers(data, list) {
|
1692
|
+
data.$uuid = data.$uuid || generateID()
|
1693
|
+
list.$uuid = list.$uuid || generateID()
|
1694
|
+
var obj = {
|
1695
|
+
data: data,
|
1696
|
+
list: list,
|
1697
|
+
$$uuid: data.$uuid + list.$uuid
|
1698
|
+
}
|
1699
|
+
if (!$$subscribers[obj.$$uuid]) {
|
1700
|
+
$$subscribers[obj.$$uuid] = 1
|
1701
|
+
$$subscribers.push(obj)
|
1702
|
+
}
|
1703
|
+
}
|
1704
|
+
|
1705
|
+
function disposeData(data) {
|
1706
|
+
data.element = null
|
1707
|
+
data.rollback && data.rollback()
|
1708
|
+
for (var key in data) {
|
1709
|
+
data[key] = null
|
1710
|
+
}
|
1711
|
+
}
|
1712
|
+
|
1713
|
+
function isRemove(el) {
|
1714
|
+
try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错
|
1715
|
+
if (!el.parentNode) {
|
1716
|
+
return true
|
1717
|
+
}
|
1718
|
+
} catch (e) {
|
1719
|
+
return true
|
1720
|
+
}
|
1721
|
+
return el.msRetain ? 0 : (el.nodeType === 1 ? typeof el.sourceIndex === "number" ?
|
1722
|
+
el.sourceIndex === 0 : !root.contains(el) : !avalon.contains(root, el))
|
1723
|
+
}
|
1724
|
+
var $$subscribers = avalon.$$subscribers = []
|
1725
|
+
var beginTime = new Date()
|
1726
|
+
var oldInfo = {}
|
1727
|
+
function removeSubscribers() {
|
1728
|
+
var i = $$subscribers.length
|
1729
|
+
var n = i
|
1730
|
+
var k = 0
|
1731
|
+
var obj
|
1732
|
+
var types = []
|
1733
|
+
var newInfo = {}
|
1734
|
+
var needTest = {}
|
1735
|
+
while (obj = $$subscribers[--i]) {
|
1736
|
+
var data = obj.data
|
1737
|
+
var type = data.type
|
1738
|
+
if (newInfo[type]) {
|
1739
|
+
newInfo[type]++
|
1740
|
+
} else {
|
1741
|
+
newInfo[type] = 1
|
1742
|
+
types.push(type)
|
1743
|
+
}
|
1744
|
+
}
|
1745
|
+
var diff = false
|
1746
|
+
types.forEach(function(type) {
|
1747
|
+
if (oldInfo[type] !== newInfo[type]) {
|
1748
|
+
needTest[type] = 1
|
1749
|
+
diff = true
|
1750
|
+
}
|
1751
|
+
})
|
1752
|
+
i = n
|
1753
|
+
//avalon.log("需要检测的个数 " + i)
|
1754
|
+
if (diff) {
|
1755
|
+
//avalon.log("有需要移除的元素")
|
1756
|
+
while (obj = $$subscribers[--i]) {
|
1757
|
+
var data = obj.data
|
1758
|
+
if (data.element === void 0)
|
1759
|
+
continue
|
1760
|
+
if (needTest[data.type] && isRemove(data.element)) { //如果它没有在DOM树
|
1761
|
+
k++
|
1762
|
+
$$subscribers.splice(i, 1)
|
1763
|
+
delete $$subscribers[obj.$$uuid]
|
1764
|
+
avalon.Array.remove(obj.list, data)
|
1765
|
+
//log("debug: remove " + data.type)
|
1766
|
+
disposeData(data)
|
1767
|
+
obj.data = obj.list = null
|
1768
|
+
}
|
1769
|
+
}
|
1770
|
+
}
|
1771
|
+
oldInfo = newInfo
|
1772
|
+
// avalon.log("已经移除的个数 " + k)
|
1773
|
+
beginTime = new Date()
|
1774
|
+
}
|
1775
|
+
|
1776
|
+
function notifySubscribers(list) { //通知依赖于这个访问器的订阅者更新自身
|
1777
|
+
if (list && list.length) {
|
1778
|
+
if (new Date() - beginTime > 444 && typeof list[0] === "object") {
|
1779
|
+
removeSubscribers()
|
1780
|
+
}
|
1781
|
+
var args = aslice.call(arguments, 1)
|
1782
|
+
for (var i = list.length, fn; fn = list[--i]; ) {
|
1783
|
+
var el = fn.element
|
1784
|
+
if (el && el.parentNode) {
|
1785
|
+
if (fn.$repeat) {
|
1786
|
+
fn.handler.apply(fn, args) //处理监控数组的方法
|
1787
|
+
} else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发
|
1788
|
+
var fun = fn.evaluator || noop
|
1789
|
+
fn.handler(fun.apply(0, fn.args || []), el, fn)
|
1790
|
+
}
|
1791
|
+
}
|
1792
|
+
}
|
1793
|
+
}
|
1794
|
+
}
|
1795
|
+
|
1796
|
+
/************************************************************************
|
1797
|
+
* HTML处理(parseHTML, innerHTML, clearHTML) *
|
1798
|
+
************************************************************************/
|
1799
|
+
//parseHTML的辅助变量
|
1800
|
+
var tagHooks = {
|
1801
|
+
area: [1, "<map>"],
|
1802
|
+
param: [1, "<object>"],
|
1803
|
+
col: [2, "<table><tbody></tbody><colgroup>", "</table>"],
|
1804
|
+
legend: [1, "<fieldset>"],
|
1805
|
+
option: [1, "<select multiple='multiple'>"],
|
1806
|
+
thead: [1, "<table>", "</table>"],
|
1807
|
+
//如果这里不写</tbody></table>,在IE6-9会在多出一个奇怪的caption标签
|
1808
|
+
tr: [2, "<table><tbody>", "</tbody></table>"],
|
1809
|
+
//如果这里不写</tr></tbody></table>,在IE6-9会在多出一个奇怪的caption标签
|
1810
|
+
th: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
|
1811
|
+
td: [3, "<table><tbody><tr>"],
|
1812
|
+
g: [1, '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">', '</svg>'],
|
1813
|
+
//IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签
|
1814
|
+
_default: W3C ? [0, ""] : [1, "X<div>"] //div可以不用闭合
|
1815
|
+
}
|
1816
|
+
|
1817
|
+
tagHooks.optgroup = tagHooks.option
|
1818
|
+
tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead
|
1819
|
+
String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function(tag) {
|
1820
|
+
tagHooks[tag] = tagHooks.g //处理SVG
|
1821
|
+
})
|
1822
|
+
var rtagName = /<([\w:]+)/ //取得其tagName
|
1823
|
+
var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
|
1824
|
+
var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig
|
1825
|
+
var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"])
|
1826
|
+
var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签
|
1827
|
+
var script = DOC.createElement("script")
|
1828
|
+
avalon.parseHTML = function(html) {
|
1829
|
+
if (typeof html !== "string" ) {
|
1830
|
+
return DOC.createDocumentFragment()
|
1831
|
+
}
|
1832
|
+
html = html.replace(rxhtml, "<$1></$2>").trim()
|
1833
|
+
var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(),
|
1834
|
+
//取得其标签名
|
1835
|
+
wrap = tagHooks[tag] || tagHooks._default,
|
1836
|
+
fragment = hyperspace.cloneNode(false),
|
1837
|
+
wrapper = cinerator,
|
1838
|
+
firstChild, neo
|
1839
|
+
if (!W3C) { //fix IE
|
1840
|
+
html = html.replace(rcreate, "<br class=msNoScope>$1") //在link style script等标签之前添加一个补丁
|
1841
|
+
}
|
1842
|
+
wrapper.innerHTML = wrap[1] + html + (wrap[2] || "")
|
1843
|
+
var els = wrapper.getElementsByTagName("script")
|
1844
|
+
if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性
|
1845
|
+
for (var i = 0, el; el = els[i++]; ) {
|
1846
|
+
if (scriptTypes[el.type]) {
|
1847
|
+
//以偷龙转凤方式恢复执行脚本功能
|
1848
|
+
neo = script.cloneNode(false) //FF不能省略参数
|
1849
|
+
ap.forEach.call(el.attributes, function(attr) {
|
1850
|
+
if (attr && attr.specified) {
|
1851
|
+
neo[attr.name] = attr.value //复制其属性
|
1852
|
+
neo.setAttribute(attr.name, attr.value)
|
1853
|
+
}
|
1854
|
+
})
|
1855
|
+
neo.text = el.text
|
1856
|
+
el.parentNode.replaceChild(neo, el) //替换节点
|
1857
|
+
}
|
1858
|
+
}
|
1859
|
+
}
|
1860
|
+
//移除我们为了符合套嵌关系而添加的标签
|
1861
|
+
for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {
|
1862
|
+
}
|
1863
|
+
if (!W3C) { //fix IE
|
1864
|
+
var els = wrapper.getElementsByTagName("br"), n = els.length
|
1865
|
+
while (el = els[--n]) {
|
1866
|
+
if (el.className === "msNoScope") {
|
1867
|
+
el.parentNode.removeChild(el)
|
1868
|
+
}
|
1869
|
+
}
|
1870
|
+
for (els = wrapper.all, i = 0; el = els[i++]; ) { //fix VML
|
1871
|
+
if (isVML(el)) {
|
1872
|
+
fixVML(el)
|
1873
|
+
}
|
1874
|
+
}
|
1875
|
+
}
|
1876
|
+
|
1877
|
+
while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上!
|
1878
|
+
fragment.appendChild(firstChild)
|
1879
|
+
}
|
1880
|
+
return fragment
|
1881
|
+
}
|
1882
|
+
|
1883
|
+
function isVML(src) {
|
1884
|
+
var nodeName = src.nodeName
|
1885
|
+
return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === ""
|
1886
|
+
}
|
1887
|
+
|
1888
|
+
function fixVML(node) {
|
1889
|
+
if (node.currentStyle.behavior !== "url(#default#VML)") {
|
1890
|
+
node.style.behavior = "url(#default#VML)"
|
1891
|
+
node.style.display = "inline-block"
|
1892
|
+
node.style.zoom = 1 //hasLayout
|
1893
|
+
}
|
1894
|
+
}
|
1895
|
+
avalon.innerHTML = function(node, html) {
|
1896
|
+
if (!W3C && (!rcreate.test(html) && !rnest.test(html))) {
|
1897
|
+
try {
|
1898
|
+
node.innerHTML = html
|
1899
|
+
return
|
1900
|
+
} catch (e) {
|
1901
|
+
}
|
1902
|
+
}
|
1903
|
+
var a = this.parseHTML(html)
|
1904
|
+
this.clearHTML(node).appendChild(a)
|
1905
|
+
}
|
1906
|
+
avalon.clearHTML = function(node) {
|
1907
|
+
node.textContent = ""
|
1908
|
+
while (node.firstChild) {
|
1909
|
+
node.removeChild(node.firstChild)
|
1910
|
+
}
|
1911
|
+
return node
|
1912
|
+
}
|
1913
|
+
|
1914
|
+
/*********************************************************************
|
1915
|
+
* 扫描系统 *
|
1916
|
+
**********************************************************************/
|
1917
|
+
|
1918
|
+
avalon.scan = function(elem, vmodel, group) {
|
1919
|
+
elem = elem || root
|
1920
|
+
var vmodels = vmodel ? [].concat(vmodel) : []
|
1921
|
+
scanTag(elem, vmodels)
|
1922
|
+
}
|
1923
|
+
|
1924
|
+
//http://www.w3.org/TR/html5/syntax.html#void-elements
|
1925
|
+
var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase())
|
1926
|
+
|
1927
|
+
function checkScan(elem, callback, innerHTML) {
|
1928
|
+
var id = setTimeout(function() {
|
1929
|
+
var currHTML = elem.innerHTML
|
1930
|
+
clearTimeout(id)
|
1931
|
+
if (currHTML === innerHTML) {
|
1932
|
+
callback()
|
1933
|
+
} else {
|
1934
|
+
checkScan(elem, callback, currHTML)
|
1935
|
+
}
|
1936
|
+
})
|
1937
|
+
}
|
1938
|
+
|
1939
|
+
|
1940
|
+
function createSignalTower(elem, vmodel) {
|
1941
|
+
var id = elem.getAttribute("avalonctrl") || vmodel.$id
|
1942
|
+
elem.setAttribute("avalonctrl", id)
|
1943
|
+
vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]'
|
1944
|
+
}
|
1945
|
+
|
1946
|
+
var getBindingCallback = function(elem, name, vmodels) {
|
1947
|
+
var callback = elem.getAttribute(name)
|
1948
|
+
if (callback) {
|
1949
|
+
for (var i = 0, vm; vm = vmodels[i++]; ) {
|
1950
|
+
if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
|
1951
|
+
return vm[callback]
|
1952
|
+
}
|
1953
|
+
}
|
1954
|
+
}
|
1955
|
+
}
|
1956
|
+
|
1957
|
+
function executeBindings(bindings, vmodels) {
|
1958
|
+
for (var i = 0, data; data = bindings[i++]; ) {
|
1959
|
+
data.vmodels = vmodels
|
1960
|
+
bindingHandlers[data.type](data, vmodels)
|
1961
|
+
if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
|
1962
|
+
//chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
|
1963
|
+
data.element.removeAttribute(data.name)
|
1964
|
+
}
|
1965
|
+
}
|
1966
|
+
bindings.length = 0
|
1967
|
+
}
|
1968
|
+
|
1969
|
+
//https://github.com/RubyLouvre/avalon/issues/636
|
1970
|
+
var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) {
|
1971
|
+
var node = elem.firstChild, text
|
1972
|
+
while (node) {
|
1973
|
+
var aaa = node.nextSibling
|
1974
|
+
if (node.nodeType === 3) {
|
1975
|
+
if (text) {
|
1976
|
+
text.nodeValue += node.nodeValue
|
1977
|
+
elem.removeChild(node)
|
1978
|
+
} else {
|
1979
|
+
text = node
|
1980
|
+
}
|
1981
|
+
} else {
|
1982
|
+
text = null
|
1983
|
+
}
|
1984
|
+
node = aaa
|
1985
|
+
}
|
1986
|
+
} : 0
|
1987
|
+
|
1988
|
+
var rmsAttr = /ms-(\w+)-?(.*)/
|
1989
|
+
var priorityMap = {
|
1990
|
+
"if": 10,
|
1991
|
+
"repeat": 90,
|
1992
|
+
"data": 100,
|
1993
|
+
"widget": 110,
|
1994
|
+
"each": 1400,
|
1995
|
+
"with": 1500,
|
1996
|
+
"duplex": 2000,
|
1997
|
+
"on": 3000
|
1998
|
+
}
|
1999
|
+
|
2000
|
+
var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
|
2001
|
+
var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled")
|
2002
|
+
function bindingSorter(a, b) {
|
2003
|
+
return a.priority - b.priority
|
2004
|
+
}
|
2005
|
+
|
2006
|
+
function scanTag(elem, vmodels, node) {
|
2007
|
+
//扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
|
2008
|
+
//--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
|
2009
|
+
var a = elem.getAttribute("ms-skip")
|
2010
|
+
//#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形
|
2011
|
+
if (!elem.getAttributeNode) {
|
2012
|
+
return log("warning " + elem.tagName + " no getAttributeNode method")
|
2013
|
+
}
|
2014
|
+
var b = elem.getAttributeNode("ms-important")
|
2015
|
+
var c = elem.getAttributeNode("ms-controller")
|
2016
|
+
if (typeof a === "string") {
|
2017
|
+
return
|
2018
|
+
} else if (node = b || c) {
|
2019
|
+
var newVmodel = avalon.vmodels[node.value]
|
2020
|
+
if (!newVmodel) {
|
2021
|
+
return
|
2022
|
+
}
|
2023
|
+
//ms-important不包含父VM,ms-controller相反
|
2024
|
+
vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
|
2025
|
+
var name = node.name
|
2026
|
+
elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则
|
2027
|
+
avalon(elem).removeClass(name)
|
2028
|
+
createSignalTower(elem, newVmodel)
|
2029
|
+
}
|
2030
|
+
scanAttr(elem, vmodels) //扫描特性节点
|
2031
|
+
}
|
2032
|
+
function scanNodeList(parent, vmodels) {
|
2033
|
+
var node = parent.firstChild
|
2034
|
+
while (node) {
|
2035
|
+
var nextNode = node.nextSibling
|
2036
|
+
scanNode(node, node.nodeType, vmodels)
|
2037
|
+
node = nextNode
|
2038
|
+
}
|
2039
|
+
}
|
2040
|
+
|
2041
|
+
function scanNodeArray(nodes, vmodels) {
|
2042
|
+
for (var i = 0, node; node = nodes[i++]; ) {
|
2043
|
+
scanNode(node, node.nodeType, vmodels)
|
2044
|
+
}
|
2045
|
+
}
|
2046
|
+
function scanNode(node, nodeType, vmodels) {
|
2047
|
+
if (nodeType === 1) {
|
2048
|
+
scanTag(node, vmodels) //扫描元素节点
|
2049
|
+
} else if (nodeType === 3 && rexpr.test(node.data)){
|
2050
|
+
scanText(node, vmodels) //扫描文本节点
|
2051
|
+
} else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
|
2052
|
+
scanText(node, vmodels) //扫描注释节点
|
2053
|
+
}
|
2054
|
+
}
|
2055
|
+
function scanAttr(elem, vmodels) {
|
2056
|
+
//防止setAttribute, removeAttribute时 attributes自动被同步,导致for循环出错
|
2057
|
+
var attributes = getAttributes ? getAttributes(elem) : avalon.slice(elem.attributes)
|
2058
|
+
var bindings = [],
|
2059
|
+
msData = {},
|
2060
|
+
match
|
2061
|
+
for (var i = 0, attr; attr = attributes[i++]; ) {
|
2062
|
+
if (attr.specified) {
|
2063
|
+
if (match = attr.name.match(rmsAttr)) {
|
2064
|
+
//如果是以指定前缀命名的
|
2065
|
+
var type = match[1]
|
2066
|
+
var param = match[2] || ""
|
2067
|
+
var value = attr.value
|
2068
|
+
var name = attr.name
|
2069
|
+
msData[name] = value
|
2070
|
+
if (events[type]) {
|
2071
|
+
param = type
|
2072
|
+
type = "on"
|
2073
|
+
} else if (obsoleteAttrs[type]) {
|
2074
|
+
log("ms-" + type + "已经被废弃,请使用ms-attr-*代替")
|
2075
|
+
if (type === "enabled") { //吃掉ms-enabled绑定,用ms-disabled代替
|
2076
|
+
type = "disabled"
|
2077
|
+
value = "!(" + value + ")"
|
2078
|
+
}
|
2079
|
+
param = type
|
2080
|
+
type = "attr"
|
2081
|
+
elem.removeAttribute(name)
|
2082
|
+
name = "ms-attr-" + param
|
2083
|
+
elem.setAttribute(name, value)
|
2084
|
+
match = [name]
|
2085
|
+
msData[name] = value
|
2086
|
+
}
|
2087
|
+
if (typeof bindingHandlers[type] === "function") {
|
2088
|
+
var binding = {
|
2089
|
+
type: type,
|
2090
|
+
param: param,
|
2091
|
+
element: elem,
|
2092
|
+
name: match[0],
|
2093
|
+
value: value,
|
2094
|
+
priority: type in priorityMap ? priorityMap[type] : type.charCodeAt(0) * 10 + (Number(param) || 0)
|
2095
|
+
}
|
2096
|
+
if (type === "html" || type === "text") {
|
2097
|
+
var token = getToken(value)
|
2098
|
+
avalon.mix(binding, token)
|
2099
|
+
binding.filters = binding.filters.replace(rhasHtml, function() {
|
2100
|
+
binding.type = "html"
|
2101
|
+
binding.group = 1
|
2102
|
+
return ""
|
2103
|
+
})
|
2104
|
+
}
|
2105
|
+
if (name === "ms-if-loop") {
|
2106
|
+
binding.priority += 100
|
2107
|
+
}
|
2108
|
+
if (vmodels.length) {
|
2109
|
+
bindings.push(binding)
|
2110
|
+
if (type === "widget") {
|
2111
|
+
elem.msData = elem.msData || msData
|
2112
|
+
}
|
2113
|
+
}
|
2114
|
+
}
|
2115
|
+
}
|
2116
|
+
}
|
2117
|
+
}
|
2118
|
+
bindings.sort(bindingSorter)
|
2119
|
+
if (msData["ms-attr-checked"] && msData["ms-duplex"]) {
|
2120
|
+
log("warning!一个元素上不能同时定义ms-attr-checked与ms-duplex")
|
2121
|
+
}
|
2122
|
+
var scanNode = true
|
2123
|
+
for (var i = 0, binding; binding = bindings[i]; i++) {
|
2124
|
+
var type = binding.type
|
2125
|
+
if (rnoscanAttrBinding.test(type)) {
|
2126
|
+
return executeBindings(bindings.slice(0, i + 1), vmodels)
|
2127
|
+
} else if (scanNode) {
|
2128
|
+
scanNode = !rnoscanNodeBinding.test(type)
|
2129
|
+
}
|
2130
|
+
}
|
2131
|
+
executeBindings(bindings, vmodels)
|
2132
|
+
if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) {
|
2133
|
+
mergeTextNodes && mergeTextNodes(elem)
|
2134
|
+
scanNodeList(elem, vmodels) //扫描子孙元素
|
2135
|
+
}
|
2136
|
+
}
|
2137
|
+
|
2138
|
+
var rnoscanAttrBinding = /^if|widget|repeat$/
|
2139
|
+
var rnoscanNodeBinding = /^each|with|html|include$/
|
2140
|
+
//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支,
|
2141
|
+
//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面
|
2142
|
+
if (!"1" [0]) {
|
2143
|
+
var cacheAttrs = createCache(512)
|
2144
|
+
var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g,
|
2145
|
+
rquote = /^['"]/,
|
2146
|
+
rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i,
|
2147
|
+
ramp = /&/g
|
2148
|
+
//IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点
|
2149
|
+
//<body><section>ddd</section></body>
|
2150
|
+
// window.onload = function() {
|
2151
|
+
// var body = document.body
|
2152
|
+
// for (var i = 0, el; el = body.children[i++]; ) {
|
2153
|
+
// avalon.log(el.outerHTML)
|
2154
|
+
// }
|
2155
|
+
// }
|
2156
|
+
//依次输出<SECTION>, </SECTION>
|
2157
|
+
var getAttributes = function(elem) {
|
2158
|
+
var html = elem.outerHTML
|
2159
|
+
//处理IE6-8解析HTML5新标签的情况,及<br>等半闭合标签outerHTML为空的情况
|
2160
|
+
if (html.slice(0, 2) === "</" || !html.trim()) {
|
2161
|
+
return []
|
2162
|
+
}
|
2163
|
+
var str = html.match(rtag)[0]
|
2164
|
+
var attributes = [],
|
2165
|
+
match,
|
2166
|
+
k, v;
|
2167
|
+
if (cacheAttrs[str]) {
|
2168
|
+
return cacheAttrs[str]
|
2169
|
+
}
|
2170
|
+
while (k = rattrs.exec(str)) {
|
2171
|
+
v = k[2]
|
2172
|
+
if (v) {
|
2173
|
+
v = (rquote.test(v) ? v.slice(1, -1) : v).replace(ramp, "&")
|
2174
|
+
}
|
2175
|
+
var name = k[1].toLowerCase()
|
2176
|
+
match = name.match(rmsAttr)
|
2177
|
+
var binding = {
|
2178
|
+
name: name,
|
2179
|
+
specified: true,
|
2180
|
+
value: v || ""
|
2181
|
+
}
|
2182
|
+
attributes.push(binding)
|
2183
|
+
}
|
2184
|
+
return cacheAttrs(str, attributes)
|
2185
|
+
}
|
2186
|
+
}
|
2187
|
+
|
2188
|
+
var rhasHtml = /\|\s*html\s*/,
|
2189
|
+
r11a = /\|\|/g,
|
2190
|
+
rlt = /</g,
|
2191
|
+
rgt = />/g
|
2192
|
+
|
2193
|
+
function getToken(value) {
|
2194
|
+
if (value.indexOf("|") > 0) {
|
2195
|
+
var index = value.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
|
2196
|
+
if (index > -1) {
|
2197
|
+
return {
|
2198
|
+
filters: value.slice(index),
|
2199
|
+
value: value.slice(0, index),
|
2200
|
+
expr: true
|
2201
|
+
}
|
2202
|
+
}
|
2203
|
+
}
|
2204
|
+
return {
|
2205
|
+
value: value,
|
2206
|
+
filters: "",
|
2207
|
+
expr: true
|
2208
|
+
}
|
2209
|
+
}
|
2210
|
+
|
2211
|
+
function scanExpr(str) {
|
2212
|
+
var tokens = [],
|
2213
|
+
value, start = 0,
|
2214
|
+
stop
|
2215
|
+
do {
|
2216
|
+
stop = str.indexOf(openTag, start)
|
2217
|
+
if (stop === -1) {
|
2218
|
+
break
|
2219
|
+
}
|
2220
|
+
value = str.slice(start, stop)
|
2221
|
+
if (value) { // {{ 左边的文本
|
2222
|
+
tokens.push({
|
2223
|
+
value: value,
|
2224
|
+
filters: "",
|
2225
|
+
expr: false
|
2226
|
+
})
|
2227
|
+
}
|
2228
|
+
start = stop + openTag.length
|
2229
|
+
stop = str.indexOf(closeTag, start)
|
2230
|
+
if (stop === -1) {
|
2231
|
+
break
|
2232
|
+
}
|
2233
|
+
value = str.slice(start, stop)
|
2234
|
+
if (value) { //处理{{ }}插值表达式
|
2235
|
+
tokens.push(getToken(value))
|
2236
|
+
}
|
2237
|
+
start = stop + closeTag.length
|
2238
|
+
} while (1)
|
2239
|
+
value = str.slice(start)
|
2240
|
+
if (value) { //}} 右边的文本
|
2241
|
+
tokens.push({
|
2242
|
+
value: value,
|
2243
|
+
expr: false,
|
2244
|
+
filters: ""
|
2245
|
+
})
|
2246
|
+
}
|
2247
|
+
return tokens
|
2248
|
+
}
|
2249
|
+
|
2250
|
+
function scanText(textNode, vmodels) {
|
2251
|
+
var bindings = []
|
2252
|
+
if (textNode.nodeType === 8) {
|
2253
|
+
var token = getToken(textNode.nodeValue)
|
2254
|
+
var tokens = [token]
|
2255
|
+
} else {
|
2256
|
+
tokens = scanExpr(textNode.data)
|
2257
|
+
}
|
2258
|
+
if (tokens.length) {
|
2259
|
+
for (var i = 0, token; token = tokens[i++]; ) {
|
2260
|
+
var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点
|
2261
|
+
if (token.expr) {
|
2262
|
+
token.type = "text"
|
2263
|
+
token.element = node
|
2264
|
+
token.filters = token.filters.replace(rhasHtml, function() {
|
2265
|
+
token.type = "html"
|
2266
|
+
token.group = 1
|
2267
|
+
return ""
|
2268
|
+
})
|
2269
|
+
bindings.push(token) //收集带有插值表达式的文本
|
2270
|
+
}
|
2271
|
+
hyperspace.appendChild(node)
|
2272
|
+
}
|
2273
|
+
textNode.parentNode.replaceChild(hyperspace, textNode)
|
2274
|
+
if (bindings.length)
|
2275
|
+
executeBindings(bindings, vmodels)
|
2276
|
+
}
|
2277
|
+
}
|
2278
|
+
|
2279
|
+
/*********************************************************************
|
2280
|
+
* avalon的原型方法定义区 *
|
2281
|
+
**********************************************************************/
|
2282
|
+
|
2283
|
+
function hyphen(target) {
|
2284
|
+
//转换为连字符线风格
|
2285
|
+
return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase()
|
2286
|
+
}
|
2287
|
+
|
2288
|
+
function camelize(target) {
|
2289
|
+
//转换为驼峰风格
|
2290
|
+
if (target.indexOf("-") < 0 && target.indexOf("_") < 0) {
|
2291
|
+
return target //提前判断,提高getStyle等的效率
|
2292
|
+
}
|
2293
|
+
return target.replace(/[-_][^-_]/g, function(match) {
|
2294
|
+
return match.charAt(1).toUpperCase()
|
2295
|
+
})
|
2296
|
+
}
|
2297
|
+
|
2298
|
+
var ClassListMethods = {
|
2299
|
+
_toString: function() {
|
2300
|
+
var node = this.node
|
2301
|
+
var cls = node.className
|
2302
|
+
var str = typeof cls === "string" ? cls : cls.baseVal
|
2303
|
+
return str.split(/\s+/).join(" ")
|
2304
|
+
},
|
2305
|
+
_contains: function(cls) {
|
2306
|
+
return (" " + this + " ").indexOf(" " + cls + " ") > -1
|
2307
|
+
},
|
2308
|
+
_add: function(cls) {
|
2309
|
+
if (!this.contains(cls)) {
|
2310
|
+
this._set(this + " " + cls)
|
2311
|
+
}
|
2312
|
+
},
|
2313
|
+
_remove: function(cls) {
|
2314
|
+
this._set((" " + this + " ").replace(" " + cls + " ", " "))
|
2315
|
+
},
|
2316
|
+
__set: function(cls) {
|
2317
|
+
cls = cls.trim()
|
2318
|
+
var node = this.node
|
2319
|
+
if (rsvg.test(node)) {
|
2320
|
+
//SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作
|
2321
|
+
node.setAttribute("class", cls)
|
2322
|
+
} else {
|
2323
|
+
node.className = cls
|
2324
|
+
}
|
2325
|
+
} //toggle存在版本差异,因此不使用它
|
2326
|
+
}
|
2327
|
+
|
2328
|
+
function ClassList(node) {
|
2329
|
+
if (!("classList" in node)) {
|
2330
|
+
node.classList = {
|
2331
|
+
node: node
|
2332
|
+
}
|
2333
|
+
for (var k in ClassListMethods) {
|
2334
|
+
node.classList[k.slice(1)] = ClassListMethods[k]
|
2335
|
+
}
|
2336
|
+
}
|
2337
|
+
return node.classList
|
2338
|
+
}
|
2339
|
+
|
2340
|
+
|
2341
|
+
"add,remove".replace(rword, function(method) {
|
2342
|
+
avalon.fn[method + "Class"] = function(cls) {
|
2343
|
+
var el = this[0]
|
2344
|
+
//https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26
|
2345
|
+
if (cls && typeof cls === "string" && el && el.nodeType === 1) {
|
2346
|
+
cls.replace(/\S+/g, function(c) {
|
2347
|
+
ClassList(el)[method](c)
|
2348
|
+
})
|
2349
|
+
}
|
2350
|
+
return this
|
2351
|
+
}
|
2352
|
+
})
|
2353
|
+
avalon.fn.mix({
|
2354
|
+
hasClass: function(cls) {
|
2355
|
+
var el = this[0] || {}
|
2356
|
+
return el.nodeType === 1 && ClassList(el).contains(cls)
|
2357
|
+
},
|
2358
|
+
toggleClass: function(value, stateVal) {
|
2359
|
+
var className, i = 0
|
2360
|
+
var classNames = value.split(/\s+/)
|
2361
|
+
var isBool = typeof stateVal === "boolean"
|
2362
|
+
while ((className = classNames[i++])) {
|
2363
|
+
var state = isBool ? stateVal : !this.hasClass(className)
|
2364
|
+
this[state ? "addClass" : "removeClass"](className)
|
2365
|
+
}
|
2366
|
+
return this
|
2367
|
+
},
|
2368
|
+
attr: function(name, value) {
|
2369
|
+
if (arguments.length === 2) {
|
2370
|
+
this[0].setAttribute(name, value)
|
2371
|
+
return this
|
2372
|
+
} else {
|
2373
|
+
return this[0].getAttribute(name)
|
2374
|
+
}
|
2375
|
+
},
|
2376
|
+
data: function(name, value) {
|
2377
|
+
name = "data-" + hyphen(name || "")
|
2378
|
+
switch (arguments.length) {
|
2379
|
+
case 2:
|
2380
|
+
this.attr(name, value)
|
2381
|
+
return this
|
2382
|
+
case 1:
|
2383
|
+
var val = this.attr(name)
|
2384
|
+
return parseData(val)
|
2385
|
+
case 0:
|
2386
|
+
var ret = {}
|
2387
|
+
ap.forEach.call(this[0].attributes, function(attr) {
|
2388
|
+
if (attr) {
|
2389
|
+
name = attr.name
|
2390
|
+
if (!name.indexOf("data-")) {
|
2391
|
+
name = camelize(name.slice(5))
|
2392
|
+
ret[name] = parseData(attr.value)
|
2393
|
+
}
|
2394
|
+
}
|
2395
|
+
})
|
2396
|
+
return ret
|
2397
|
+
}
|
2398
|
+
},
|
2399
|
+
removeData: function(name) {
|
2400
|
+
name = "data-" + hyphen(name)
|
2401
|
+
this[0].removeAttribute(name)
|
2402
|
+
return this
|
2403
|
+
},
|
2404
|
+
css: function(name, value) {
|
2405
|
+
if (avalon.isPlainObject(name)) {
|
2406
|
+
for (var i in name) {
|
2407
|
+
avalon.css(this, i, name[i])
|
2408
|
+
}
|
2409
|
+
} else {
|
2410
|
+
var ret = avalon.css(this, name, value)
|
2411
|
+
}
|
2412
|
+
return ret !== void 0 ? ret : this
|
2413
|
+
},
|
2414
|
+
position: function() {
|
2415
|
+
var offsetParent, offset,
|
2416
|
+
elem = this[0],
|
2417
|
+
parentOffset = {
|
2418
|
+
top: 0,
|
2419
|
+
left: 0
|
2420
|
+
}
|
2421
|
+
if (!elem) {
|
2422
|
+
return
|
2423
|
+
}
|
2424
|
+
if (this.css("position") === "fixed") {
|
2425
|
+
offset = elem.getBoundingClientRect()
|
2426
|
+
} else {
|
2427
|
+
offsetParent = this.offsetParent() //得到真正的offsetParent
|
2428
|
+
offset = this.offset() // 得到正确的offsetParent
|
2429
|
+
if (offsetParent[0].tagName !== "HTML") {
|
2430
|
+
parentOffset = offsetParent.offset()
|
2431
|
+
}
|
2432
|
+
parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true)
|
2433
|
+
parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true)
|
2434
|
+
}
|
2435
|
+
return {
|
2436
|
+
top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true),
|
2437
|
+
left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true)
|
2438
|
+
}
|
2439
|
+
},
|
2440
|
+
offsetParent: function() {
|
2441
|
+
var offsetParent = this[0].offsetParent
|
2442
|
+
while (offsetParent && avalon.css(offsetParent, "position") === "static") {
|
2443
|
+
offsetParent = offsetParent.offsetParent;
|
2444
|
+
}
|
2445
|
+
return avalon(offsetParent)
|
2446
|
+
},
|
2447
|
+
bind: function(type, fn, phase) {
|
2448
|
+
if (this[0]) { //此方法不会链
|
2449
|
+
return avalon.bind(this[0], type, fn, phase)
|
2450
|
+
}
|
2451
|
+
},
|
2452
|
+
unbind: function(type, fn, phase) {
|
2453
|
+
if (this[0]) {
|
2454
|
+
avalon.unbind(this[0], type, fn, phase)
|
2455
|
+
}
|
2456
|
+
return this
|
2457
|
+
},
|
2458
|
+
val: function(value) {
|
2459
|
+
var node = this[0]
|
2460
|
+
if (node && node.nodeType === 1) {
|
2461
|
+
var get = arguments.length === 0
|
2462
|
+
var access = get ? ":get" : ":set"
|
2463
|
+
var fn = valHooks[getValType(node) + access]
|
2464
|
+
if (fn) {
|
2465
|
+
var val = fn(node, value)
|
2466
|
+
} else if (get) {
|
2467
|
+
return (node.value || "").replace(/\r/g, "")
|
2468
|
+
} else {
|
2469
|
+
node.value = value
|
2470
|
+
}
|
2471
|
+
}
|
2472
|
+
return get ? val : this
|
2473
|
+
}
|
2474
|
+
})
|
2475
|
+
|
2476
|
+
function parseData(data) {
|
2477
|
+
try {
|
2478
|
+
if (typeof data === "object")
|
2479
|
+
return data
|
2480
|
+
data = data === "true" ? true :
|
2481
|
+
data === "false" ? false :
|
2482
|
+
data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data
|
2483
|
+
} catch (e) {
|
2484
|
+
}
|
2485
|
+
return data
|
2486
|
+
}
|
2487
|
+
var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
|
2488
|
+
rvalidchars = /^[\],:{}\s]*$/,
|
2489
|
+
rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
|
2490
|
+
rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
|
2491
|
+
rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g
|
2492
|
+
avalon.parseJSON = window.JSON ? JSON.parse : function(data) {
|
2493
|
+
if (typeof data === "string") {
|
2494
|
+
data = data.trim();
|
2495
|
+
if (data) {
|
2496
|
+
if (rvalidchars.test(data.replace(rvalidescape, "@")
|
2497
|
+
.replace(rvalidtokens, "]")
|
2498
|
+
.replace(rvalidbraces, ""))) {
|
2499
|
+
return (new Function("return " + data))();
|
2500
|
+
}
|
2501
|
+
}
|
2502
|
+
avalon.error("Invalid JSON: " + data);
|
2503
|
+
}
|
2504
|
+
return data
|
2505
|
+
}
|
2506
|
+
|
2507
|
+
//生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法
|
2508
|
+
avalon.each({
|
2509
|
+
scrollLeft: "pageXOffset",
|
2510
|
+
scrollTop: "pageYOffset"
|
2511
|
+
}, function(method, prop) {
|
2512
|
+
avalon.fn[method] = function(val) {
|
2513
|
+
var node = this[0] || {}, win = getWindow(node),
|
2514
|
+
top = method === "scrollTop"
|
2515
|
+
if (!arguments.length) {
|
2516
|
+
return win ? (prop in win) ? win[prop] : root[method] : node[method]
|
2517
|
+
} else {
|
2518
|
+
if (win) {
|
2519
|
+
win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop())
|
2520
|
+
} else {
|
2521
|
+
node[method] = val
|
2522
|
+
}
|
2523
|
+
}
|
2524
|
+
}
|
2525
|
+
})
|
2526
|
+
|
2527
|
+
function getWindow(node) {
|
2528
|
+
return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false;
|
2529
|
+
}
|
2530
|
+
//=============================css相关=======================
|
2531
|
+
var cssHooks = avalon.cssHooks = {}
|
2532
|
+
var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"]
|
2533
|
+
var cssMap = {
|
2534
|
+
"float": W3C ? "cssFloat" : "styleFloat"
|
2535
|
+
}
|
2536
|
+
avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
|
2537
|
+
|
2538
|
+
avalon.cssName = function(name, host, camelCase) {
|
2539
|
+
if (cssMap[name]) {
|
2540
|
+
return cssMap[name]
|
2541
|
+
}
|
2542
|
+
host = host || root.style
|
2543
|
+
for (var i = 0, n = prefixes.length; i < n; i++) {
|
2544
|
+
camelCase = camelize(prefixes[i] + name)
|
2545
|
+
if (camelCase in host) {
|
2546
|
+
return (cssMap[name] = camelCase)
|
2547
|
+
}
|
2548
|
+
}
|
2549
|
+
return null
|
2550
|
+
}
|
2551
|
+
cssHooks["@:set"] = function(node, name, value) {
|
2552
|
+
try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常
|
2553
|
+
node.style[name] = value
|
2554
|
+
} catch (e) {
|
2555
|
+
}
|
2556
|
+
}
|
2557
|
+
if (window.getComputedStyle) {
|
2558
|
+
cssHooks["@:get"] = function(node, name) {
|
2559
|
+
if (!node || !node.style) {
|
2560
|
+
throw new Error("getComputedStyle要求传入一个节点 " + node)
|
2561
|
+
}
|
2562
|
+
var ret, styles = getComputedStyle(node, null)
|
2563
|
+
if (styles) {
|
2564
|
+
ret = name === "filter" ? styles.getPropertyValue(name) : styles[name]
|
2565
|
+
if (ret === "") {
|
2566
|
+
ret = node.style[name] //其他浏览器需要我们手动取内联样式
|
2567
|
+
}
|
2568
|
+
}
|
2569
|
+
return ret
|
2570
|
+
}
|
2571
|
+
cssHooks["opacity:get"] = function(node) {
|
2572
|
+
var ret = cssHooks["@:get"](node, "opacity")
|
2573
|
+
return ret === "" ? "1" : ret
|
2574
|
+
}
|
2575
|
+
} else {
|
2576
|
+
var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i
|
2577
|
+
var rposition = /^(top|right|bottom|left)$/
|
2578
|
+
var ralpha = /alpha\([^)]*\)/i
|
2579
|
+
var ie8 = !!window.XDomainRequest
|
2580
|
+
var salpha = "DXImageTransform.Microsoft.Alpha"
|
2581
|
+
var border = {
|
2582
|
+
thin: ie8 ? '1px' : '2px',
|
2583
|
+
medium: ie8 ? '3px' : '4px',
|
2584
|
+
thick: ie8 ? '5px' : '6px'
|
2585
|
+
}
|
2586
|
+
cssHooks["@:get"] = function(node, name) {
|
2587
|
+
//取得精确值,不过它有可能是带em,pc,mm,pt,%等单位
|
2588
|
+
var currentStyle = node.currentStyle
|
2589
|
+
var ret = currentStyle[name]
|
2590
|
+
if ((rnumnonpx.test(ret) && !rposition.test(ret))) {
|
2591
|
+
//①,保存原有的style.left, runtimeStyle.left,
|
2592
|
+
var style = node.style,
|
2593
|
+
left = style.left,
|
2594
|
+
rsLeft = node.runtimeStyle.left
|
2595
|
+
//②由于③处的style.left = xxx会影响到currentStyle.left,
|
2596
|
+
//因此把它currentStyle.left放到runtimeStyle.left,
|
2597
|
+
//runtimeStyle.left拥有最高优先级,不会style.left影响
|
2598
|
+
node.runtimeStyle.left = currentStyle.left
|
2599
|
+
//③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft
|
2600
|
+
//得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760
|
2601
|
+
style.left = name === 'fontSize' ? '1em' : (ret || 0)
|
2602
|
+
ret = style.pixelLeft + "px"
|
2603
|
+
//④还原 style.left,runtimeStyle.left
|
2604
|
+
style.left = left
|
2605
|
+
node.runtimeStyle.left = rsLeft
|
2606
|
+
}
|
2607
|
+
if (ret === "medium") {
|
2608
|
+
name = name.replace("Width", "Style")
|
2609
|
+
//border width 默认值为medium,即使其为0"
|
2610
|
+
if (currentStyle[name] === "none") {
|
2611
|
+
ret = "0px"
|
2612
|
+
}
|
2613
|
+
}
|
2614
|
+
return ret === "" ? "auto" : border[ret] || ret
|
2615
|
+
}
|
2616
|
+
cssHooks["opacity:set"] = function(node, name, value) {
|
2617
|
+
var style = node.style
|
2618
|
+
var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : ""
|
2619
|
+
var filter = style.filter || "";
|
2620
|
+
style.zoom = 1
|
2621
|
+
//不能使用以下方式设置透明度
|
2622
|
+
//node.filters.alpha.opacity = value * 100
|
2623
|
+
style.filter = (ralpha.test(filter) ?
|
2624
|
+
filter.replace(ralpha, opacity) :
|
2625
|
+
filter + " " + opacity).trim()
|
2626
|
+
if (!style.filter) {
|
2627
|
+
style.removeAttribute("filter")
|
2628
|
+
}
|
2629
|
+
}
|
2630
|
+
cssHooks["opacity:get"] = function(node) {
|
2631
|
+
//这是最快的获取IE透明值的方式,不需要动用正则了!
|
2632
|
+
var alpha = node.filters.alpha || node.filters[salpha],
|
2633
|
+
op = alpha && alpha.enabled ? alpha.opacity : 100
|
2634
|
+
return (op / 100) + "" //确保返回的是字符串
|
2635
|
+
}
|
2636
|
+
}
|
2637
|
+
|
2638
|
+
"top,left".replace(rword, function(name) {
|
2639
|
+
cssHooks[name + ":get"] = function(node) {
|
2640
|
+
var computed = cssHooks["@:get"](node, name)
|
2641
|
+
return /px$/.test(computed) ? computed :
|
2642
|
+
avalon(node).position()[name] + "px"
|
2643
|
+
}
|
2644
|
+
})
|
2645
|
+
|
2646
|
+
var cssShow = {
|
2647
|
+
position: "absolute",
|
2648
|
+
visibility: "hidden",
|
2649
|
+
display: "block"
|
2650
|
+
}
|
2651
|
+
|
2652
|
+
var rdisplayswap = /^(none|table(?!-c[ea]).+)/
|
2653
|
+
|
2654
|
+
function showHidden(node, array) {
|
2655
|
+
//http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
|
2656
|
+
if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0
|
2657
|
+
if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) {
|
2658
|
+
var obj = {
|
2659
|
+
node: node
|
2660
|
+
}
|
2661
|
+
for (var name in cssShow) {
|
2662
|
+
obj[name] = node.style[name]
|
2663
|
+
node.style[name] = cssShow[name]
|
2664
|
+
}
|
2665
|
+
array.push(obj)
|
2666
|
+
}
|
2667
|
+
var parent = node.parentNode
|
2668
|
+
if (parent && parent.nodeType === 1) {
|
2669
|
+
showHidden(parent, array)
|
2670
|
+
}
|
2671
|
+
}
|
2672
|
+
}
|
2673
|
+
"Width,Height".replace(rword, function(name) { //fix 481
|
2674
|
+
var method = name.toLowerCase(),
|
2675
|
+
clientProp = "client" + name,
|
2676
|
+
scrollProp = "scroll" + name,
|
2677
|
+
offsetProp = "offset" + name
|
2678
|
+
cssHooks[method + ":get"] = function(node, which, override) {
|
2679
|
+
var boxSizing = -4
|
2680
|
+
if (typeof override === "number") {
|
2681
|
+
boxSizing = override
|
2682
|
+
}
|
2683
|
+
which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
|
2684
|
+
var ret = node[offsetProp] // border-box 0
|
2685
|
+
if (boxSizing === 2) { // margin-box 2
|
2686
|
+
return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
|
2687
|
+
}
|
2688
|
+
if (boxSizing < 0) { // padding-box -2
|
2689
|
+
ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
|
2690
|
+
}
|
2691
|
+
if (boxSizing === -4) { // content-box -4
|
2692
|
+
ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
|
2693
|
+
}
|
2694
|
+
return ret
|
2695
|
+
}
|
2696
|
+
cssHooks[method + "&get"] = function(node) {
|
2697
|
+
var hidden = [];
|
2698
|
+
showHidden(node, hidden);
|
2699
|
+
var val = cssHooks[method + ":get"](node)
|
2700
|
+
for (var i = 0, obj; obj = hidden[i++]; ) {
|
2701
|
+
node = obj.node
|
2702
|
+
for (var n in obj) {
|
2703
|
+
if (typeof obj[n] === "string") {
|
2704
|
+
node.style[n] = obj[n]
|
2705
|
+
}
|
2706
|
+
}
|
2707
|
+
}
|
2708
|
+
return val;
|
2709
|
+
}
|
2710
|
+
avalon.fn[method] = function(value) { //会忽视其display
|
2711
|
+
var node = this[0]
|
2712
|
+
if (arguments.length === 0) {
|
2713
|
+
if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
|
2714
|
+
return node["inner" + name] || node.document.documentElement[clientProp]
|
2715
|
+
}
|
2716
|
+
if (node.nodeType === 9) { //取得页面尺寸
|
2717
|
+
var doc = node.documentElement
|
2718
|
+
//FF chrome html.scrollHeight< body.scrollHeight
|
2719
|
+
//IE 标准模式 : html.scrollHeight> body.scrollHeight
|
2720
|
+
//IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
|
2721
|
+
return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp])
|
2722
|
+
}
|
2723
|
+
return cssHooks[method + "&get"](node)
|
2724
|
+
} else {
|
2725
|
+
return this.css(method, value)
|
2726
|
+
}
|
2727
|
+
}
|
2728
|
+
avalon.fn["inner" + name] = function() {
|
2729
|
+
return cssHooks[method + ":get"](this[0], void 0, -2)
|
2730
|
+
}
|
2731
|
+
avalon.fn["outer" + name] = function(includeMargin) {
|
2732
|
+
return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
|
2733
|
+
}
|
2734
|
+
})
|
2735
|
+
avalon.fn.offset = function() { //取得距离页面左右角的坐标
|
2736
|
+
var node = this[0],
|
2737
|
+
box = {
|
2738
|
+
left: 0,
|
2739
|
+
top: 0
|
2740
|
+
}
|
2741
|
+
if (!node || !node.tagName || !node.ownerDocument) {
|
2742
|
+
return box
|
2743
|
+
}
|
2744
|
+
var doc = node.ownerDocument,
|
2745
|
+
body = doc.body,
|
2746
|
+
root = doc.documentElement,
|
2747
|
+
win = doc.defaultView || doc.parentWindow
|
2748
|
+
if (!avalon.contains(root, node)) {
|
2749
|
+
return box
|
2750
|
+
}
|
2751
|
+
//http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的
|
2752
|
+
//我们可以通过getBoundingClientRect来获得元素相对于client的rect.
|
2753
|
+
//http://msdn.microsoft.com/en-us/library/ms536433.aspx
|
2754
|
+
if (node.getBoundingClientRect) {
|
2755
|
+
box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone)
|
2756
|
+
}
|
2757
|
+
//chrome/IE6: body.scrollTop, firefox/other: root.scrollTop
|
2758
|
+
var clientTop = root.clientTop || body.clientTop,
|
2759
|
+
clientLeft = root.clientLeft || body.clientLeft,
|
2760
|
+
scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop),
|
2761
|
+
scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft)
|
2762
|
+
// 把滚动距离加到left,top中去。
|
2763
|
+
// IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它
|
2764
|
+
// http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx
|
2765
|
+
return {
|
2766
|
+
top: box.top + scrollTop - clientTop,
|
2767
|
+
left: box.left + scrollLeft - clientLeft
|
2768
|
+
}
|
2769
|
+
}
|
2770
|
+
|
2771
|
+
//==================================val相关============================
|
2772
|
+
|
2773
|
+
function getValType(el) {
|
2774
|
+
var ret = el.tagName.toLowerCase()
|
2775
|
+
return ret === "input" && /checkbox|radio/.test(el.type) ? "checked" : ret
|
2776
|
+
}
|
2777
|
+
var roption = /^<option(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*\s+value[\s=]/i
|
2778
|
+
var valHooks = {
|
2779
|
+
"option:get": IEVersion ? function(node) {
|
2780
|
+
//在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作)
|
2781
|
+
//specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value
|
2782
|
+
return roption.test(node.outerHTML) ? node.value : node.text.trim()
|
2783
|
+
} : function(node) {
|
2784
|
+
return node.value
|
2785
|
+
},
|
2786
|
+
"select:get": function(node, value) {
|
2787
|
+
var option, options = node.options,
|
2788
|
+
index = node.selectedIndex,
|
2789
|
+
getter = valHooks["option:get"],
|
2790
|
+
one = node.type === "select-one" || index < 0,
|
2791
|
+
values = one ? null : [],
|
2792
|
+
max = one ? index + 1 : options.length,
|
2793
|
+
i = index < 0 ? max : one ? index : 0
|
2794
|
+
for (; i < max; i++) {
|
2795
|
+
option = options[i]
|
2796
|
+
//旧式IE在reset后不会改变selected,需要改用i === index判定
|
2797
|
+
//我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable
|
2798
|
+
//因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况
|
2799
|
+
if ((option.selected || i === index) && !option.disabled) {
|
2800
|
+
value = getter(option)
|
2801
|
+
if (one) {
|
2802
|
+
return value
|
2803
|
+
}
|
2804
|
+
//收集所有selected值组成数组返回
|
2805
|
+
values.push(value)
|
2806
|
+
}
|
2807
|
+
}
|
2808
|
+
return values
|
2809
|
+
},
|
2810
|
+
"select:set": function(node, values, optionSet) {
|
2811
|
+
values = [].concat(values) //强制转换为数组
|
2812
|
+
var getter = valHooks["option:get"]
|
2813
|
+
for (var i = 0, el; el = node.options[i++]; ) {
|
2814
|
+
if ((el.selected = values.indexOf(getter(el)) > -1)) {
|
2815
|
+
optionSet = true
|
2816
|
+
}
|
2817
|
+
}
|
2818
|
+
if (!optionSet) {
|
2819
|
+
node.selectedIndex = -1
|
2820
|
+
}
|
2821
|
+
}
|
2822
|
+
}
|
2823
|
+
|
2824
|
+
/*********************************************************************
|
2825
|
+
* 编译系统 *
|
2826
|
+
**********************************************************************/
|
2827
|
+
var meta = {
|
2828
|
+
'\b': '\\b',
|
2829
|
+
'\t': '\\t',
|
2830
|
+
'\n': '\\n',
|
2831
|
+
'\f': '\\f',
|
2832
|
+
'\r': '\\r',
|
2833
|
+
'"': '\\"',
|
2834
|
+
'\\': '\\\\'
|
2835
|
+
}
|
2836
|
+
var quote = window.JSON && JSON.stringify || function(str) {
|
2837
|
+
return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) {
|
2838
|
+
var c = meta[a];
|
2839
|
+
return typeof c === 'string' ? c :
|
2840
|
+
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
|
2841
|
+
}) + '"'
|
2842
|
+
}
|
2843
|
+
|
2844
|
+
var keywords =
|
2845
|
+
// 关键字
|
2846
|
+
"break,case,catch,continue,debugger,default,delete,do,else,false" +
|
2847
|
+
",finally,for,function,if,in,instanceof,new,null,return,switch,this" +
|
2848
|
+
",throw,true,try,typeof,var,void,while,with"
|
2849
|
+
// 保留字
|
2850
|
+
+ ",abstract,boolean,byte,char,class,const,double,enum,export,extends" +
|
2851
|
+
",final,float,goto,implements,import,int,interface,long,native" +
|
2852
|
+
",package,private,protected,public,short,static,super,synchronized" +
|
2853
|
+
",throws,transient,volatile"
|
2854
|
+
// ECMA 5 - use strict
|
2855
|
+
+ ",arguments,let,yield" + ",undefined"
|
2856
|
+
var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g
|
2857
|
+
var rsplit = /[^\w$]+/g
|
2858
|
+
var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g')
|
2859
|
+
var rnumber = /\b\d[^,]*/g
|
2860
|
+
var rcomma = /^,+|,+$/g
|
2861
|
+
var cacheVars = createCache(512)
|
2862
|
+
var getVariables = function(code) {
|
2863
|
+
var key = "," + code.trim()
|
2864
|
+
if (cacheVars[key]) {
|
2865
|
+
return cacheVars[key]
|
2866
|
+
}
|
2867
|
+
var match = code
|
2868
|
+
.replace(rrexpstr, "")
|
2869
|
+
.replace(rsplit, ",")
|
2870
|
+
.replace(rkeywords, "")
|
2871
|
+
.replace(rnumber, "")
|
2872
|
+
.replace(rcomma, "")
|
2873
|
+
.split(/^$|,+/)
|
2874
|
+
return cacheVars(key, uniqSet(match))
|
2875
|
+
}
|
2876
|
+
/*添加赋值语句*/
|
2877
|
+
|
2878
|
+
function addAssign(vars, scope, name, data) {
|
2879
|
+
var ret = [],
|
2880
|
+
prefix = " = " + name + "."
|
2881
|
+
for (var i = vars.length, prop; prop = vars[--i]; ) {
|
2882
|
+
if (scope.hasOwnProperty(prop)) {
|
2883
|
+
ret.push(prop + prefix + prop)
|
2884
|
+
data.vars.push(prop)
|
2885
|
+
if (data.type === "duplex") {
|
2886
|
+
vars.get = name + "." + prop
|
2887
|
+
}
|
2888
|
+
vars.splice(i, 1)
|
2889
|
+
}
|
2890
|
+
}
|
2891
|
+
return ret
|
2892
|
+
}
|
2893
|
+
|
2894
|
+
function uniqSet(array) {
|
2895
|
+
var ret = [],
|
2896
|
+
unique = {}
|
2897
|
+
for (var i = 0; i < array.length; i++) {
|
2898
|
+
var el = array[i]
|
2899
|
+
var id = el && typeof el.$id === "string" ? el.$id : el
|
2900
|
+
if (!unique[id]) {
|
2901
|
+
unique[id] = ret.push(el)
|
2902
|
+
}
|
2903
|
+
}
|
2904
|
+
return ret
|
2905
|
+
}
|
2906
|
+
//缓存求值函数,以便多次利用
|
2907
|
+
var cacheExprs = createCache(128)
|
2908
|
+
//取得求值函数及其传参
|
2909
|
+
var rduplex = /\w\[.*\]|\w\.\w/
|
2910
|
+
var rproxy = /(\$proxy\$[a-z]+)\d+$/
|
2911
|
+
var rthimRightParentheses = /\)\s*$/
|
2912
|
+
var rthimOtherParentheses = /\)\s*\|/g
|
2913
|
+
var rquoteFilterName = /\|\s*([$\w]+)/g
|
2914
|
+
var rpatchBracket = /"\s*\["/g
|
2915
|
+
var rthimLeftParentheses = /"\s*\(/g
|
2916
|
+
function parseFilter(val, filters) {
|
2917
|
+
filters = filters
|
2918
|
+
.replace(rthimRightParentheses, "")//处理最后的小括号
|
2919
|
+
.replace(rthimOtherParentheses, function() {//处理其他小括号
|
2920
|
+
return "],|"
|
2921
|
+
})
|
2922
|
+
.replace(rquoteFilterName, function(a, b) { //处理|及它后面的过滤器的名字
|
2923
|
+
return "[" + quote(b)
|
2924
|
+
})
|
2925
|
+
.replace(rpatchBracket, function() {
|
2926
|
+
return '"],["'
|
2927
|
+
})
|
2928
|
+
.replace(rthimLeftParentheses, function() {
|
2929
|
+
return '",'
|
2930
|
+
}) + "]"
|
2931
|
+
return "return avalon.filters.$filter(" + val + ", " + filters + ")"
|
2932
|
+
}
|
2933
|
+
|
2934
|
+
function parseExpr(code, scopes, data) {
|
2935
|
+
var dataType = data.type
|
2936
|
+
var filters = data.filters || ""
|
2937
|
+
var exprId = scopes.map(function(el) {
|
2938
|
+
return String(el.$id).replace(rproxy, "$1")
|
2939
|
+
}) + code + dataType + filters
|
2940
|
+
var vars = getVariables(code).concat(),
|
2941
|
+
assigns = [],
|
2942
|
+
names = [],
|
2943
|
+
args = [],
|
2944
|
+
prefix = ""
|
2945
|
+
//args 是一个对象数组, names 是将要生成的求值函数的参数
|
2946
|
+
scopes = uniqSet(scopes)
|
2947
|
+
data.vars = []
|
2948
|
+
for (var i = 0, sn = scopes.length; i < sn; i++) {
|
2949
|
+
if (vars.length) {
|
2950
|
+
var name = "vm" + expose + "_" + i
|
2951
|
+
names.push(name)
|
2952
|
+
args.push(scopes[i])
|
2953
|
+
assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data))
|
2954
|
+
}
|
2955
|
+
}
|
2956
|
+
if (!assigns.length && dataType === "duplex") {
|
2957
|
+
return
|
2958
|
+
}
|
2959
|
+
if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) {
|
2960
|
+
//https://github.com/RubyLouvre/avalon/issues/583
|
2961
|
+
data.vars.forEach(function(v) {
|
2962
|
+
var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
|
2963
|
+
code = code.replace(reg, function(_) {
|
2964
|
+
var c = _.charAt(v.length)
|
2965
|
+
var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
|
2966
|
+
var method = /^\s*\(/.test(r)
|
2967
|
+
if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx
|
2968
|
+
var name = "var" + String(Math.random()).replace(/^0\./, "")
|
2969
|
+
if (method) {//array.size()
|
2970
|
+
var array = _.split(".")
|
2971
|
+
if (array.length > 2) {
|
2972
|
+
var last = array.pop()
|
2973
|
+
assigns.push(name + " = " + array.join("."))
|
2974
|
+
return name + "." + last
|
2975
|
+
} else {
|
2976
|
+
return _
|
2977
|
+
}
|
2978
|
+
}
|
2979
|
+
assigns.push(name + " = " + _)
|
2980
|
+
return name
|
2981
|
+
} else {
|
2982
|
+
return _
|
2983
|
+
}
|
2984
|
+
})
|
2985
|
+
})
|
2986
|
+
}
|
2987
|
+
//---------------args----------------
|
2988
|
+
data.args = args
|
2989
|
+
//---------------cache----------------
|
2990
|
+
var fn = cacheExprs[exprId] //直接从缓存,免得重复生成
|
2991
|
+
if (fn) {
|
2992
|
+
data.evaluator = fn
|
2993
|
+
return
|
2994
|
+
}
|
2995
|
+
var prefix = assigns.join(", ")
|
2996
|
+
if (prefix) {
|
2997
|
+
prefix = "var " + prefix
|
2998
|
+
}
|
2999
|
+
if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器
|
3000
|
+
if (!/text|html/.test(data.type)) {
|
3001
|
+
throw Error("ms-" + data.type + "不支持过滤器")
|
3002
|
+
}
|
3003
|
+
code = "\nvar ret" + expose + " = " + code + ";\r\n"
|
3004
|
+
code += parseFilter("ret" + expose, filters)
|
3005
|
+
} else if (dataType === "duplex") { //双工绑定
|
3006
|
+
var _body = "'use strict';\nreturn function(vvv){\n\t" +
|
3007
|
+
prefix +
|
3008
|
+
";\n\tif(!arguments.length){\n\t\treturn " +
|
3009
|
+
code +
|
3010
|
+
"\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) +
|
3011
|
+
"= vvv;\n} "
|
3012
|
+
try {
|
3013
|
+
fn = Function.apply(noop, names.concat(_body))
|
3014
|
+
data.evaluator = cacheExprs(exprId, fn)
|
3015
|
+
} catch (e) {
|
3016
|
+
log("debug: parse error," + e.message)
|
3017
|
+
}
|
3018
|
+
return
|
3019
|
+
} else if (dataType === "on") { //事件绑定
|
3020
|
+
if (code.indexOf("(") === -1) {
|
3021
|
+
code += ".call(this, $event)"
|
3022
|
+
} else {
|
3023
|
+
code = code.replace("(", ".call(this,")
|
3024
|
+
}
|
3025
|
+
names.push("$event")
|
3026
|
+
code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
|
3027
|
+
var lastIndex = code.lastIndexOf("\nreturn")
|
3028
|
+
var header = code.slice(0, lastIndex)
|
3029
|
+
var footer = code.slice(lastIndex)
|
3030
|
+
code = header + "\n" + footer
|
3031
|
+
} else { //其他绑定
|
3032
|
+
code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
|
3033
|
+
}
|
3034
|
+
try {
|
3035
|
+
fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
|
3036
|
+
data.evaluator = cacheExprs(exprId, fn)
|
3037
|
+
} catch (e) {
|
3038
|
+
log("debug: parse error," + e.message)
|
3039
|
+
} finally {
|
3040
|
+
vars = textBuffer = names = null //释放内存
|
3041
|
+
}
|
3042
|
+
}
|
3043
|
+
|
3044
|
+
|
3045
|
+
//parseExpr的智能引用代理
|
3046
|
+
|
3047
|
+
function parseExprProxy(code, scopes, data, tokens, noregister) {
|
3048
|
+
if (Array.isArray(tokens)) {
|
3049
|
+
code = tokens.map(function(el) {
|
3050
|
+
return el.expr ? "(" + el.value + ")" : quote(el.value)
|
3051
|
+
}).join(" + ")
|
3052
|
+
}
|
3053
|
+
parseExpr(code, scopes, data)
|
3054
|
+
if (data.evaluator && !noregister) {
|
3055
|
+
data.handler = bindingExecutors[data.handlerName || data.type]
|
3056
|
+
//方便调试
|
3057
|
+
//这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定
|
3058
|
+
//将它移出订阅者列表
|
3059
|
+
registerSubscriber(data)
|
3060
|
+
}
|
3061
|
+
}
|
3062
|
+
avalon.parseExprProxy = parseExprProxy
|
3063
|
+
/*********************************************************************
|
3064
|
+
* 各种指令 *
|
3065
|
+
**********************************************************************/
|
3066
|
+
//ms-skip绑定已经在scanTag 方法中实现
|
3067
|
+
//ms-controller绑定已经在scanTag 方法中实现
|
3068
|
+
//ms-important绑定已经在scanTag 方法中实现
|
3069
|
+
var bools = "autofocus,autoplay,async,allowTransparency,checked,controls,declare,disabled,defer,defaultChecked,defaultSelected" +
|
3070
|
+
"contentEditable,isMap,loop,multiple,noHref,noResize,noShade,open,readOnly,selected"
|
3071
|
+
var boolMap = {}
|
3072
|
+
bools.replace(rword, function(name) {
|
3073
|
+
boolMap[name.toLowerCase()] = name
|
3074
|
+
})
|
3075
|
+
|
3076
|
+
var propMap = {//属性名映射
|
3077
|
+
"accept-charset": "acceptCharset",
|
3078
|
+
"char": "ch",
|
3079
|
+
"charoff": "chOff",
|
3080
|
+
"class": "className",
|
3081
|
+
"for": "htmlFor",
|
3082
|
+
"http-equiv": "httpEquiv"
|
3083
|
+
}
|
3084
|
+
|
3085
|
+
var anomaly = "accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan," + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight," + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
|
3086
|
+
anomaly.replace(rword, function(name) {
|
3087
|
+
propMap[name.toLowerCase()] = name
|
3088
|
+
})
|
3089
|
+
|
3090
|
+
var rnoscripts = /<noscript.*?>(?:[\s\S]+?)<\/noscript>/img
|
3091
|
+
var rnoscriptText = /<noscript.*?>([\s\S]+?)<\/noscript>/im
|
3092
|
+
|
3093
|
+
var getXHR = function() {
|
3094
|
+
return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")
|
3095
|
+
}
|
3096
|
+
|
3097
|
+
var cacheTmpls = avalon.templateCache = {}
|
3098
|
+
|
3099
|
+
bindingHandlers.attr = function(data, vmodels) {
|
3100
|
+
var text = data.value.trim(),
|
3101
|
+
simple = true
|
3102
|
+
if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) {
|
3103
|
+
simple = false
|
3104
|
+
if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") {
|
3105
|
+
simple = true
|
3106
|
+
text = RegExp.$1
|
3107
|
+
}
|
3108
|
+
}
|
3109
|
+
if (data.type === "include") {
|
3110
|
+
var elem = data.element
|
3111
|
+
data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels)
|
3112
|
+
data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels)
|
3113
|
+
var outer = data.includeReplaced = !!avalon(elem).data("includeReplace")
|
3114
|
+
data.startInclude = DOC.createComment("ms-include")
|
3115
|
+
data.endInclude = DOC.createComment("ms-include-end")
|
3116
|
+
if (outer) {
|
3117
|
+
data.element = data.startInclude
|
3118
|
+
elem.parentNode.insertBefore(data.startInclude, elem)
|
3119
|
+
elem.parentNode.insertBefore(data.endInclude, elem.nextSibling)
|
3120
|
+
} else {
|
3121
|
+
elem.insertBefore(data.startInclude, elem.firstChild)
|
3122
|
+
elem.appendChild(data.endInclude)
|
3123
|
+
}
|
3124
|
+
}
|
3125
|
+
data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况
|
3126
|
+
parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value)))
|
3127
|
+
}
|
3128
|
+
|
3129
|
+
bindingExecutors.attr = function(val, elem, data) {
|
3130
|
+
var method = data.type,
|
3131
|
+
attrName = data.param
|
3132
|
+
if (method === "css") {
|
3133
|
+
avalon(elem).css(attrName, val)
|
3134
|
+
} else if (method === "attr") {
|
3135
|
+
// ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc
|
3136
|
+
// ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名
|
3137
|
+
// ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性
|
3138
|
+
if (boolMap[attrName]) {
|
3139
|
+
var bool = boolMap[attrName]
|
3140
|
+
if (typeof elem[bool] === "boolean") {
|
3141
|
+
// IE6-11不支持动态设置fieldset的disabled属性,IE11下样式是生效了,但无法阻止用户对其底下的input元素进行设值……
|
3142
|
+
return elem[bool] = !!val
|
3143
|
+
}
|
3144
|
+
}
|
3145
|
+
var toRemove = (val === false) || (val === null) || (val === void 0)
|
3146
|
+
|
3147
|
+
if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射
|
3148
|
+
attrName = propMap[attrName]
|
3149
|
+
}
|
3150
|
+
if (toRemove) {
|
3151
|
+
return elem.removeAttribute(attrName)
|
3152
|
+
}
|
3153
|
+
//SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy
|
3154
|
+
var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false)
|
3155
|
+
if (isInnate) {
|
3156
|
+
elem[attrName] = val
|
3157
|
+
} else {
|
3158
|
+
elem.setAttribute(attrName, val)
|
3159
|
+
}
|
3160
|
+
} else if (method === "include" && val) {
|
3161
|
+
var vmodels = data.vmodels
|
3162
|
+
var rendered = data.includeRendered
|
3163
|
+
var loaded = data.includeLoaded
|
3164
|
+
var replace = data.includeReplaced
|
3165
|
+
var target = replace ? elem.parentNode : elem
|
3166
|
+
function scanTemplate(text) {
|
3167
|
+
if (loaded) {
|
3168
|
+
text = loaded.apply(target, [text].concat(vmodels))
|
3169
|
+
}
|
3170
|
+
if (rendered) {
|
3171
|
+
checkScan(target, function() {
|
3172
|
+
rendered.call(target)
|
3173
|
+
}, NaN)
|
3174
|
+
}
|
3175
|
+
while (true) {
|
3176
|
+
var node = data.startInclude.nextSibling
|
3177
|
+
if (node && node !== data.endInclude) {
|
3178
|
+
target.removeChild(node)
|
3179
|
+
} else {
|
3180
|
+
break
|
3181
|
+
}
|
3182
|
+
}
|
3183
|
+
var dom = avalon.parseHTML(text)
|
3184
|
+
var nodes = avalon.slice(dom.childNodes)
|
3185
|
+
target.insertBefore(dom, data.endInclude)
|
3186
|
+
scanNodeArray(nodes, vmodels)
|
3187
|
+
}
|
3188
|
+
if (data.param === "src") {
|
3189
|
+
if (cacheTmpls[val]) {
|
3190
|
+
avalon.nextTick(function() {
|
3191
|
+
scanTemplate(cacheTmpls[val])
|
3192
|
+
})
|
3193
|
+
} else {
|
3194
|
+
var xhr = getXHR()
|
3195
|
+
xhr.onreadystatechange = function() {
|
3196
|
+
if (xhr.readyState === 4) {
|
3197
|
+
var s = xhr.status
|
3198
|
+
if (s >= 200 && s < 300 || s === 304 || s === 1223) {
|
3199
|
+
scanTemplate(cacheTmpls[val] = xhr.responseText)
|
3200
|
+
}
|
3201
|
+
}
|
3202
|
+
}
|
3203
|
+
xhr.open("GET", val, true)
|
3204
|
+
if ("withCredentials" in xhr) {
|
3205
|
+
xhr.withCredentials = true
|
3206
|
+
}
|
3207
|
+
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
3208
|
+
xhr.send(null)
|
3209
|
+
}
|
3210
|
+
} else {
|
3211
|
+
//IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+)
|
3212
|
+
//http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/
|
3213
|
+
var el = val && val.nodeType === 1 ? val : DOC.getElementById(val)
|
3214
|
+
if (el) {
|
3215
|
+
if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML
|
3216
|
+
var xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以
|
3217
|
+
xhr.open("GET", location, false) //谢谢Nodejs 乱炖群 深圳-纯属虚构
|
3218
|
+
xhr.send(null)
|
3219
|
+
//http://bbs.csdn.net/topics/390349046?page=1#post-393492653
|
3220
|
+
var noscripts = DOC.getElementsByTagName("noscript")
|
3221
|
+
var array = (xhr.responseText || "").match(rnoscripts) || []
|
3222
|
+
var n = array.length
|
3223
|
+
for (var i = 0; i < n; i++) {
|
3224
|
+
var tag = noscripts[i]
|
3225
|
+
if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的
|
3226
|
+
tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug
|
3227
|
+
tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1]
|
3228
|
+
}
|
3229
|
+
}
|
3230
|
+
}
|
3231
|
+
avalon.nextTick(function() {
|
3232
|
+
scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML)
|
3233
|
+
})
|
3234
|
+
}
|
3235
|
+
}
|
3236
|
+
} else {
|
3237
|
+
if (!root.hasAttribute && typeof val === "string" && (method === "src" || method === "href")) {
|
3238
|
+
val = val.replace(/&/g, "&") //处理IE67自动转义的问题
|
3239
|
+
}
|
3240
|
+
elem[method] = val
|
3241
|
+
if (window.chrome && elem.tagName === "EMBED") {
|
3242
|
+
var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求
|
3243
|
+
var comment = document.createComment("ms-src")
|
3244
|
+
parent.replaceChild(comment, elem)
|
3245
|
+
parent.replaceChild(elem, comment)
|
3246
|
+
}
|
3247
|
+
}
|
3248
|
+
}
|
3249
|
+
|
3250
|
+
//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html"
|
3251
|
+
"title,alt,src,value,css,include,href".replace(rword, function(name) {
|
3252
|
+
bindingHandlers[name] = bindingHandlers.attr
|
3253
|
+
})
|
3254
|
+
//ms-include绑定已由ms-attr绑定实现
|
3255
|
+
|
3256
|
+
//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag"
|
3257
|
+
//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html
|
3258
|
+
bindingHandlers["class"] = function(data, vmodels) {
|
3259
|
+
var oldStyle = data.param,
|
3260
|
+
text = data.value,
|
3261
|
+
rightExpr
|
3262
|
+
data.handlerName = "class"
|
3263
|
+
if (!oldStyle || isFinite(oldStyle)) {
|
3264
|
+
data.param = "" //去掉数字
|
3265
|
+
var noExpr = text.replace(rexprg, function(a) {
|
3266
|
+
return a.replace(/./g, "0")
|
3267
|
+
//return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位
|
3268
|
+
})
|
3269
|
+
var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置
|
3270
|
+
if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况
|
3271
|
+
var className = text
|
3272
|
+
} else { // 比如 ms-class-1="ui-state-active:checked" 的情况
|
3273
|
+
className = text.slice(0, colonIndex)
|
3274
|
+
rightExpr = text.slice(colonIndex + 1)
|
3275
|
+
parseExpr(rightExpr, vmodels, data) //决定是添加还是删除
|
3276
|
+
if (!data.evaluator) {
|
3277
|
+
log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中")
|
3278
|
+
return false
|
3279
|
+
} else {
|
3280
|
+
data._evaluator = data.evaluator
|
3281
|
+
data._args = data.args
|
3282
|
+
}
|
3283
|
+
}
|
3284
|
+
var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况
|
3285
|
+
if (!hasExpr) {
|
3286
|
+
data.immobileClass = className
|
3287
|
+
}
|
3288
|
+
parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0))
|
3289
|
+
} else {
|
3290
|
+
data.immobileClass = data.oldStyle = data.param
|
3291
|
+
parseExprProxy(text, vmodels, data)
|
3292
|
+
}
|
3293
|
+
}
|
3294
|
+
|
3295
|
+
bindingExecutors ["class"] = function(val, elem, data) {
|
3296
|
+
var $elem = avalon(elem),
|
3297
|
+
method = data.type
|
3298
|
+
if (method === "class" && data.oldStyle) { //如果是旧风格
|
3299
|
+
$elem.toggleClass(data.oldStyle, !!val)
|
3300
|
+
} else {
|
3301
|
+
//如果存在冒号就有求值函数
|
3302
|
+
data.toggleClass = data._evaluator ? !!data._evaluator.apply(elem, data._args) : true
|
3303
|
+
data.newClass = data.immobileClass || val
|
3304
|
+
if (data.oldClass && data.newClass !== data.oldClass) {
|
3305
|
+
$elem.removeClass(data.oldClass)
|
3306
|
+
}
|
3307
|
+
data.oldClass = data.newClass
|
3308
|
+
switch (method) {
|
3309
|
+
case "class":
|
3310
|
+
$elem.toggleClass(data.newClass, data.toggleClass)
|
3311
|
+
break
|
3312
|
+
case "hover":
|
3313
|
+
case "active":
|
3314
|
+
if (!data.hasBindEvent) { //确保只绑定一次
|
3315
|
+
var activate = "mouseenter" //在移出移入时切换类名
|
3316
|
+
var abandon = "mouseleave"
|
3317
|
+
if (method === "active") { //在聚焦失焦中切换类名
|
3318
|
+
elem.tabIndex = elem.tabIndex || -1
|
3319
|
+
activate = "mousedown"
|
3320
|
+
abandon = "mouseup"
|
3321
|
+
var fn0 = $elem.bind("mouseleave", function() {
|
3322
|
+
data.toggleClass && $elem.removeClass(data.newClass)
|
3323
|
+
})
|
3324
|
+
}
|
3325
|
+
var fn1 = $elem.bind(activate, function() {
|
3326
|
+
data.toggleClass && $elem.addClass(data.newClass)
|
3327
|
+
})
|
3328
|
+
var fn2 = $elem.bind(abandon, function() {
|
3329
|
+
data.toggleClass && $elem.removeClass(data.newClass)
|
3330
|
+
})
|
3331
|
+
data.rollback = function() {
|
3332
|
+
$elem.unbind("mouseleave", fn0)
|
3333
|
+
$elem.unbind(activate, fn1)
|
3334
|
+
$elem.unbind(abandon, fn2)
|
3335
|
+
}
|
3336
|
+
data.hasBindEvent = true
|
3337
|
+
}
|
3338
|
+
break;
|
3339
|
+
}
|
3340
|
+
}
|
3341
|
+
}
|
3342
|
+
|
3343
|
+
"hover,active".replace(rword, function(method) {
|
3344
|
+
bindingHandlers[method] = bindingHandlers["class"]
|
3345
|
+
})
|
3346
|
+
// bindingHandlers.data 定义在if.js
|
3347
|
+
bindingExecutors.data = function(val, elem, data) {
|
3348
|
+
var key = "data-" + data.param
|
3349
|
+
if (val && typeof val === "object") {
|
3350
|
+
elem[key] = val
|
3351
|
+
} else {
|
3352
|
+
elem.setAttribute(key, String(val))
|
3353
|
+
}
|
3354
|
+
}
|
3355
|
+
|
3356
|
+
// bindingHandlers.text 定义在if.js
|
3357
|
+
bindingExecutors.text = function(val, elem) {
|
3358
|
+
val = val == null ? "" : val //不在页面上显示undefined null
|
3359
|
+
if (elem.nodeType === 3) { //绑定在文本节点上
|
3360
|
+
try { //IE对游离于DOM树外的节点赋值会报错
|
3361
|
+
elem.data = val
|
3362
|
+
} catch (e) {
|
3363
|
+
}
|
3364
|
+
} else { //绑定在特性节点上
|
3365
|
+
if ("textContent" in elem) {
|
3366
|
+
elem.textContent = val
|
3367
|
+
} else {
|
3368
|
+
elem.innerText = val
|
3369
|
+
}
|
3370
|
+
}
|
3371
|
+
}
|
3372
|
+
|
3373
|
+
|
3374
|
+
// bindingHandlers.html 定义在if.js
|
3375
|
+
bindingExecutors.html = function(val, elem, data) {
|
3376
|
+
val = val == null ? "" : val
|
3377
|
+
var isHtmlFilter = "group" in data
|
3378
|
+
var parent = isHtmlFilter ? elem.parentNode : elem
|
3379
|
+
if (!parent)
|
3380
|
+
return
|
3381
|
+
if (val.nodeType === 11) { //将val转换为文档碎片
|
3382
|
+
var fragment = val
|
3383
|
+
} else if (val.nodeType === 1 || val.item) {
|
3384
|
+
var nodes = val.nodeType === 1 ? val.childNodes : val.item ? val : []
|
3385
|
+
fragment = hyperspace.cloneNode(true)
|
3386
|
+
while (nodes[0]) {
|
3387
|
+
fragment.appendChild(nodes[0])
|
3388
|
+
}
|
3389
|
+
} else {
|
3390
|
+
fragment = avalon.parseHTML(val)
|
3391
|
+
}
|
3392
|
+
//插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空
|
3393
|
+
var comment = DOC.createComment("ms-html")
|
3394
|
+
if (isHtmlFilter) {
|
3395
|
+
parent.insertBefore(comment, elem)
|
3396
|
+
var n = data.group, i = 1
|
3397
|
+
while (i < n) {
|
3398
|
+
var node = elem.nextSibling
|
3399
|
+
if (node) {
|
3400
|
+
parent.removeChild(node)
|
3401
|
+
i++
|
3402
|
+
}
|
3403
|
+
}
|
3404
|
+
parent.removeChild(elem)
|
3405
|
+
data.element = comment //防止被CG
|
3406
|
+
} else {
|
3407
|
+
avalon.clearHTML(parent).appendChild(comment)
|
3408
|
+
}
|
3409
|
+
if (isHtmlFilter) {
|
3410
|
+
data.group = fragment.childNodes.length || 1
|
3411
|
+
}
|
3412
|
+
var nodes = avalon.slice(fragment.childNodes)
|
3413
|
+
if (nodes[0]) {
|
3414
|
+
if (comment.parentNode)
|
3415
|
+
comment.parentNode.replaceChild(fragment, comment)
|
3416
|
+
if (isHtmlFilter) {
|
3417
|
+
data.element = nodes[0]
|
3418
|
+
}
|
3419
|
+
}
|
3420
|
+
scanNodeArray(nodes, data.vmodels)
|
3421
|
+
}
|
3422
|
+
|
3423
|
+
bindingHandlers["if"] =
|
3424
|
+
bindingHandlers.data =
|
3425
|
+
bindingHandlers.text =
|
3426
|
+
bindingHandlers.html =
|
3427
|
+
function(data, vmodels) {
|
3428
|
+
parseExprProxy(data.value, vmodels, data)
|
3429
|
+
}
|
3430
|
+
|
3431
|
+
bindingExecutors["if"] = function(val, elem, data) {
|
3432
|
+
if (val) { //插回DOM树
|
3433
|
+
if (elem.nodeType === 8) {
|
3434
|
+
elem.parentNode.replaceChild(data.template, elem)
|
3435
|
+
elem = data.element = data.template //这时可能为null
|
3436
|
+
}
|
3437
|
+
if (elem.getAttribute(data.name)) {
|
3438
|
+
elem.removeAttribute(data.name)
|
3439
|
+
scanAttr(elem, data.vmodels)
|
3440
|
+
}
|
3441
|
+
data.rollback = null
|
3442
|
+
} else { //移出DOM树,并用注释节点占据原位置
|
3443
|
+
if (elem.nodeType === 1) {
|
3444
|
+
var node = data.element = DOC.createComment("ms-if")
|
3445
|
+
elem.parentNode.replaceChild(node, elem)
|
3446
|
+
data.template = elem //元素节点
|
3447
|
+
ifGroup.appendChild(elem)
|
3448
|
+
data.rollback = function() {
|
3449
|
+
if (elem.parentNode === ifGroup) {
|
3450
|
+
ifGroup.removeChild(elem)
|
3451
|
+
}
|
3452
|
+
}
|
3453
|
+
}
|
3454
|
+
}
|
3455
|
+
}
|
3456
|
+
|
3457
|
+
|
3458
|
+
function parseDisplay(nodeName, val) {
|
3459
|
+
//用于取得此类标签的默认display值
|
3460
|
+
var key = "_" + nodeName
|
3461
|
+
if (!parseDisplay[key]) {
|
3462
|
+
var node = DOC.createElement(nodeName)
|
3463
|
+
root.appendChild(node)
|
3464
|
+
if (W3C) {
|
3465
|
+
val = getComputedStyle(node, null).display
|
3466
|
+
} else {
|
3467
|
+
val = node.currentStyle.display
|
3468
|
+
}
|
3469
|
+
root.removeChild(node)
|
3470
|
+
parseDisplay[key] = val
|
3471
|
+
}
|
3472
|
+
return parseDisplay[key]
|
3473
|
+
}
|
3474
|
+
|
3475
|
+
avalon.parseDisplay = parseDisplay
|
3476
|
+
|
3477
|
+
bindingHandlers.visible = function(data, vmodels) {
|
3478
|
+
var elem = avalon(data.element)
|
3479
|
+
var display = elem.css("display")
|
3480
|
+
if (display === "none") {
|
3481
|
+
var style = elem[0].style
|
3482
|
+
var has = /visibility/i.test(style.cssText)
|
3483
|
+
var visible = elem.css("visibility")
|
3484
|
+
style.display = ""
|
3485
|
+
style.visibility = "hidden"
|
3486
|
+
display = elem.css("display")
|
3487
|
+
if (display === "none") {
|
3488
|
+
display = parseDisplay(elem[0].nodeName)
|
3489
|
+
}
|
3490
|
+
style.visibility = has ? visible : ""
|
3491
|
+
}
|
3492
|
+
data.display = display
|
3493
|
+
parseExprProxy(data.value, vmodels, data)
|
3494
|
+
}
|
3495
|
+
|
3496
|
+
bindingExecutors.visible = function(val, elem, data) {
|
3497
|
+
elem.style.display = val ? data.display : "none"
|
3498
|
+
}
|
3499
|
+
|
3500
|
+
var rdash = /\(([^)]*)\)/
|
3501
|
+
bindingHandlers.on = function(data, vmodels) {
|
3502
|
+
var value = data.value
|
3503
|
+
data.type = "on"
|
3504
|
+
var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
|
3505
|
+
if (typeof bindingHandlers.on[eventType + "Hook"] === "function") {
|
3506
|
+
bindingHandlers.on[eventType + "Hook"](data)
|
3507
|
+
}
|
3508
|
+
if (value.indexOf("(") > 0 && value.indexOf(")") > -1) {
|
3509
|
+
var matched = (value.match(rdash) || ["", ""])[1].trim()
|
3510
|
+
if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理
|
3511
|
+
value = value.replace(rdash, "")
|
3512
|
+
}
|
3513
|
+
}
|
3514
|
+
parseExprProxy(value, vmodels, data)
|
3515
|
+
}
|
3516
|
+
|
3517
|
+
bindingExecutors.on = function(callback, elem, data) {
|
3518
|
+
callback = function(e) {
|
3519
|
+
var fn = data.evaluator || noop
|
3520
|
+
return fn.apply(this, data.args.concat(e))
|
3521
|
+
}
|
3522
|
+
var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
|
3523
|
+
if (eventType === "scan") {
|
3524
|
+
callback.call(elem, {
|
3525
|
+
type: eventType
|
3526
|
+
})
|
3527
|
+
} else if (typeof data.specialBind === "function") {
|
3528
|
+
data.specialBind(elem, callback)
|
3529
|
+
} else {
|
3530
|
+
var removeFn = avalon.bind(elem, eventType, callback)
|
3531
|
+
}
|
3532
|
+
data.rollback = function() {
|
3533
|
+
if (typeof data.specialUnbind === "function") {
|
3534
|
+
data.specialUnbind()
|
3535
|
+
} else {
|
3536
|
+
avalon.unbind(elem, eventType, removeFn)
|
3537
|
+
}
|
3538
|
+
}
|
3539
|
+
}
|
3540
|
+
|
3541
|
+
|
3542
|
+
bindingHandlers.widget = function(data, vmodels) {
|
3543
|
+
var args = data.value.match(rword)
|
3544
|
+
var elem = data.element
|
3545
|
+
var widget = args[0]
|
3546
|
+
var id = args[1]
|
3547
|
+
if (!id || id === "$") {//没有定义或为$时,取组件名+随机数
|
3548
|
+
id = generateID(widget)
|
3549
|
+
}
|
3550
|
+
var optName = args[2] || widget//没有定义,取组件名
|
3551
|
+
var constructor = avalon.ui[widget]
|
3552
|
+
if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname"
|
3553
|
+
vmodels = elem.vmodels || vmodels
|
3554
|
+
for (var i = 0, v; v = vmodels[i++]; ) {
|
3555
|
+
if (v.hasOwnProperty(optName) && typeof v[optName] === "object") {
|
3556
|
+
var vmOptions = v[optName]
|
3557
|
+
vmOptions = vmOptions.$model || vmOptions
|
3558
|
+
break
|
3559
|
+
}
|
3560
|
+
}
|
3561
|
+
if (vmOptions) {
|
3562
|
+
var wid = vmOptions[widget + "Id"]
|
3563
|
+
if (typeof wid === "string") {
|
3564
|
+
id = wid
|
3565
|
+
}
|
3566
|
+
}
|
3567
|
+
//抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象
|
3568
|
+
var widgetData = avalon.getWidgetData(elem, widget)
|
3569
|
+
data.value = [widget, id, optName].join(",")
|
3570
|
+
data[widget + "Id"] = id
|
3571
|
+
data.evaluator = noop
|
3572
|
+
elem.msData["ms-widget-id"] = id
|
3573
|
+
var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData)
|
3574
|
+
elem.removeAttribute("ms-widget")
|
3575
|
+
var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM
|
3576
|
+
if (vmodel.$id) {
|
3577
|
+
avalon.vmodels[id] = vmodel
|
3578
|
+
createSignalTower(elem, vmodel)
|
3579
|
+
if (vmodel.hasOwnProperty("$init")) {
|
3580
|
+
vmodel.$init(function() {
|
3581
|
+
avalon.scan(elem, [vmodel].concat(vmodels))
|
3582
|
+
if (typeof options.onInit === "function") {
|
3583
|
+
options.onInit.call(elem, vmodel, options, vmodels)
|
3584
|
+
}
|
3585
|
+
})
|
3586
|
+
}
|
3587
|
+
data.rollback = function() {
|
3588
|
+
try {
|
3589
|
+
vmodel.widgetElement = null
|
3590
|
+
vmodel.$remove()
|
3591
|
+
} catch (e) {
|
3592
|
+
}
|
3593
|
+
elem.msData = {}
|
3594
|
+
delete avalon.vmodels[vmodel.$id]
|
3595
|
+
}
|
3596
|
+
addSubscribers(data, widgetList)
|
3597
|
+
if (window.chrome) {
|
3598
|
+
elem.addEventListener("DOMNodeRemovedFromDocument", function() {
|
3599
|
+
setTimeout(removeSubscribers)
|
3600
|
+
})
|
3601
|
+
}
|
3602
|
+
} else {
|
3603
|
+
avalon.scan(elem, vmodels)
|
3604
|
+
}
|
3605
|
+
} else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels
|
3606
|
+
elem.vmodels = vmodels
|
3607
|
+
}
|
3608
|
+
}
|
3609
|
+
var widgetList = []
|
3610
|
+
//不存在 bindingExecutors.widget
|
3611
|
+
//双工绑定
|
3612
|
+
var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
|
3613
|
+
var elem = data.element,
|
3614
|
+
hasCast
|
3615
|
+
parseExprProxy(data.value, vmodels, data, 0, 1)
|
3616
|
+
|
3617
|
+
data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
|
3618
|
+
if (data.evaluator && data.args) {
|
3619
|
+
var params = []
|
3620
|
+
var casting = oneObject("string,number,boolean,checked")
|
3621
|
+
if (elem.type === "radio" && data.param === "") {
|
3622
|
+
data.param = "checked"
|
3623
|
+
}
|
3624
|
+
if (elem.msData) {
|
3625
|
+
elem.msData["ms-duplex"] = data.value
|
3626
|
+
}
|
3627
|
+
data.param.replace(/\w+/g, function(name) {
|
3628
|
+
if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) {
|
3629
|
+
if (name === "radio")
|
3630
|
+
log("ms-duplex-radio已经更名为ms-duplex-checked")
|
3631
|
+
name = "checked"
|
3632
|
+
data.isChecked = true
|
3633
|
+
}
|
3634
|
+
if (name === "bool") {
|
3635
|
+
name = "boolean"
|
3636
|
+
log("ms-duplex-bool已经更名为ms-duplex-boolean")
|
3637
|
+
} else if (name === "text") {
|
3638
|
+
name = "string"
|
3639
|
+
log("ms-duplex-text已经更名为ms-duplex-string")
|
3640
|
+
}
|
3641
|
+
if (casting[name]) {
|
3642
|
+
hasCast = true
|
3643
|
+
}
|
3644
|
+
avalon.Array.ensure(params, name)
|
3645
|
+
})
|
3646
|
+
if (!hasCast) {
|
3647
|
+
params.push("string")
|
3648
|
+
}
|
3649
|
+
data.param = params.join("-")
|
3650
|
+
data.bound = function(type, callback) {
|
3651
|
+
if (elem.addEventListener) {
|
3652
|
+
elem.addEventListener(type, callback, false)
|
3653
|
+
} else {
|
3654
|
+
elem.attachEvent("on" + type, callback)
|
3655
|
+
}
|
3656
|
+
var old = data.rollback
|
3657
|
+
data.rollback = function() {
|
3658
|
+
elem.avalonSetter = null
|
3659
|
+
avalon.unbind(elem, type, callback)
|
3660
|
+
old && old()
|
3661
|
+
}
|
3662
|
+
}
|
3663
|
+
for (var i in avalon.vmodels) {
|
3664
|
+
var v = avalon.vmodels[i]
|
3665
|
+
v.$fire("avalon-ms-duplex-init", data)
|
3666
|
+
}
|
3667
|
+
var cpipe = data.pipe || (data.pipe = pipe)
|
3668
|
+
cpipe(null, data, "init")
|
3669
|
+
var tagName = elem.tagName
|
3670
|
+
duplexBinding[tagName] && duplexBinding[tagName](elem, data.evaluator.apply(null, data.args), data)
|
3671
|
+
}
|
3672
|
+
}
|
3673
|
+
//不存在 bindingExecutors.duplex
|
3674
|
+
function fixNull(val) {
|
3675
|
+
return val == null ? "" : val
|
3676
|
+
}
|
3677
|
+
avalon.duplexHooks = {
|
3678
|
+
checked: {
|
3679
|
+
get: function(val, data) {
|
3680
|
+
return !data.element.oldValue
|
3681
|
+
}
|
3682
|
+
},
|
3683
|
+
string: {
|
3684
|
+
get: function(val) { //同步到VM
|
3685
|
+
return val
|
3686
|
+
},
|
3687
|
+
set: fixNull
|
3688
|
+
},
|
3689
|
+
"boolean": {
|
3690
|
+
get: function(val) {
|
3691
|
+
return val === "true"
|
3692
|
+
},
|
3693
|
+
set: fixNull
|
3694
|
+
},
|
3695
|
+
number: {
|
3696
|
+
get: function(val) {
|
3697
|
+
return isFinite(val) ? parseFloat(val) || 0 : val
|
3698
|
+
},
|
3699
|
+
set: fixNull
|
3700
|
+
}
|
3701
|
+
}
|
3702
|
+
|
3703
|
+
function pipe(val, data, action, e) {
|
3704
|
+
data.param.replace(/\w+/g, function(name) {
|
3705
|
+
var hook = avalon.duplexHooks[name]
|
3706
|
+
if (hook && typeof hook[action] === "function") {
|
3707
|
+
val = hook[action](val, data)
|
3708
|
+
}
|
3709
|
+
})
|
3710
|
+
return val
|
3711
|
+
}
|
3712
|
+
|
3713
|
+
var TimerID, ribbon = []
|
3714
|
+
function W3CFire(el, name, detail) {
|
3715
|
+
var event = DOC.createEvent("Events")
|
3716
|
+
event.initEvent(name, true, true)
|
3717
|
+
event.fireByAvalon = true//签名,标记事件是由avalon触发
|
3718
|
+
//event.isTrusted = false 设置这个opera会报错
|
3719
|
+
if (detail)
|
3720
|
+
event.detail = detail
|
3721
|
+
el.dispatchEvent(event)
|
3722
|
+
}
|
3723
|
+
|
3724
|
+
|
3725
|
+
avalon.tick = function(fn) {
|
3726
|
+
if (ribbon.push(fn) === 1) {
|
3727
|
+
TimerID = setInterval(ticker, 60)
|
3728
|
+
}
|
3729
|
+
}
|
3730
|
+
|
3731
|
+
function ticker() {
|
3732
|
+
for (var n = ribbon.length - 1; n >= 0; n--) {
|
3733
|
+
var el = ribbon[n]
|
3734
|
+
if (el() === false) {
|
3735
|
+
ribbon.splice(n, 1)
|
3736
|
+
}
|
3737
|
+
}
|
3738
|
+
if (!ribbon.length) {
|
3739
|
+
clearInterval(TimerID)
|
3740
|
+
}
|
3741
|
+
}
|
3742
|
+
|
3743
|
+
var watchValueInTimer = noop
|
3744
|
+
new function() {
|
3745
|
+
try {//#272 IE9-IE11, firefox
|
3746
|
+
var setters = {}
|
3747
|
+
var aproto = HTMLInputElement.prototype
|
3748
|
+
var bproto = HTMLTextAreaElement.prototype
|
3749
|
+
function newSetter(value) {
|
3750
|
+
if (avalon.contains(root, this)) {
|
3751
|
+
setters[this.tagName].call(this, value)
|
3752
|
+
if (this.avalonSetter) {
|
3753
|
+
this.avalonSetter()
|
3754
|
+
}
|
3755
|
+
}
|
3756
|
+
}
|
3757
|
+
var inputProto = HTMLInputElement.prototype
|
3758
|
+
Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错
|
3759
|
+
setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set
|
3760
|
+
Object.defineProperty(aproto, "value", {
|
3761
|
+
set: newSetter
|
3762
|
+
})
|
3763
|
+
setters["TEXTAREA"] = Object.getOwnPropertyDescriptor(bproto, "value").set
|
3764
|
+
Object.defineProperty(bproto, "value", {
|
3765
|
+
set: newSetter
|
3766
|
+
})
|
3767
|
+
} catch (e) {
|
3768
|
+
watchValueInTimer = avalon.tick
|
3769
|
+
}
|
3770
|
+
}
|
3771
|
+
|
3772
|
+
if (IEVersion) {
|
3773
|
+
avalon.bind(DOC, "selectionchange", function(e) {
|
3774
|
+
var el = DOC.activeElement
|
3775
|
+
if (el && typeof el.avalonSetter === "function") {
|
3776
|
+
el.avalonSetter()
|
3777
|
+
}
|
3778
|
+
})
|
3779
|
+
}
|
3780
|
+
|
3781
|
+
//处理radio, checkbox, text, textarea, password
|
3782
|
+
duplexBinding.INPUT = function(element, evaluator, data) {
|
3783
|
+
var type = element.type,
|
3784
|
+
bound = data.bound,
|
3785
|
+
$elem = avalon(element),
|
3786
|
+
composing = false
|
3787
|
+
|
3788
|
+
function callback(value) {
|
3789
|
+
data.changed.call(this, value, data)
|
3790
|
+
}
|
3791
|
+
|
3792
|
+
function compositionStart() {
|
3793
|
+
composing = true
|
3794
|
+
}
|
3795
|
+
|
3796
|
+
function compositionEnd() {
|
3797
|
+
composing = false
|
3798
|
+
}
|
3799
|
+
//当value变化时改变model的值
|
3800
|
+
|
3801
|
+
function updateVModel() {
|
3802
|
+
if (composing) //处理中文输入法在minlengh下引发的BUG
|
3803
|
+
return
|
3804
|
+
var val = element.oldValue = element.value //防止递归调用形成死循环
|
3805
|
+
var lastValue = data.pipe(val, data, "get")
|
3806
|
+
if ($elem.data("duplex-observe") !== false) {
|
3807
|
+
evaluator(lastValue)
|
3808
|
+
callback.call(element, lastValue)
|
3809
|
+
if ($elem.data("duplex-focus")) {
|
3810
|
+
avalon.nextTick(function() {
|
3811
|
+
element.focus()
|
3812
|
+
})
|
3813
|
+
}
|
3814
|
+
}
|
3815
|
+
}
|
3816
|
+
//当model变化时,它就会改变value的值
|
3817
|
+
data.handler = function() {
|
3818
|
+
var val = data.pipe(evaluator(), data, "set") + ""//fix #673
|
3819
|
+
if (val !== element.oldValue) {
|
3820
|
+
element.value = val
|
3821
|
+
}
|
3822
|
+
}
|
3823
|
+
if (data.isChecked || element.type === "radio") {
|
3824
|
+
var IE6 = IEVersion === 6
|
3825
|
+
updateVModel = function() {
|
3826
|
+
if ($elem.data("duplex-observe") !== false) {
|
3827
|
+
var lastValue = data.pipe(element.value, data, "get")
|
3828
|
+
evaluator(lastValue)
|
3829
|
+
callback.call(element, lastValue)
|
3830
|
+
}
|
3831
|
+
}
|
3832
|
+
data.handler = function() {
|
3833
|
+
var val = evaluator()
|
3834
|
+
var checked = data.isChecked ? !!val : val + "" === element.value
|
3835
|
+
element.oldValue = checked
|
3836
|
+
if (IE6) {
|
3837
|
+
setTimeout(function() {
|
3838
|
+
//IE8 checkbox, radio是使用defaultChecked控制选中状态,
|
3839
|
+
//并且要先设置defaultChecked后设置checked
|
3840
|
+
//并且必须设置延迟
|
3841
|
+
element.defaultChecked = checked
|
3842
|
+
element.checked = checked
|
3843
|
+
}, 31)
|
3844
|
+
} else {
|
3845
|
+
element.checked = checked
|
3846
|
+
}
|
3847
|
+
}
|
3848
|
+
bound("click", updateVModel)
|
3849
|
+
} else if (type === "checkbox") {
|
3850
|
+
updateVModel = function() {
|
3851
|
+
if ($elem.data("duplex-observe") !== false) {
|
3852
|
+
var method = element.checked ? "ensure" : "remove"
|
3853
|
+
var array = evaluator()
|
3854
|
+
if (!Array.isArray(array)) {
|
3855
|
+
log("ms-duplex应用于checkbox上要对应一个数组")
|
3856
|
+
array = [array]
|
3857
|
+
}
|
3858
|
+
avalon.Array[method](array, data.pipe(element.value, data, "get"))
|
3859
|
+
callback.call(element, array)
|
3860
|
+
}
|
3861
|
+
}
|
3862
|
+
|
3863
|
+
data.handler = function() {
|
3864
|
+
var array = [].concat(evaluator()) //强制转换为数组
|
3865
|
+
element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1
|
3866
|
+
}
|
3867
|
+
bound(W3C ? "change" : "click", updateVModel)
|
3868
|
+
} else {
|
3869
|
+
var events = element.getAttribute("data-duplex-event") || element.getAttribute("data-event") || "input"
|
3870
|
+
if (element.attributes["data-event"]) {
|
3871
|
+
log("data-event指令已经废弃,请改用data-duplex-event")
|
3872
|
+
}
|
3873
|
+
function delay(e) {
|
3874
|
+
setTimeout(function() {
|
3875
|
+
updateVModel(e)
|
3876
|
+
})
|
3877
|
+
}
|
3878
|
+
events.replace(rword, function(name) {
|
3879
|
+
switch (name) {
|
3880
|
+
case "input":
|
3881
|
+
if (!IEVersion) { // W3C
|
3882
|
+
bound("input", updateVModel)
|
3883
|
+
//非IE浏览器才用这个
|
3884
|
+
bound("compositionstart", compositionStart)
|
3885
|
+
bound("compositionend", compositionEnd)
|
3886
|
+
bound("DOMAutoComplete", updateVModel)
|
3887
|
+
} else { //onpropertychange事件无法区分是程序触发还是用户触发
|
3888
|
+
// IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为
|
3889
|
+
if (IEVersion > 8) {
|
3890
|
+
bound("input", updateVModel)//IE9使用propertychange无法监听中文输入改动
|
3891
|
+
} else {
|
3892
|
+
bound("propertychange", function(e) {//IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正
|
3893
|
+
if (e.propertyName === "value") {
|
3894
|
+
updateVModel()
|
3895
|
+
}
|
3896
|
+
})
|
3897
|
+
}
|
3898
|
+
bound("dragend", delay)
|
3899
|
+
//http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html
|
3900
|
+
//http://www.matts411.com/post/internet-explorer-9-oninput/
|
3901
|
+
}
|
3902
|
+
break
|
3903
|
+
default:
|
3904
|
+
bound(name, updateVModel)
|
3905
|
+
break
|
3906
|
+
}
|
3907
|
+
})
|
3908
|
+
}
|
3909
|
+
if (/text|password/.test(element.type)) {
|
3910
|
+
watchValueInTimer(function() {
|
3911
|
+
if (root.contains(element)) {
|
3912
|
+
if (element.value !== element.oldValue) {
|
3913
|
+
updateVModel()
|
3914
|
+
}
|
3915
|
+
} else if (!element.msRetain) {
|
3916
|
+
return false
|
3917
|
+
}
|
3918
|
+
})
|
3919
|
+
}
|
3920
|
+
element.avalonSetter = updateVModel
|
3921
|
+
element.oldValue = element.value
|
3922
|
+
registerSubscriber(data)
|
3923
|
+
callback.call(element, element.value)
|
3924
|
+
}
|
3925
|
+
duplexBinding.TEXTAREA = duplexBinding.INPUT
|
3926
|
+
|
3927
|
+
|
3928
|
+
duplexBinding.SELECT = function(element, evaluator, data) {
|
3929
|
+
var $elem = avalon(element)
|
3930
|
+
function updateVModel() {
|
3931
|
+
if ($elem.data("duplex-observe") !== false) {
|
3932
|
+
var val = $elem.val() //字符串或字符串数组
|
3933
|
+
if (Array.isArray(val)) {
|
3934
|
+
val = val.map(function(v) {
|
3935
|
+
return data.pipe(v, data, "get")
|
3936
|
+
})
|
3937
|
+
} else {
|
3938
|
+
val = data.pipe(val, data, "get")
|
3939
|
+
}
|
3940
|
+
if (val + "" !== element.oldValue) {
|
3941
|
+
evaluator(val)
|
3942
|
+
}
|
3943
|
+
data.changed.call(element, val, data)
|
3944
|
+
}
|
3945
|
+
}
|
3946
|
+
data.handler = function() {
|
3947
|
+
var val = evaluator()
|
3948
|
+
val = val && val.$model || val
|
3949
|
+
if (Array.isArray(val)) {
|
3950
|
+
if (!element.multiple) {
|
3951
|
+
log("ms-duplex在<select multiple=true>上要求对应一个数组")
|
3952
|
+
}
|
3953
|
+
} else {
|
3954
|
+
if (element.multiple) {
|
3955
|
+
log("ms-duplex在<select multiple=false>不能对应一个数组")
|
3956
|
+
}
|
3957
|
+
}
|
3958
|
+
//必须变成字符串后才能比较
|
3959
|
+
val = Array.isArray(val) ? val.map(String) : val + ""
|
3960
|
+
if (val + "" !== element.oldValue) {
|
3961
|
+
$elem.val(val)
|
3962
|
+
element.oldValue = val + ""
|
3963
|
+
}
|
3964
|
+
}
|
3965
|
+
data.bound("change", updateVModel)
|
3966
|
+
checkScan(element, function() {
|
3967
|
+
registerSubscriber(data)
|
3968
|
+
data.changed.call(element, evaluator(), data)
|
3969
|
+
}, NaN)
|
3970
|
+
}
|
3971
|
+
|
3972
|
+
|
3973
|
+
bindingHandlers.repeat = function(data, vmodels) {
|
3974
|
+
var type = data.type
|
3975
|
+
parseExprProxy(data.value, vmodels, data, 0, 1)
|
3976
|
+
data.proxies = []
|
3977
|
+
var freturn = false
|
3978
|
+
try {
|
3979
|
+
var $repeat = data.$repeat = data.evaluator.apply(0, data.args || [])
|
3980
|
+
var xtype = avalon.type($repeat)
|
3981
|
+
if (xtype !== "object" && xtype !== "array") {
|
3982
|
+
freturn = true
|
3983
|
+
avalon.log("warning:" + data.value + "对应类型不正确")
|
3984
|
+
}
|
3985
|
+
} catch (e) {
|
3986
|
+
freturn = true
|
3987
|
+
avalon.log("warning:" + data.value + "编译出错")
|
3988
|
+
}
|
3989
|
+
|
3990
|
+
var arr = data.value.split(".") || []
|
3991
|
+
if (arr.length > 1) {
|
3992
|
+
arr.pop()
|
3993
|
+
var n = arr[0]
|
3994
|
+
for (var i = 0, v; v = vmodels[i++]; ) {
|
3995
|
+
if (v && v.hasOwnProperty(n)) {
|
3996
|
+
var events = v[n].$events || {}
|
3997
|
+
events[subscribers] = events[subscribers] || []
|
3998
|
+
events[subscribers].push(data)
|
3999
|
+
break
|
4000
|
+
}
|
4001
|
+
}
|
4002
|
+
}
|
4003
|
+
var elem = data.element
|
4004
|
+
elem.removeAttribute(data.name)
|
4005
|
+
|
4006
|
+
data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
|
4007
|
+
data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
|
4008
|
+
var signature = generateID(type)
|
4009
|
+
var comment = data.element = DOC.createComment(signature + ":end")
|
4010
|
+
data.clone = DOC.createComment(signature)
|
4011
|
+
hyperspace.appendChild(comment)
|
4012
|
+
|
4013
|
+
if (type === "each" || type === "with") {
|
4014
|
+
data.template = elem.innerHTML.trim()
|
4015
|
+
avalon.clearHTML(elem).appendChild(comment)
|
4016
|
+
} else {
|
4017
|
+
data.template = elem.outerHTML.trim()
|
4018
|
+
elem.parentNode.replaceChild(comment, elem)
|
4019
|
+
}
|
4020
|
+
data.template = avalon.parseHTML(data.template)
|
4021
|
+
data.rollback = function() {
|
4022
|
+
var elem = data.element
|
4023
|
+
if (!elem)
|
4024
|
+
return
|
4025
|
+
bindingExecutors.repeat.call(data, "clear")
|
4026
|
+
var parentNode = elem.parentNode
|
4027
|
+
var content = data.template
|
4028
|
+
var target = content.firstChild
|
4029
|
+
parentNode.replaceChild(content, elem)
|
4030
|
+
var start = data.$stamp
|
4031
|
+
start && start.parentNode && start.parentNode.removeChild(start)
|
4032
|
+
target = data.element = data.type === "repeat" ? target : parentNode
|
4033
|
+
}
|
4034
|
+
if (freturn) {
|
4035
|
+
return
|
4036
|
+
}
|
4037
|
+
data.handler = bindingExecutors.repeat
|
4038
|
+
data.$outer = {}
|
4039
|
+
var check0 = "$key"
|
4040
|
+
var check1 = "$val"
|
4041
|
+
if (Array.isArray($repeat)) {
|
4042
|
+
check0 = "$first"
|
4043
|
+
check1 = "$last"
|
4044
|
+
}
|
4045
|
+
for (var i = 0, p; p = vmodels[i++]; ) {
|
4046
|
+
if (p.hasOwnProperty(check0) && p.hasOwnProperty(check1)) {
|
4047
|
+
data.$outer = p
|
4048
|
+
break
|
4049
|
+
}
|
4050
|
+
}
|
4051
|
+
var $events = $repeat.$events
|
4052
|
+
var $list = ($events || {})[subscribers]
|
4053
|
+
if ($list && avalon.Array.ensure($list, data)) {
|
4054
|
+
addSubscribers(data, $list)
|
4055
|
+
}
|
4056
|
+
if (xtype === "object") {
|
4057
|
+
data.$with = true
|
4058
|
+
var pool = !$events ? {} : $events.$withProxyPool || ($events.$withProxyPool = {})
|
4059
|
+
data.handler("append", $repeat, pool)
|
4060
|
+
} else if ($repeat.length) {
|
4061
|
+
data.handler("add", 0, $repeat.length)
|
4062
|
+
}
|
4063
|
+
}
|
4064
|
+
|
4065
|
+
bindingExecutors.repeat = function(method, pos, el) {
|
4066
|
+
if (method) {
|
4067
|
+
var data = this
|
4068
|
+
var end = data.element
|
4069
|
+
var parent = end.parentNode
|
4070
|
+
var proxies = data.proxies
|
4071
|
+
var transation = hyperspace.cloneNode(false)
|
4072
|
+
switch (method) {
|
4073
|
+
case "add": //在pos位置后添加el数组(pos为数字,el为数组)
|
4074
|
+
var n = pos + el
|
4075
|
+
var array = data.$repeat
|
4076
|
+
var last = array.length - 1
|
4077
|
+
var fragments = []
|
4078
|
+
var start = locateNode(data, pos)
|
4079
|
+
for (var i = pos; i < n; i++) {
|
4080
|
+
var proxy = eachProxyAgent(i, data)
|
4081
|
+
proxies.splice(i, 0, proxy)
|
4082
|
+
shimController(data, transation, proxy, fragments)
|
4083
|
+
}
|
4084
|
+
parent.insertBefore(transation, start)
|
4085
|
+
for (var i = 0, fragment; fragment = fragments[i++]; ) {
|
4086
|
+
scanNodeArray(fragment.nodes, fragment.vmodels)
|
4087
|
+
fragment.nodes = fragment.vmodels = null
|
4088
|
+
}
|
4089
|
+
break
|
4090
|
+
case "del": //将pos后的el个元素删掉(pos, el都是数字)
|
4091
|
+
start = proxies[pos].$stamp
|
4092
|
+
end = locateNode(data, pos + el)
|
4093
|
+
sweepNodes(start, end)
|
4094
|
+
var removed = proxies.splice(pos, el)
|
4095
|
+
recycleProxies(removed, "each")
|
4096
|
+
break
|
4097
|
+
case "clear":
|
4098
|
+
var check = data.$stamp || proxies[0]
|
4099
|
+
if (check) {
|
4100
|
+
start = check.$stamp || check
|
4101
|
+
sweepNodes(start, end)
|
4102
|
+
}
|
4103
|
+
recycleProxies(proxies, "each")
|
4104
|
+
break
|
4105
|
+
case "move":
|
4106
|
+
start = proxies[0].$stamp
|
4107
|
+
var signature = start.nodeValue
|
4108
|
+
var rooms = []
|
4109
|
+
var room = [], node
|
4110
|
+
sweepNodes(start, end, function() {
|
4111
|
+
room.unshift(this)
|
4112
|
+
if (this.nodeValue === signature) {
|
4113
|
+
rooms.unshift(room)
|
4114
|
+
room = []
|
4115
|
+
}
|
4116
|
+
})
|
4117
|
+
sortByIndex(proxies, pos)
|
4118
|
+
sortByIndex(rooms, pos)
|
4119
|
+
while (room = rooms.shift()) {
|
4120
|
+
while (node = room.shift()) {
|
4121
|
+
transation.appendChild(node)
|
4122
|
+
}
|
4123
|
+
}
|
4124
|
+
parent.insertBefore(transation, end)
|
4125
|
+
break
|
4126
|
+
case "index": //将proxies中的第pos个起的所有元素重新索引
|
4127
|
+
var last = proxies.length - 1
|
4128
|
+
for (; el = proxies[pos]; pos++) {
|
4129
|
+
el.$index = pos
|
4130
|
+
el.$first = pos === 0
|
4131
|
+
el.$last = pos === last
|
4132
|
+
}
|
4133
|
+
return
|
4134
|
+
case "set": //将proxies中的第pos个元素的VM设置为el(pos为数字,el任意)
|
4135
|
+
var proxy = proxies[pos]
|
4136
|
+
if (proxy) {
|
4137
|
+
notifySubscribers(proxy.$events.$index)
|
4138
|
+
}
|
4139
|
+
return
|
4140
|
+
case "append": //将pos的键值对从el中取出(pos为一个普通对象,el为预先生成好的代理VM对象池)
|
4141
|
+
var pool = el
|
4142
|
+
var keys = []
|
4143
|
+
var fragments = []
|
4144
|
+
for (var key in pos) { //得到所有键名
|
4145
|
+
if (pos.hasOwnProperty(key) && key !== "hasOwnProperty") {
|
4146
|
+
keys.push(key)
|
4147
|
+
}
|
4148
|
+
}
|
4149
|
+
if (data.sortedCallback) { //如果有回调,则让它们排序
|
4150
|
+
var keys2 = data.sortedCallback.call(parent, keys)
|
4151
|
+
if (keys2 && Array.isArray(keys2) && keys2.length) {
|
4152
|
+
keys = keys2
|
4153
|
+
}
|
4154
|
+
}
|
4155
|
+
for (var i = 0, key; key = keys[i++]; ) {
|
4156
|
+
if (key !== "hasOwnProperty") {
|
4157
|
+
if (!pool[key]) {
|
4158
|
+
pool[key] = withProxyAgent(key, data)
|
4159
|
+
}
|
4160
|
+
shimController(data, transation, pool[key], fragments)
|
4161
|
+
}
|
4162
|
+
}
|
4163
|
+
var comment = data.$stamp = data.clone
|
4164
|
+
parent.insertBefore(comment, end)
|
4165
|
+
parent.insertBefore(transation, end)
|
4166
|
+
for (var i = 0, fragment; fragment = fragments[i++]; ) {
|
4167
|
+
scanNodeArray(fragment.nodes, fragment.vmodels)
|
4168
|
+
fragment.nodes = fragment.vmodels = null
|
4169
|
+
}
|
4170
|
+
break
|
4171
|
+
}
|
4172
|
+
if (method === "clear")
|
4173
|
+
method = "del"
|
4174
|
+
var callback = data.renderedCallback || noop,
|
4175
|
+
args = arguments
|
4176
|
+
checkScan(parent, function() {
|
4177
|
+
callback.apply(parent, args)
|
4178
|
+
if (parent.oldValue && parent.tagName === "SELECT") { //fix #503
|
4179
|
+
avalon(parent).val(parent.oldValue.split(","))
|
4180
|
+
}
|
4181
|
+
}, NaN)
|
4182
|
+
}
|
4183
|
+
}
|
4184
|
+
|
4185
|
+
"with,each".replace(rword, function(name) {
|
4186
|
+
bindingHandlers[name] = bindingHandlers.repeat
|
4187
|
+
})
|
4188
|
+
|
4189
|
+
function shimController(data, transation, proxy, fragments) {
|
4190
|
+
var content = data.template.cloneNode(true)
|
4191
|
+
var nodes = avalon.slice(content.childNodes)
|
4192
|
+
if (proxy.$stamp) {
|
4193
|
+
content.insertBefore(proxy.$stamp, content.firstChild)
|
4194
|
+
}
|
4195
|
+
transation.appendChild(content)
|
4196
|
+
var nv = [proxy].concat(data.vmodels)
|
4197
|
+
var fragment = {
|
4198
|
+
nodes: nodes,
|
4199
|
+
vmodels: nv
|
4200
|
+
}
|
4201
|
+
fragments.push(fragment)
|
4202
|
+
}
|
4203
|
+
|
4204
|
+
function locateNode(data, pos) {
|
4205
|
+
var proxy = data.proxies[pos]
|
4206
|
+
return proxy ? proxy.$stamp : data.element
|
4207
|
+
}
|
4208
|
+
|
4209
|
+
function sweepNodes(start, end, callback) {
|
4210
|
+
while (true) {
|
4211
|
+
var node = end.previousSibling
|
4212
|
+
if (!node)
|
4213
|
+
break
|
4214
|
+
node.parentNode.removeChild(node)
|
4215
|
+
callback && callback.call(node)
|
4216
|
+
if (node === start) {
|
4217
|
+
break
|
4218
|
+
}
|
4219
|
+
}
|
4220
|
+
}
|
4221
|
+
|
4222
|
+
// 为ms-each,ms-with, ms-repeat会创建一个代理VM,
|
4223
|
+
// 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法
|
4224
|
+
// 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现
|
4225
|
+
var eachProxyPool = []
|
4226
|
+
var withProxyPool = []
|
4227
|
+
function eachProxyFactory(name) {
|
4228
|
+
var source = {
|
4229
|
+
$host: [],
|
4230
|
+
$outer: {},
|
4231
|
+
$stamp: 1,
|
4232
|
+
$index: 0,
|
4233
|
+
$first: false,
|
4234
|
+
$last: false,
|
4235
|
+
$remove: avalon.noop
|
4236
|
+
}
|
4237
|
+
source[name] = {
|
4238
|
+
get: function() {
|
4239
|
+
return this.$host[this.$index]
|
4240
|
+
},
|
4241
|
+
set: function(val) {
|
4242
|
+
this.$host.set(this.$index, val)
|
4243
|
+
}
|
4244
|
+
}
|
4245
|
+
var second = {
|
4246
|
+
$last: 1,
|
4247
|
+
$first: 1,
|
4248
|
+
$index: 1
|
4249
|
+
}
|
4250
|
+
var proxy = modelFactory(source, second)
|
4251
|
+
var e = proxy.$events
|
4252
|
+
e[name] = e.$first = e.$last = e.$index
|
4253
|
+
proxy.$id = generateID("$proxy$each")
|
4254
|
+
return proxy
|
4255
|
+
}
|
4256
|
+
|
4257
|
+
function eachProxyAgent(index, data) {
|
4258
|
+
var param = data.param || "el", proxy
|
4259
|
+
for (var i = 0, n = eachProxyPool.length; i < n; i++) {
|
4260
|
+
var candidate = eachProxyPool[i]
|
4261
|
+
if (candidate && candidate.hasOwnProperty(param)) {
|
4262
|
+
proxy = candidate
|
4263
|
+
eachProxyPool.splice(i, 1)
|
4264
|
+
}
|
4265
|
+
}
|
4266
|
+
if (!proxy) {
|
4267
|
+
proxy = eachProxyFactory(param)
|
4268
|
+
}
|
4269
|
+
var host = data.$repeat
|
4270
|
+
var last = host.length - 1
|
4271
|
+
proxy.$index = index
|
4272
|
+
proxy.$first = index === 0
|
4273
|
+
proxy.$last = index === last
|
4274
|
+
proxy.$host = host
|
4275
|
+
proxy.$outer = data.$outer
|
4276
|
+
proxy.$stamp = data.clone.cloneNode(false)
|
4277
|
+
proxy.$remove = function() {
|
4278
|
+
return host.removeAt(proxy.$index)
|
4279
|
+
}
|
4280
|
+
return proxy
|
4281
|
+
}
|
4282
|
+
|
4283
|
+
function withProxyFactory() {
|
4284
|
+
var proxy = modelFactory({
|
4285
|
+
$key: "",
|
4286
|
+
$outer: {},
|
4287
|
+
$host: {},
|
4288
|
+
$val: {
|
4289
|
+
get: function() {
|
4290
|
+
return this.$host[this.$key]
|
4291
|
+
},
|
4292
|
+
set: function(val) {
|
4293
|
+
this.$host[this.$key] = val
|
4294
|
+
}
|
4295
|
+
}
|
4296
|
+
}, {
|
4297
|
+
$val: 1
|
4298
|
+
})
|
4299
|
+
proxy.$id = generateID("$proxy$with")
|
4300
|
+
return proxy
|
4301
|
+
}
|
4302
|
+
|
4303
|
+
function withProxyAgent(key, data) {
|
4304
|
+
var proxy = withProxyPool.pop()
|
4305
|
+
if (!proxy) {
|
4306
|
+
proxy = withProxyFactory()
|
4307
|
+
}
|
4308
|
+
var host = data.$repeat
|
4309
|
+
proxy.$key = key
|
4310
|
+
proxy.$host = host
|
4311
|
+
proxy.$outer = data.$outer
|
4312
|
+
if (host.$events) {
|
4313
|
+
proxy.$events.$val = host.$events[key]
|
4314
|
+
} else {
|
4315
|
+
proxy.$events = {}
|
4316
|
+
}
|
4317
|
+
return proxy
|
4318
|
+
}
|
4319
|
+
|
4320
|
+
function recycleProxies(proxies, type) {
|
4321
|
+
var proxyPool = type === "each" ? eachProxyPool : withProxyPool
|
4322
|
+
avalon.each(proxies, function(key, proxy) {
|
4323
|
+
if (proxy.$events) {
|
4324
|
+
for (var i in proxy.$events) {
|
4325
|
+
if (Array.isArray(proxy.$events[i])) {
|
4326
|
+
proxy.$events[i].forEach(function(data) {
|
4327
|
+
if (typeof data === "object")
|
4328
|
+
disposeData(data)
|
4329
|
+
})
|
4330
|
+
proxy.$events[i].length = 0
|
4331
|
+
}
|
4332
|
+
}
|
4333
|
+
proxy.$host = proxy.$outer = {}
|
4334
|
+
if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) {
|
4335
|
+
proxyPool.pop()
|
4336
|
+
}
|
4337
|
+
}
|
4338
|
+
})
|
4339
|
+
if (type === "each")
|
4340
|
+
proxies.length = 0
|
4341
|
+
}
|
4342
|
+
|
4343
|
+
|
4344
|
+
|
4345
|
+
|
4346
|
+
/*********************************************************************
|
4347
|
+
* 自带过滤器 *
|
4348
|
+
**********************************************************************/
|
4349
|
+
var rscripts = /<script[^>]*>([\S\s]*?)<\/script\s*>/gim
|
4350
|
+
var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g
|
4351
|
+
var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig
|
4352
|
+
var rsanitize = {
|
4353
|
+
a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig,
|
4354
|
+
img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig,
|
4355
|
+
form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig
|
4356
|
+
}
|
4357
|
+
var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
|
4358
|
+
var rnoalphanumeric = /([^\#-~| |!])/g;
|
4359
|
+
|
4360
|
+
function numberFormat(number, decimals, dec_point, thousands_sep) {
|
4361
|
+
//form http://phpjs.org/functions/number_format/
|
4362
|
+
//number 必需,要格式化的数字
|
4363
|
+
//decimals 可选,规定多少个小数位。
|
4364
|
+
//dec_point 可选,规定用作小数点的字符串(默认为 . )。
|
4365
|
+
//thousands_sep 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
|
4366
|
+
number = (number + '')
|
4367
|
+
.replace(/[^0-9+\-Ee.]/g, '')
|
4368
|
+
var n = !isFinite(+number) ? 0 : +number,
|
4369
|
+
prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
|
4370
|
+
sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
|
4371
|
+
dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
|
4372
|
+
s = '',
|
4373
|
+
toFixedFix = function(n, prec) {
|
4374
|
+
var k = Math.pow(10, prec)
|
4375
|
+
return '' + (Math.round(n * k) / k)
|
4376
|
+
.toFixed(prec)
|
4377
|
+
}
|
4378
|
+
// Fix for IE parseFloat(0.55).toFixed(0) = 0;
|
4379
|
+
s = (prec ? toFixedFix(n, prec) : '' + Math.round(n))
|
4380
|
+
.split('.')
|
4381
|
+
if (s[0].length > 3) {
|
4382
|
+
s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep)
|
4383
|
+
}
|
4384
|
+
if ((s[1] || '')
|
4385
|
+
.length < prec) {
|
4386
|
+
s[1] = s[1] || ''
|
4387
|
+
s[1] += new Array(prec - s[1].length + 1)
|
4388
|
+
.join('0')
|
4389
|
+
}
|
4390
|
+
return s.join(dec)
|
4391
|
+
}
|
4392
|
+
|
4393
|
+
|
4394
|
+
var filters = avalon.filters = {
|
4395
|
+
uppercase: function(str) {
|
4396
|
+
return str.toUpperCase()
|
4397
|
+
},
|
4398
|
+
lowercase: function(str) {
|
4399
|
+
return str.toLowerCase()
|
4400
|
+
},
|
4401
|
+
truncate: function(str, length, truncation) {
|
4402
|
+
//length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串
|
4403
|
+
length = length || 30
|
4404
|
+
truncation = truncation === void(0) ? "..." : truncation
|
4405
|
+
return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str)
|
4406
|
+
},
|
4407
|
+
$filter: function(val) {
|
4408
|
+
for (var i = 1, n = arguments.length; i < n; i++) {
|
4409
|
+
var array = arguments[i]
|
4410
|
+
var fn = avalon.filters[array.shift()]
|
4411
|
+
if (typeof fn === "function") {
|
4412
|
+
var arr = [val].concat(array)
|
4413
|
+
val = fn.apply(null, arr)
|
4414
|
+
}
|
4415
|
+
}
|
4416
|
+
return val
|
4417
|
+
},
|
4418
|
+
camelize: camelize,
|
4419
|
+
//https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
|
4420
|
+
// <a href="javasc
ript:alert('XSS')">chrome</a>
|
4421
|
+
// <a href="data:text/html;base64, PGltZyBzcmM9eCBvbmVycm9yPWFsZXJ0KDEpPg==">chrome</a>
|
4422
|
+
// <a href="jav ascript:alert('XSS');">IE67chrome</a>
|
4423
|
+
// <a href="jav	ascript:alert('XSS');">IE67chrome</a>
|
4424
|
+
// <a href="jav
ascript:alert('XSS');">IE67chrome</a>
|
4425
|
+
sanitize: function(str) {
|
4426
|
+
return str.replace(rscripts, "").replace(ropen, function(a, b) {
|
4427
|
+
var match = a.toLowerCase().match(/<(\w+)\s/)
|
4428
|
+
if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性
|
4429
|
+
var reg = rsanitize[match[1]]
|
4430
|
+
if (reg) {
|
4431
|
+
a = a.replace(reg, function(s, name, value) {
|
4432
|
+
var quote = value.charAt(0)
|
4433
|
+
return name + "=" + quote + "javascript:void(0)" + quote
|
4434
|
+
})
|
4435
|
+
}
|
4436
|
+
}
|
4437
|
+
return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件
|
4438
|
+
})
|
4439
|
+
},
|
4440
|
+
escape: function(str) {
|
4441
|
+
//将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 <
|
4442
|
+
return String(str).
|
4443
|
+
replace(/&/g, '&').
|
4444
|
+
replace(rsurrogate, function(value) {
|
4445
|
+
var hi = value.charCodeAt(0)
|
4446
|
+
var low = value.charCodeAt(1)
|
4447
|
+
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'
|
4448
|
+
}).
|
4449
|
+
replace(rnoalphanumeric, function(value) {
|
4450
|
+
return '&#' + value.charCodeAt(0) + ';'
|
4451
|
+
}).
|
4452
|
+
replace(/</g, '<').
|
4453
|
+
replace(/>/g, '>')
|
4454
|
+
},
|
4455
|
+
currency: function(amount, symbol, fractionSize) {
|
4456
|
+
return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2)
|
4457
|
+
},
|
4458
|
+
number: function(number, fractionSize) {
|
4459
|
+
return numberFormat(number, isFinite(fractionSize) ? fractionSize : 3)
|
4460
|
+
}
|
4461
|
+
}
|
4462
|
+
/*
|
4463
|
+
'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
|
4464
|
+
'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10)
|
4465
|
+
'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199)
|
4466
|
+
'MMMM': Month in year (January-December)
|
4467
|
+
'MMM': Month in year (Jan-Dec)
|
4468
|
+
'MM': Month in year, padded (01-12)
|
4469
|
+
'M': Month in year (1-12)
|
4470
|
+
'dd': Day in month, padded (01-31)
|
4471
|
+
'd': Day in month (1-31)
|
4472
|
+
'EEEE': Day in Week,(Sunday-Saturday)
|
4473
|
+
'EEE': Day in Week, (Sun-Sat)
|
4474
|
+
'HH': Hour in day, padded (00-23)
|
4475
|
+
'H': Hour in day (0-23)
|
4476
|
+
'hh': Hour in am/pm, padded (01-12)
|
4477
|
+
'h': Hour in am/pm, (1-12)
|
4478
|
+
'mm': Minute in hour, padded (00-59)
|
4479
|
+
'm': Minute in hour (0-59)
|
4480
|
+
'ss': Second in minute, padded (00-59)
|
4481
|
+
's': Second in minute (0-59)
|
4482
|
+
'a': am/pm marker
|
4483
|
+
'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200)
|
4484
|
+
format string can also be one of the following predefined localizable formats:
|
4485
|
+
|
4486
|
+
'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm)
|
4487
|
+
'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm)
|
4488
|
+
'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010)
|
4489
|
+
'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010
|
4490
|
+
'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010)
|
4491
|
+
'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10)
|
4492
|
+
'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm)
|
4493
|
+
'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm)
|
4494
|
+
*/
|
4495
|
+
new function() {
|
4496
|
+
function toInt(str) {
|
4497
|
+
return parseInt(str, 10) || 0
|
4498
|
+
}
|
4499
|
+
|
4500
|
+
function padNumber(num, digits, trim) {
|
4501
|
+
var neg = ""
|
4502
|
+
if (num < 0) {
|
4503
|
+
neg = '-'
|
4504
|
+
num = -num
|
4505
|
+
}
|
4506
|
+
num = "" + num
|
4507
|
+
while (num.length < digits)
|
4508
|
+
num = "0" + num
|
4509
|
+
if (trim)
|
4510
|
+
num = num.substr(num.length - digits)
|
4511
|
+
return neg + num
|
4512
|
+
}
|
4513
|
+
|
4514
|
+
function dateGetter(name, size, offset, trim) {
|
4515
|
+
return function(date) {
|
4516
|
+
var value = date["get" + name]()
|
4517
|
+
if (offset > 0 || value > -offset)
|
4518
|
+
value += offset
|
4519
|
+
if (value === 0 && offset === -12) {
|
4520
|
+
value = 12
|
4521
|
+
}
|
4522
|
+
return padNumber(value, size, trim)
|
4523
|
+
}
|
4524
|
+
}
|
4525
|
+
|
4526
|
+
function dateStrGetter(name, shortForm) {
|
4527
|
+
return function(date, formats) {
|
4528
|
+
var value = date["get" + name]()
|
4529
|
+
var get = (shortForm ? ("SHORT" + name) : name).toUpperCase()
|
4530
|
+
return formats[get][value]
|
4531
|
+
}
|
4532
|
+
}
|
4533
|
+
|
4534
|
+
function timeZoneGetter(date) {
|
4535
|
+
var zone = -1 * date.getTimezoneOffset()
|
4536
|
+
var paddedZone = (zone >= 0) ? "+" : ""
|
4537
|
+
paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2)
|
4538
|
+
return paddedZone
|
4539
|
+
}
|
4540
|
+
//取得上午下午
|
4541
|
+
|
4542
|
+
function ampmGetter(date, formats) {
|
4543
|
+
return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1]
|
4544
|
+
}
|
4545
|
+
var DATE_FORMATS = {
|
4546
|
+
yyyy: dateGetter("FullYear", 4),
|
4547
|
+
yy: dateGetter("FullYear", 2, 0, true),
|
4548
|
+
y: dateGetter("FullYear", 1),
|
4549
|
+
MMMM: dateStrGetter("Month"),
|
4550
|
+
MMM: dateStrGetter("Month", true),
|
4551
|
+
MM: dateGetter("Month", 2, 1),
|
4552
|
+
M: dateGetter("Month", 1, 1),
|
4553
|
+
dd: dateGetter("Date", 2),
|
4554
|
+
d: dateGetter("Date", 1),
|
4555
|
+
HH: dateGetter("Hours", 2),
|
4556
|
+
H: dateGetter("Hours", 1),
|
4557
|
+
hh: dateGetter("Hours", 2, -12),
|
4558
|
+
h: dateGetter("Hours", 1, -12),
|
4559
|
+
mm: dateGetter("Minutes", 2),
|
4560
|
+
m: dateGetter("Minutes", 1),
|
4561
|
+
ss: dateGetter("Seconds", 2),
|
4562
|
+
s: dateGetter("Seconds", 1),
|
4563
|
+
sss: dateGetter("Milliseconds", 3),
|
4564
|
+
EEEE: dateStrGetter("Day"),
|
4565
|
+
EEE: dateStrGetter("Day", true),
|
4566
|
+
a: ampmGetter,
|
4567
|
+
Z: timeZoneGetter
|
4568
|
+
}
|
4569
|
+
var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/
|
4570
|
+
filters.date = function(date, format) {
|
4571
|
+
var locate = filters.date.locate,
|
4572
|
+
text = "",
|
4573
|
+
parts = [],
|
4574
|
+
fn, match
|
4575
|
+
format = format || "mediumDate"
|
4576
|
+
format = locate[format] || format
|
4577
|
+
if (typeof date === "string") {
|
4578
|
+
if (/^\d+$/.test(date)) {
|
4579
|
+
date = toInt(date)
|
4580
|
+
} else {
|
4581
|
+
var trimDate = date.trim()
|
4582
|
+
var dateArray = [0, 0, 0, 0, 0, 0, 0]
|
4583
|
+
var oDate = new Date(0)
|
4584
|
+
//取得年月日
|
4585
|
+
trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) {
|
4586
|
+
var array = c.length === 4 ? [c, a, b] : [a, b, c]
|
4587
|
+
dateArray[0] = toInt(array[0]) //年
|
4588
|
+
dateArray[1] = toInt(array[1]) - 1 //月
|
4589
|
+
dateArray[2] = toInt(array[2])//日
|
4590
|
+
return ""
|
4591
|
+
})
|
4592
|
+
var dateSetter = oDate.setFullYear
|
4593
|
+
var timeSetter = oDate.setHours
|
4594
|
+
trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) {
|
4595
|
+
dateArray[3] = toInt(a) //小时
|
4596
|
+
dateArray[4] = toInt(b) //分钟
|
4597
|
+
dateArray[5] = toInt(c) //秒
|
4598
|
+
if (d) {
|
4599
|
+
dateArray[6] = Math.round(parseFloat("0." + d) * 1000) //毫秒
|
4600
|
+
}
|
4601
|
+
dateArray[6] = d || ""
|
4602
|
+
return ""
|
4603
|
+
})
|
4604
|
+
var tzHour = 0
|
4605
|
+
var tzMin = 0
|
4606
|
+
trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) {
|
4607
|
+
dateSetter = oDate.setUTCFullYear
|
4608
|
+
timeSetter = oDate.setUTCHours
|
4609
|
+
if (symbol) {
|
4610
|
+
tzHour = toInt(symbol + c)
|
4611
|
+
tzMin = toInt(symbol + d)
|
4612
|
+
}
|
4613
|
+
return ""
|
4614
|
+
})
|
4615
|
+
|
4616
|
+
dateArray[3] -= tzHour
|
4617
|
+
dateArray[4] -= tzMin
|
4618
|
+
dateSetter.apply(oDate, dateArray.slice(0, 3))
|
4619
|
+
timeSetter.apply(oDate, dateArray.slice(3))
|
4620
|
+
date = oDate
|
4621
|
+
}
|
4622
|
+
}
|
4623
|
+
if (typeof date === "number") {
|
4624
|
+
date = new Date(date)
|
4625
|
+
}
|
4626
|
+
if (avalon.type(date) !== "date") {
|
4627
|
+
return
|
4628
|
+
}
|
4629
|
+
while (format) {
|
4630
|
+
match = rdateFormat.exec(format)
|
4631
|
+
if (match) {
|
4632
|
+
parts = parts.concat(match.slice(1))
|
4633
|
+
format = parts.pop()
|
4634
|
+
} else {
|
4635
|
+
parts.push(format)
|
4636
|
+
format = null
|
4637
|
+
}
|
4638
|
+
}
|
4639
|
+
parts.forEach(function(value) {
|
4640
|
+
fn = DATE_FORMATS[value]
|
4641
|
+
text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'")
|
4642
|
+
})
|
4643
|
+
return text
|
4644
|
+
}
|
4645
|
+
var locate = {
|
4646
|
+
AMPMS: {
|
4647
|
+
0: "上午",
|
4648
|
+
1: "下午"
|
4649
|
+
},
|
4650
|
+
DAY: {
|
4651
|
+
0: "星期日",
|
4652
|
+
1: "星期一",
|
4653
|
+
2: "星期二",
|
4654
|
+
3: "星期三",
|
4655
|
+
4: "星期四",
|
4656
|
+
5: "星期五",
|
4657
|
+
6: "星期六"
|
4658
|
+
},
|
4659
|
+
MONTH: {
|
4660
|
+
0: "1月",
|
4661
|
+
1: "2月",
|
4662
|
+
2: "3月",
|
4663
|
+
3: "4月",
|
4664
|
+
4: "5月",
|
4665
|
+
5: "6月",
|
4666
|
+
6: "7月",
|
4667
|
+
7: "8月",
|
4668
|
+
8: "9月",
|
4669
|
+
9: "10月",
|
4670
|
+
10: "11月",
|
4671
|
+
11: "12月"
|
4672
|
+
},
|
4673
|
+
SHORTDAY: {
|
4674
|
+
"0": "周日",
|
4675
|
+
"1": "周一",
|
4676
|
+
"2": "周二",
|
4677
|
+
"3": "周三",
|
4678
|
+
"4": "周四",
|
4679
|
+
"5": "周五",
|
4680
|
+
"6": "周六"
|
4681
|
+
},
|
4682
|
+
fullDate: "y年M月d日EEEE",
|
4683
|
+
longDate: "y年M月d日",
|
4684
|
+
medium: "yyyy-M-d H:mm:ss",
|
4685
|
+
mediumDate: "yyyy-M-d",
|
4686
|
+
mediumTime: "H:mm:ss",
|
4687
|
+
"short": "yy-M-d ah:mm",
|
4688
|
+
shortDate: "yy-M-d",
|
4689
|
+
shortTime: "ah:mm"
|
4690
|
+
}
|
4691
|
+
locate.SHORTMONTH = locate.MONTH
|
4692
|
+
filters.date.locate = locate
|
4693
|
+
}
|
4694
|
+
/*********************************************************************
|
4695
|
+
* END *
|
4696
|
+
**********************************************************************/
|
4697
|
+
new function() {
|
4698
|
+
avalon.config({
|
4699
|
+
loader: false
|
4700
|
+
})
|
4701
|
+
var fns = [], fn, loaded
|
4702
|
+
function flush(f) {
|
4703
|
+
loaded = 1
|
4704
|
+
while (f = fns.shift())
|
4705
|
+
f()
|
4706
|
+
}
|
4707
|
+
if (W3C) {
|
4708
|
+
avalon.bind(DOC, "DOMContentLoaded", fn = function() {
|
4709
|
+
avalon.unbind(DOC, "DOMContentLoaded", fn)
|
4710
|
+
flush()
|
4711
|
+
})
|
4712
|
+
} else {
|
4713
|
+
var id = setInterval(function() {
|
4714
|
+
if (document.readyState === "complete" && document.body) {
|
4715
|
+
clearInterval(id)
|
4716
|
+
flush()
|
4717
|
+
}
|
4718
|
+
}, 50)
|
4719
|
+
}
|
4720
|
+
avalon.ready = function(fn) {
|
4721
|
+
loaded ? fn() : fns.push(fn)
|
4722
|
+
}
|
4723
|
+
avalon.ready(function() {
|
4724
|
+
avalon.scan(DOC.body)
|
4725
|
+
})
|
4726
|
+
}
|
4727
|
+
|
4728
|
+
|
4729
|
+
// Register as a named AMD module, since avalon can be concatenated with other
|
4730
|
+
// files that may use define, but not via a proper concatenation script that
|
4731
|
+
// understands anonymous AMD modules. A named AMD is safest and most robust
|
4732
|
+
// way to register. Lowercase avalon is used because AMD module names are
|
4733
|
+
// derived from file names, and Avalon is normally delivered in a lowercase
|
4734
|
+
// file name. Do this after creating the global so that if an AMD module wants
|
4735
|
+
// to call noConflict to hide this version of avalon, it will work.
|
4736
|
+
|
4737
|
+
// Note that for maximum portability, libraries that are not avalon should
|
4738
|
+
// declare themselves as anonymous modules, and avoid setting a global if an
|
4739
|
+
// AMD loader is present. avalon is a special case. For more information, see
|
4740
|
+
// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
|
4741
|
+
if (typeof define === "function" && define.amd) {
|
4742
|
+
define("avalon", [], function() {
|
4743
|
+
return avalon
|
4744
|
+
})
|
4745
|
+
}
|
4746
|
+
// Map over avalon in case of overwrite
|
4747
|
+
var _avalon = window.avalon
|
4748
|
+
avalon.noConflict = function(deep) {
|
4749
|
+
if (deep && window.avalon === avalon) {
|
4750
|
+
window.avalon = avalon
|
4751
|
+
}
|
4752
|
+
return avalon
|
4753
|
+
}
|
4754
|
+
// Expose avalon and $ identifiers, even in AMD
|
4755
|
+
// and CommonJS for browser emulators
|
4756
|
+
if (noGlobal === void 0) {
|
4757
|
+
window.avalon = avalon
|
4758
|
+
}
|
4759
|
+
return avalon
|
4760
|
+
|
4761
|
+
}));
|