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