avalon-rails 0.0.3 → 1.3.9.1

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