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