avalon-rails 0.0.2

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