From 206809d06825ad3b1f665c82b198dd9ef6837354 Mon Sep 17 00:00:00 2001 From: RubyLouvre Date: Thu, 25 Aug 2016 23:23:27 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E4=B8=8A=E8=A6=86=E7=9B=96?= =?UTF-8?q?=E7=8E=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dist/avalon.js | 12 +- dist/avalon.modern.js | 12 +- dist/avalon.next.js | 12 +- dist/avalon.test.js | 12449 +++++++++++++++++++--------------------- karma.conf.js | 21 +- karma/seed/browser.js | 43 + karma/seed/cache.js | 24 + karma/seed/config.js | 14 + karma/seed/core.js | 80 + karma/seed/lang.js | 175 + package.json | 3 +- src/avalon.test.js | 1 + src/seed/__test__.js | 366 -- src/seed/browser.js | 7 +- src/seed/config.js | 3 + 15 files changed, 6215 insertions(+), 7007 deletions(-) create mode 100644 karma/seed/browser.js create mode 100644 karma/seed/cache.js create mode 100644 karma/seed/config.js create mode 100644 karma/seed/core.js create mode 100644 karma/seed/lang.js delete mode 100644 src/seed/__test__.js diff --git a/dist/avalon.js b/dist/avalon.js index e13f48baa..4d67a5f7f 100644 --- a/dist/avalon.js +++ b/dist/avalon.js @@ -1,5 +1,5 @@ /*! - * built in 2016-8-25:19 version 2.113 by 司徒正美 + * built in 2016-8-25:23 version 2.113 by 司徒正美 * 2.1.5 and npm 2.1.15 * 修正 ms-controller, ms-important的移除类名的实现 * 实现后端渲染, @@ -314,22 +314,25 @@ return /******/ (function(modules) { // webpackBootstrap outerHTML: 'x' }, msie: NaN, + browser: false, modern: true, avalonDiv: {}, avalonFragment: null } window.avalon = avalon - + /* istanbul ignore if */ if (window.location && window.navigator && window.window) { var doc = window.document browser.browser = true browser.document = doc - browser.modern = window.dispatchEvent browser.root = doc.documentElement browser.avalonDiv = doc.createElement('div') browser.avalonFragment = doc.createDocumentFragment() if (window.VBArray) { browser.msie = doc.documentMode || (window.XMLHttpRequest ? 7 : 6) + browser.modern = browser.msie > 8 + } else { + browser.modern = true } } @@ -832,6 +835,7 @@ return /******/ (function(modules) { // webpackBootstrap var avalon = __webpack_require__(4) function kernel(settings) { for (var p in settings) { + /* istanbul ignore if */ if (!avalon.ohasOwn.call(settings, p)) continue var val = settings[p] @@ -853,12 +857,14 @@ return /******/ (function(modules) { // webpackBootstrap var openTag = array[0] var closeTag = array[1] /*eslint-disable */ + /* istanbul ignore if */ if (openTag === closeTag) { throw new SyntaxError('openTag!==closeTag') } var test = openTag + 'test' + closeTag var div = avalon.avalonDiv div.innerHTML = test + /* istanbul ignore if */ if (div.innerHTML !== test && div.innerHTML.indexOf('<') > -1) { throw new SyntaxError('此定界符不合法') } diff --git a/dist/avalon.modern.js b/dist/avalon.modern.js index ef5a74646..7860bf891 100644 --- a/dist/avalon.modern.js +++ b/dist/avalon.modern.js @@ -1,5 +1,5 @@ /*! - * built in 2016-8-25:19 version 2.113 by 司徒正美 + * built in 2016-8-25:23 version 2.113 by 司徒正美 * 2.1.5 and npm 2.1.15 * 修正 ms-controller, ms-important的移除类名的实现 * 实现后端渲染, @@ -99,22 +99,25 @@ return /******/ (function(modules) { // webpackBootstrap outerHTML: 'x' }, msie: NaN, + browser: false, modern: true, avalonDiv: {}, avalonFragment: null } window.avalon = avalon - + /* istanbul ignore if */ if (window.location && window.navigator && window.window) { var doc = window.document browser.browser = true browser.document = doc - browser.modern = window.dispatchEvent browser.root = doc.documentElement browser.avalonDiv = doc.createElement('div') browser.avalonFragment = doc.createDocumentFragment() if (window.VBArray) { browser.msie = doc.documentMode || (window.XMLHttpRequest ? 7 : 6) + browser.modern = browser.msie > 8 + } else { + browser.modern = true } } @@ -372,6 +375,7 @@ return /******/ (function(modules) { // webpackBootstrap var avalon = __webpack_require__(4) function kernel(settings) { for (var p in settings) { + /* istanbul ignore if */ if (!avalon.ohasOwn.call(settings, p)) continue var val = settings[p] @@ -393,12 +397,14 @@ return /******/ (function(modules) { // webpackBootstrap var openTag = array[0] var closeTag = array[1] /*eslint-disable */ + /* istanbul ignore if */ if (openTag === closeTag) { throw new SyntaxError('openTag!==closeTag') } var test = openTag + 'test' + closeTag var div = avalon.avalonDiv div.innerHTML = test + /* istanbul ignore if */ if (div.innerHTML !== test && div.innerHTML.indexOf('<') > -1) { throw new SyntaxError('此定界符不合法') } diff --git a/dist/avalon.next.js b/dist/avalon.next.js index 19a9abb98..aeac404e4 100644 --- a/dist/avalon.next.js +++ b/dist/avalon.next.js @@ -1,5 +1,5 @@ /*! - * built in 2016-8-25:19 version 2.113 by 司徒正美 + * built in 2016-8-25:23 version 2.113 by 司徒正美 * 2.1.5 and npm 2.1.15 * 修正 ms-controller, ms-important的移除类名的实现 * 实现后端渲染, @@ -100,22 +100,25 @@ return /******/ (function(modules) { // webpackBootstrap outerHTML: 'x' }, msie: NaN, + browser: false, modern: true, avalonDiv: {}, avalonFragment: null } window.avalon = avalon - + /* istanbul ignore if */ if (window.location && window.navigator && window.window) { var doc = window.document browser.browser = true browser.document = doc - browser.modern = window.dispatchEvent browser.root = doc.documentElement browser.avalonDiv = doc.createElement('div') browser.avalonFragment = doc.createDocumentFragment() if (window.VBArray) { browser.msie = doc.documentMode || (window.XMLHttpRequest ? 7 : 6) + browser.modern = browser.msie > 8 + } else { + browser.modern = true } } @@ -373,6 +376,7 @@ return /******/ (function(modules) { // webpackBootstrap var avalon = __webpack_require__(4) function kernel(settings) { for (var p in settings) { + /* istanbul ignore if */ if (!avalon.ohasOwn.call(settings, p)) continue var val = settings[p] @@ -394,12 +398,14 @@ return /******/ (function(modules) { // webpackBootstrap var openTag = array[0] var closeTag = array[1] /*eslint-disable */ + /* istanbul ignore if */ if (openTag === closeTag) { throw new SyntaxError('openTag!==closeTag') } var test = openTag + 'test' + closeTag var div = avalon.avalonDiv div.innerHTML = test + /* istanbul ignore if */ if (div.innerHTML !== test && div.innerHTML.indexOf('<') > -1) { throw new SyntaxError('此定界符不合法') } diff --git a/dist/avalon.test.js b/dist/avalon.test.js index f338f1ea4..0db1bce37 100644 --- a/dist/avalon.test.js +++ b/dist/avalon.test.js @@ -1,5 +1,5 @@ /*! - * built in 2016-8-25:19 version 2.113 by 司徒正美 + * built in 2016-8-25:23 version 2.113 by 司徒正美 * 2.1.5 and npm 2.1.15 * 修正 ms-controller, ms-important的移除类名的实现 * 实现后端渲染, @@ -66,6 +66,7 @@ return /******/ (function(modules) { // webpackBootstrap var avalon = __webpack_require__(108) + avalon.cache = __webpack_require__(30) __webpack_require__(109) __webpack_require__(110) @@ -74,13 +75,129 @@ return /******/ (function(modules) { // webpackBootstrap +/***/ }, + +/***/ 30: +/***/ function(module, exports) { + + + /* + https://github.com/rsms/js-lru + entry entry entry entry + ______ ______ ______ ______ + | head |.newer => | |.newer => | |.newer => | tail | + | A | | B | | C | | D | + |______| <= older.|______| <= older.|______| <= older.|______| + + removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added + */ + function LRU(maxLength) { + // 标识当前缓存数组的大小 + this.size = 0 + // 标识缓存数组能达到的最大长度 + this.limit = maxLength + // head(最不常用的项),tail(最常用的项)全部初始化为undefined + + this.head = this.tail = void 0 + this._keymap = {} + } + + var p = LRU.prototype + + p.put = function (key, value) { + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + // 如果存在tail(缓存数组的长度不为0),将tail指向新的 entry + + this.tail.newer = entry + entry.older = this.tail + } else { + // 如果缓存数组的长度为0,将head指向新的entry + this.head = entry + } + this.tail = entry + // 如果缓存数组达到上限,则先删除 head 指向的缓存对象 + if (this.size === this.limit) { + this.shift() + } else { + this.size++ + } + return value + } + + p.shift = function () { + var entry = this.head + if (entry) { + // 删除 head ,并改变指向 + this.head = this.head.newer + // 同步更新 _keymap 里面的属性值 + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = + void 0 + delete this._keymap[entry.key] //#1029 + // 同步更新 缓存数组的长度 + this.size-- + } + } + p.get = function (key) { + var entry = this._keymap[key] + // 如果查找不到含有`key`这个属性的缓存对象 + if (entry === void 0) + return + // 如果查找到的缓存对象已经是 tail (最近使用过的) + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + // 处理 newer 指向 + if (entry === this.head) { + // 如果查找到的缓存对象是 head (最近最少使用过的) + // 则将 head 指向原 head 的 newer 所指向的缓存对象 + this.head = entry.newer + } + // 将所查找的缓存对象的下一级的 older 指向所查找的缓存对象的older所指向的值 + // 例如:A B C D E + // 如果查找到的是D,那么将E指向C,不再指向D + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + // 处理 older 指向 + // 如果查找到的是D,那么C指向E,不再指向D + entry.older.newer = entry.newer // C. --> E + } + // 处理所查找到的对象的 newer 以及 older 指向 + entry.newer = void 0 // D --x + // older指向之前使用过的变量,即D指向E + entry.older = this.tail // D. --> E + if (this.tail) { + // 将E的newer指向D + this.tail.newer = entry // E. <-- D + } + // 改变 tail 为D + this.tail = entry + return entry.value + } + + module.exports = LRU + + /***/ }, /***/ 108: /***/ function(module, exports, __webpack_require__) { /*! - * built in 2016-8-25:19 version 2.113 by 司徒正美 + * built in 2016-8-25:23 version 2.113 by 司徒正美 * 2.1.5 and npm 2.1.15 * 修正 ms-controller, ms-important的移除类名的实现 * 实现后端渲染, @@ -144,237 +261,22 @@ return /******/ (function(modules) { // webpackBootstrap /* 0 */ /***/ function(module, exports, __webpack_require__) { - - var avalon = __webpack_require__(1) //这个版本兼容IE6 + var avalon = __webpack_require__(87) __webpack_require__(8) - __webpack_require__(15) - __webpack_require__(21) - __webpack_require__(43) + __webpack_require__(89) + __webpack_require__(91) + __webpack_require__(99) __webpack_require__(72) - avalon.onComponentDispose = __webpack_require__(79) - __webpack_require__(81) + __webpack_require__(104) + avalon.onComponentDispose = __webpack_require__(106) module.exports = avalon - - - /***/ }, - /* 1 */ - /***/ function(module, exports, __webpack_require__) { - - - __webpack_require__(2) - __webpack_require__(3) - __webpack_require__(5) - __webpack_require__(6) - module.exports = __webpack_require__(7) - - - /***/ }, - /* 2 */ - /***/ function(module, exports) { - - - /** - * 此模块不依赖任何模块,用于修复语言的底层缺陷 - */ - - var ohasOwn = Object.prototype.hasOwnProperty - function isNative(fn){ - return /\[native code\]/.test(fn) - } - if (!isNative('司徒正美'.trim)) { - var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g - String.prototype.trim = function () { - return this.replace(rtrim, '') - } - } - var hasDontEnumBug = !({ - 'toString': null - }).propertyIsEnumerable('toString'), - hasProtoEnumBug = (function () { - }).propertyIsEnumerable('prototype'), - dontEnums = [ - 'toString', - 'toLocaleString', - 'valueOf', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'constructor' - ], - dontEnumsLength = dontEnums.length; - if (!isNative(Object.keys)) { - Object.keys = function (object) { //ecma262v5 15.2.3.14 - var theKeys = [] - var skipProto = hasProtoEnumBug && typeof object === 'function' - if (typeof object === 'string' || (object && object.callee)) { - for (var i = 0; i < object.length; ++i) { - theKeys.push(String(i)) - } - } else { - for (var name in object) { - if (!(skipProto && name === 'prototype') && - ohasOwn.call(object, name)) { - theKeys.push(String(name)) - } - } - } - - if (hasDontEnumBug) { - var ctor = object.constructor, - skipConstructor = ctor && ctor.prototype === object - for (var j = 0; j < dontEnumsLength; j++) { - var dontEnum = dontEnums[j] - if (!(skipConstructor && dontEnum === 'constructor') && ohasOwn.call(object, dontEnum)) { - theKeys.push(dontEnum) - } - } - } - return theKeys - } - } - if (!isNative(Array.isArray)) { - Array.isArray = function (a) { - return Object.prototype.toString.call(a) === '[object Array]' - } - } - - if (!isNative(isNative.bind)) { - Function.prototype.bind = function (scope) { - if (arguments.length < 2 && scope === void 0) - return this - var fn = this, - argv = arguments - return function () { - var args = [], - i - for (i = 1; i < argv.length; i++) - args.push(argv[i]) - for (i = 0; i < arguments.length; i++) - args.push(arguments[i]) - return fn.apply(scope, args) - } - } - } - //https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/slice - /** - * Shim for "fixing" IE's lack of support (IE < 9) for applying slice - * on host objects like NamedNodeMap, NodeList, and HTMLCollection - * (technically, since host objects have been implementation-dependent, - * at least before ES6, IE hasn't needed to work this way). - * Also works on strings, fixes IE < 9 to allow an explicit undefined - * for the 2nd argument (as in Firefox), and prevents errors when - * called on other DOM objects. - */ - var ap = Array.prototype - - var _slice = ap.slice - try { - // Can't be used with DOM elements in IE < 9 - _slice.call(document.documentElement) - } catch (e) { // Fails in IE < 9 - // This will work for genuine arrays, array-like objects, - // NamedNodeMap (attributes, entities, notations), - // NodeList (e.g., getElementsByTagName), HTMLCollection (e.g., childNodes), - // and will not fail on other DOM objects (as do DOM elements in IE < 9) - ap.slice = function (begin, end) { - // IE < 9 gets unhappy with an undefined end argument - end = (typeof end !== 'undefined') ? end : this.length - - // For native Array objects, we use the native slice function - if (Array.isArray(this) ) { - return _slice.call(this, begin, end) - } - - // For array like object we handle it ourselves. - var i, cloned = [], - size, len = this.length - - // Handle negative value for "begin" - var start = begin || 0 - start = (start >= 0) ? start : len + start - - // Handle negative value for "end" - var upTo = (end) ? end : len - if (end < 0) { - upTo = len + end - } - - // Actual expected size of the slice - size = upTo - start - - if (size > 0) { - cloned = new Array(size) - if (this.charAt) { - for (i = 0; i < size; i++) { - cloned[i] = this.charAt(start + i) - } - } else { - for (i = 0; i < size; i++) { - cloned[i] = this[start + i] - } - } - } - - return cloned - } - } - - function iterator(vars, body, ret) { - 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 - /* jshint ignore:start */ - return Function('fn,scope', fun) - /* jshint ignore:end */ - } - - if (!isNative(ap.map)) { - var shim = { - //定位操作,返回数组中第一个等于给定参数的元素的索引值。 - indexOf: function (item, index) { - var n = this.length, - i = ~~index - if (i < 0) - i += n - for (; i < n; i++) - if (this[i] === item) - return i - return -1 - }, - //定位操作,同上,不过是从后遍历。 - lastIndexOf: function (item, index) { - var n = this.length, - i = index == null ? n - 1 : index - if (i < 0) - i = Math.max(0, n + i) - for (; i >= 0; i--) - if (this[i] === item) - return i - return -1 - }, - //迭代操作,将数组的元素挨个儿传入一个函数中执行。Prototype.js的对应名字为each。 - forEach: iterator('', '_', ''), - //迭代类 在数组中的每个项上运行一个函数,如果此函数的值为真,则此元素作为新数组的元素收集起来,并返回新数组 - filter: iterator('r=[],j=0,', 'if(_)r[j++]=this[i]', 'return r'), - //收集操作,将数组的元素挨个儿传入一个函数中执行,然后把它们的返回值组成一个新数组返回。Prototype.js的对应名字为collect。 - map: iterator('r=[],', 'r[i]=_', 'return r'), - //只要数组中有一个元素满足条件(放进给定函数返回true),那么它就返回true。Prototype.js的对应名字为any。 - some: iterator('', 'if(_)return true', 'return false'), - //只有数组中的元素都满足条件(放进给定函数返回true),它才返回true。Prototype.js的对应名字为all。 - every: iterator('', 'if(!_)return false', 'return true') - } - - for (var i in shim) { - ap[i] = shim[i] - } - } - module.exports = {} - /***/ }, + /* 1 */, + /* 2 */, /* 3 */ /***/ function(module, exports, __webpack_require__) { @@ -395,22 +297,25 @@ return /******/ (function(modules) { // webpackBootstrap outerHTML: 'x' }, msie: NaN, + browser: false, modern: true, avalonDiv: {}, avalonFragment: null } window.avalon = avalon - + /* istanbul ignore if */ if (window.location && window.navigator && window.window) { var doc = window.document browser.browser = true browser.document = doc - browser.modern = window.dispatchEvent browser.root = doc.documentElement browser.avalonDiv = doc.createElement('div') browser.avalonFragment = doc.createDocumentFragment() if (window.VBArray) { browser.msie = doc.documentMode || (window.XMLHttpRequest ? 7 : 6) + browser.modern = browser.msie > 8 + } else { + browser.modern = true } } @@ -486,252 +391,7 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = avalon /***/ }, - /* 5 */ - /***/ function(module, exports, __webpack_require__) { - - //这里放置存在异议的方法 - var avalon = __webpack_require__(4) - - - avalon.quote = typeof JSON !== 'undefined' ? JSON.stringify : new function () { - //https://github.com/bestiejs/json3/blob/master/lib/json3.js - var Escapes = { - 92: "\\\\", - 34: '\\"', - 8: "\\b", - 12: "\\f", - 10: "\\n", - 13: "\\r", - 9: "\\t" - } - - var leadingZeroes = '000000' - var toPaddedString = function (width, value) { - return (leadingZeroes + (value || 0)).slice(-width) - }; - var unicodePrefix = '\\u00' - var escapeChar = function (character) { - var charCode = character.charCodeAt(0), escaped = Escapes[charCode] - if (escaped) { - return escaped - } - return unicodePrefix + toPaddedString(2, charCode.toString(16)) - }; - var reEscape = /[\x00-\x1f\x22\x5c]/g - return function (value) { - reEscape.lastIndex = 0 - return '"' + (reEscape.test(value) ? String(value).replace(reEscape, escapeChar) : value) + '"' - } - } - - - - var tos = avalon.inspect - var class2type = {} - 'Boolean Number String Function Array Date RegExp Object Error'.replace(avalon.rword, function (name) { - class2type['[object ' + name + ']'] = name.toLowerCase() - }) - - avalon.type = function (obj) { //取得目标的类型 - if (obj == null) { - return String(obj) - } - // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function - return typeof obj === 'object' || typeof obj === 'function' ? - class2type[tos.call(obj)] || 'object' : - typeof obj - } - - - - - - var rfunction = /^\s*\bfunction\b/ - - avalon.isFunction = typeof alert === 'object' ? function (fn) { - try { - return rfunction.test(fn + '') - } catch (e) { - return false - } - } : function (fn) { - return tos.call(fn) === '[object Function]' - } - - - - - function isWindowCompact(obj) { - if (!obj) - return false - // 利用IE678 window == document为true,document == window竟然为false的神奇特性 - // 标准浏览器及IE9,IE10等使用 正则检测 - return obj == obj.document && obj.document != obj //jshint ignore:line - } - - var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ - function isWindowModern(obj) { - return rwindow.test(tos.call(obj)) - } - - avalon.isWindow = isWindowModern(avalon.window) ? - isWindowModern : isWindowCompact - - - var enu, enumerateBUG - for (enu in avalon({})) { - break - } - - var ohasOwn = avalon.ohasOwn - enumerateBUG = enu !== '0' //IE6下为true, 其他为false - - /*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ - function isPlainObjectCompact(obj, key) { - if (!obj || avalon.type(obj) !== 'object' || obj.nodeType || avalon.isWindow(obj)) { - return false - } - try { //IE内置对象没有constructor - if (obj.constructor && - !ohasOwn.call(obj, 'constructor') && - !ohasOwn.call(obj.constructor.prototype || {}, 'isPrototypeOf')) { - return false - } - } catch (e) { //IE8 9会在这里抛错 - return false - } - if (enumerateBUG) { - for (key in obj) { - return ohasOwn.call(obj, key) - } - } - for (key in obj) { - } - return key === void 0 || ohasOwn.call(obj, key) - } - - function isPlainObjectModern(obj) { - // 简单的 typeof obj === 'object'检测,会致使用isPlainObject(window)在opera下通不过 - return tos.call(obj) === '[object Object]' && - Object.getPrototypeOf(obj) === Object.prototype - } - - avalon.isPlainObject = /\[native code\]/.test(Object.getPrototypeOf) ? - isPlainObjectModern : isPlainObjectCompact - - - //与jQuery.extend方法,可用于浅拷贝,深拷贝 - avalon.mix = avalon.fn.mix = function () { - var options, name, src, copy, copyIsArray, clone, - target = arguments[0] || {}, - i = 1, - length = arguments.length, - deep = false - - // 如果第一个参数为布尔,判定是否深拷贝 - if (typeof target === 'boolean') { - deep = target - target = arguments[1] || {} - i++ - } - - //确保接受方为一个复杂的数据类型 - if (typeof target !== 'object' && !avalon.isFunction(target)) { - target = {} - } - - //如果只有一个参数,那么新成员添加于mix所在的对象上 - if (i === length) { - target = this - i-- - } - - for (; i < length; i++) { - //只处理非空参数 - if ((options = arguments[i]) != null) { - for (name in options) { - try { - src = target[name] - copy = options[name] //当options为VBS对象时报错 - } catch (e) { - continue - } - - // 防止环引用 - if (target === copy) { - continue - } - if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { - - if (copyIsArray) { - copyIsArray = false - clone = src && Array.isArray(src) ? src : [] - - } else { - clone = src && avalon.isPlainObject(src) ? src : {} - } - - target[name] = avalon.mix(deep, clone, copy) - } else if (copy !== void 0) { - target[name] = copy - } - } - } - } - return target - } - - var rarraylike = /(Array|List|Collection|Map|Arguments)\]$/ - /*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ - function isArrayLike(obj) { - if (!obj) - return false - var n = obj.length - if (n === (n >>> 0)) { //检测length属性是否为非负整数 - var type = tos.call(obj).slice(8, -1) - if (rarraylike.test(type)) - return false - if (type === 'Array') - return true - try { - if ({}.propertyIsEnumerable.call(obj, 'length') === false) { //如果是原生对象 - return rfunction.test(obj.item || obj.callee) - } - return true - } catch (e) { //IE的NodeList直接抛错 - return !obj.window //IE6-8 window - } - } - return false - } - - - avalon.each = function (obj, fn) { - if (obj) { //排除null, undefined - var i = 0 - if (isArrayLike(obj)) { - for (var n = obj.length; i < n; i++) { - if (fn(i, obj[i]) === false) - break - } - } else { - for (i in obj) { - if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { - break - } - } - } - } - } - - module.exports = { - avalon: avalon, - isArrayLike: isArrayLike - } - - - - /***/ }, + /* 5 */, /* 6 */ /***/ function(module, exports, __webpack_require__) { @@ -913,6 +573,7 @@ return /******/ (function(modules) { // webpackBootstrap var avalon = __webpack_require__(4) function kernel(settings) { for (var p in settings) { + /* istanbul ignore if */ if (!avalon.ohasOwn.call(settings, p)) continue var val = settings[p] @@ -934,12 +595,14 @@ return /******/ (function(modules) { // webpackBootstrap var openTag = array[0] var closeTag = array[1] /*eslint-disable */ + /* istanbul ignore if */ if (openTag === closeTag) { throw new SyntaxError('openTag!==closeTag') } var test = openTag + 'test' + closeTag var div = avalon.avalonDiv div.innerHTML = test + /* istanbul ignore if */ if (div.innerHTML !== test && div.innerHTML.indexOf('<') > -1) { throw new SyntaxError('此定界符不合法') } @@ -1561,54 +1224,17 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = escape /***/ }, - /* 15 */ + /* 15 */, + /* 16 */ /***/ function(module, exports, __webpack_require__) { - /** - * 虚拟DOM的3大构造器 - */ - var VText = __webpack_require__(16) - var VComment = __webpack_require__(18) - var VElement = __webpack_require__(19) - var VFragment = __webpack_require__(20) - - avalon.vdomAdaptor = function (obj, method) { - if (!obj) {//obj在ms-for循环里面可能是null - return method === "toHTML" ? '' : document.createDocumentFragment() - } - switch (obj.nodeName) { - case '#text': - return VText.prototype[method].call(obj) - case '#comment': - return VComment.prototype[method].call(obj) - case '#document-fragment': - return VFragment.prototype[method].call(obj) - case void(0): - return (new VFragment(obj))[method]() - default: - return VElement.prototype[method].call(obj) - } - } - - module.exports = { - VText: VText, - VComment: VComment, - VElement: VElement, - VFragment: VFragment - } - - - /***/ }, - /* 16 */ - /***/ function(module, exports, __webpack_require__) { - - var rexpr = avalon.config.rexpr - var decode = __webpack_require__(17) - function VText(text) { - this.nodeName = '#text' - this.nodeValue = text - this.skipContent = !rexpr.test(text) - } + var rexpr = avalon.config.rexpr + var decode = __webpack_require__(17) + function VText(text) { + this.nodeName = '#text' + this.nodeValue = text + this.skipContent = !rexpr.test(text) + } VText.prototype = { constructor: VText, @@ -1670,158 +1296,7 @@ return /******/ (function(modules) { // webpackBootstrap /***/ }, - /* 19 */ - /***/ function(module, exports) { - - - function VElement(type, props, children) { - this.nodeName = type - this.props = props - this.children = children - - } - function skipFalseAndFunction(a) { - return a !== false && (Object(a) !== a) - } - var specal = { - "class": function (dom, val) { - dom.className = val - }, - style: function (dom, val) { - dom.style.cssText = val - }, - 'for': function (dom, val) { - dom.htmlFor = val - } - } - - function createVML(type) { - if (document.styleSheets.length < 31) { - document.createStyleSheet().addRule(".rvml", "behavior:url(#default#VML)"); - } else { - // no more room, add to the existing one - // http://msdn.microsoft.com/en-us/library/ms531194%28VS.85%29.aspx - document.styleSheets[0].addRule(".rvml", "behavior:url(#default#VML)"); - } - var arr = type.split(':') - if (arr.length === 1) { - arr.unshift('v') - } - var tag = arr[1] - var ns = arr[0] - if (!document.namespaces[ns]) { - document.namespaces.add(ns, "urn:schemas-microsoft-com:vml") - } - return document.createElement('<' + ns + ':' + tag + ' class="rvml">'); - } - - function createSVG(type) { - return document.createElementNS('http://www.w3.org/2000/svg', type) - } - var svgTags = avalon.oneObject('circle,defs,ellipse,image,line,' + - 'path,polygon,polyline,rect,symbol,text,use,g,svg') - var VMLTags = avalon.oneObject('shape,line,polyline,rect,roundrect,oval,arc,' + - 'curve,background,image,shapetype,group,fill,' + - 'stroke,shadow, extrusion, textbox, imagedata, textpath') - - var rvml = /^\w+\:\w+/ - - VElement.prototype = { - constructor: VElement, - toDOM: function () { - if (this.dom) - return this.dom - var dom, tagName = this.nodeName - if (avalon.modern && svgTags[tagName]) { - dom = createSVG(tagName) - } else if (!avalon.modern && (VMLTags[tagName] || rvml.test(tagName))) { - dom = createVML(tagName) - } else { - dom = document.createElement(tagName) - } - - var props = this.props || {} - var wid = (props['ms-important'] || - props['ms-controller'] || this.wid) - if (wid) { - var scope = avalon.scopes[wid] - var element = scope && scope.vmodel && scope.vmodel.$element - if (element) { - var oldVdom = element.vtree[0] - if (oldVdom.children) { - this.children = oldVdom.children - } - return element - } - } - for (var i in props) { - var val = props[i] - if (skipFalseAndFunction(val)) { - if (specal[i] && avalon.msie < 8) { - specal[i](dom, val) - } else { - dom.setAttribute(i, val + '') - } - } - } - var c = this.children || [] - var template = c[0] ? c[0].nodeValue : '' - switch (this.nodeName) { - case 'script': - dom.text = template - break - case 'style': - if ('styleSheet' in dom) { - dom.setAttribute('type', 'text/css') - dom.styleSheet.cssText = template - } else { - dom.innerHTML = template - } - break - case 'xmp'://IE6-8,XMP元素里面只能有文本节点,不能使用innerHTML - case 'noscript': - dom.innerText = dom.textContent = template - break - case 'template': - dom.innerHTML = template - break - default: - if (!this.isVoidTag) { - this.children.forEach(function (c) { - c && dom.appendChild(avalon.vdomAdaptor(c, 'toDOM')) - }) - } - break - } - return this.dom = dom - }, - toHTML: function () { - var arr = [] - var props = this.props || {} - for (var i in props) { - var val = props[i] - if (skipFalseAndFunction(val)) { - arr.push(i + '=' + avalon.quote(props[i] + '')) - } - } - arr = arr.length ? ' ' + arr.join(' ') : '' - var str = '<' + this.nodeName + arr - if (this.isVoidTag) { - return str + '/>' - } - str += '>' - if (this.children) { - str += this.children.map(function (c) { - return c ? avalon.vdomAdaptor(c, 'toHTML') : '' - }).join('') - } - return str + '' - } - } - - module.exports = VElement - - /***/ }, + /* 19 */, /* 20 */ /***/ function(module, exports) { @@ -1852,7063 +1327,6783 @@ return /******/ (function(modules) { // webpackBootstrap module.exports = VFragment /***/ }, - /* 21 */ - /***/ function(module, exports, __webpack_require__) { + /* 21 */, + /* 22 */, + /* 23 */, + /* 24 */, + /* 25 */ + /***/ function(module, exports) { - /** - * ------------------------------------------------------------ - * DOM Api - * shim,class,data,css,val,html,event,ready - * ------------------------------------------------------------ + var propMap = {//不规则的属性名映射 + 'accept-charset': 'acceptCharset', + 'char': 'ch', + charoff: 'chOff', + 'class': 'className', + 'for': 'htmlFor', + 'http-equiv': 'httpEquiv' + } + /* + contenteditable不是布尔属性 + http://www.zhangxinxu.com/wordpress/2016/01/contenteditable-plaintext-only/ + contenteditable='' + contenteditable='events' + contenteditable='caret' + contenteditable='plaintext-only' + contenteditable='true' + contenteditable='false' */ + var bools = ['autofocus,autoplay,async,allowTransparency,checked,controls', + 'declare,disabled,defer,defaultChecked,defaultSelected,', + 'isMap,loop,multiple,noHref,noResize,noShade', + 'open,readOnly,selected' + ].join(',') - __webpack_require__(22) - __webpack_require__(23) - __webpack_require__(24) - __webpack_require__(27) - __webpack_require__(28) - __webpack_require__(29) - __webpack_require__(32) - __webpack_require__(34) + bools.replace(/\w+/g, function (name) { + propMap[name.toLowerCase()] = name + }) + + var anomaly = ['accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan', + 'dateTime,defaultValue,contentEditable,frameBorder,longDesc,maxLength,'+ + 'marginWidth,marginHeight,rowSpan,tabIndex,useMap,vSpace,valueType,vAlign' + ].join(',') + + anomaly.replace(/\w+/g, function (name) { + propMap[name.toLowerCase()] = name + }) + + module.exports = propMap - module.exports = avalon /***/ }, - /* 22 */ + /* 26 */, + /* 27 */, + /* 28 */, + /* 29 */ /***/ function(module, exports, __webpack_require__) { + var Cache = __webpack_require__(30) var avalon = __webpack_require__(4) - function fixContains(root, el) { - try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 - while ((el = el.parentNode)) - if (el === root) - return true - return false - } catch (e) { - return false - } - } + var fixCloneNode = __webpack_require__(31) - avalon.contains = fixContains - //IE6-11的文档对象没有contains - if (avalon.browser) { - if (!document.contains) { - document.contains = function (b) { - return fixContains(document, b) - } + var rhtml = /<|&#?\w+;/ + var htmlCache = new Cache(128) + var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig + + avalon.parseHTML = function (html) { + var fragment = avalon.avalonFragment.cloneNode(false) + //处理非字符串 + if (typeof html !== 'string') { + return fragment } - if (window.Node && !document.createTextNode('x').contains) { - Node.prototype.contains = function (arg) {//IE6-8没有Node对象 - return !!(this.compareDocumentPosition(arg) & 16) - } + //处理非HTML字符串 + if (!rhtml.test(html)) { + return document.createTextNode(html) } - //firefox 到11时才有outerHTML - if (window.HTMLElement && !avalon.root.outerHTML) { - HTMLElement.prototype.__defineGetter__('outerHTML', function () { - var div = document.createElement('div') - div.appendChild(this) - return div.innerHTML - }) + html = html.replace(rxhtml, '<$1>').trim() + var hasCache = htmlCache.get(html) + if (hasCache) { + return fixCloneNode(hasCache) } - + var vnodes = avalon.lexer(html) + for (var i = 0, el; el = vnodes[i++]; ) { + fragment.appendChild(avalon.vdomAdaptor(el, 'toDOM')) + } + if (html.length < 1024) { + htmlCache.put(html, fixCloneNode(fragment)) + } + return fragment } + avalon.innerHTML = function (node, html) { + + var parsed = this.parseHTML(html) + this.clearHTML(node).appendChild(parsed) + } - - - /***/ }, - /* 23 */ - /***/ function(module, exports, __webpack_require__) { - - var avalon = __webpack_require__(4) - var rnowhite = /\S+/g - var fakeClassListMethods = { - _toString: function () { - var node = this.node - var cls = node.className - var str = typeof cls === 'string' ? cls : cls.baseVal - var match = str.match(rnowhite) - return match ? match.join(' ') : '' - }, - _contains: function (cls) { - return (' ' + this + ' ').indexOf(' ' + cls + ' ') > -1 - }, - _add: function (cls) { - if (!this.contains(cls)) { - this._set(this + ' ' + cls) - } - }, - _remove: function (cls) { - this._set((' ' + this + ' ').replace(' ' + cls + ' ', ' ')) - }, - __set: function (cls) { - cls = cls.trim() - var node = this.node - if (typeof node.className === 'object') { - //SVG元素的className是一个对象 SVGAnimatedString { baseVal='', animVal=''},只能通过set/getAttribute操作 - node.setAttribute('class', cls) - } else { - node.className = cls - } - } //toggle存在版本差异,因此不使用它 + var reunescapeHTML = /&(?:amp|lt|gt|quot|#39|#96);/g + var htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", + '`': '`' } - - function fakeClassList(node) { - if (!('classList' in node)) { - node.classList = { - node: node - } - for (var k in fakeClassListMethods) { - node.classList[k.slice(1)] = fakeClassListMethods[k] - } - } - return node.classList + avalon.unescapeHTML = function (string) { + var str = '' + string + return str.replace(reunescapeHTML, function (c) { + return htmlUnescapes[c] + }) } - 'add,remove'.replace(avalon.rword, function (method) { - avalon.fn[method + 'Class'] = function (cls) { - var el = this[0] || {} - //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 - if (cls && typeof cls === 'string' && el.nodeType === 1) { - cls.replace(rnowhite, function (c) { - fakeClassList(el)[method](c) - }) - } - return this - } - }) - avalon.fn.mix({ - hasClass: function (cls) { - var el = this[0] || {} - return el.nodeType === 1 && fakeClassList(el).contains(cls) - }, - toggleClass: function (value, stateVal) { - var isBool = typeof stateVal === 'boolean' - var me = this - String(value).replace(rnowhite, function (c) { - var state = isBool ? stateVal : !me.hasClass(c) - me[state ? 'addClass' : 'removeClass'](c) - }) - return this + avalon.clearHTML = function (node) { + node.textContent = '' + while (node.lastChild) { + node.removeChild(node.lastChild) } - }) - + return node + } /***/ }, - /* 24 */ - /***/ function(module, exports, __webpack_require__) { - - var avalon = __webpack_require__(4) - var propMap = __webpack_require__(25) - var isVML = __webpack_require__(26) - var rsvg =/^\[object SVG\w*Element\]$/ - var ramp = /&/g + /* 30 */ + /***/ function(module, exports) { - function attrUpdate(node, vnode) { - var attrs = vnode.changeAttr - if (!node || node.nodeType !== 1 ) { - return - } - if (attrs) { - vnode.dynamic['ms-attr'] = 1 - for (var attrName in attrs) { - var val = attrs[attrName] - // 处理路径属性 - if (attrName === 'href' || attrName === 'src') { - if (!node.hasAttribute) { - val = String(val).replace(ramp, '&') //处理IE67自动转义的问题 - } - node[attrName] = val - if (window.chrome && node.tagName === 'EMBED') { - var parent = node.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 - var comment = document.createComment('ms-src') - parent.replaceChild(comment, node) - parent.replaceChild(node, comment) - } - //处理HTML5 data-*属性 - } else if (attrName.indexOf('data-') === 0) { - node.setAttribute(attrName, val) + + /* + https://github.com/rsms/js-lru + entry entry entry entry + ______ ______ ______ ______ + | head |.newer => | |.newer => | |.newer => | tail | + | A | | B | | C | | D | + |______| <= older.|______| <= older.|______| <= older.|______| + + removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added + */ + function LRU(maxLength) { + // 标识当前缓存数组的大小 + this.size = 0 + // 标识缓存数组能达到的最大长度 + this.limit = maxLength + // head(最不常用的项),tail(最常用的项)全部初始化为undefined - } else { - var propName = propMap[attrName] || attrName - if (typeof node[propName] === 'boolean') { - node[propName] = !!val - - //布尔属性必须使用el.xxx = true|false方式设值 - //如果为false, IE全系列下相当于setAttribute(xxx,''), - //会影响到样式,需要进一步处理 - } + this.head = this.tail = void 0 + this._keymap = {} + } - if (val === false ) {//移除属性 - node.removeAttribute(propName) - continue - } - //SVG只能使用setAttribute(xxx, yyy), VML只能使用node.xxx = yyy , - //HTML的固有属性必须node.xxx = yyy - - var isInnate = rsvg.test(node) ? false : - (!avalon.modern && isVML(node)) ? true : - attrName in node.cloneNode(false) - if (isInnate) { - node[propName] = val + '' - } else { - node.setAttribute(attrName, val) - } + var p = LRU.prototype - } + p.put = function (key, value) { + var entry = { + key: key, + value: value + } + this._keymap[key] = entry + if (this.tail) { + // 如果存在tail(缓存数组的长度不为0),将tail指向新的 entry - } - vnode.changeAttr = null + this.tail.newer = entry + entry.older = this.tail + } else { + // 如果缓存数组的长度为0,将head指向新的entry + this.head = entry + } + this.tail = entry + // 如果缓存数组达到上限,则先删除 head 指向的缓存对象 + if (this.size === this.limit) { + this.shift() + } else { + this.size++ } + return value } - var rvalidchars = /^[\],:{}\s]*$/, - rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, - rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g, - rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g - - avalon.parseJSON = typeof JSON === 'object' ? JSON.parse : function (data) { - if (typeof data === 'string') { - data = data.trim() - if (data) { - if (rvalidchars.test(data.replace(rvalidescape, '@') - .replace(rvalidtokens, ']') - .replace(rvalidbraces, ''))) { - return (new Function('return ' + data))() // jshint ignore:line - } - } - avalon.error('Invalid JSON: ' + data) + p.shift = function () { + var entry = this.head + if (entry) { + // 删除 head ,并改变指向 + this.head = this.head.newer + // 同步更新 _keymap 里面的属性值 + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = + void 0 + delete this._keymap[entry.key] //#1029 + // 同步更新 缓存数组的长度 + this.size-- } - return data } - - - avalon.fn.attr = function (name, value) { - if (arguments.length === 2) { - this[0].setAttribute(name, value) - return this - } else { - return this[0].getAttribute(name) + p.get = function (key) { + var entry = this._keymap[key] + // 如果查找不到含有`key`这个属性的缓存对象 + if (entry === void 0) + return + // 如果查找到的缓存对象已经是 tail (最近使用过的) + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + // 处理 newer 指向 + if (entry === this.head) { + // 如果查找到的缓存对象是 head (最近最少使用过的) + // 则将 head 指向原 head 的 newer 所指向的缓存对象 + this.head = entry.newer + } + // 将所查找的缓存对象的下一级的 older 指向所查找的缓存对象的older所指向的值 + // 例如:A B C D E + // 如果查找到的是D,那么将E指向C,不再指向D + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + // 处理 older 指向 + // 如果查找到的是D,那么C指向E,不再指向D + entry.older.newer = entry.newer // C. --> E + } + // 处理所查找到的对象的 newer 以及 older 指向 + entry.newer = void 0 // D --x + // older指向之前使用过的变量,即D指向E + entry.older = this.tail // D. --> E + if (this.tail) { + // 将E的newer指向D + this.tail.newer = entry // E. <-- D } + // 改变 tail 为D + this.tail = entry + return entry.value } - module.exports = attrUpdate + module.exports = LRU + /***/ }, - /* 25 */ + /* 31 */ /***/ function(module, exports) { - var propMap = {//不规则的属性名映射 - 'accept-charset': 'acceptCharset', - 'char': 'ch', - charoff: 'chOff', - 'class': 'className', - 'for': 'htmlFor', - 'http-equiv': 'httpEquiv' - } - /* - contenteditable不是布尔属性 - http://www.zhangxinxu.com/wordpress/2016/01/contenteditable-plaintext-only/ - contenteditable='' - contenteditable='events' - contenteditable='caret' - contenteditable='plaintext-only' - contenteditable='true' - contenteditable='false' - */ - var bools = ['autofocus,autoplay,async,allowTransparency,checked,controls', - 'declare,disabled,defer,defaultChecked,defaultSelected,', - 'isMap,loop,multiple,noHref,noResize,noShade', - 'open,readOnly,selected' - ].join(',') + var rcheckedType = /radio|checkbox/ - bools.replace(/\w+/g, function (name) { - propMap[name.toLowerCase()] = name - }) + function fix(dest, src) { + if (dest.nodeType !== 1) { + return + } + var nodeName = dest.nodeName.toLowerCase() + if (nodeName === 'object') { + if (dest.parentNode) { + dest.outerHTML = src.outerHTML + } - var anomaly = ['accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan', - 'dateTime,defaultValue,contentEditable,frameBorder,longDesc,maxLength,'+ - 'marginWidth,marginHeight,rowSpan,tabIndex,useMap,vSpace,valueType,vAlign' - ].join(',') + } else if (nodeName === 'input' && rcheckedType.test(src.nodeName)) { - anomaly.replace(/\w+/g, function (name) { - propMap[name.toLowerCase()] = name - }) + dest.defaultChecked = dest.checked = src.checked - module.exports = propMap + if (dest.value !== src.value) { + dest.value = src.value + } + } else if (nodeName === 'option') { + dest.defaultSelected = dest.selected = src.defaultSelected + } else if (nodeName === 'input' || nodeName === 'textarea') { + dest.defaultValue = src.defaultValue + } + } - /***/ }, - /* 26 */ - /***/ function(module, exports) { - function isVML(src) { - var nodeName = src.nodeName - return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === '' + function getAll(context) { + return typeof context.getElementsByTagName !== 'undefined' ? + context.getElementsByTagName('*') : + typeof context.querySelectorAll !== 'undefined' ? + context.querySelectorAll('*') : [] + } + + function fixCloneNode(src) { + var target = src.cloneNode(true) + if (avalon.modern) + return target + var t = getAll(target) + var s = getAll(src) + avalon.each(s, function (i) { + fix(t[i], s[i]) + }) + return target } - module.exports = isVML + module.exports = fixCloneNode + + /***/ }, + /* 32 */, + /* 33 */ + /***/ function(module, exports) { + + //http://www.feiesoft.com/html/events.html + //http://segmentfault.com/q/1010000000687977/a-1020000000688757 + module.exports = { + click: true, + dblclick: true, + keydown: true, + keypress: true, + keyup: true, + mousedown: true, + mousemove: true, + mouseup: true, + mouseover: true, + mouseout: true, + wheel: true, + mousewheel: true, + input: true, + change: true, + beforeinput: true, + compositionstart: true, + compositionupdate: true, + compositionend: true, + select: true, + //http://blog.csdn.net/lee_magnum/article/details/17761441 + cut: true, + copy: true, + paste: true, + beforecut: true, + beforecopy: true, + beforepaste: true, + focusin: true, + focusout: true, + DOMFocusIn: true, + DOMFocusOut: true, + DOMActivate: true, + dragend: true, + datasetchanged: true + } /***/ }, - /* 27 */ + /* 34 */, + /* 35 */ /***/ function(module, exports, __webpack_require__) { - var avalon = __webpack_require__(4) - var root = avalon.root - var camelize = avalon.camelize - var cssHooks = avalon.cssHooks + var onceWarn = true //只警告一次 + var dom2vdom = __webpack_require__(36) - var prefixes = ['', '-webkit-', '-o-', '-moz-', '-ms-'] - var cssMap = { - 'float': avalon.modern ? 'cssFloat' : 'styleFloat' - } - avalon.cssNumber = avalon.oneObject('animationIterationCount,columnCount,order,flex,flexGrow,flexShrink,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom') + function scan(nodes) { + for (var i = 0, elem; elem = nodes[i++]; ) { + if (elem.nodeType === 1) { + var $id = getController(elem) - avalon.cssName = function (name, host, camelCase) { - if (cssMap[name]) { - return cssMap[name] - } - host = host || root.style || {} - for (var i = 0, n = prefixes.length; i < n; i++) { - camelCase = camelize(prefixes[i] + name) - if (camelCase in host) { - return (cssMap[name] = camelCase) - } - } - return null - } + var vm = avalon.vmodels[$id] + if (vm && !vm.$element) { + vm.$element = elem + + if (avalon.serverTemplates && avalon.serverTemplates[$id]) { + var tmpl = avalon.serverTemplates[$id] + var oldTree = avalon.speedUp(avalon.lexer(tmpl)) + var render = avalon.render(oldTree) + var vtree = render(vm) + var dom = avalon.vdomAdaptor(vtree[0], 'toDOM') + vm.$element = dom + dom.vtree = vtree + vm.$render = render + elem.parentNode.replaceChild(dom, elem) + avalon.diff(vtree, vtree) + continue + } + //IE6-8下元素的outerHTML前面会有空白 + //第一次扫描就清空所有空白节点,并生成最初的vtree + var vtree = dom2vdom(elem) + var now = new Date() + elem.vtree = avalon.speedUp(vtree) - avalon.fn.css = function (name, value) { - if (avalon.isPlainObject(name)) { - for (var i in name) { - avalon.css(this, i, name[i]) - } - } else { - var ret = avalon.css(this, name, value) - } - return ret !== void 0 ? ret : this - } + var now2 = new Date() + onceWarn && avalon.log('构建虚拟DOM耗时', now2 - now, 'ms') - avalon.fn.position = function () { - var offsetParent, offset, - elem = this[0], - parentOffset = { - top: 0, - left: 0 + vm.$render = avalon.render(elem.vtree) + avalon.scopes[vm.$id] = { + vmodel: vm, + local: {}, + isTemp: true + } + var now3 = new Date() + onceWarn && avalon.log('构建当前vm的$render方法耗时 ', now3 - now2, 'ms\n', + '如果此时间太长,达100ms以上\n', + '建议将当前ms-controller拆分成多个ms-controller,减少每个vm管辖的区域') + avalon.rerenderStart = now3 + onceWarn = false + avalon.batch($id) + + } else if (!$id) { + scan(elem.childNodes) } - if (!elem) { - return parentOffset - } - if (this.css('position') === 'fixed') { - offset = elem.getBoundingClientRect() - } else { - offsetParent = this.offsetParent() //得到真正的offsetParent - offset = this.offset() // 得到正确的offsetParent - if (offsetParent[0].tagName !== 'HTML') { - parentOffset = offsetParent.offset() } - parentOffset.top += avalon.css(offsetParent[0], 'borderTopWidth', true) - parentOffset.left += avalon.css(offsetParent[0], 'borderLeftWidth', true) - - // Subtract offsetParent scroll positions - parentOffset.top -= offsetParent.scrollTop() - parentOffset.left -= offsetParent.scrollLeft() - } - return { - top: offset.top - parentOffset.top - avalon.css(elem, 'marginTop', true), - left: offset.left - parentOffset.left - avalon.css(elem, 'marginLeft', true) } } - avalon.fn.offsetParent = function () { - var offsetParent = this[0].offsetParent - while (offsetParent && avalon.css(offsetParent, 'position') === 'static') { - offsetParent = offsetParent.offsetParent + module.exports = avalon.scan = function (a) { + if (!a || !a.nodeType) { + avalon.warn('[avalon.scan] first argument must be element , documentFragment, or document') + return } - return avalon(offsetParent || root) + scan([a]) } - cssHooks['@:set'] = function (node, name, value) { - try { - //node.style.width = NaN;node.style.width = 'xxxxxxx'; - //node.style.width = undefine 在旧式IE下会抛异常 - node.style[name] = value - } catch (e) { - } + function getController(a) { + return a.getAttribute('ms-controller') || + a.getAttribute(':controller') } - if (typeof getComputedStyle === 'function') { - cssHooks['@:get'] = function (node, name) { - if (!node || !node.style) { - throw new Error('getComputedStyle要求传入一个节点 ' + node) - } - var ret, styles = getComputedStyle(node, null) - if (styles) { - ret = name === 'filter' ? styles.getPropertyValue(name) : styles[name] - if (ret === '') { - ret = node.style[name] //其他浏览器需要我们手动取内联样式 + /***/ }, + /* 36 */ + /***/ function(module, exports, __webpack_require__) { + + + + var voidTag = __webpack_require__(37) + var vdom2body = __webpack_require__(38) + var rformElement = /input|textarea|select/i + var rcolon = /^\:/ + function getAttributes(node) { + var attrs = node.attributes, ret = {} + for (var i = 0, n = attrs.length; i < n; i++) { + var attr = attrs[i] + if (attr.specified) { + var name = attr.name + if (name.charAt(0) === ':') { + name = name.replace(rcolon, 'ms-') } + ret[name] = attr.value } - return ret } - cssHooks['opacity:get'] = function (node) { - var ret = cssHooks['@:get'](node, 'opacity') - return ret === '' ? '1' : ret - } - } else { - var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i - var rposition = /^(top|right|bottom|left)$/ - var ralpha = /alpha\([^)]*\)/i - var ie8 = avalon.msie === 8 - var salpha = 'DXImageTransform.Microsoft.Alpha' - var border = { - thin: ie8 ? '1px' : '2px', - medium: ie8 ? '3px' : '4px', - thick: ie8 ? '5px' : '6px' - } - cssHooks['@:get'] = function (node, name) { - //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位 - var currentStyle = node.currentStyle - var ret = currentStyle[name] - if ((rnumnonpx.test(ret) && !rposition.test(ret))) { - //①,保存原有的style.left, runtimeStyle.left, - var style = node.style, - left = style.left, - rsLeft = node.runtimeStyle.left - //②由于③处的style.left = xxx会影响到currentStyle.left, - //因此把它currentStyle.left放到runtimeStyle.left, - //runtimeStyle.left拥有最高优先级,不会style.left影响 - node.runtimeStyle.left = currentStyle.left - //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft - //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760 - style.left = name === 'fontSize' ? '1em' : (ret || 0) - ret = style.pixelLeft + 'px' - //④还原 style.left,runtimeStyle.left - style.left = left - node.runtimeStyle.left = rsLeft - } - if (ret === 'medium') { - name = name.replace('Width', 'Style') - //border width 默认值为medium,即使其为0' - if (currentStyle[name] === 'none') { - ret = '0px' - } - } - return ret === '' ? 'auto' : border[ret] || ret - } - cssHooks['opacity:set'] = function (node, name, value) { - var style = node.style - var opacity = isFinite(value) && value <= 1 ? 'alpha(opacity=' + value * 100 + ')' : '' - var filter = style.filter || '' - style.zoom = 1 - //不能使用以下方式设置透明度 - //node.filters.alpha.opacity = value * 100 - style.filter = (ralpha.test(filter) ? - filter.replace(ralpha, opacity) : - filter + ' ' + opacity).trim() - if (!style.filter) { - style.removeAttribute('filter') - } - } - cssHooks['opacity:get'] = function (node) { - //这是最快的获取IE透明值的方式,不需要动用正则了! - var alpha = node.filters.alpha || node.filters[salpha], - op = alpha && alpha.enabled ? alpha.opacity : 100 - return (op / 100) + '' //确保返回的是字符串 - } - } - - 'top,left'.replace(avalon.rword, function (name) { - cssHooks[name + ':get'] = function (node) { - var computed = cssHooks['@:get'](node, name) - return /px$/.test(computed) ? computed : - avalon(node).position()[name] + 'px' + if (rformElement.test(node.nodeName)) { + ret.type = node.type } - }) - - var cssShow = { - position: 'absolute', - visibility: 'hidden', - display: 'block' + var style = node.style.cssText + if (style) { + ret.style = style + } + //类名 = 去重(静态类名+动态类名+ hover类名? + active类名) + if (ret.type === 'select-one') { + ret.selectedIndex = node.selectedIndex + } + if (isEmpty(ret)) { + return null + } + return ret } - var rdisplayswap = /^(none|table(?!-c[ea]).+)/ - - function showHidden(node, array) { - //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html - if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 - if (rdisplayswap.test(cssHooks['@:get'](node, 'display'))) { - var obj = { - node: node - } - for (var name in cssShow) { - obj[name] = node.style[name] - node.style[name] = cssShow[name] - } - array.push(obj) - } - var parent = node.parentNode - if (parent && parent.nodeType === 1) { - showHidden(parent, array) - } + function isEmpty(a) { + for (var i in a) { + return false } + return true } - avalon.each({ - Width: 'width', - Height: 'height' - }, function (name, method) { - var clientProp = 'client' + name, - scrollProp = 'scroll' + name, - offsetProp = 'offset' + name - cssHooks[method + ':get'] = function (node, which, override) { - var boxSizing = -4 - if (typeof override === 'number') { - boxSizing = override + function toHTML(a) { + return avalon.vdomAdaptor(a, 'toHTML') + } + + function createVDOM(node) { + var ret = {} + var type = node.nodeName.toLowerCase() + ret.nodeName = type + ret.dom = node + if (type.charAt(0) === '#') {//2, 8 + var nodeValue = node.nodeValue + if (/\S/.test(nodeValue)) { + ret.nodeValue = nodeValue } - which = name === 'Width' ? ['Left', 'Right'] : ['Top', 'Bottom'] - var ret = node[offsetProp] // border-box 0 - if (boxSizing === 2) { // margin-box 2 - return ret + avalon.css(node, 'margin' + which[0], true) + avalon.css(node, 'margin' + which[1], true) + } else { + var props = getAttributes(node) + if (voidTag[type]) { + ret.isVoidTag = true } - if (boxSizing < 0) { // padding-box -2 - ret = ret - avalon.css(node, 'border' + which[0] + 'Width', true) - avalon.css(node, 'border' + which[1] + 'Width', true) - } - if (boxSizing === -4) { // content-box -4 - ret = ret - avalon.css(node, 'padding' + which[0], true) - avalon.css(node, 'padding' + which[1], true) - } - return ret - } - cssHooks[method + '&get'] = function (node) { - var hidden = [] - showHidden(node, hidden) - var val = cssHooks[method + ':get'](node) - for (var i = 0, obj; obj = hidden[i++]; ) { - node = obj.node - for (var n in obj) { - if (typeof obj[n] === 'string') { - node.style[n] = obj[n] - } - } - } - return val - } - avalon.fn[method] = function (value) { //会忽视其display - var node = this[0] - if (arguments.length === 0) { - if (node.setTimeout) { //取得窗口尺寸 - return node['inner' + name] || - node.document.documentElement[clientProp] || - node.document.body[clientProp] //IE6下前两个分别为undefined,0 - } - if (node.nodeType === 9) { //取得页面尺寸 - var doc = node.documentElement - //FF chrome html.scrollHeight< body.scrollHeight - //IE 标准模式 : html.scrollHeight> body.scrollHeight - //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? - return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) - } - return cssHooks[method + '&get'](node) - } else { - return this.css(method, value) + if (props) { + ret.props = props } - } - avalon.fn['inner' + name] = function () { - return cssHooks[method + ':get'](this[0], void 0, -2) - } - avalon.fn['outer' + name] = function (includeMargin) { - return cssHooks[method + ':get'](this[0], void 0, includeMargin === true ? 2 : 0) - } - }) - - avalon.fn.offset = function () { //取得距离页面左右角的坐标 - var node = this[0], - box = { - left: 0, - top: 0 - } - if (!node || !node.tagName || !node.ownerDocument) { - return box - } - var doc = node.ownerDocument, - body = doc.body, - root = doc.documentElement, - win = doc.defaultView || doc.parentWindow - if (!avalon.contains(root, node)) { - return box - } - //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的 - //我们可以通过getBoundingClientRect来获得元素相对于client的rect. - //http://msdn.microsoft.com/en-us/library/ms536433.aspx - if (node.getBoundingClientRect) { - box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone) - } - //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop - var clientTop = root.clientTop || body.clientTop, - clientLeft = root.clientLeft || body.clientLeft, - scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop), - scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft) - // 把滚动距离加到left,top中去。 - // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它 - // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx - return { - top: box.top + scrollTop - clientTop, - left: box.left + scrollLeft - clientLeft - } - } - //生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法 - avalon.each({ - scrollLeft: 'pageXOffset', - scrollTop: 'pageYOffset' - }, function (method, prop) { - avalon.fn[method] = function (val) { - var node = this[0] || {}, - win = getWindow(node), - top = method === 'scrollTop' - if (!arguments.length) { - return win ? (prop in win) ? win[prop] : root[method] : node[method] - } else { - if (win) { - win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop()) - } else { - node[method] = val + ret.children = createVDOMs(node.childNodes, node) + if (props && 'selectedIndex' in ret) { + node.selectedIndex = ret.selectedIndex + delete ret.selectedIndex + if (isEmpty(props)) { + delete ret.props } } } - }) - - function getWindow(node) { - return node.window || node.defaultView || node.parentWindow || false + return ret } - - /***/ }, - /* 28 */ - /***/ function(module, exports, __webpack_require__) { - - var avalon = __webpack_require__(4) - function getValType(elem) { - var ret = elem.tagName.toLowerCase() - return ret === 'input' && /checkbox|radio/.test(elem.type) ? 'checked' : ret + //根据 outerHTML 创建 虚拟DOM + function render(node) { + return createVDOMs([node], null) } - var roption = /^]+))?)*\s+value[\s=]/i - var valHooks = { - 'option:get': avalon.msie ? function (node) { - //在IE11及W3C,如果没有指定value,那么node.value默认为node.text(存在trim作),但IE9-10则是取innerHTML(没trim操作) - //specified并不可靠,因此通过分析outerHTML判定用户有没有显示定义value - return roption.test(node.outerHTML) ? node.value : node.text.trim() - } : function (node) { - return node.value - }, - 'select:get': function (node, value) { - var option, options = node.options, - index = node.selectedIndex, - getter = valHooks['option:get'], - one = node.type === 'select-one' || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? max : one ? index : 0 - for (; i < max; i++) { - option = options[i] - //IE6-9在reset后不会改变selected,需要改用i === index判定 - //我们过滤所有disabled的option元素,但在safari5下, - //如果设置optgroup为disable,那么其所有孩子都disable - //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 - if ((option.selected || i === index) && !option.disabled && - (!option.parentNode.disabled || option.parentNode.tagName !== 'OPTGROUP') - ) { - value = getter(option) - if (one) { - return value + function createVDOMs(nodes, parent) { + var arr = [] + nodes = avalon.slice(nodes) + for (var i = 0; i < nodes.length; i++) { + var node = nodes[i] + switch (node.nodeType) { + case 1: + + var a = node.getAttributeNode(':for') || node.getAttributeNode('ms-for') + + if (a) { + var start = document.createComment('ms-for:' + a.value) + var end = document.createComment('ms-for-end:') + node.removeAttributeNode(a) + + if (parent) { + parent.insertBefore(end, node.nextSibling) + parent.insertBefore(start, node) + } + arr.push(createVDOM(start), createVDOM(node), createVDOM(end)) + + } else { + arr.push(createVDOM(node)) } - //收集所有selected值组成数组返回 - values.push(value) - } - } - return values - }, - 'select:set': function (node, values, optionSet) { - values = [].concat(values) //强制转换为数组 - var getter = valHooks['option:get'] - for (var i = 0, el; el = node.options[i++]; ) { - if ((el.selected = values.indexOf(getter(el)) > -1)) { - optionSet = true - } - } - if (!optionSet) { - node.selectedIndex = -1 + break + case 3: + if (/\S/.test(node.nodeValue)) { + arr.push(createVDOM(node)) + } else { + removeNode(node) + } + break + case 8: + arr.push(createVDOM(node)) } } + return arr } - avalon.fn.val = function (value) { - var node = this[0] - if (node && node.nodeType === 1) { - var get = arguments.length === 0 - var access = get ? ':get' : ':set' - var fn = valHooks[getValType(node) + access] - if (fn) { - var val = fn(node, value) - } else if (get) { - return (node.value || '').replace(/\r/g, '') - } else { - node.value = value - } - } - return get ? val : this + var f = avalon.avalonFragment + function removeNode(node) { + f.appendChild(node) + f.removeChild(node) + return node } - /***/ }, - /* 29 */ - /***/ function(module, exports, __webpack_require__) { - var Cache = __webpack_require__(30) - var avalon = __webpack_require__(4) + module.exports = render - var fixCloneNode = __webpack_require__(31) - var rhtml = /<|&#?\w+;/ - var htmlCache = new Cache(128) - var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig - avalon.parseHTML = function (html) { - var fragment = avalon.avalonFragment.cloneNode(false) - //处理非字符串 - if (typeof html !== 'string') { - return fragment - } - //处理非HTML字符串 - if (!rhtml.test(html)) { - return document.createTextNode(html) - } + /***/ }, + /* 37 */ + /***/ function(module, exports) { - html = html.replace(rxhtml, '<$1>').trim() - var hasCache = htmlCache.get(html) - if (hasCache) { - return fixCloneNode(hasCache) - } - var vnodes = avalon.lexer(html) - for (var i = 0, el; el = vnodes[i++]; ) { - fragment.appendChild(avalon.vdomAdaptor(el, 'toDOM')) - } - if (html.length < 1024) { - htmlCache.put(html, fixCloneNode(fragment)) - } - return fragment - } + module.exports = avalon.oneObject('area,base,basefont,bgsound,br,col,command,embed,' + + 'frame,hr,img,input,keygen,link,meta,param,source,track,wbr') - avalon.innerHTML = function (node, html) { - - var parsed = this.parseHTML(html) - this.clearHTML(node).appendChild(parsed) - } + /***/ }, + /* 38 */ + /***/ function(module, exports, __webpack_require__) { - var reunescapeHTML = /&(?:amp|lt|gt|quot|#39|#96);/g - var htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'", - '`': '`' - } - avalon.unescapeHTML = function (string) { - var str = '' + string - return str.replace(reunescapeHTML, function (c) { - return htmlUnescapes[c] - }) - } + /* + * 本模块是用于将虚拟DOM变成一个函数 + */ + + var extractBindings = __webpack_require__(39) + var stringify = __webpack_require__(40) + var parseExpr = __webpack_require__(41) + var decode = __webpack_require__(17) + var config = avalon.config + var quote = avalon.quote + var rident = /^[$a-zA-Z_][$a-zA-Z0-9_]*$/ + var rstatement = /^\s*var\s+([$\w]+)\s*\=\s*\S+/ + var skips = {__local__: 1, vmode: 1, dom: 1} + function parseNodes(source, inner) { + //ms-important, ms-controller , ms-for 不可复制,省得死循环 + //ms-important --> ms-controller --> ms-for --> ms-widget --> ms-effect --> ms-if + var buffer = inner ? [] : ['\nvar vnodes = [];'] - avalon.clearHTML = function (node) { - node.textContent = '' - while (node.lastChild) { - node.removeChild(node.lastChild) + for (var i = 0, el; el = source[i++]; ) { + var vnode = parseNode(el) + if (el.$prepend) { + buffer.push(el.$prepend) + } + var append = el.$append + delete el.$append + delete el.$prepend + if (vnode) { + buffer.push(vnode + '\n') + } + if (append) { + buffer.push(append) + } } - return node + if (!inner) { + buffer.push('return vnodes\n') + } + return buffer.join('\n') } - /***/ }, - /* 30 */ - /***/ function(module, exports) { - - /* - https://github.com/rsms/js-lru - entry entry entry entry - ______ ______ ______ ______ - | head |.newer => | |.newer => | |.newer => | tail | - | A | | B | | C | | D | - |______| <= older.|______| <= older.|______| <= older.|______| - - removed <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- <-- added - */ - function LRU(maxLength) { - // 标识当前缓存数组的大小 - this.size = 0 - // 标识缓存数组能达到的最大长度 - this.limit = maxLength - // head(最不常用的项),tail(最常用的项)全部初始化为undefined - - this.head = this.tail = void 0 - this._keymap = {} - } + function parseNode(vdom) { + if (!vdom.nodeName) + return false + switch (vdom.nodeName) { + case '#text': + if (vdom.dynamic) { + return add(parseText(vdom)) + } else { + return addTag(vdom) + } - var p = LRU.prototype + case '#comment': + var nodeValue = vdom.nodeValue + if (vdom.forExpr) {// 处理ms-for指令 + var copy = { + dynamic: true, + vmodel: '__vmodel__' + } + for (var i in vdom) { + if (vdom.hasOwnProperty(i) && !skips[i]) { + copy[i] = vdom[i] + } + } + avalon.directives['for'].parse(copy, vdom, vdom) - p.put = function (key, value) { - var entry = { - key: key, - value: value - } - this._keymap[key] = entry - if (this.tail) { - // 如果存在tail(缓存数组的长度不为0),将tail指向新的 entry + vdom.$append += avalon.caches[vdom.signature] //vdom.template + return addTag(copy) + } else if (nodeValue === 'ms-for-end:') { + vdom.$append = addTag({ + nodeName: '#comment', + nodeValue: vdom.signature - this.tail.newer = entry - entry.older = this.tail - } else { - // 如果缓存数组的长度为0,将head指向新的entry - this.head = entry - } - this.tail = entry - // 如果缓存数组达到上限,则先删除 head 指向的缓存对象 - if (this.size === this.limit) { - this.shift() - } else { - this.size++ - } - return value - } + }) + + ' return vnodes}\n })\n},__local__,vnodes)\n' + + addTag({ + nodeName: "#comment", + signature: vdom.signature, + nodeValue: "ms-for-end:" + }) + '\n' + return '' - p.shift = function () { - var entry = this.head - if (entry) { - // 删除 head ,并改变指向 - this.head = this.head.newer - // 同步更新 _keymap 里面的属性值 - this.head.older = - entry.newer = - entry.older = - this._keymap[entry.key] = - void 0 - delete this._keymap[entry.key] //#1029 - // 同步更新 缓存数组的长度 - this.size-- - } - } - p.get = function (key) { - var entry = this._keymap[key] - // 如果查找不到含有`key`这个属性的缓存对象 - if (entry === void 0) - return - // 如果查找到的缓存对象已经是 tail (最近使用过的) - if (entry === this.tail) { - return entry.value - } - // HEAD--------------TAIL - // <.older .newer> - // <--- add direction -- - // A B C E - if (entry.newer) { - // 处理 newer 指向 - if (entry === this.head) { - // 如果查找到的缓存对象是 head (最近最少使用过的) - // 则将 head 指向原 head 的 newer 所指向的缓存对象 - this.head = entry.newer - } - // 将所查找的缓存对象的下一级的 older 指向所查找的缓存对象的older所指向的值 - // 例如:A B C D E - // 如果查找到的是D,那么将E指向C,不再指向D - entry.newer.older = entry.older // C <-- E. - } - if (entry.older) { - // 处理 older 指向 - // 如果查找到的是D,那么C指向E,不再指向D - entry.older.newer = entry.newer // C. --> E - } - // 处理所查找到的对象的 newer 以及 older 指向 - entry.newer = void 0 // D --x - // older指向之前使用过的变量,即D指向E - entry.older = this.tail // D. --> E - if (this.tail) { - // 将E的newer指向D - this.tail.newer = entry // E. <-- D - } - // 改变 tail 为D - this.tail = entry - return entry.value - } + } else if (nodeValue.indexOf('ms-js:') === 0) {//插入JS声明语句 + var statement = parseExpr(nodeValue.replace('ms-js:', ''), 'js') + '\n' + var ret = addTag(vdom) + var match = statement.match(rstatement) + if (match && match[1]) { + vdom.$append = (vdom.$append || '') + statement + + "\n__local__." + match[1] + ' = ' + match[1] + '\n' + } else { + avalon.warn(nodeValue + ' parse fail!') + } + return ret + } else { + return addTag(vdom) + } + default: + if (!vdom.dynamic && vdom.skipContent) { + return addTag(vdom) + } - module.exports = LRU + var copy = { + nodeName: vdom.nodeName + } + var props = vdom.props + if (vdom.dynamic) { + copy.dynamic = '{}' + var bindings = extractBindings(copy, props) + bindings.map(function (b) { + //将ms-*的值变成函数,并赋给copy.props[ms-*] + //如果涉及到修改结构,则在source添加$append,$prepend + avalon.directives[b.type].parse(copy, vdom, b) + return b.name + }) - /***/ }, - /* 31 */ - /***/ function(module, exports) { + } else if (props) { + copy.props = {} + for (var i in props) { + copy.props[i] = props[i] + } + } - var rcheckedType = /radio|checkbox/ + if (vdom.isVoidTag) { + copy.isVoidTag = true + } else { + if (!('children' in copy)) { + var c = vdom.children + if (c) { + if (vdom.skipContent) { + copy.children = '[' + c.map(function (a) { + return stringify(a) + }) + ']' + } else if (c.length === 1 && c[0].nodeName === '#text') { - function fix(dest, src) { - if (dest.nodeType !== 1) { - return - } - var nodeName = dest.nodeName.toLowerCase() - if (nodeName === 'object') { - if (dest.parentNode) { - dest.outerHTML = src.outerHTML - } + if (c[0].dynamic) { + copy.children = '[' + parseText(c[0]) + ']' + } else { + copy.children = '[' + stringify(c[0]) + ']' + } - } else if (nodeName === 'input' && rcheckedType.test(src.nodeName)) { + } else { - dest.defaultChecked = dest.checked = src.checked + copy.children = '(function(){' + parseNodes(c) + '})()' + } + } + } + } + if (vdom.template) + copy.template = vdom.template + if (vdom.skipContent) + copy.skipContent = true - if (dest.value !== src.value) { - dest.value = src.value - } + return addTag(copy) - } else if (nodeName === 'option') { - dest.defaultSelected = dest.selected = src.defaultSelected - } else if (nodeName === 'input' || nodeName === 'textarea') { - dest.defaultValue = src.defaultValue } + } + module.exports = parseNodes - function getAll(context) { - return typeof context.getElementsByTagName !== 'undefined' ? - context.getElementsByTagName('*') : - typeof context.querySelectorAll !== 'undefined' ? - context.querySelectorAll('*') : [] + function wrapDelimiter(expr) { + return rident.test(expr) ? expr : parseExpr(expr, 'text') } - function fixCloneNode(src) { - var target = src.cloneNode(true) - if (avalon.modern) - return target - var t = getAll(target) - var s = getAll(src) - avalon.each(s, function (i) { - fix(t[i], s[i]) - }) - return target + function add(a) { + return 'vnodes.push(' + a + ');' + } + function addTag(obj) { + return add(stringify(obj)) } - module.exports = fixCloneNode + function parseText(el) { + var array = extractExpr(el.nodeValue)//返回一个数组 + var nodeValue = '' + if (array.length === 1) { + nodeValue = wrapDelimiter(array[0].expr) + } else { + var token = array.map(function (el) { + return el.type ? wrapDelimiter(el.expr) : quote(el.expr) + }).join(' + ') + nodeValue = 'String(' + token + ')' + } + return '{\nnodeName: "#text",\ndynamic:true,\nnodeValue: ' + nodeValue + '\n}' + } - /***/ }, - /* 32 */ - /***/ function(module, exports, __webpack_require__) { + var rlineSp = /\n\s*/g - var avalon = __webpack_require__(4) - var document = avalon.document - var root = avalon.root - var window = avalon.window + function extractExpr(str) { + var ret = [] + do {//aaa{{@bbb}}ccc + var index = str.indexOf(config.openTag) + index = index === -1 ? str.length : index + var value = str.slice(0, index) + if (/\S/.test(value)) { + ret.push({expr: decode(value)}) + } + str = str.slice(index + config.openTag.length) + if (str) { + index = str.indexOf(config.closeTag) + var value = str.slice(0, index) + ret.push({ + expr: avalon.unescapeHTML(value.replace(rlineSp, '')), + type: '{{}}' + }) + str = str.slice(index + config.closeTag.length) + } + } while (str.length) + return ret + } - var W3C = avalon.modern - var getShortID = __webpack_require__(6).getShortID - //http://www.feiesoft.com/html/events.html - //http://segmentfault.com/q/1010000000687977/a-1020000000688757 - var canBubbleUp = __webpack_require__(33) + /***/ }, + /* 39 */ + /***/ function(module, exports) { - if (!W3C) { - delete canBubbleUp.change - delete canBubbleUp.select - } + var directives = avalon.directives + var rbinding = /^(\:|ms\-)\w+/ + var eventMap = avalon.oneObject('animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit') - var eventHooks = avalon.eventHooks - /*绑定事件*/ - avalon.bind = function (elem, type, fn) { - if (elem.nodeType === 1) { - var value = elem.getAttribute('avalon-events') || '' - //如果是使用ms-on-*绑定的回调,其uuid格式为e12122324, - //如果是使用bind方法绑定的回调,其uuid格式为_12 - var uuid = getShortID(fn) - var hook = eventHooks[type] - if (hook) { - type = hook.type || type - if (hook.fix) { - fn = hook.fix(elem, fn) - fn.uuid = uuid + function extractBindings(cur, props) { + var bindings = [] + var attrs = {} + var skip = 'ms-skip' in props//old + var uniq = {} + for (var i in props) { + var value = props[i], match + attrs[i] = props[i] + if ((match = i.match(rbinding))) { + if (skip) + continue + + var arr = i.replace(match[1], '').split('-') + + if (eventMap[arr[0]]) { + arr.unshift('on') } - } - var key = type + ':' + uuid - avalon.eventListeners[fn.uuid] = fn - if (value.indexOf(type + ':') === -1) {//同一种事件只绑定一次 - if (canBubbleUp[type] || (avalon.modern && focusBlur[type])) { - delegateEvent(type) - } else { - nativeBind(elem, type, dispatch) + if (arr[0] === 'on') { + arr[2] = parseFloat(arr[2]) || 0 } - } - var keys = value.split(',') - if (keys[0] === '') { - keys.shift() - } - if (keys.indexOf(key) === -1) { - keys.push(key) - elem.setAttribute('avalon-events', keys.join(',')) - //将令牌放进avalon-events属性中 - } + arr.unshift('ms') + var type = arr[1] + if (directives[type]) { + var binding = { + type: type, + param: arr[2], + name: arr.join('-'), + expr: value, + priority: directives[type].priority || type.charCodeAt(0) * 100 + } - } else { - nativeBind(elem, type, fn) + if (type === 'on') { + binding.priority += arr[3] + } + if (!uniq[binding.name]) { + uniq[binding.name] = value + bindings.push(binding) + } + } + } } - return fn //兼容之前的版本 + + cur.props = attrs + + bindings.sort(byPriority) + + return bindings } - avalon.unbind = function (elem, type, fn) { - if (elem.nodeType === 1) { - var value = elem.getAttribute('avalon-events') || '' - switch (arguments.length) { - case 1: - nativeUnBind(elem, type, dispatch) - elem.removeAttribute('avalon-events') - break - case 2: - value = value.split(',').filter(function (str) { - return str.indexOf(type + ':') === -1 - }).join(',') - elem.setAttribute('avalon-events', value) - break - default: - var search = type + ':' + fn.uuid - value = value.split(',').filter(function (str) { - return str !== search - }).join(',') - elem.setAttribute('avalon-events', value) - delete avalon.eventListeners[fn.uuid] - break - } - } else { - nativeUnBind(elem, type, fn) - } + function byPriority(a, b) { + return a.priority - b.priority } - var typeRegExp = {} - function collectHandlers(elem, type, handlers) { - var value = elem.getAttribute('avalon-events') - if (value && (elem.disabled !== true || type !== 'click')) { - var uuids = [] - var reg = typeRegExp[type] || (typeRegExp[type] = new RegExp("\\b" + type + '\\:([^,\\s]+)', 'g')) - value.replace(reg, function (a, b) { - uuids.push(b) - return a - }) - if (uuids.length) { - handlers.push({ - elem: elem, - uuids: uuids - }) - } - } - elem = elem.parentNode - var g = avalon.gestureEvents || {} - if (elem && elem.getAttribute && (canBubbleUp[type] || g[type])) { - collectHandlers(elem, type, handlers) - } + module.exports = extractBindings + + + /***/ }, + /* 40 */ + /***/ function(module, exports) { + var keyMap = avalon.oneObject("break,case,catch,continue,debugger,default,delete,do,else,false," + + "finally,for,function,if,in,instanceof,new,null,return,switch,this," + + "throw,true,try,typeof,var,void,while,with," + /* 关键字*/ + "abstract,boolean,byte,char,class,const,double,enum,export,extends," + + "final,float,goto,implements,import,int,interface,long,native," + + "package,private,protected,public,short,static,super,synchronized," + + "throws,transient,volatile") + avalon.keyMap = keyMap + var quoted = { + nodeName: 1, + template: 1, + forExpr: 1, + type: 1, + nodeValue: 1, + signature: 1, + wid: 1 } - var rhandleHasVm = /^e/ - var stopImmediate = false - function dispatch(event) { - event = new avEvent(event) - var type = event.type - var elem = event.target - var handlers = [] - collectHandlers(elem, type, handlers) - var i = 0, j, uuid, handler - while ((handler = handlers[i++]) && !event.cancelBubble) { - var host = event.currentTarget = handler.elem - j = 0 - while ((uuid = handler.uuids[ j++ ])) { - if (stopImmediate) { - stopImmediate = false - break - } - var fn = avalon.eventListeners[uuid] - if (fn) { - var vm = rhandleHasVm.test(uuid) ? handler.elem._ms_context_ : 0 - if (vm && vm.$hashcode === false) { - return avalon.unbind(elem, type, fn) - } - var ret = fn.call(vm || elem, event, host._ms_local) + var rneedQuote = /[W-]/ + var quote = avalon.quote + function fixKey(k) { + return (rneedQuote.test(k) || keyMap[k]) ? quote(k) : k + } - if (ret === false) { - event.preventDefault() - event.stopPropagation() + function stringify(obj) { + var arr1 = [] + //字符不用东西包起来就变成变量 + for (var i in obj) { + var type = typeof obj[i] + if (type === 'object') { + if (i === 'props' ) { + var arr2 = [] + for (var k in obj.props) { + var kv = obj.props[k] + if (typeof kv === 'string') { + kv = quote(kv) + } + arr2.push(fixKey(k) + ': ' + kv) } + arr1.push(i+': {' + arr2.join(',\n') + '}') + + } else if (i === 'children') { + arr1.push('children: [' + obj[i].map(function (a) { + return stringify(a) + }) + ']') + } + } else if (obj.hasOwnProperty(i)) { + var v = obj[i] + if (type === 'string') { + v = quoted[i] ? quote(v) : v } + arr1.push(fixKey(i) + ':' + v) } } + return '{\n' + arr1.join(',\n') + '}' } - var focusBlur = { - focus: true, - blur: true - } - var nativeBind = W3C ? function (el, type, fn, capture) { - el.addEventListener(type, fn, capture) - } : function (el, type, fn) { - el.attachEvent('on' + type, fn) - } - var nativeUnBind = W3C ? function (el, type, fn) { - el.removeEventListener(type, fn) - } : function (el, type, fn) { - el.detachEvent('on' + type, fn) - } + module.exports = stringify - function delegateEvent(type) { - var value = root.getAttribute('delegate-events') || '' - if (value.indexOf(type) === -1) { - var arr = value.match(avalon.rword) || [] - arr.push(type) - root.setAttribute('delegate-events', arr.join(',')) - nativeBind(root, type, dispatch, !!focusBlur[type]) - } - } - avalon.fireDom = function (elem, type, opts) { - if (document.createEvent) { - var hackEvent = document.createEvent('Events') - hackEvent.initEvent(type, true, true, opts) - avalon.shadowCopy(hackEvent, opts) + /***/ }, + /* 41 */ + /***/ function(module, exports, __webpack_require__) { - elem.dispatchEvent(hackEvent) - } else if (root.contains(elem)) {//IE6-8触发事件必须保证在DOM树中,否则报'SCRIPT16389: 未指明的错误' - hackEvent = document.createEventObject() - avalon.shadowCopy(hackEvent, opts) - elem.fireEvent('on' + type, hackEvent) - } - } + - var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ - var rconstant = /^[A-Z_]+$/ - function avEvent(event) { - if (event.originalEvent) { - return this - } - for (var i in event) { - if (!rconstant.test(i) && typeof event[i] !== 'function') { - this[i] = event[i] - } - } - if (!this.target) { - this.target = event.srcElement - } - var target = this.target - if (this.which == null && event.type.indexOf('key') === 0) { - this.which = event.charCode != null ? event.charCode : event.keyCode - } else if (rmouseEvent.test(event.type) && !('pageX' in this)) { - var doc = target.ownerDocument || document - var box = doc.compatMode === 'BackCompat' ? doc.body : doc.documentElement - this.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) - this.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) - this.wheelDeltaY = this.wheelDelta - this.wheelDeltaX = 0 - } - this.timeStamp = new Date() - 0 - this.originalEvent = event - } - avEvent.prototype = { - preventDefault: function () { - var e = this.originalEvent || {} - e.returnValue = this.returnValue = false - if (e.preventDefault) { - e.preventDefault() - } - }, - stopPropagation: function () { - var e = this.originalEvent || {} - e.cancelBubble = this.cancelBubble = true - if (e.stopPropagation) { - e.stopPropagation() - } - }, - stopImmediatePropagation: function () { - stopImmediate = true; - this.stopPropagation() - }, - toString: function () { - return '[object Event]'//#1619 - } - } + //缓存求值函数,以便多次利用 + var evaluatorPool = __webpack_require__(42) - //针对firefox, chrome修正mouseenter, mouseleave - if (!('onmouseenter' in root)) { - avalon.each({ - mouseenter: 'mouseover', - mouseleave: 'mouseout' - }, function (origType, fixType) { - eventHooks[origType] = { - type: fixType, - fix: function (elem, fn) { - return function (e) { - var t = e.relatedTarget - if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { - delete e.type - e.type = origType - return fn.apply(this, arguments) - } - } - } - } - }) - } - //针对IE9+, w3c修正animationend - avalon.each({ - AnimationEvent: 'animationend', - WebKitAnimationEvent: 'webkitAnimationEnd' - }, function (construct, fixType) { - if (window[construct] && !eventHooks.animationend) { - eventHooks.animationend = { - type: fixType - } - } - }) - //针对IE6-8修正input - if (!('oninput' in document.createElement('input'))) { - eventHooks.input = { - type: 'propertychange', - fix: function (elem, fn) { - return function (e) { - if (e.propertyName === 'value') { - e.type = 'input' - return fn.apply(this, arguments) - } - } - } - } - } - if (document.onmousewheel === void 0) { - /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 - firefox DOMMouseScroll detail 下3 上-3 - firefox wheel detlaY 下3 上-3 - IE9-11 wheel deltaY 下40 上-40 - chrome wheel deltaY 下100 上-100 */ - var fixWheelType = document.onwheel !== void 0 ? 'wheel' : 'DOMMouseScroll' - var fixWheelDelta = fixWheelType === 'wheel' ? 'deltaY' : 'detail' - eventHooks.mousewheel = { - type: fixWheelType, - fix: function (elem, fn) { - return function (e) { - var delta = e[fixWheelDelta] > 0 ? -120 : 120 - e.wheelDelta = ~~elem._ms_wheel_ + delta - elem._ms_wheel_ = e.wheelDeltaY = e.wheelDelta + var rregexp = /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/g + var rstring = /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/g + var rfill = /\?\?\d+/g + var brackets = /\(([^)]*)\)/ - e.wheelDeltaX = 0 - if (Object.defineProperty) { - Object.defineProperty(e, 'type', { - value: 'mousewheel' - }) - } - return fn.apply(this, arguments) + var rshortCircuit = /\|\|/g + var rpipeline = /\|(?=\w)/ + var ruselessSp = /\s*(\.|\|)\s*/g + + var rAt = /(^|[^\w\u00c0-\uFFFF_])(@|##)(?=[$\w])/g + var rhandleName = /^(?:\@|##)[$\w\.]+$/i + + var rfilters = /\|.+/g + var rvar = /((?:\@|\$|\#\#)?\w+)/g + + function collectLocal(str, ret) { + var arr = str.replace(rfilters, '').match(rvar) + if (arr) { + arr.filter(function (el) { + if (!/^[@\d\-]/.test(el) && + el.slice(0, 2) !== '##' && + el !== '$event' && !avalon.keyMap[el]) { + ret[el] = 1 } - } + }) } } - avalon.fn.bind = function (type, fn, phase) { - if (this[0]) { //此方法不会链 - return avalon.bind(this[0], type, fn, phase) + function extLocal(ret) { + var arr = [] + for (var i in ret) { + arr.push('var ' + i + ' = __local__[' + avalon.quote(i) + ']') } + return arr } - avalon.fn.unbind = function (type, fn, phase) { - if (this[0]) { - avalon.unbind(this[0], type, fn, phase) + function parseExpr(str, category) { + var binding = {} + category = category || 'other' + if (typeof str === 'object') { + category = str.type + binding = str + str = binding.expr } - return this - } - - - /***/ }, - /* 33 */ - /***/ function(module, exports) { - - //http://www.feiesoft.com/html/events.html - //http://segmentfault.com/q/1010000000687977/a-1020000000688757 - module.exports = { - click: true, - dblclick: true, - keydown: true, - keypress: true, - keyup: true, - mousedown: true, - mousemove: true, - mouseup: true, - mouseover: true, - mouseout: true, - wheel: true, - mousewheel: true, - input: true, - change: true, - beforeinput: true, - compositionstart: true, - compositionupdate: true, - compositionend: true, - select: true, - //http://blog.csdn.net/lee_magnum/article/details/17761441 - cut: true, - copy: true, - paste: true, - beforecut: true, - beforecopy: true, - beforepaste: true, - focusin: true, - focusout: true, - DOMFocusIn: true, - DOMFocusOut: true, - DOMActivate: true, - dragend: true, - datasetchanged: true - } - - /***/ }, - /* 34 */ - /***/ function(module, exports, __webpack_require__) { - - var avalon = __webpack_require__(4) - var scan = __webpack_require__(35) - var document = avalon.document - - var readyList = [], isReady - var fireReady = function (fn) { - isReady = true + if (typeof str !== 'string') + return '' + var cacheID = str + var cacheStr = evaluatorPool.get(category + ':' + cacheID) - while (fn = readyList.shift()) { - fn(avalon) + if (cacheStr) { + return cacheStr } - } - avalon.ready = function (fn) { - if (!isReady) { - readyList.push(fn) - } else { - fn(avalon) + + var number = 1 + //相同的表达式生成相同的函数 + var maps = {} + function dig(a) { + var key = '??' + number++ + maps[key] = a + return key } - } - avalon.ready(function () { - scan(document.body) - }) + function fill(a) { + return maps[a] + } - new function () { - if (!avalon.browser) - return - var root = avalon.root + var input = str.replace(rregexp, dig).//移除所有正则 + replace(rstring, dig).//移除所有字符串 - function doScrollCheck() { - try { //IE下通过doScrollCheck检测DOM树是否建完 - root.doScroll('left') - fireReady() - } catch (e) { - setTimeout(doScrollCheck) - } + replace(rshortCircuit, dig).//移除所有短路或 + replace(ruselessSp, '$1').//移除. |两端空白 + split(rpipeline) //使用管道符分离所有过滤器及表达式的正体 + //还原body + var _body = input.shift() + var local = {} + var body = _body.replace(rfill, fill).trim() + if (category === 'on' && rhandleName.test(body)) { + body = body + '($event)' } - if (document.readyState === 'complete') { - setTimeout(fireReady) //如果在domReady之外加载 - } else if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', fireReady) - } else if (document.attachEvent) { - document.attachEvent('onreadystatechange', function () { - if (document.readyState === 'complete') { - fireReady() - } - }) - try { - var isTop = window.frameElement === null - } catch (e) { - } - if (root.doScroll && isTop && window.external) {//fix IE iframe BUG - doScrollCheck() - } + body = body.replace(rAt, '$1__vmodel__.') + if (category === 'js') { + return evaluatorPool.put(category + ':' + cacheID, body) + } else if (category === 'on') { + collectLocal(_body, local) } - avalon.bind(window, 'load', fireReady) - } - + //处理表达式的过滤器部分 + var filters = input.map(function (str) { + collectLocal(str.replace(/^\w+/g, ""), local) + str = str.replace(rfill, fill).replace(rAt, '$1__vmodel__.') //还原 + var hasBracket = false + str = str.replace(brackets, function (a, b) { + hasBracket = true + return /\S/.test(b) ? + '(__value__,' + b + ');' : + '(__value__);' + }) + if (!hasBracket) { + str += '(__value__);' + } + str = str.replace(/(\w+)/, 'avalon.__format__("$1")') + return '__value__ = ' + str + }) + var ret = [] + if (category === 'on') { + filters = filters.map(function (el) { + return el.replace(/__value__/g, '$event') + }) + if (filters.length) { + filters.push('if($event.$return){\n\treturn;\n}') + } + if (!avalon.modern) { + body = body.replace(/__vmodel__\.([^(]+)\(([^)]*)\)/, function (a, b, c) { + return '__vmodel__.' + b + ".call(__vmodel__" + (/\S/.test(c) ? ',' + c : "") + ")" + }) + } + ret = ['function ($event, __local__){', + 'try{', + extLocal(local).join('\n'), + '\tvar __vmodel__ = this;', + '\t' + body, + '}catch(e){', + quoteError(str, category), + '}', + '}'] + filters.unshift(2, 0) + } else if (category === 'duplex') { + //给vm同步某个属性 + var setterBody = [ + 'function (__vmodel__,__value__){', + 'try{', + '\t' + body + ' = __value__', + '}catch(e){', + quoteError(str, category).replace('parse', 'set'), + '}', + '}'] + evaluatorPool.put('duplex:set:' + cacheID, setterBody.join('\n')) + //对某个值进行格式化 - /***/ }, - /* 35 */ - /***/ function(module, exports, __webpack_require__) { + var getterBody = [ + 'function (__vmodel__){', + 'try{', + 'var __value__ = ' + body + '\n', + filters.join('\n'), + 'return __value__\n', + '}catch(e){', + quoteError(str, category).replace('parse', 'get'), + '}', + '}'].join('\n') + evaluatorPool.put('duplex:get:' + cacheID, getterBody) - var onceWarn = true //只警告一次 - var dom2vdom = __webpack_require__(36) + return getterBody + } else { + ret = [ + '(function(){', + 'try{', + 'var __value__ = ' + body, + (category === 'text' ? + 'return avalon.parsers.string(__value__)' : + 'return __value__'), + '}catch(e){', + quoteError(str, category), + '\treturn ""', + '}', + '})()' + ] + filters.unshift(3, 0) + } + ret.splice.apply(ret, filters) + cacheStr = ret.join('\n') + evaluatorPool.put(category + ':' + cacheID, cacheStr) + return cacheStr - function scan(nodes) { - for (var i = 0, elem; elem = nodes[i++]; ) { - if (elem.nodeType === 1) { - var $id = getController(elem) + } - var vm = avalon.vmodels[$id] - if (vm && !vm.$element) { - vm.$element = elem + function quoteError(str, type) { + return '\tavalon.warn(e, ' + + avalon.quote('parse ' + type + ' binding【 ' + str + ' 】fail') + + ')' + } + module.exports = avalon.parseExpr = parseExpr - if (avalon.serverTemplates && avalon.serverTemplates[$id]) { - var tmpl = avalon.serverTemplates[$id] - var oldTree = avalon.speedUp(avalon.lexer(tmpl)) - var render = avalon.render(oldTree) - var vtree = render(vm) - var dom = avalon.vdomAdaptor(vtree[0], 'toDOM') - vm.$element = dom - dom.vtree = vtree - vm.$render = render - elem.parentNode.replaceChild(dom, elem) - avalon.diff(vtree, vtree) - continue - } - //IE6-8下元素的outerHTML前面会有空白 - //第一次扫描就清空所有空白节点,并生成最初的vtree - var vtree = dom2vdom(elem) - var now = new Date() - elem.vtree = avalon.speedUp(vtree) - var now2 = new Date() - onceWarn && avalon.log('构建虚拟DOM耗时', now2 - now, 'ms') - vm.$render = avalon.render(elem.vtree) - avalon.scopes[vm.$id] = { - vmodel: vm, - local: {}, - isTemp: true - } - var now3 = new Date() - onceWarn && avalon.log('构建当前vm的$render方法耗时 ', now3 - now2, 'ms\n', - '如果此时间太长,达100ms以上\n', - '建议将当前ms-controller拆分成多个ms-controller,减少每个vm管辖的区域') - avalon.rerenderStart = now3 - onceWarn = false - avalon.batch($id) + /***/ }, + /* 42 */ + /***/ function(module, exports, __webpack_require__) { - } else if (!$id) { - scan(elem.childNodes) - } + + var Cache = __webpack_require__(30) + //缓存求值函数,以便多次利用 + module.exports = new Cache(888) + + + /***/ }, + /* 43 */, + /* 44 */ + /***/ function(module, exports, __webpack_require__) { + + // 抽离出来公用 + var update = __webpack_require__(45) + + avalon.directive('important', { + priority: 1, + parse: function (copy, src, binding) { + var quoted = avalon.quote(binding.expr) + copy.local = '{}' + copy.vmodel = '__vmodel__' + copy[binding.name] = 1 + + var vmodel = '(function(){ return __vmodel__ = avalon.vmodels[' + quoted + ']})()' + src.$prepend = ['(function(__vmodel__){', + 'var important = avalon.scopes[' + quoted + ']', + 'if(important){avalon.log("不进入"+' + quoted + ');return }', + ].join('\n') + '\n' + vmodel + src.$append = '\n})();' + }, + diff: function (copy, src, name) { + if (!src.dynamic[name]) { + src.local = copy.local + src.vmodel = copy.vmodel + update(src, this.update) } + }, + update: function (dom, vdom, parent) { + avalon.directives.controller.update(dom, vdom, parent, 'important') } - } + }) - module.exports = avalon.scan = function (a) { - if (!a || !a.nodeType) { - avalon.warn('[avalon.scan] first argument must be element , documentFragment, or document') - return + + /***/ }, + /* 45 */ + /***/ function(module, exports) { + + module.exports = function (vdom, update, hookName) { + if (hookName) { + vdom.afterChange = vdom.afterChange || [] + avalon.Array.ensure(vdom.afterChange, update) + } else { + var dom = vdom.dom + update(vdom.dom, vdom, dom && dom.parentNode) } - scan([a]) } - function getController(a) { - return a.getAttribute('ms-controller') || - a.getAttribute(':controller') - } /***/ }, - /* 36 */ + /* 46 */ /***/ function(module, exports, __webpack_require__) { - + // 抽离出来公用 + var update = __webpack_require__(45) + //var reconcile = require('../strategy/reconcile') - var voidTag = __webpack_require__(37) - var vdom2body = __webpack_require__(38) - var rformElement = /input|textarea|select/i - var rcolon = /^\:/ - function getAttributes(node) { - var attrs = node.attributes, ret = {} - for (var i = 0, n = attrs.length; i < n; i++) { - var attr = attrs[i] - if (attr.specified) { - var name = attr.name - if (name.charAt(0) === ':') { - name = name.replace(rcolon, 'ms-') - } - ret[name] = attr.value - } - } - if (rformElement.test(node.nodeName)) { - ret.type = node.type - } - var style = node.style.cssText - if (style) { - ret.style = style - } - //类名 = 去重(静态类名+动态类名+ hover类名? + active类名) - if (ret.type === 'select-one') { - ret.selectedIndex = node.selectedIndex - } - if (isEmpty(ret)) { - return null + var cache = {} + avalon.mediatorFactoryCache = function (__vmodel__, __present__) { + var a = __vmodel__.$hashcode + var b = __present__.$hashcode + var id = a + b + if (cache[id]) { + return cache[id] } - return ret + var c = avalon.mediatorFactory(__vmodel__, __present__) + return cache[id] = c } + avalon.directive('controller', { + priority: 2, + parse: function (copy, src, binding) { + var quoted = avalon.quote(binding.expr) + copy.local = '__local__' + copy.vmodel = '__vmodel__' + copy[binding.name] = 1 - function isEmpty(a) { - for (var i in a) { - return false - } - return true - } + var vmodel = [ + '(function(){', + 'var vm = avalon.vmodels[' + quoted + ']', + 'if(vm && __vmodel__&& vm !== __vmodel__){', + 'return __vmodel__ = avalon.mediatorFactoryCache(__vmodel__, vm)', + '}else if(vm){', + 'return __vmodel__ = vm', + '}', + '})();' + ].join('\n') - function toHTML(a) { - return avalon.vdomAdaptor(a, 'toHTML') - } + src.$prepend = '(function(__vmodel__){' + vmodel + src.$append = '\n})(__vmodel__);' + }, + diff: function (copy, src, name) { + if (!src.dynamic[name]) { + src.local = copy.local + src.vmodel = copy.vmodel - function createVDOM(node) { - var ret = {} - var type = node.nodeName.toLowerCase() - ret.nodeName = type - ret.dom = node - if (type.charAt(0) === '#') {//2, 8 - var nodeValue = node.nodeValue - if (/\S/.test(nodeValue)) { - ret.nodeValue = nodeValue - } - } else { - var props = getAttributes(node) - if (voidTag[type]) { - ret.isVoidTag = true + update(src, this.update) } - if (props) { - ret.props = props + }, + update: function (dom, vdom, parent, important) { + var vmodel = vdom.vmodel + var local = vdom.local + var name = important ? 'ms-important' : 'ms-controller' + vdom.dynamic[name] = 1 + var id = vdom.props[name] + var scope = avalon.scopes[id] + if (scope) { + return } - ret.children = createVDOMs(node.childNodes, node) - if (props && 'selectedIndex' in ret) { - node.selectedIndex = ret.selectedIndex - delete ret.selectedIndex - if (isEmpty(props)) { - delete ret.props - } + var top = avalon.vmodels[id] + if (vmodel.$element && vmodel.$element.vtree[0] === vdom) { + var render = vmodel.$render + } else { + render = avalon.render([vdom], local) } - } - return ret - } - //根据 outerHTML 创建 虚拟DOM - function render(node) { - return createVDOMs([node], null) - } - function createVDOMs(nodes, parent) { - var arr = [] - nodes = avalon.slice(nodes) - for (var i = 0; i < nodes.length; i++) { - var node = nodes[i] - switch (node.nodeType) { - case 1: - - var a = node.getAttributeNode(':for') || node.getAttributeNode('ms-for') - - if (a) { - var start = document.createComment('ms-for:' + a.value) - var end = document.createComment('ms-for-end:') - node.removeAttributeNode(a) - - if (parent) { - parent.insertBefore(end, node.nextSibling) - parent.insertBefore(start, node) - } - arr.push(createVDOM(start), createVDOM(node), createVDOM(end)) - - } else { - arr.push(createVDOM(node)) - } - break - case 3: - if (/\S/.test(node.nodeValue)) { - arr.push(createVDOM(node)) - } else { - removeNode(node) - } - break - case 8: - arr.push(createVDOM(node)) + vmodel.$render = render + vmodel.$element = dom + dom.vtree = [vdom] + if (top !== vmodel) { + top.$render = top.$render || render + top.$element = top.$element || dom } - } - return arr - } - - var f = avalon.avalonFragment - function removeNode(node) { - f.appendChild(node) - f.removeChild(node) - return node - } + var needFire = important ? vmodel : top + var scope = avalon.scopes[id] = { + vmodel: vmodel, + local: local + } + update(vdom, function () { + avalon(dom).removeClass('ms-controller') + var events = needFire.$events["onReady"] + if (events) { + needFire.$fire('onReady') + delete needFire.$events.onReady + } + scope.isMount = true + }, 'afterChange') + } + }) - module.exports = render + /***/ }, + /* 47 */, + /* 48 */ + /***/ function(module, exports, __webpack_require__) { + + var update = __webpack_require__(45) - /***/ }, - /* 37 */ - /***/ function(module, exports) { + avalon.directive('css', { + diff: function (copy, src, name) { + var a = copy[name] + var p = src[name] + if (Object(a) === a) { + a = a.$model || a//安全的遍历VBscript + if (Array.isArray(a)) {//转换成对象 + var b = {} + a.forEach(function (el) { + el && avalon.shadowCopy(b, el) + }) + a = b + } + var hasChange = false + if (!src.dynamic[name] || !p) {//如果一开始为空 + src[name] = a + hasChange = true + } else { + var patch = {} + for (var i in a) {//diff差异点 + if (a[i] !== p[i]) { + hasChange = true + } + patch[i] = a[i] + } + for (var i in p) { + if (!(i in patch)) { + hasChange = true + patch[i] = '' + } + } + src[name] = patch + } + if (hasChange) { + update(src, this.update) + } + } + delete copy[name]//释放内存 + }, + update: function (dom, vdom) { + if (dom && dom.nodeType === 1) { + var wrap = avalon(dom) + vdom.dynamic['ms-css'] = 1 + var change = vdom['ms-css'] + for (var name in change) { + wrap.css(name, change[name]) + } + } + } + }) - module.exports = avalon.oneObject('area,base,basefont,bgsound,br,col,command,embed,' + - 'frame,hr,img,input,keygen,link,meta,param,source,track,wbr') /***/ }, - /* 38 */ + /* 49 */ /***/ function(module, exports, __webpack_require__) { - /* - * 本模块是用于将虚拟DOM变成一个函数 - */ - - var extractBindings = __webpack_require__(39) - var stringify = __webpack_require__(40) - var parseExpr = __webpack_require__(41) - var decode = __webpack_require__(17) - var config = avalon.config - var quote = avalon.quote - var rident = /^[$a-zA-Z_][$a-zA-Z0-9_]*$/ - var rstatement = /^\s*var\s+([$\w]+)\s*\=\s*\S+/ - var skips = {__local__: 1, vmode: 1, dom: 1} - - - function parseNodes(source, inner) { - //ms-important, ms-controller , ms-for 不可复制,省得死循环 - //ms-important --> ms-controller --> ms-for --> ms-widget --> ms-effect --> ms-if - var buffer = inner ? [] : ['\nvar vnodes = [];'] + var update = __webpack_require__(45) - for (var i = 0, el; el = source[i++]; ) { - var vnode = parseNode(el) - if (el.$prepend) { - buffer.push(el.$prepend) - } - var append = el.$append - delete el.$append - delete el.$prepend - if (vnode) { - buffer.push(vnode + '\n') + var none = 'none' + function parseDisplay(elem, val) { + //用于取得此类标签的默认display值 + var doc = elem.ownerDocument + var nodeName = elem.nodeName + var key = '_' + nodeName + if (!parseDisplay[key]) { + var temp = doc.body.appendChild(doc.createElement(nodeName)) + if (avalon.modern) { + val = getComputedStyle(temp, null).display + } else { + val = temp.currentStyle.display } - if (append) { - buffer.push(append) + doc.body.removeChild(temp) + if (val === none) { + val = 'block' } + parseDisplay[key] = val } - if (!inner) { - buffer.push('return vnodes\n') - } - return buffer.join('\n') + return parseDisplay[key] } + avalon.parseDisplay = parseDisplay - - function parseNode(vdom) { - if (!vdom.nodeName) - return false - switch (vdom.nodeName) { - case '#text': - if (vdom.dynamic) { - return add(parseText(vdom)) - } else { - return addTag(vdom) - } - - case '#comment': - var nodeValue = vdom.nodeValue - if (vdom.forExpr) {// 处理ms-for指令 - var copy = { - dynamic: true, - vmodel: '__vmodel__' - } - for (var i in vdom) { - if (vdom.hasOwnProperty(i) && !skips[i]) { - copy[i] = vdom[i] + avalon.directive('visible', { + diff: function (copy, src, name) { + var c = !!copy[name] + if (!src.dynamic[name] || c !== src[name]) { + src[name] = c + update(src, this.update) + } + }, + update: function (dom, vdom) { + if (dom && dom.nodeType === 1) { + vdom.dynamic['ms-visible'] = 1 + var show = vdom['ms-visible'] + var display = dom.style.display + var value + if (show) { + if (display === none) { + value = vdom.displayValue + if (!value) { + dom.style.display = '' } } - avalon.directives['for'].parse(copy, vdom, vdom) - - vdom.$append += avalon.caches[vdom.signature] //vdom.template - return addTag(copy) - } else if (nodeValue === 'ms-for-end:') { - vdom.$append = addTag({ - nodeName: '#comment', - nodeValue: vdom.signature - - }) + - ' return vnodes}\n })\n},__local__,vnodes)\n' + - addTag({ - nodeName: "#comment", - signature: vdom.signature, - nodeValue: "ms-for-end:" - }) + '\n' - return '' + if (dom.style.display === '' && avalon(dom).css('display') === none && + // fix firefox BUG,必须挂到页面上 + avalon.contains(dom.ownerDocument, dom)) { - } else if (nodeValue.indexOf('ms-js:') === 0) {//插入JS声明语句 - var statement = parseExpr(nodeValue.replace('ms-js:', ''), 'js') + '\n' - var ret = addTag(vdom) - var match = statement.match(rstatement) - if (match && match[1]) { - vdom.$append = (vdom.$append || '') + statement + - "\n__local__." + match[1] + ' = ' + match[1] + '\n' - } else { - avalon.warn(nodeValue + ' parse fail!') + value = parseDisplay(dom) } - return ret } else { - return addTag(vdom) - } - default: - if (!vdom.dynamic && vdom.skipContent) { - return addTag(vdom) - } - - var copy = { - nodeName: vdom.nodeName + if (display !== none) { + value = none + vdom.displayValue = display + } } - var props = vdom.props - if (vdom.dynamic) { - copy.dynamic = '{}' - - var bindings = extractBindings(copy, props) - bindings.map(function (b) { - //将ms-*的值变成函数,并赋给copy.props[ms-*] - //如果涉及到修改结构,则在source添加$append,$prepend - avalon.directives[b.type].parse(copy, vdom, b) - return b.name - }) - - } else if (props) { - copy.props = {} - for (var i in props) { - copy.props[i] = props[i] + function cb() { + if (value !== void 0) { + dom.style.display = value } } + avalon.applyEffect(dom, vdom, { + hook: show ? 'onEnterDone' : 'onLeaveDone', + cb: cb + }) + } - if (vdom.isVoidTag) { - copy.isVoidTag = true - } else { - if (!('children' in copy)) { - var c = vdom.children - if (c) { - if (vdom.skipContent) { - copy.children = '[' + c.map(function (a) { - return stringify(a) - }) + ']' - } else if (c.length === 1 && c[0].nodeName === '#text') { - - if (c[0].dynamic) { - copy.children = '[' + parseText(c[0]) + ']' - } else { - copy.children = '[' + stringify(c[0]) + ']' - } + } + }) - } else { - copy.children = '(function(){' + parseNodes(c) + '})()' - } - } - } - } - if (vdom.template) - copy.template = vdom.template - if (vdom.skipContent) - copy.skipContent = true - return addTag(copy) + /***/ }, + /* 50 */ + /***/ function(module, exports, __webpack_require__) { - } + var update = __webpack_require__(45) - } + avalon.directive('expr', { + parse: avalon.noop + }) - module.exports = parseNodes - function wrapDelimiter(expr) { - return rident.test(expr) ? expr : parseExpr(expr, 'text') - } - function add(a) { - return 'vnodes.push(' + a + ');' - } - function addTag(obj) { - return add(stringify(obj)) - } - function parseText(el) { - var array = extractExpr(el.nodeValue)//返回一个数组 - var nodeValue = '' - if (array.length === 1) { - nodeValue = wrapDelimiter(array[0].expr) - } else { - var token = array.map(function (el) { - return el.type ? wrapDelimiter(el.expr) : quote(el.expr) - }).join(' + ') - nodeValue = 'String(' + token + ')' - } - return '{\nnodeName: "#text",\ndynamic:true,\nnodeValue: ' + nodeValue + '\n}' - } + /***/ }, + /* 51 */ + /***/ function(module, exports, __webpack_require__) { - var rlineSp = /\n\s*/g + //此指令实际上不会操作DOM,交由expr指令处理 + var update = __webpack_require__(45) - function extractExpr(str) { - var ret = [] - do {//aaa{{@bbb}}ccc - var index = str.indexOf(config.openTag) - index = index === -1 ? str.length : index - var value = str.slice(0, index) - if (/\S/.test(value)) { - ret.push({expr: decode(value)}) + avalon.directive('text', { + parse: function (copy, src, binding) { + copy[binding.name] = 1 + src.children = [] + copy.children = '[{\nnodeName:"#text",\ndynamic:true,' + + '\nnodeValue:avalon.parsers.string(' + + avalon.parseExpr(binding) + ')}]' + }, + diff: function (copy, src) { + if(!src.children.length){ + update(src, this.update) } - str = str.slice(index + config.openTag.length) - if (str) { - index = str.indexOf(config.closeTag) - var value = str.slice(0, index) - ret.push({ - expr: avalon.unescapeHTML(value.replace(rlineSp, '')), - type: '{{}}' - }) - str = str.slice(index + config.closeTag.length) + }, + update: function(dom, vdom){ + if (dom && !vdom.isVoidTag ) { + var parent = dom + while (parent.firstChild) { + parent.removeChild(parent.firstChild) + } + var dom = document.createTextNode('x') + parent.appendChild(dom) + var a = {nodeType: 3, nodeName:'#text', dom: dom} + vdom.children.push(a) } - } while (str.length) - return ret - } - + } + }) /***/ }, - /* 39 */ - /***/ function(module, exports) { + /* 52 */ + /***/ function(module, exports, __webpack_require__) { - var directives = avalon.directives - var rbinding = /^(\:|ms\-)\w+/ - var eventMap = avalon.oneObject('animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit') + var update = __webpack_require__(45) + var reconcile = __webpack_require__(53) - function extractBindings(cur, props) { - var bindings = [] - var attrs = {} - var skip = 'ms-skip' in props//old - var uniq = {} - for (var i in props) { - var value = props[i], match - attrs[i] = props[i] - if ((match = i.match(rbinding))) { - if (skip) - continue + avalon.directive('html', { + parse: function (copy, src, binding) { + if (!src.isVoidTag) { + //将渲染函数的某一部分存起来,渲在c方法中转换为函数 + copy[binding.name] = avalon.parseExpr(binding) + copy.vmodel = '__vmodel__' + copy.local = '__local__' + } else { + copy.children = '[]' + } + }, + diff: function (copy, src, name) { + var copyValue = copy[name] + '' - var arr = i.replace(match[1], '').split('-') + if (!src.dynamic['ms-html'] || !src.render || copyValue !== src[name]) { + src[name] = copyValue + + var oldTree = avalon.speedUp(avalon.lexer(copyValue)) - if (eventMap[arr[0]]) { - arr.unshift('on') + var render = avalon.render(oldTree, copy.local) + src.render = render + + var newTree = render(copy.vmodel, copy.local) + + src.children = copy.children = newTree + update(src, this.update) + } else if (src.render) { + var newTree = src.render(copy.vmodel, copy.local) + copy.children = newTree + } + }, + update: function (dom, vdom, parent) { + vdom.dynamic['ms-html'] = 1 + avalon.clearHTML(dom) + dom.appendChild(avalon.domize(vdom.children)) + } + }) + + + /***/ }, + /* 53 */ + /***/ function(module, exports) { + + /* + * + 节点对齐算法 + 元素节点是1+其类型 + 文本节点是3+其是否能移除 + 注释节点是8+其内容 + 发现不一样,就对真实DOM树添加或删除 + 添加的是 ms-for,ms-for-end占位的注释节点 + 删除的是多余的空白文本节点,与IE6-8私下添加的奇怪节点 + */ + var rforHolder = /^ms\-for/ + var rwhiteRetain = /[\S\xA0]/ + var plainTag = avalon.oneObject('script,style,xmp,template,noscript,textarea') + + function reconcile(nodes, vnodes, parent) { + //遍平化虚拟DOM树 + vnodes = flatten(vnodes) + var map = {} + var vn = vnodes.length + if (vn === 0) + return + + vnodes.forEach(function (el, index) { + map[index] = getType(el) + }) + var newNodes = [], change = false, el, i = 0 + var breakLoop = 0 + while (true) { + el = nodes[i++] + if (breakLoop++ > 5000) { + break + } + var vtype = el && getType(el) + var v = newNodes.length, check + if (map[v] === vtype) { + if (check && el.nodeType === 1 && (el.getAttribute(':for')||el.getAttribute('ms-for'))) { + check = false + continue } - if (arr[0] === 'on') { - arr[2] = parseFloat(arr[2]) || 0 + newNodes.push(el) + var vnode = vnodes[v] + + if (vnode.dynamic) { + vnode.dom = el } - arr.unshift('ms') - var type = arr[1] - if (directives[type]) { - var binding = { - type: type, - param: arr[2], - name: arr.join('-'), - expr: value, - priority: directives[type].priority || type.charCodeAt(0) * 100 - } - if (type === 'on') { - binding.priority += arr[3] + if (el.nodeType === 1 && !vnode.isVoidTag && !plainTag[vnode.nodeName]) { + if (el.type === 'select-one') { + //在chrome与firefox下删掉select中的空白节点,会影响到selectedIndex + var fixIndex = el.selectedIndex } - if (!uniq[binding.name]) { - uniq[binding.name] = value - bindings.push(binding) + reconcile(el.childNodes, vnode.children, el) + if (el.type === 'select-one') { + el.selectedIndex = fixIndex } } - } + } else { + change = true + if (map[v] === '8true') { + var vv = vnodes[v] + var nn = document.createComment(vv.nodeValue) + vv.dom = nn + newNodes.push(nn) + if (vv.forExpr) { + check = true + } + i = Math.max(0, --i) + } + } + if (newNodes.length === vn) { + break + } + } + if (change) { + var f = document.createDocumentFragment(), i = 0 + while (el = newNodes[i++]) { + f.appendChild(el) + } + while (parent.firstChild) { + parent.removeChild(parent.firstChild) + } + parent.appendChild(f) } + } - cur.props = attrs + module.exports = reconcile - bindings.sort(byPriority) - return bindings + function getType(node) { + switch (node.nodeType) { + case 3: + return '3' + rwhiteRetain.test(node.nodeValue) + case 1: + return '1' + node.nodeName.toLowerCase() + case 8: + return '8' + rforHolder.test(node.nodeValue) + } } - function byPriority(a, b) { - return a.priority - b.priority + function flatten(nodes) { + var arr = [] + for (var i = 0, el; el = nodes[i]; i++) { + if (Array.isArray(el)) { + arr = arr.concat(flatten(el)) + } else { + arr.push(el) + } + } + return arr } - module.exports = extractBindings /***/ }, - /* 40 */ - /***/ function(module, exports) { - - var keyMap = avalon.oneObject("break,case,catch,continue,debugger,default,delete,do,else,false," + - "finally,for,function,if,in,instanceof,new,null,return,switch,this," + - "throw,true,try,typeof,var,void,while,with," + /* 关键字*/ - "abstract,boolean,byte,char,class,const,double,enum,export,extends," + - "final,float,goto,implements,import,int,interface,long,native," + - "package,private,protected,public,short,static,super,synchronized," + - "throws,transient,volatile") - avalon.keyMap = keyMap - var quoted = { - nodeName: 1, - template: 1, - forExpr: 1, - type: 1, - nodeValue: 1, - signature: 1, - wid: 1 - } + /* 54 */ + /***/ function(module, exports, __webpack_require__) { - var rneedQuote = /[W-]/ - var quote = avalon.quote - function fixKey(k) { - return (rneedQuote.test(k) || keyMap[k]) ? quote(k) : k - } + //根据VM的属性值或表达式的值切换类名,ms-class='xxx yyy zzz:flag' + //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html + var markID = __webpack_require__(6).getLongID + var update = __webpack_require__(45) - function stringify(obj) { - var arr1 = [] - //字符不用东西包起来就变成变量 - for (var i in obj) { - var type = typeof obj[i] - if (type === 'object') { - if (i === 'props' ) { - var arr2 = [] - for (var k in obj.props) { - var kv = obj.props[k] - if (typeof kv === 'string') { - kv = quote(kv) - } - arr2.push(fixKey(k) + ': ' + kv) + function classNames() { + var classes = [] + for (var i = 0; i < arguments.length; i++) { + var arg = arguments[i] + var argType = typeof arg + if (argType === 'string' || argType === 'number' || arg === true) { + classes.push(arg) + } else if (Array.isArray(arg)) { + classes.push(classNames.apply(null, arg)) + } else if (argType === 'object') { + for (var key in arg) { + if (arg.hasOwnProperty(key) && arg[key]) { + classes.push(key) } - arr1.push(i+': {' + arr2.join(',\n') + '}') - - } else if (i === 'children') { - arr1.push('children: [' + obj[i].map(function (a) { - return stringify(a) - }) + ']') - } - } else if (obj.hasOwnProperty(i)) { - var v = obj[i] - if (type === 'string') { - v = quoted[i] ? quote(v) : v } - arr1.push(fixKey(i) + ':' + v) } } - return '{\n' + arr1.join(',\n') + '}' + + return classes.join(' ') } - module.exports = stringify - /***/ }, - /* 41 */ - /***/ function(module, exports, __webpack_require__) { + var directives = avalon.directives + avalon.directive('class', { + diff: function (copy, src, name) { + var type = name.slice(3) + var copyValue = copy[name] + var srcValue = src[name] || '' + var classEvent = src.classEvent || {} + if (type === 'hover') {//在移出移入时切换类名 + classEvent.mouseenter = activateClass + classEvent.mouseleave = abandonClass + } else if (type === 'active') {//在获得焦点时切换类名 + src.props.tabindex = copy.props.tabindex || -1 + classEvent.tabIndex = src.props.tabindex + classEvent.mousedown = activateClass + classEvent.mouseup = abandonClass + classEvent.mouseleave = abandonClass + } + src.classEvent = classEvent - + var className = classNames(copyValue) - //缓存求值函数,以便多次利用 - var evaluatorPool = __webpack_require__(42) + if (!src.dynamic[name] || srcValue !== className) { + src[name] = className + src['change-' + type] = className + update(src, this.update, type) + } + }, + update: function (dom, vdom) { + if (!dom || dom.nodeType !== 1) + return - var rregexp = /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\\\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})]))/g - var rstring = /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/g - var rfill = /\?\?\d+/g - var brackets = /\(([^)]*)\)/ - - var rshortCircuit = /\|\|/g - var rpipeline = /\|(?=\w)/ - var ruselessSp = /\s*(\.|\|)\s*/g - - var rAt = /(^|[^\w\u00c0-\uFFFF_])(@|##)(?=[$\w])/g - var rhandleName = /^(?:\@|##)[$\w\.]+$/i - - var rfilters = /\|.+/g - var rvar = /((?:\@|\$|\#\#)?\w+)/g - - function collectLocal(str, ret) { - var arr = str.replace(rfilters, '').match(rvar) - if (arr) { - arr.filter(function (el) { - if (!/^[@\d\-]/.test(el) && - el.slice(0, 2) !== '##' && - el !== '$event' && !avalon.keyMap[el]) { - ret[el] = 1 + var classEvent = vdom.classEvent + if (classEvent) { + for (var i in classEvent) { + if (i === 'tabIndex') { + dom[i] = classEvent[i] + } else { + avalon.bind(dom, i, classEvent[i]) + } + } + vdom.classEvent = {} + } + var names = ['class', 'hover', 'active'] + names.forEach(function (type) { + var name = 'change-' + type + var value = vdom[name] + if (value === void 0) + return + vdom.dynamic['ms-' + type] = 1 + if (type === 'class') { + dom && setClass(dom, vdom) + } else { + var oldType = dom.getAttribute('change-' + type) + if (oldType) { + avalon(dom).removeClass(oldType) + } + dom.setAttribute(name, value) } }) } - } - - function extLocal(ret) { - var arr = [] - for (var i in ret) { - arr.push('var ' + i + ' = __local__[' + avalon.quote(i) + ']') - } - return arr - } - - function parseExpr(str, category) { - var binding = {} - category = category || 'other' - if (typeof str === 'object') { - category = str.type - binding = str - str = binding.expr - } - if (typeof str !== 'string') - return '' - var cacheID = str - var cacheStr = evaluatorPool.get(category + ':' + cacheID) - - if (cacheStr) { - return cacheStr - } + }) - var number = 1 - //相同的表达式生成相同的函数 - var maps = {} - function dig(a) { - var key = '??' + number++ - maps[key] = a - return key - } + directives.active = directives.hover = directives['class'] - function fill(a) { - return maps[a] - } - var input = str.replace(rregexp, dig).//移除所有正则 - replace(rstring, dig).//移除所有字符串 + var classMap = { + mouseenter: 'change-hover', + mouseleave: 'change-hover', + mousedown: 'change-active', + mouseup: 'change-active' + } - replace(rshortCircuit, dig).//移除所有短路或 - replace(ruselessSp, '$1').//移除. |两端空白 - split(rpipeline) //使用管道符分离所有过滤器及表达式的正体 - //还原body - var _body = input.shift() - var local = {} - var body = _body.replace(rfill, fill).trim() - if (category === 'on' && rhandleName.test(body)) { - body = body + '($event)' - } + function activateClass(e) { + var elem = e.target + avalon(elem).addClass(elem.getAttribute(classMap[e.type]) || '') + } - body = body.replace(rAt, '$1__vmodel__.') - if (category === 'js') { - return evaluatorPool.put(category + ':' + cacheID, body) - } else if (category === 'on') { - collectLocal(_body, local) + function abandonClass(e) { + var elem = e.target + var name = classMap[e.type] + avalon(elem).removeClass(elem.getAttribute(name) || '') + if (name !== 'change-active') { + avalon(elem).removeClass(elem.getAttribute('change-active') || '') } + } - //处理表达式的过滤器部分 - - var filters = input.map(function (str) { - collectLocal(str.replace(/^\w+/g, ""), local) - str = str.replace(rfill, fill).replace(rAt, '$1__vmodel__.') //还原 - var hasBracket = false - str = str.replace(brackets, function (a, b) { - hasBracket = true - return /\S/.test(b) ? - '(__value__,' + b + ');' : - '(__value__);' - }) - if (!hasBracket) { - str += '(__value__);' - } - str = str.replace(/(\w+)/, 'avalon.__format__("$1")') - return '__value__ = ' + str - }) - var ret = [] - if (category === 'on') { - filters = filters.map(function (el) { - return el.replace(/__value__/g, '$event') - }) - if (filters.length) { - filters.push('if($event.$return){\n\treturn;\n}') - } - if (!avalon.modern) { - body = body.replace(/__vmodel__\.([^(]+)\(([^)]*)\)/, function (a, b, c) { - return '__vmodel__.' + b + ".call(__vmodel__" + (/\S/.test(c) ? ',' + c : "") + ")" - }) - } - - ret = ['function ($event, __local__){', - 'try{', - extLocal(local).join('\n'), - '\tvar __vmodel__ = this;', - '\t' + body, - '}catch(e){', - quoteError(str, category), - '}', - '}'] - filters.unshift(2, 0) - } else if (category === 'duplex') { - - //给vm同步某个属性 - var setterBody = [ - 'function (__vmodel__,__value__){', - 'try{', - '\t' + body + ' = __value__', - '}catch(e){', - quoteError(str, category).replace('parse', 'set'), - '}', - '}'] - evaluatorPool.put('duplex:set:' + cacheID, setterBody.join('\n')) - //对某个值进行格式化 - - var getterBody = [ - 'function (__vmodel__){', - 'try{', - 'var __value__ = ' + body + '\n', - filters.join('\n'), - 'return __value__\n', - '}catch(e){', - quoteError(str, category).replace('parse', 'get'), - '}', - '}'].join('\n') - evaluatorPool.put('duplex:get:' + cacheID, getterBody) - - return getterBody - } else { - ret = [ - '(function(){', - 'try{', - 'var __value__ = ' + body, - (category === 'text' ? - 'return avalon.parsers.string(__value__)' : - 'return __value__'), - '}catch(e){', - quoteError(str, category), - '\treturn ""', - '}', - '})()' - ] - filters.unshift(3, 0) + function setClass(dom, vdom) { + var old = dom.getAttribute('old-change-class') + var neo = vdom['ms-class'] + if (old !== neo) { + avalon(dom).removeClass(old).addClass(neo) + dom.setAttribute('old-change-class', neo) } - ret.splice.apply(ret, filters) - cacheStr = ret.join('\n') - evaluatorPool.put(category + ':' + cacheID, cacheStr) - return cacheStr } - function quoteError(str, type) { - return '\tavalon.warn(e, ' + - avalon.quote('parse ' + type + ' binding【 ' + str + ' 】fail') - + ')' - } - module.exports = avalon.parseExpr = parseExpr + markID(activateClass) + markID(abandonClass) /***/ }, - /* 42 */ + /* 55 */ /***/ function(module, exports, __webpack_require__) { - var Cache = __webpack_require__(30) - //缓存求值函数,以便多次利用 - module.exports = new Cache(888) - - - /***/ }, - /* 43 */ - /***/ function(module, exports, __webpack_require__) { + var eventCache = new Cache(128) + var update = __webpack_require__(45) + var markID = __webpack_require__(6).getLongID - __webpack_require__(44) - __webpack_require__(46) - //处理属性样式 - __webpack_require__(47) + var rfilters = /\|.+/g + //Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes + // The assumption is that future DOM event attribute names will begin with + // 'on' and be composed of only English letters. + var rfilters = /\|.+/g + var rvar = /((?:\@|\$|\#\#)?\w+)/g + var rstring = /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/g + var rmson = /^ms\-on\-(\w+)/ + //基于事件代理的高性能事件绑定 + avalon.directive('on', { + priority: 3000, + parse: function (copy, src, binding) { + var underline = binding.name.replace('ms-on-', 'e').replace('-', '_') + var uuid = underline + '_' + binding.expr. + replace(/\s/g, ''). + replace(/[^$a-z]/ig, function (e) { + return e.charCodeAt(0) + }) - __webpack_require__(48) - __webpack_require__(49) - ////处理内容 - __webpack_require__(50) - __webpack_require__(51) - __webpack_require__(52) - ////需要用到事件的 - __webpack_require__(54) - __webpack_require__(55) - __webpack_require__(56) - __webpack_require__(63) - __webpack_require__(64) - // - ////处理逻辑 - __webpack_require__(65) - __webpack_require__(66) - // - __webpack_require__(67) - __webpack_require__(70) - //优先级 ms-important, ms-controller, ms-for, ms-widget, ms-effect, ms-if - //....... - //ms-duplex + var quoted = avalon.quote(uuid) + var fn = '(function(){\n' + + 'var fn610 = ' + + avalon.parseExpr(binding, 'on') + + '\nfn610.uuid =' + quoted + ';\nreturn fn610})()' + copy.vmodel = '__vmodel__' + copy.local = '__local__' + copy[binding.name] = fn + }, + diff: function (copy, src, name) { + var fn = copy[name] + var uuid = fn.uuid + var srcFn = src[name] || {} + var hasChange = false - /***/ }, - /* 44 */ - /***/ function(module, exports, __webpack_require__) { - // 抽离出来公用 - var update = __webpack_require__(45) + if (!src.dynamic[name] || srcFn.uuid !== uuid) { + src[name] = fn + avalon.eventListeners[uuid] = fn + hasChange = true + } - avalon.directive('important', { - priority: 1, - parse: function (copy, src, binding) { - var quoted = avalon.quote(binding.expr) - copy.local = '{}' - copy.vmodel = '__vmodel__' - copy[binding.name] = 1 - - var vmodel = '(function(){ return __vmodel__ = avalon.vmodels[' + quoted + ']})()' - src.$prepend = ['(function(__vmodel__){', - 'var important = avalon.scopes[' + quoted + ']', - 'if(important){avalon.log("不进入"+' + quoted + ');return }', - ].join('\n') + '\n' + vmodel - src.$append = '\n})();' - }, - diff: function (copy, src, name) { - if (!src.dynamic[name]) { + if (diffObj(src.local || {}, copy.local)) { + hasChange = true + } + if (hasChange) { src.local = copy.local src.vmodel = copy.vmodel update(src, this.update) } }, - update: function (dom, vdom, parent) { - avalon.directives.controller.update(dom, vdom, parent, 'important') + update: function (dom, vdom) { + if (dom && dom.nodeType === 1) { //在循环绑定中,这里为null + var key, listener + dom._ms_context_ = vdom.vmodel + dom._ms_local = vdom.local + for (key in vdom) { + var match = key.match(rmson) + if (match) { + listener = vdom[key] + vdom.dynamic[key] = 1 + avalon.bind(dom, match[1], listener) + } + } + } } }) - - /***/ }, - /* 45 */ - /***/ function(module, exports) { - - module.exports = function (vdom, update, hookName) { - if (hookName) { - vdom.afterChange = vdom.afterChange || [] - avalon.Array.ensure(vdom.afterChange, update) - } else { - var dom = vdom.dom - update(vdom.dom, vdom, dom && dom.parentNode) + function diffObj(a, b) { + for (var i in a) {//diff差异点 + if (a[i] !== b[i]) { + return true + } } + return false } + /***/ }, - /* 46 */ + /* 56 */, + /* 57 */, + /* 58 */ /***/ function(module, exports, __webpack_require__) { - // 抽离出来公用 - var update = __webpack_require__(45) - //var reconcile = require('../strategy/reconcile') + var updateModelMethods = __webpack_require__(59) - var cache = {} - avalon.mediatorFactoryCache = function (__vmodel__, __present__) { - var a = __vmodel__.$hashcode - var b = __present__.$hashcode - var id = a + b - if (cache[id]) { - return cache[id] + function updateModelHandle(event) { + var elem = this + var field = this.__ms_duplex__ + if (elem.composing) { + //防止onpropertychange引发爆栈 + return } - var c = avalon.mediatorFactory(__vmodel__, __present__) - return cache[id] = c - } - avalon.directive('controller', { - priority: 2, - parse: function (copy, src, binding) { - var quoted = avalon.quote(binding.expr) - copy.local = '__local__' - copy.vmodel = '__vmodel__' - copy[binding.name] = 1 - - var vmodel = [ - '(function(){', - 'var vm = avalon.vmodels[' + quoted + ']', - 'if(vm && __vmodel__&& vm !== __vmodel__){', - 'return __vmodel__ = avalon.mediatorFactoryCache(__vmodel__, vm)', - '}else if(vm){', - 'return __vmodel__ = vm', - '}', - '})();' - ].join('\n') - - src.$prepend = '(function(__vmodel__){' + vmodel - src.$append = '\n})(__vmodel__);' - }, - diff: function (copy, src, name) { - if (!src.dynamic[name]) { - src.local = copy.local - src.vmodel = copy.vmodel - - update(src, this.update) - } - }, - update: function (dom, vdom, parent, important) { - var vmodel = vdom.vmodel - var local = vdom.local - var name = important ? 'ms-important' : 'ms-controller' - vdom.dynamic[name] = 1 - var id = vdom.props[name] - var scope = avalon.scopes[id] - if (scope) { - return + if (elem.value === field.value) { + return + } + if (elem.caret) { + try { + var pos = field.getCaret(elem) + field.pos = pos + } catch (e) { + avalon.warn('fixCaret error', e) } - - var top = avalon.vmodels[id] - if (vmodel.$element && vmodel.$element.vtree[0] === vdom) { - var render = vmodel.$render + } + + if (field.debounceTime > 4) { + var timestamp = new Date() + var left = timestamp - field.time || 0 + field.time = timestamp + if (left >= field.debounceTime) { + updateModelMethods[field.type].call(field) } else { - render = avalon.render([vdom], local) - } - vmodel.$render = render - vmodel.$element = dom - dom.vtree = [vdom] - if (top !== vmodel) { - top.$render = top.$render || render - top.$element = top.$element || dom - } - var needFire = important ? vmodel : top - var scope = avalon.scopes[id] = { - vmodel: vmodel, - local: local + clearTimeout(field.debounceID) + field.debounceID = setTimeout(function () { + updateModelMethods[field.type].call(field) + }, left) } - update(vdom, function () { - avalon(dom).removeClass('ms-controller') - var events = needFire.$events["onReady"] - if (events) { - needFire.$fire('onReady') - delete needFire.$events.onReady - } - scope.isMount = true - }, 'afterChange') - + } else { + updateModelMethods[field.type].call(field) } - }) + } + module.exports = updateModelHandle /***/ }, - /* 47 */ - /***/ function(module, exports, __webpack_require__) { + /* 59 */ + /***/ function(module, exports) { - - var attrUpdate = __webpack_require__(24) - var update = __webpack_require__(45) + var updateModelMethods = { + input: function (prop) {//处理单个value值处理 + var data = this + prop = prop || 'value' + var dom = data.dom + var rawValue = dom[prop] + var parsedValue = data.parse(rawValue) - avalon.directive('attr', { - diff: function (copy, src, name) { - var a = copy[name] - var p = src[name] - if (a && typeof a === 'object') { - a = a.$model || a //安全的遍历VBscript - if (Array.isArray(a)) {//转换成对象 - a = avalon.mix.apply({}, a) - } - if (!src.dynamic[name] || !p) {//如果一开始为空 - src.changeAttr = src[name] = a - } else { - var patch = {} - var hasChange = false - for (var i in a) {//diff差异点 - if (a[i] !== p[i]) { - hasChange = true - patch[i] = a[i] - } - } - if (hasChange) { - src[name] = a - src.changeAttr = patch - } - } - if (src.changeAttr) { - update(src, this.update ) - } - } - if(copy !== src){ - delete copy[name]//释放内存 - } - }, - //dom, vnode - update: attrUpdate - }) + //有时候parse后一致,vm不会改变,但input里面的值 + data.value = rawValue + data.set(data.vmodel, parsedValue) + callback(data) - /***/ }, - /* 48 */ - /***/ function(module, exports, __webpack_require__) { + var pos = data.pos + if (dom.caret) { + data.setCaret(dom, pos) + } + //vm.aaa = '1234567890' + //处理 {{@aaa}} 这种格式化同步不一致的情况 - - var update = __webpack_require__(45) + }, + radio: function () { + var data = this + if (data.isChecked) { + var val = !data.value + data.set(data.vmodel, val) + callback(data) + } else { + updateModelMethods.input.call(data) + data.value = NaN + } + }, + checkbox: function () { + var data = this + var array = data.value + if (!Array.isArray(array)) { + avalon.warn('ms-duplex应用于checkbox上要对应一个数组') + array = [array] + } + var method = data.dom.checked ? 'ensure' : 'remove' + if (array[method]) { + var val = data.parse(data.dom.value) + array[method](val) + callback(data) + } - avalon.directive('css', { - diff: function (copy, src, name) { - var a = copy[name] - var p = src[name] - if (Object(a) === a) { - a = a.$model || a//安全的遍历VBscript - if (Array.isArray(a)) {//转换成对象 - var b = {} - a.forEach(function (el) { - el && avalon.shadowCopy(b, el) + }, + select: function () { + var data = this + var val = avalon(data.dom).val() //字符串或字符串数组 + if (val + '' !== this.value + '') { + if (Array.isArray(val)) { //转换布尔数组或其他 + val = val.map(function (v) { + return data.parse(v) }) - a = b - } - var hasChange = false - if (!src.dynamic[name] || !p) {//如果一开始为空 - src[name] = a - hasChange = true } else { - var patch = {} - for (var i in a) {//diff差异点 - if (a[i] !== p[i]) { - hasChange = true - } - patch[i] = a[i] - } - for (var i in p) { - if (!(i in patch)) { - hasChange = true - patch[i] = '' - } - } - src[name] = patch - } - if (hasChange) { - update(src, this.update) + val = data.parse(val) } + data.set(data.vmodel, val) + callback(data) } - delete copy[name]//释放内存 }, - update: function (dom, vdom) { - if (dom && dom.nodeType === 1) { - var wrap = avalon(dom) - vdom.dynamic['ms-css'] = 1 - var change = vdom['ms-css'] - for (var name in change) { - wrap.css(name, change[name]) - } - } + contenteditable: function () { + updateModelMethods.input.call(this, 'innerHTML') } - }) + } + + function callback(data) { + if (data.callback) { + data.callback.call(data.vmodel, { + type: 'changed', + target: data.dom + }) + } + } - /***/ }, - /* 49 */ - /***/ function(module, exports, __webpack_require__) { - var update = __webpack_require__(45) + module.exports = updateModelMethods - var none = 'none' - function parseDisplay(elem, val) { - //用于取得此类标签的默认display值 - var doc = elem.ownerDocument - var nodeName = elem.nodeName - var key = '_' + nodeName - if (!parseDisplay[key]) { - var temp = doc.body.appendChild(doc.createElement(nodeName)) - if (avalon.modern) { - val = getComputedStyle(temp, null).display - } else { - val = temp.currentStyle.display - } - doc.body.removeChild(temp) - if (val === none) { - val = 'block' + + /***/ }, + /* 60 */ + /***/ function(module, exports) { + + var valueHijack = false + try { //#272 IE9-IE11, firefox + + var setters = {} + var aproto = HTMLInputElement.prototype + var bproto = HTMLTextAreaElement.prototype + function newSetter(value) { // jshint ignore:line + setters[this.tagName].call(this, value) + var data = this.__ms_duplex__ + if (!this.caret && data && data.isString) { + data.update.call(this, {type: 'setter'}) } - parseDisplay[key] = val } - return parseDisplay[key] - } + var inputProto = HTMLInputElement.prototype + Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 + setters['INPUT'] = Object.getOwnPropertyDescriptor(aproto, 'value').set - avalon.parseDisplay = parseDisplay + Object.defineProperty(aproto, 'value', { + set: newSetter + }) + setters['TEXTAREA'] = Object.getOwnPropertyDescriptor(bproto, 'value').set + Object.defineProperty(bproto, 'value', { + set: newSetter + }) + valueHijack = true + } catch (e) { + //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 + // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype + // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 + } + module.exports = valueHijack - avalon.directive('visible', { - diff: function (copy, src, name) { - var c = !!copy[name] - if (!src.dynamic[name] || c !== src[name]) { - src[name] = c - update(src, this.update) - } - }, - update: function (dom, vdom) { - if (dom && dom.nodeType === 1) { - vdom.dynamic['ms-visible'] = 1 - var show = vdom['ms-visible'] - var display = dom.style.display - var value - if (show) { - if (display === none) { - value = vdom.displayValue - if (!value) { - dom.style.display = '' - } - } - if (dom.style.display === '' && avalon(dom).css('display') === none && - // fix firefox BUG,必须挂到页面上 - avalon.contains(dom.ownerDocument, dom)) { + /***/ }, + /* 61 */, + /* 62 */ + /***/ function(module, exports) { - value = parseDisplay(dom) - } - } else { - if (display !== none) { - value = none - vdom.displayValue = display - } - } - function cb() { - if (value !== void 0) { - dom.style.display = value + + module.exports = function addField(node, vnode) { + var field = node.__ms_duplex__ + var rules = vnode['ms-rules'] + if (rules && !field.validator) { + while (node && node.nodeType === 1) { + var validator = node._ms_validator_ + if (validator ) { + field.rules = rules + field.validator = validator + if(avalon.Array.ensure(validator.fields, field)){ + validator.addField(field) } + break } - avalon.applyEffect(dom, vdom, { - hook: show ? 'onEnterDone' : 'onLeaveDone', - cb: cb - }) + node = node.parentNode } - } - }) - + } /***/ }, - /* 50 */ + /* 63 */ /***/ function(module, exports, __webpack_require__) { var update = __webpack_require__(45) - avalon.directive('expr', { - parse: avalon.noop - }) - - - - - /***/ }, - /* 51 */ - /***/ function(module, exports, __webpack_require__) { + var dir = avalon.directive('validate', { + //验证单个表单元素 + diff: function (copy, src, name) { + var validator = copy[name] + var p = src[name] + if (p && p.onError && p.addField) { + return + } else if (Object(validator) === validator) { + src.vmValidator = validator + if (validator.$id) {//转换为普通对象 + validator = validator.$model + } - //此指令实际上不会操作DOM,交由expr指令处理 - var update = __webpack_require__(45) + src[name] = validator + for (var name in dir.defaults) { + if (!validator.hasOwnProperty(name)) { + validator[name] = dir.defaults[name] + } + } + validator.fields = validator.fields || [] + update(src, this.update) - avalon.directive('text', { - parse: function (copy, src, binding) { - copy[binding.name] = 1 - src.children = [] - copy.children = '[{\nnodeName:"#text",\ndynamic:true,' + - '\nnodeValue:avalon.parsers.string(' + - avalon.parseExpr(binding) + ')}]' - }, - diff: function (copy, src) { - if(!src.children.length){ - update(src, this.update) } }, - update: function(dom, vdom){ - if (dom && !vdom.isVoidTag ) { - var parent = dom - while (parent.firstChild) { - parent.removeChild(parent.firstChild) - } - var dom = document.createTextNode('x') - parent.appendChild(dom) - var a = {nodeType: 3, nodeName:'#text', dom: dom} - vdom.children.push(a) + update: function (dom, vdom) { + var validator = vdom['ms-validate'] + dom._ms_validator_ = validator + validator.dom = dom + var v = vdom.vmValidator + try { + v.onManual = onManual + } catch (e) { } - } - }) - - /***/ }, - /* 52 */ - /***/ function(module, exports, __webpack_require__) { - - var update = __webpack_require__(45) - var reconcile = __webpack_require__(53) - - avalon.directive('html', { - parse: function (copy, src, binding) { - if (!src.isVoidTag) { - //将渲染函数的某一部分存起来,渲在c方法中转换为函数 - copy[binding.name] = avalon.parseExpr(binding) - copy.vmodel = '__vmodel__' - copy.local = '__local__' - } else { - copy.children = '[]' + delete vdom.vmValidator + dom.setAttribute('novalidate', 'novalidate') + function onManual() { + dir.validateAll.call(validator, validator.onValidateAll) } - }, - diff: function (copy, src, name) { - var copyValue = copy[name] + '' - - if (!src.dynamic['ms-html'] || !src.render || copyValue !== src[name]) { - src[name] = copyValue - - var oldTree = avalon.speedUp(avalon.lexer(copyValue)) - - var render = avalon.render(oldTree, copy.local) - src.render = render - - var newTree = render(copy.vmodel, copy.local) - - src.children = copy.children = newTree - update(src, this.update) - } else if (src.render) { - var newTree = src.render(copy.vmodel, copy.local) - copy.children = newTree + if (validator.validateAllInSubmit) { + avalon.bind(dom, 'submit', function (e) { + e.preventDefault() + onManual() + }) } - }, - update: function (dom, vdom, parent) { - vdom.dynamic['ms-html'] = 1 - avalon.clearHTML(dom) - dom.appendChild(avalon.domize(vdom.children)) - } - }) - - /***/ }, - /* 53 */ - /***/ function(module, exports) { - - /* - * - 节点对齐算法 - 元素节点是1+其类型 - 文本节点是3+其是否能移除 - 注释节点是8+其内容 - 发现不一样,就对真实DOM树添加或删除 - 添加的是 ms-for,ms-for-end占位的注释节点 - 删除的是多余的空白文本节点,与IE6-8私下添加的奇怪节点 - */ - var rforHolder = /^ms\-for/ - var rwhiteRetain = /[\S\xA0]/ - var plainTag = avalon.oneObject('script,style,xmp,template,noscript,textarea') - - function reconcile(nodes, vnodes, parent) { - //遍平化虚拟DOM树 - vnodes = flatten(vnodes) - var map = {} - var vn = vnodes.length - if (vn === 0) - return - - vnodes.forEach(function (el, index) { - map[index] = getType(el) - }) - var newNodes = [], change = false, el, i = 0 - var breakLoop = 0 - while (true) { - el = nodes[i++] - if (breakLoop++ > 5000) { - break + if (typeof validator.onInit === 'function') { //vmodels是不包括vmodel的 + validator.onInit.call(dom, { + type: 'init', + target: dom, + validator: validator + }) } - var vtype = el && getType(el) - var v = newNodes.length, check - if (map[v] === vtype) { - if (check && el.nodeType === 1 && (el.getAttribute(':for')||el.getAttribute('ms-for'))) { - check = false - continue - } - newNodes.push(el) - var vnode = vnodes[v] - - if (vnode.dynamic) { - vnode.dom = el - } - - if (el.nodeType === 1 && !vnode.isVoidTag && !plainTag[vnode.nodeName]) { - if (el.type === 'select-one') { - //在chrome与firefox下删掉select中的空白节点,会影响到selectedIndex - var fixIndex = el.selectedIndex - } - reconcile(el.childNodes, vnode.children, el) - if (el.type === 'select-one') { - el.selectedIndex = fixIndex - } - } - } else { - change = true - if (map[v] === '8true') { - var vv = vnodes[v] - var nn = document.createComment(vv.nodeValue) - vv.dom = nn - newNodes.push(nn) - if (vv.forExpr) { - check = true - } - i = Math.max(0, --i) + }, + validateAll: function (callback) { + var validator = this + var fn = typeof callback === 'function' ? callback : validator.onValidateAll + var promise = validator.fields.filter(function (field) { + var el = field.dom + return el && !el.disabled && validator.dom.contains(el) + }).map(function (field) { + return dir.validate(field, true) + }) + var reasons = [] + Promise.all(promise).then(function (array) { + for (var i = 0, el; el = array[i++]; ) { + reasons = reasons.concat(el) } - } - if (newNodes.length === vn) { - break - } - } - if (change) { - var f = document.createDocumentFragment(), i = 0 - while (el = newNodes[i++]) { - f.appendChild(el) - } - while (parent.firstChild) { - parent.removeChild(parent.firstChild) - } - parent.appendChild(f) - } - } - - module.exports = reconcile - - - function getType(node) { - switch (node.nodeType) { - case 3: - return '3' + rwhiteRetain.test(node.nodeValue) - case 1: - return '1' + node.nodeName.toLowerCase() - case 8: - return '8' + rforHolder.test(node.nodeValue) - } - } - - function flatten(nodes) { - var arr = [] - for (var i = 0, el; el = nodes[i]; i++) { - if (Array.isArray(el)) { - arr = arr.concat(flatten(el)) - } else { - arr.push(el) - } - } - return arr - } - - - - /***/ }, - /* 54 */ - /***/ function(module, exports, __webpack_require__) { - - //根据VM的属性值或表达式的值切换类名,ms-class='xxx yyy zzz:flag' - //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html - var markID = __webpack_require__(6).getLongID - var update = __webpack_require__(45) - - function classNames() { - var classes = [] - for (var i = 0; i < arguments.length; i++) { - var arg = arguments[i] - var argType = typeof arg - if (argType === 'string' || argType === 'number' || arg === true) { - classes.push(arg) - } else if (Array.isArray(arg)) { - classes.push(classNames.apply(null, arg)) - } else if (argType === 'object') { - for (var key in arg) { - if (arg.hasOwnProperty(key) && arg[key]) { - classes.push(key) - } + if (validator.deduplicateInValidateAll) { + var uniq = {} + reasons = reasons.filter(function (field) { + var el = field.dom + var uuid = el.uniqueID || (el.uniqueID = setTimeout('1')) + if (uniq[uuid]) { + return false + } else { + uniq[uuid] = true + return true + } + }) } + fn.call(validator.dom, reasons) //这里只放置未通过验证的组件 + }) + }, + addField: function (field) { + var validator = this + var node = field.dom + if (validator.validateInKeyup && (!field.isChanged && !field.debounceTime)) { + avalon.bind(node, 'keyup', function (e) { + dir.validate(field, 0, e) + }) } - } - - return classes.join(' ') - } - - - - var directives = avalon.directives - avalon.directive('class', { - diff: function (copy, src, name) { - var type = name.slice(3) - var copyValue = copy[name] - var srcValue = src[name] || '' - var classEvent = src.classEvent || {} - if (type === 'hover') {//在移出移入时切换类名 - classEvent.mouseenter = activateClass - classEvent.mouseleave = abandonClass - } else if (type === 'active') {//在获得焦点时切换类名 - src.props.tabindex = copy.props.tabindex || -1 - classEvent.tabIndex = src.props.tabindex - classEvent.mousedown = activateClass - classEvent.mouseup = abandonClass - classEvent.mouseleave = abandonClass + if (validator.validateInBlur) { + avalon.bind(node, 'blur', function (e) { + dir.validate(field, 0, e) + }) } - src.classEvent = classEvent - - var className = classNames(copyValue) - - if (!src.dynamic[name] || srcValue !== className) { - src[name] = className - src['change-' + type] = className - update(src, this.update, type) + if (validator.resetInFocus) { + avalon.bind(node, 'focus', function (e) { + validator.onReset.call(node, e, field) + }) } }, - update: function (dom, vdom) { - if (!dom || dom.nodeType !== 1) + validate: function (field, isValidateAll, event) { + var promises = [] + var value = field.value + var elem = field.dom + var validator = field.validator + if (elem.disabled) return - - var classEvent = vdom.classEvent - if (classEvent) { - for (var i in classEvent) { - if (i === 'tabIndex') { - dom[i] = classEvent[i] + for (var ruleName in field.rules) { + var ruleValue = field.rules[ruleName] + if (ruleValue === false) + continue + var hook = avalon.validators[ruleName] + var resolve, reject + promises.push(new Promise(function (a, b) { + resolve = a + reject = b + })) + var next = function (a) { + if (field.norequired && value === '') { + a = true + } + if (a) { + resolve(true) } else { - avalon.bind(dom, i, classEvent[i]) + var reason = { + element: elem, + data: field.data, + message: elem.getAttribute('data-' + ruleName + '-message') || elem.getAttribute('data-message') || hook.message, + validateRule: ruleName, + getMessage: getMessage + } + resolve(reason) } } - vdom.classEvent = {} + field.data = {} + field.data[ruleName] = ruleValue + hook.get(value, field, next) } - var names = ['class', 'hover', 'active'] - names.forEach(function (type) { - var name = 'change-' + type - var value = vdom[name] - if (value === void 0) - return - vdom.dynamic['ms-' + type] = 1 - if (type === 'class') { - dom && setClass(dom, vdom) - } else { - var oldType = dom.getAttribute('change-' + type) - if (oldType) { - avalon(dom).removeClass(oldType) + var reasons = [] + //如果promises不为空,说明经过验证拦截器 + var lastPromise = Promise.all(promises).then(function (array) { + for (var i = 0, el; el = array[i++]; ) { + if (typeof el === 'object') { + reasons.push(el) } - dom.setAttribute(name, value) } + if (!isValidateAll) { + if (reasons.length) { + validator.onError.call(elem, reasons, event) + } else { + validator.onSuccess.call(elem, reasons, event) + } + validator.onComplete.call(elem, reasons, event) + } + return reasons }) + return lastPromise } }) - directives.active = directives.hover = directives['class'] - + var rformat = /\\?{{([^{}]+)\}}/gm - var classMap = { - mouseenter: 'change-hover', - mouseleave: 'change-hover', - mousedown: 'change-active', - mouseup: 'change-active' + function getMessage() { + var data = this.data || {} + return this.message.replace(rformat, function (_, name) { + return data[name] == null ? '' : data[name] + }) } - - function activateClass(e) { - var elem = e.target - avalon(elem).addClass(elem.getAttribute(classMap[e.type]) || '') + dir.defaults = { + addField: dir.addField, //供内部使用,收集此元素底下的所有ms-duplex的域对象 + onError: avalon.noop, + onSuccess: avalon.noop, + onComplete: avalon.noop, + onManual: avalon.noop, + onReset: avalon.noop, + onValidateAll: avalon.noop, + validateInBlur: true, //@config {Boolean} true,在blur事件中进行验证,触发onSuccess, onError, onComplete回调 + validateInKeyup: true, //@config {Boolean} true,在keyup事件中进行验证,触发onSuccess, onError, onComplete回调 + validateAllInSubmit: true, //@config {Boolean} true,在submit事件中执行onValidateAll回调 + resetInFocus: true, //@config {Boolean} true,在focus事件中执行onReset回调, + deduplicateInValidateAll: false //@config {Boolean} false,在validateAll回调中对reason数组根据元素节点进行去重 } - function abandonClass(e) { - var elem = e.target - var name = classMap[e.type] - avalon(elem).removeClass(elem.getAttribute(name) || '') - if (name !== 'change-active') { - avalon(elem).removeClass(elem.getAttribute('change-active') || '') + /***/ }, + /* 64 */ + /***/ function(module, exports) { + + avalon.directive('rules', { + diff: function (copy, src, name) { + var neo = copy[name] + if (neo && Object.prototype.toString.call(neo) === '[object Object]') { + src[name] = neo.$model || neo + var field = src.dom && src.dom.__ms_duplex__ + if (field) { + field.rules = copy[name] + } + } } + }) + function isRegExp(value) { + return avalon.type(value) === 'regexp' } - - function setClass(dom, vdom) { - var old = dom.getAttribute('old-change-class') - var neo = vdom['ms-class'] - if (old !== neo) { - avalon(dom).removeClass(old).addClass(neo) - dom.setAttribute('old-change-class', neo) + var rmail = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/i + var rurl = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ + function isCorrectDate(value) { + if (typeof value === "string" && value) { //是字符串但不能是空字符 + var arr = value.split("-") //可以被-切成3份,并且第1个是4个字符 + if (arr.length === 3 && arr[0].length === 4) { + var year = ~~arr[0] //全部转换为非负整数 + var month = ~~arr[1] - 1 + var date = ~~arr[2] + var d = new Date(year, month, date) + return d.getFullYear() === year && d.getMonth() === month && d.getDate() === date + } } - + return false } - - markID(activateClass) - markID(abandonClass) - - - - - /***/ }, - /* 55 */ - /***/ function(module, exports, __webpack_require__) { - - var Cache = __webpack_require__(30) - var eventCache = new Cache(128) - var update = __webpack_require__(45) - var markID = __webpack_require__(6).getLongID - - var rfilters = /\|.+/g - //Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes - // The assumption is that future DOM event attribute names will begin with - // 'on' and be composed of only English letters. - var rfilters = /\|.+/g - var rvar = /((?:\@|\$|\#\#)?\w+)/g - var rstring = /(["'])(\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/g - var rmson = /^ms\-on\-(\w+)/ - //基于事件代理的高性能事件绑定 - avalon.directive('on', { - priority: 3000, - parse: function (copy, src, binding) { - var underline = binding.name.replace('ms-on-', 'e').replace('-', '_') - var uuid = underline + '_' + binding.expr. - replace(/\s/g, ''). - replace(/[^$a-z]/ig, function (e) { - return e.charCodeAt(0) - }) - - var quoted = avalon.quote(uuid) - var fn = '(function(){\n' + - 'var fn610 = ' + - avalon.parseExpr(binding, 'on') + - '\nfn610.uuid =' + quoted + ';\nreturn fn610})()' - copy.vmodel = '__vmodel__' - copy.local = '__local__' - copy[binding.name] = fn - + avalon.shadowCopy(avalon.validators, { + pattern: { + message: '必须匹配{{pattern}}这样的格式', + get: function (value, field, next) { + var elem = field.element + var data = field.data + if (!isRegExp(data.pattern)) { + var h5pattern = elem.getAttribute("pattern") + data.pattern = new RegExp('^(?:' + h5pattern + ')$') + } + next(data.pattern.test(value)) + return value + } }, - diff: function (copy, src, name) { - var fn = copy[name] - var uuid = fn.uuid - var srcFn = src[name] || {} - var hasChange = false - - - if (!src.dynamic[name] || srcFn.uuid !== uuid) { - src[name] = fn - avalon.eventListeners[uuid] = fn - hasChange = true + digits: { + message: '必须整数', + get: function (value, field, next) {//整数 + next(/^\-?\d+$/.test(value)) + return value + } + }, + number: { + message: '必须数字', + get: function (value, field, next) {//数值 + next(isFinite(value)) + return value + } + }, + required: { + message: '必须填写', + get: function (value, field, next) { + next(value !== "") + return value + } + }, + equalto: { + message: '密码输入不一致', + get: function (value, field, next) { + var id = String(field.data.equalto) + var other = avalon(document.getElementById(id)).val() || "" + next(value === other) + return value + } + }, + date: { + message: '日期格式不正确', + get: function (value, field, next) { + var data = field.data + if (avalon.type(data.date) === 'regexp') { + next(data.date.test(value)) + } else { + next(isCorrectDate(value)) + } + return value + } + }, + url: { + message: 'URL格式不正确', + get: function (value, field, next) { + next(rurl.test(value)) + return value + } + }, + email: { + message: 'email格式不正确', + get: function (value, field, next) { + next(rmail.test(value)) + return value + } + }, + minlength: { + message: '最少输入{{minlength}}个字', + get: function (value, field, next) { + var num = parseInt(field.data.minlength, 10) + next(value.length >= num) + return value + } + }, + maxlength: { + message: '最多输入{{maxlength}}个字', + get: function (value, field, next) { + var num = parseInt(field.data.maxlength, 10) + next(value.length <= num) + return value } - - if (diffObj(src.local || {}, copy.local)) { - hasChange = true + }, + min: { + message: '输入值不能小于{{min}}', + get: function (value, field, next) { + var num = parseInt(field.data.min, 10) + next(parseFloat(value) >= num) + return value } - if (hasChange) { - src.local = copy.local - src.vmodel = copy.vmodel - update(src, this.update) + }, + max: { + message: '输入值不能大于{{max}}', + get: function (value, field, next) { + var num = parseInt(field.data.max, 10) + next(parseFloat(value) <= num) + return value } }, - update: function (dom, vdom) { - if (dom && dom.nodeType === 1) { //在循环绑定中,这里为null - var key, listener - dom._ms_context_ = vdom.vmodel - dom._ms_local = vdom.local - for (key in vdom) { - var match = key.match(rmson) - if (match) { - listener = vdom[key] - vdom.dynamic[key] = 1 - avalon.bind(dom, match[1], listener) - } - } + chs: { + message: '必须是中文字符', + get: function (value, field, next) { + next(/^[\u4e00-\u9fa5]+$/.test(value)) + return value } } }) - function diffObj(a, b) { - for (var i in a) {//diff差异点 - if (a[i] !== b[i]) { - return true + /***/ }, + /* 65 */ + /***/ function(module, exports, __webpack_require__) { + + var update = __webpack_require__(45) + //ms-imporant ms-controller ms-for ms-widget ms-effect ms-if ... + avalon.directive('if', { + priority: 6, + diff: function (copy, src, name, copys, sources, index) { + var cur = !!copy[name] + src[name] = cur + update(src, this.update) + + }, + update: function (dom, vdom, parent) { + var show = vdom['ms-if'] + if (vdom.dynamic['ms-if']) { + vdom.dynamic['ms-if'] = vdom.nodeName + } + if (show) { + if (vdom.nodeName === '#comment') { + vdom.nodeName = vdom.dynamic['ms-if'] + delete vdom.nodeValue + var comment = vdom.comment + if (!comment) { + return + } + parent = comment.parentNode + if (parent) + parent.replaceChild(dom, comment) + delete vdom.comment + avalon.applyEffect(dom, vdom, { + hook: 'onEnterDone' + }) + } + } else { + + //要移除元素节点,在对应位置上插入注释节点 + if (!vdom.comment) { + vdom.comment = document.createComment('if') + } + vdom.nodeName = '#comment' + vdom.nodeValue = 'if' + avalon.applyEffect(dom, vdom, { + hook: 'onLeaveDone', + cb: function () { + //去掉注释节点临时添加的ms-effect + //https://github.com/RubyLouvre/avalon/issues/1577 + //这里必须设置nodeValue为ms-if,否则会在节点对齐算法中出现乱删节点的BUG + parent = parent || dom.parentNode + if (!parent) { + return + } + parent.replaceChild(vdom.comment, dom) + } + }) } } - return false - } + }) /***/ }, - /* 56 */ + /* 66 */ /***/ function(module, exports, __webpack_require__) { - var update = __webpack_require__(45) - var evaluatorPool = __webpack_require__(42) - var stringify = __webpack_require__(40) - var rchangeFilter = /\|\s*change\b/ - var rcheckedType = /^(?:checkbox|radio)$/ - var rdebounceFilter = /\|\s*debounce(?:\(([^)]+)\))?/ - var updateModelByEvent = __webpack_require__(57) - var updateModelByValue = __webpack_require__(60) - var updateModel = __webpack_require__(58) - var updateView = __webpack_require__(61) - var addValidateField = __webpack_require__(62) - var duplexDir = 'ms-duplex' + var rforAs = /\s+as\s+([$\w]+)/ + var rident = /^[$a-zA-Z_][$a-zA-Z0-9_]*$/ + var rinvalid = /^(null|undefined|NaN|window|this|\$index|\$id)$/ + var rargs = /[$\w]+/g + + function getTraceKey(item) { + var type = typeof item + return item && type === 'object' ? item.$hashcode : type + ':' + item + } + avalon._each = function (obj, fn, local, vnodes) { + var repeat = [] + vnodes.push(repeat) + var arr = (fn + '').slice(0, 40).match(rargs) - avalon.directive('duplex', { - priority: 2000, - parse: function (copy, src, binding) { - var expr = binding.expr - var etype = src.props.type - //处理数据转换器 - var parsers = binding.param, dtype - var isChecked = false - parsers = parsers ? parsers.split('-').map(function (a) { - if (a === 'checked') { - isChecked = true - } - return a - }) : [] + arr.shift() - if (rcheckedType.test(etype) && isChecked) { - //如果是radio, checkbox,判定用户使用了checked格式函数没有 - parsers = [] - dtype = 'radio' + if (Array.isArray(obj)) { + for (var i = 0; i < obj.length; i++) { + iterator(i, obj[i], local, fn, arr[0], arr[1], repeat, true) } - - if (!/input|textarea|select/.test(src.nodeName)) { - if ('contenteditable' in src.props) { - dtype = 'contenteditable' + } else { + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + iterator(i, obj[i], local, fn, arr[0], arr[1], repeat) } - } else if (!dtype) { - dtype = src.nodeName === 'select' ? 'select' : - etype === 'checkbox' ? 'checkbox' : - etype === 'radio' ? 'radio' : - 'input' } - var isChanged = false, debounceTime = 0 - //判定是否使用了 change debounce 过滤器 - if (dtype === 'input' || dtype === 'contenteditable') { - var isString = true - if (rchangeFilter.test(expr)) { - isChanged = true - } - if (!isChanged) { - var match = expr.match(rdebounceFilter) - if (match) { - debounceTime = parseInt(match[1], 10) || 300 - } - } + } + } + + function iterator(index, item, vars, fn, k1, k2, repeat, isArray) { + var key = isArray ? getTraceKey(item) : index + var local = {} + local[k1] = index + local[k2] = item + for (var k in vars) { + if (!(k in local)) { + local[k] = vars[k] } + } + fn(index, item, key, local, repeat) + } - var changed = copy.props['data-duplex-changed'] - var get = avalon.parseExpr(binding, 'duplex')// 输出原始数据 - var quoted = parsers.map(function (a) { - return avalon.quote(a) - }) - copy[duplexDir] = stringify({ - type: dtype, //这个决定绑定什么事件 - vmodel: '__vmodel__', - local: '__local__', - debug: avalon.quote(binding.name + '=' + binding.expr), - isChecked: isChecked, - parsers: '[' + quoted + ']', - isString: !!isString, - isChanged: isChanged, //这个决定同步的频数 - debounceTime: debounceTime, //这个决定同步的频数 - get: get, //经过所有 - set: evaluatorPool.get('duplex:set:' + expr), - callback: changed ? avalon.parseExpr(changed, 'on') : 'avalon.noop' + avalon.directive('for', { + priority: 3, + parse: function (copy, src, binding) { + var str = src.forExpr, aliasAs + str = str.replace(rforAs, function (a, b) { + if (!rident.test(b) || rinvalid.test(b)) { + avalon.error('alias ' + b + ' is invalid --- must be a valid JS identifier which is not a reserved name.') + } else { + aliasAs = b + } + return '' }) - }, - diff: function (copy, src) { - if (!src.dynamic[duplexDir]) { - //第一次为原始虚拟DOM添加duplexData - var data = src[duplexDir] = copy[duplexDir] - data.parse = parseValue - } else { - data = src[duplexDir] - } - if (copy !== src) {//释放内存 - copy[duplexDir] = null + var arr = str.split(' in ') + var assign = 'var loop = ' + avalon.parseExpr(arr[1]) + ' \n' + var alias = aliasAs ? 'var ' + aliasAs + ' = loop\n' : '' + var kv = arr[0].match(rargs) + + if (kv.length === 1) {//确保avalon._each的回调有三个参数 + kv.unshift('$key') } + kv.push('traceKey', '__local__', 'vnodes') + src.$append = assign + alias + 'avalon._each(loop,function(' + + kv.join(', ') + '){\n' + + (aliasAs ? '__local__[' + avalon.quote(aliasAs) + ']=loop\n' : '') + + 'vnodes.push({\nnodeName: "#document-fragment",\nindex: arguments[0],\nkey: traceKey,\n' + + 'children: new function(){\n var vnodes = []\n' - var curValue = data.get(data.vmodel) - var preValue = data.value - if (data.isString) {//减少不必要的视图渲染 - curValue = data.parse(curValue) - curValue += '' - if (curValue === preValue) { + }, + diff: function (copy, src, cpList, spList, index) { + //将curRepeat转换成一个个可以比较的component,并求得compareText + //如果这个元素没有插入 + if (avalon.callArray) { + if (src.list && src.forExpr.indexOf(avalon.callArray) === -1) { return } - } else if (Array.isArray(curValue)) { - var hack = true - if (curValue + '' === data.arrayHack) { + } else { + if (src.list) { return } } - data.value = curValue - //如果是curValue是一个数组,当我们改变vm中的数组, - //那么这个data.value也是跟着改变,因此必须保持一份副本才能用于比较 - if (hack) { - data.arayHack = curValue + '' - } - update(src, this.update, 'afterChange') - }, - update: function (dom, vdom) { - if (dom && dom.nodeType === 1) { - //vdom.dynamic变成字符串{} - vdom.dynamic[duplexDir] = 1 - if (!dom.__ms_duplex__) { - dom.__ms_duplex__ = avalon.mix(vdom[duplexDir],{dom: dom}) - //绑定事件 - updateModelByEvent(dom, vdom) - //添加验证 - addValidateField(dom, vdom) + + var srcRepeat = spList[index + 1] + var curRepeat = cpList[index + 1] + var end = spList[index + 2] + //preRepeat不为空时 + var cache = src.cache || {} + //for指令只做添加删除操作 + var i, c, p + var removes = [] + if (!srcRepeat.length) {//一维数组最开始初始化时 + src.action = 'init' + + /* eslint-disable no-cond-assign */ + spList[index + 1] = curRepeat + curRepeat.forEach(function (c, i) { + srcRepeat[i] = c + saveInCache(cache, c) + }) + src.cache = cache + } else if (srcRepeat === curRepeat) { + curRepeat.forEach(function (c) { + saveInCache(cache, c) + }) + src.cache = cache + var noUpdate = true + } else { + src.action = 'update' + var newCache = {} + /* eslint-disable no-cond-assign */ + var fuzzy = [] + for (i = 0; c = curRepeat[i]; i++) { + var p = isInCache(cache, c.key) + if (p) { + p.oldIndex = p.index + p.index = c.index + saveInCache(newCache, p) + } else { + //如果找不到就进行模糊搜索 + fuzzy.push(c) + } } + for (var i = 0, c; c = fuzzy[i]; i++) { + p = fuzzyMatchCache(cache, c.key) + if (p) { + p.oldIndex = p.index + p.index = c.index + p.key = c.key + } else { + p = c + srcRepeat.push(p) + } - var data = dom.__ms_duplex__ - data.dom = dom - //如果不支持input.value的Object.defineProperty的属性支持, - //需要通过轮询同步, chrome 42及以下版本需要这个hack - if (data.isString - && !avalon.msie - && updateModelByValue === false - && !dom.valueHijack) { + saveInCache(newCache, p) + } + srcRepeat.sort(function (a, b) { + return a.index - b.index + }) - dom.valueHijack = updateModel - var intervalID = setInterval(function () { - if (!avalon.contains(avalon.root, dom)) { - clearInterval(intervalID) - } else { - dom.valueHijack({type: 'poll'}) - } - }, 30) + src.cache = newCache + for (var i in cache) { + p = cache[i] + p.action = 'leave' + avalon.Array.remove(srcRepeat, p) + removes.push(p) + if (p.arr) { + p.arr.forEach(function (m) { + m.action = 'leave' + removes.push(m) + }) + delete p.arr + } } - //更新视图 - updateView[data.type].call(data) - } - } - }) - function parseValue(val) { - for (var i = 0, k; k = this.parsers[i++]; ) { - var fn = avalon.parsers[k] - if (fn) { - val = fn.call(this, val) } - } - return val - } - + if (removes.length > 1) { + removes.sort(function (a, b) { + return a.index - b.index + }) + } + src.removes = removes + var cb = avalon.caches[src.wid] + var vm = copy.vmodel + if (end && cb) { + end.afterChange = [function (dom) { + cb.call(vm, { + type: 'rendered', + target: dom, + signature: src.signature + }) + }] + } + if (!noUpdate) { + src.list = srcRepeat + update(src, this.update) + } + return true + }, + update: function (dom, vdom, parent) { + if (vdom.action === 'init') { + var b = parent + parent = document.createDocumentFragment() + } + var before = dom + var signature = vdom.signature - /***/ }, - /* 57 */ - /***/ function(module, exports, __webpack_require__) { + for (var i = 0, item; item = vdom.removes[i++]; ) { + if (item.dom) { - /* - * 通过绑定事件同步vmodel - * 总共有三种方式同步视图 - * 1. 各种事件 input, change, click, propertychange, keydown... - * 2. value属性重写 - * 3. 定时器轮询 - */ - var updateModel = __webpack_require__(58) - var markID = __webpack_require__(6).getShortID - var msie = avalon.msie - var window = avalon.window - var document = avalon.document - function updateModelByEvent(node, vnode) { - var events = {} - var data = vnode['ms-duplex'] - data.update = updateModel - //添加需要监听的事件 - switch (data.type) { - case 'radio': - case 'checkbox': - events.click = updateModel - break - case 'select': - events.change = updateModel - break - case 'contenteditable': - if (data.isChanged) { - events.blur = updateModel - } else { - if (avalon.modern) { - if (window.webkitURL) { - // http://code.metager.de/source/xref/WebKit/LayoutTests/fast/events/ - // https://bugs.webkit.org/show_bug.cgi?id=110742 - events.webkitEditableContentChanged = updateModel - } else if (window.MutationEvent) { - events.DOMCharacterDataModified = updateModel - } - events.input = updateModel + delete item.split + if (vdom.hasEffect) { + !function (obj) { + var nodes = moveItem(obj) + var children = obj.children.concat() + obj.children.length = 0 + applyEffects(nodes, children, { + hook: 'onLeaveDone', + staggerKey: signature + 'leave', + cb: function (node) { + if (node.parentNode) { + node.parentNode.removeChild(node) + } + } + }) + }(item) } else { - events.keydown = updateModelKeyDown - events.paste = updateModelDelay - events.cut = updateModelDelay - events.focus = closeComposition - events.blur = openComposition + moveItem(item, 'add') } } - break - case 'input': - if (data.isChanged) { - events.change = updateModel - } else { - //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html - //http://www.matts411.com/post/internet-explorer-9-oninput/ - if (msie) {//处理输入法问题 - events.keyup = updateModelKeyDown - } - - if (msie < 9) { - events.propertychange = updateModelHack - events.paste = updateModelDelay - events.cut = updateModelDelay + } + vdom.list.forEach(function (el, i) { + if (el.action === 'leave') + return + if (!el.dom) { + el.dom = avalon.domize(el) + } + var f = el.dom + if (el.oldIndex === void 0) { + if (vdom.hasEffect) + var nodes = avalon.slice(f.childNodes) + if (i === 0 && vdom.action === 'init') { + parent.appendChild(f) } else { - events.input = updateModel - } - //IE6-8的propertychange有BUG,第一次用JS修改值时不会触发,而且你是全部清空value也不会触发 - //IE9的propertychange不支持自动完成,退格,删除,复制,贴粘,剪切或点击右边的小X的清空操作 - //IE11微软拼音好像才会触发compositionstart 不会触发compositionend - //https://github.com/RubyLouvre/avalon/issues/1368#issuecomment-220503284 - if(!msie || msie > 9){ - events.compositionstart = openComposition - events.compositionend = closeComposition + parent.insertBefore(f, before.nextSibling) } - if (!msie) { - - //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray - //如果当前浏览器支持Int8Array,那么我们就不需要以下这些事件来打补丁了 - if (!/\[native code\]/.test(window.Int8Array)) { - events.keydown = updateModelKeyDown //safari < 5 opera < 11 - events.paste = updateModelDelay//safari < 5 - events.cut = updateModelDelay//safari < 5 - if (window.netscape) { - // Firefox <= 3.6 doesn't fire the 'input' event when text is filled in through autocomplete - events.DOMAutoComplete = updateModel - } - } + if (vdom.hasEffect) { + applyEffects(nodes, el.children, { + hook: 'onEnterDone', + staggerKey: signature + 'enter' + }) } + } else if (el.index !== el.oldIndex) { + var nodes = moveItem(el, 'add') + parent.insertBefore(el.dom, before.nextSibling) + vdom.hasEffect && applyEffects(nodes, el.children, { + hook: 'onMoveDone', + staggerKey: signature + 'move' + }) } - break - } - - if (/password|text/.test(vnode.props.type)) { - events.focus = openCaret //判定是否使用光标修正功能 - events.blur = closeCaret - data.getCaret = getCaret - data.setCaret = setCaret - } - - for (var name in events) { - avalon.bind(node, name, events[name]) + before = el.split + }) + if (vdom.action === 'init') { + b.insertBefore(parent, dom.nextSibling) + } } - } + }) - function updateModelHack(e) { - if (e.propertyName === 'value') { - updateModel.call(this, e) + function moveItem(item, addToFragment) { + var nodes = item.children.map(function (el) { + return el['ms-if'] ? el.comment : el.dom + }) + if (addToFragment) { + nodes.forEach(function (el) { + item.dom.appendChild(el) + }) } - } - - function updateModelDelay(e) { - var elem = this - setTimeout(function () { - updateModel.call(elem, e) - }, 0) + return nodes } - function openCaret() { - this.caret = true + avalon.domize = function (a) { + return avalon.vdomAdaptor(a, 'toDOM') } - function closeCaret() { - this.caret = false - } - function openComposition() { - this.composing = true - } - function closeComposition(e) { - this.composing = false - updateModelDelay.call(this, e) + var rfuzzy = /^(string|number|boolean)/ + var rkfuzzy = /^_*(string|number|boolean)/ + function fuzzyMatchCache(cache) { + var key + for (var id in cache) { + var key = id + break + } + if (key) { + return isInCache(cache, key) + } } - function updateModelKeyDown(e) { - var key = e.keyCode - // ignore - // command modifiers arrows - if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) - return - updateModel.call(this, e) - } - markID(openCaret) - markID(closeCaret) - markID(openComposition) - markID(closeComposition) - markID(updateModel) - markID(updateModelHack) - markID(updateModelDelay) - markID(updateModelKeyDown) - //IE6-8要处理光标时需要异步 - var mayBeAsync = function (fn) { - setTimeout(fn, 0) - } - var setCaret = function (target, cursorPosition) { - var range - if (target.createTextRange) { - mayBeAsync(function () { - target.focus() - range = target.createTextRange() - range.collapse(true) - range.moveEnd('character', cursorPosition) - range.moveStart('character', cursorPosition) - range.select() - }) - } else { - target.focus() - if (target.selectionStart !== undefined) { - target.setSelectionRange(cursorPosition, cursorPosition) + // 新位置: 旧位置 + function isInCache(cache, id) { + var c = cache[id] + if (c) { + var arr = c.arr + if (arr) { + var r = arr.pop() + if (!arr.length) { + c.arr = 0 + } + return r } + delete cache[id] + return c } } - - var getCaret = function (target) { - var start = 0 - var normalizedValue - var range - var textInputRange - var len - var endRange - - if (typeof target.selectionStart == 'number' && typeof target.selectionEnd == 'number') { - start = target.selectionStart + //[1,1,1] number1 number1_ number1__ + function saveInCache(cache, component) { + var trackId = component.key + if (!cache[trackId]) { + cache[trackId] = component } else { - range = document.selection.createRange() - - if (range && range.parentElement() == target) { - len = target.value.length - normalizedValue = target.value.replace(/\r\n/g, '\n') - - textInputRange = target.createTextRange() - textInputRange.moveToBookmark(range.getBookmark()) - - endRange = target.createTextRange() - endRange.collapse(false) - - if (textInputRange.compareEndPoints('StartToEnd', endRange) > -1) { - start = len - } else { - start = -textInputRange.moveStart('character', -len) - start += normalizedValue.slice(0, start).split('\n').length - 1 - } - } + var c = cache[trackId] + var arr = c.arr || (c.arr = []) + arr.push(component) } + } - return start + var applyEffects = function (nodes, vnodes, opts) { + vnodes.forEach(function (vdom, i) { + avalon.applyEffect(nodes[i], vdom, opts) + }) } - module.exports = updateModelByEvent /***/ }, - /* 58 */ + /* 67 */ /***/ function(module, exports, __webpack_require__) { - var updateModelMethods = __webpack_require__(59) + var update = __webpack_require__(45) + //var reconcile = require('../strategy/reconcile') + var tryInitComponent = __webpack_require__(68) - function updateModelHandle(event) { - var elem = this - var field = this.__ms_duplex__ - if (elem.composing) { - //防止onpropertychange引发爆栈 - return - } - if (elem.value === field.value) { - return - } - if (elem.caret) { - try { - var pos = field.getCaret(elem) - field.pos = pos - } catch (e) { - avalon.warn('fixCaret error', e) - } - } - - if (field.debounceTime > 4) { - var timestamp = new Date() - var left = timestamp - field.time || 0 - field.time = timestamp - if (left >= field.debounceTime) { - updateModelMethods[field.type].call(field) - } else { - clearTimeout(field.debounceID) - field.debounceID = setTimeout(function () { - updateModelMethods[field.type].call(field) - }, left) - } - } else { - updateModelMethods[field.type].call(field) - } + avalon.component = function (name, definition) { + //这是定义组件的分支,并将列队中的同类型对象移除 + if (!avalon.components[name]) { + avalon.components[name] = definition + }//这里没有返回值 } + avalon.directive('widget', { + priority: 4, + parse: function (copy, src, binding) { + src.props.wid = src.props.wid || avalon.makeHashCode('w') + //将渲染函数的某一部分存起来,渲在c方法中转换为函数 + copy[binding.name] = avalon.parseExpr(binding) + copy.template = src.template + copy.vmodel = '__vmodel__' + copy.local = '__local__' + }, + define: function () { + return avalon.mediatorFactory.apply(this, arguments) + }, + diff: function (copy, src, name, copyList, srcList, index) { + var a = copy[name] - module.exports = updateModelHandle - - /***/ }, - /* 59 */ - /***/ function(module, exports) { - - var updateModelMethods = { - input: function (prop) {//处理单个value值处理 - var data = this - prop = prop || 'value' - var dom = data.dom - var rawValue = dom[prop] - var parsedValue = data.parse(rawValue) + if (Object(a) === a) { + //有三个地方可以设置is, 属性,标签名,配置对象 - //有时候parse后一致,vm不会改变,但input里面的值 - data.value = rawValue - data.set(data.vmodel, parsedValue) - callback(data) + var is = src.props.is || (/^ms\-/.test(src.nodeName) ? src.nodeName : 0) + if (!is) {//开始大费周章地获取组件的类型 + a = a.$model || a//安全的遍历VBscript + if (Array.isArray(a)) {//转换成对象 + a.unshift({})// 防止污染旧数据 + avalon.mix.apply(0, a) + a = a.shift() + } + is = a.is + } + var vmName = 'component-vm:' + is - var pos = data.pos - if (dom.caret) { - data.setCaret(dom, pos) - } - //vm.aaa = '1234567890' - //处理 {{@aaa}} 这种格式化同步不一致的情况 + src.props.is = is + src.vmodel = copy.vmodel + //如果组件没有初始化,那么先初始化(生成对应的vm,$render) + if (!src[vmName]) { + if (!tryInitComponent(src, copy[name], copy.local, copy.template)) { + //替换成注释节点 + src.nodeValue = 'unresolved component placeholder' + copyList[index] = src + update(src, this.mountComment) + return + } + } - }, - radio: function () { - var data = this - if (data.isChecked) { - var val = !data.value - data.set(data.vmodel, val) - callback(data) + //如果已经存在于avalon.scopes + var comVm = src[vmName] + var scope = avalon.scopes[comVm.$id] + if (scope && scope.vmodel) { + var com = scope.vmodel.$element + if (src.dom !== com) { + var component = com.vtree[0] + srcList[index] = copyList[index] = component + src.com = com + if (!component.skipContent) { + component.skipContent = 'optimize' + } + + update(src, this.replaceCachedComponent) + + update(component, function () { + if (component.skipContent === 'optimize') { + component.skipContent = true + } + }, 'afterChange') + return + } + } + var render = comVm.$render + var tree = render(comVm, copy.local) + var component = tree[0] + if (component && isComponentReady(component)) { + component.local = copy.local + Array( + vmName, + 'component-html:' + is, + 'component-ready:' + is, + 'dom', 'dynamic' + ).forEach(function (name) { + component[name] = src[name] + }) + component.vmodel = comVm + copyList[index] = component + // 如果与ms-if配合使用, 会跑这分支 + if (src.comment && src.nodeValue) { + component.dom = src.comment + } + if (src.nodeName !== component.nodeName) { + srcList[index] = component + update(component, this.mountComponent) + } else { + update(src, this.updateComponent) + } + } else { + + src.nodeValue = 'unresolved component placeholder' + copyList[index] = { + nodeValue: 'unresolved component placeholder', + nodeName: '#comment' + } + update(src, this.mountComment) + } } else { - updateModelMethods.input.call(data) - data.value = NaN + if (src.props.is === copy.props.is) { + update(src, this.updateComponent) + } } }, - checkbox: function () { - var data = this - var array = data.value - if (!Array.isArray(array)) { - avalon.warn('ms-duplex应用于checkbox上要对应一个数组') - array = [array] - } - var method = data.dom.checked ? 'ensure' : 'remove' - if (array[method]) { - var val = data.parse(data.dom.value) - array[method](val) - callback(data) - } - + replaceCachedComponent: function (dom, vdom, parent) { + var com = vdom.com + parent.replaceChild(com, dom) + vdom.dom = com + delete vdom.com }, - select: function () { - var data = this - var val = avalon(data.dom).val() //字符串或字符串数组 - if (val + '' !== this.value + '') { - if (Array.isArray(val)) { //转换布尔数组或其他 - val = val.map(function (v) { - return data.parse(v) - }) - } else { - val = data.parse(val) - } - data.set(data.vmodel, val) - callback(data) + mountComment: function (dom, vdom, parent) { + var comment = document.createComment(vdom.nodeValue) + vdom.dom = comment + parent.replaceChild(comment, dom) + }, + updateComponent: function (dom, vdom) { + var vm = vdom["component-vm:" + vdom.props.is] + var viewChangeObservers = vm.$events.onViewChange + if (viewChangeObservers && viewChangeObservers.length) { + update(vdom, viewChangeHandle, 'afterChange') } }, - contenteditable: function () { - updateModelMethods.input.call(this, 'innerHTML') + mountComponent: function (dom, vdom, parent) { + delete vdom.dom + var com = avalon.vdomAdaptor(vdom, 'toDOM') + + var is = vdom.props.is + var vm = vdom['component-vm:' + is] + vm.$fire('onInit', { + type: 'init', + vmodel: vm, + is: is + }) + + parent.replaceChild(com, dom) + + vdom.dom = vm.$element = com + com.vtree = [vdom] + avalon.onComponentDispose(com) + vdom['component-ready:' + is] = true + //-------------- + avalon.scopes[vm.$id] = { + vmodel: vm, + top: vdom.vmodel, + local: vdom.local + } + //-------------- + update(vdom, function () { + vm.$fire('onReady', { + type: 'ready', + target: com, + vmodel: vm, + is: is + }) + }, 'afterChange') + + update(vdom, function () { + vdom[ 'component-html:' + is] = avalon.vdomAdaptor(vdom, 'toHTML') + }, 'afterChange') } - } + }) - function callback(data) { - if (data.callback) { - data.callback.call(data.vmodel, { - type: 'changed', - target: data.dom + + + function viewChangeHandle(dom, vdom) { + var is = vdom.props.is + var vm = vdom['component-vm:' + is] + var html = 'component-html:' + is + var preHTML = vdom[html] + var curHTML = avalon.vdomAdaptor(vdom, 'toHTML') + if (preHTML !== curHTML) { + vdom[html] = curHTML + vm.$fire('onViewChange', { + type: 'viewchange', + target: dom, + vmodel: vm, + is: is }) } } - module.exports = updateModelMethods + function isComponentReady(vnode) { + var isReady = true + try { + hasUnresolvedComponent(vnode) + } catch (e) { + isReady = false + } + return isReady + } + function hasUnresolvedComponent(vnode) { + vnode.children.forEach(function (el) { + if (el.nodeName === '#comment') { + if (el.nodeValue === 'unresolved component placeholder') { + throw 'unresolved' + } + } else if (el.children) { + hasUnresolvedComponent(el) + } + }) + } /***/ }, - /* 60 */ - /***/ function(module, exports) { + /* 68 */ + /***/ function(module, exports, __webpack_require__) { - var valueHijack = false - try { //#272 IE9-IE11, firefox - - var setters = {} - var aproto = HTMLInputElement.prototype - var bproto = HTMLTextAreaElement.prototype - function newSetter(value) { // jshint ignore:line - setters[this.tagName].call(this, value) - var data = this.__ms_duplex__ - if (!this.caret && data && data.isString) { - data.update.call(this, {type: 'setter'}) - } + var skipArray = __webpack_require__(69) + + var legalTags = {wbr: 1, xmp: 1, template: 1} + var events = 'onInit,onReady,onViewChange,onDispose' + var componentEvents = avalon.oneObject(events) + var immunity = events.split(',').concat('is', 'define') + var onceWarn = true + function initComponent(src, rawOption, local, template) { + var tag = src.nodeName + var is = src.props.is + //判定用户传入的标签名是否符合规格 + if (!legalTags[tag] && !isCustomTag(tag)) { + avalon.warn(tag + '不合适做组件的标签') + return + } + //开始初始化组件 + var hooks = {} + //用户只能操作顶层VM + //只有$id,is的对象就是emptyOption + if (!rawOption) { + options = [] + } else { + var options = [].concat(rawOption) + options.forEach(function (a) { + if (a && typeof a === 'object') { + mixinHooks(hooks, (a.$model || a), true) + } + }) + } + var definition = avalon.components[is] + //如果连组件的定义都没有加载回来,应该立即返回 + if (!definition) { + return } - var inputProto = HTMLInputElement.prototype - Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错 - setters['INPUT'] = Object.getOwnPropertyDescriptor(aproto, 'value').set - Object.defineProperty(aproto, 'value', { - set: newSetter - }) - setters['TEXTAREA'] = Object.getOwnPropertyDescriptor(bproto, 'value').set - Object.defineProperty(bproto, 'value', { - set: newSetter - }) - valueHijack = true - } catch (e) { - //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了 - // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype - // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1 - } - module.exports = valueHijack - /***/ }, - /* 61 */ - /***/ function(module, exports) { + //得到组件在顶层vm的配置对象名 + if (!hooks.$id && onceWarn) { + avalon.warn('warning!', is, '组件最好在ms-widget配置对象中指定全局不重复的$id以提高性能!\n', + '若在ms-for循环中可以利用 ($index,el) in @array 中的$index拼写你的$id\n', + '如 ms-widget="{is:\'ms-button\',$id:\'btn\'+$index}"' + ) + onceWarn = false + } + var define = hooks.define + define = define || avalon.directives.widget.define + //生成组件VM + var $id = hooks.$id || src.props.wid || 'w' + (new Date - 0) + var defaults = avalon.mix(true, {}, definition.defaults) + mixinHooks(hooks, defaults, false)//src.vmodel, + var skipProps = immunity.concat() + function sweeper(a, b) { + skipProps.forEach(function (k) { + delete a[k] + delete b[k] + }) + } - - var updateView = { - input: function () {//处理单个value值处理 - this.dom.value = this.value - }, - radio: function () {//处理单个checked属性 - var checked - if (this.isChecked) { - checked = !!this.value - } else { - checked = this.value + '' === this.dom.value - } - var dom = this.dom - if (avalon.msie === 6) { - setTimeout(function () { - //IE8 checkbox, radio是使用defaultChecked控制选中状态, - //并且要先设置defaultChecked后设置checked - //并且必须设置延迟 - dom.defaultChecked = checked - dom.checked = checked - }, 31) - } else { - dom.checked = checked - } - }, - checkbox: function () {//处理多个checked属性 - var checked = false - var dom = this.dom - var value = dom.value - for (var i = 0; i < this.value.length; i++) { - var el = this.value[i] - if (el + '' === value) { - checked = true + sweeper.isWidget = true + var vmodel = define.apply(sweeper, [src.vmodel, defaults].concat(options)) + if (!avalon.modern) {//增强对IE的兼容 + for (var i in vmodel) { + if (!skipArray[i] && typeof vmodel[i] === 'function') { + vmodel[i] = vmodel[i].bind(vmodel) } } - dom.checked = checked - }, - select: function () {//处理子级的selected属性 - var a = Array.isArray(this.value) ? - this.value.map(String) : this.value + '' - avalon(this.dom).val(a) - }, - contenteditable: function () {//处理单个innerHTML - this.dom.innerHTML = this.value - this.update.call(this.dom) } - } - module.exports = updateView + vmodel.$id = $id + avalon.vmodels[$id] = vmodel + + //绑定组件的生命周期钩子 + for (var e in componentEvents) { + if (hooks[e]) { + hooks[e].forEach(function (fn) { + vmodel.$watch(e, fn) + }) + } + } + // 生成外部的渲染函数 + // template保存着最原始的组件容器信息 + // 我们先将它转换成虚拟DOM,如果是xmp, template, + // 它们内部是一个纯文本节点, 需要继续转换为虚拟DOM + var shell = avalon.lexer(template) + + var shellRoot = shell[0] + shellRoot.children = shellRoot.children || [] + shellRoot.props.is = is + shellRoot.props.wid = $id + avalon.speedUp(shell) - /***/ }, - /* 62 */ - /***/ function(module, exports) { + var render = avalon.render(shell, local) - - module.exports = function addField(node, vnode) { - var field = node.__ms_duplex__ - var rules = vnode['ms-rules'] - if (rules && !field.validator) { - while (node && node.nodeType === 1) { - var validator = node._ms_validator_ - if (validator ) { - field.rules = rules - field.validator = validator - if(avalon.Array.ensure(validator.fields, field)){ - validator.addField(field) - } - break - } - node = node.parentNode - } + //生成内部的渲染函数 + var finalTemplate = definition.template.trim() + if (typeof definition.getTemplate === 'function') { + finalTemplate = definition.getTemplate(vmodel, finalTemplate) } - } + var vtree = avalon.lexer(finalTemplate) + if (vtree.length > 1) { + avalon.error('组件必须用一个元素包起来') + } + var soleSlot = definition.soleSlot + replaceSlot(vtree, soleSlot) + avalon.speedUp(vtree) - /***/ }, - /* 63 */ - /***/ function(module, exports, __webpack_require__) { + var render2 = avalon.render(vtree) - var update = __webpack_require__(45) + //生成最终的组件渲染函数 + var str = fnTemplate + '' + var zzzzz = soleSlot ? avalon.quote(soleSlot) : "null" + str = str. + replace('XXXXX', stringifyAnonymous(render)). + replace('YYYYY', stringifyAnonymous(render2)). + replace('ZZZZZ', zzzzz) + var begin = str.indexOf('{') + 1 + var end = str.lastIndexOf("}") - var dir = avalon.directive('validate', { - //验证单个表单元素 - diff: function (copy, src, name) { - var validator = copy[name] - var p = src[name] - if (p && p.onError && p.addField) { - return - } else if (Object(validator) === validator) { - src.vmValidator = validator - if (validator.$id) {//转换为普通对象 - validator = validator.$model - } + var lastFn = Function('vm', 'local', str.slice(begin, end)) - src[name] = validator - for (var name in dir.defaults) { - if (!validator.hasOwnProperty(name)) { - validator[name] = dir.defaults[name] - } - } - validator.fields = validator.fields || [] - update(src, this.update) + vmodel.$render = lastFn - } - }, - update: function (dom, vdom) { - var validator = vdom['ms-validate'] - dom._ms_validator_ = validator - validator.dom = dom - var v = vdom.vmValidator - try { - v.onManual = onManual - } catch (e) { - } - delete vdom.vmValidator - dom.setAttribute('novalidate', 'novalidate') - function onManual() { - dir.validateAll.call(validator, validator.onValidateAll) - } - if (validator.validateAllInSubmit) { - avalon.bind(dom, 'submit', function (e) { - e.preventDefault() - onManual() - }) - } + src['component-vm:' + is] = vmodel - if (typeof validator.onInit === 'function') { //vmodels是不包括vmodel的 - validator.onInit.call(dom, { - type: 'init', - target: dom, - validator: validator - }) - } - }, - validateAll: function (callback) { - var validator = this - var fn = typeof callback === 'function' ? callback : validator.onValidateAll - var promise = validator.fields.filter(function (field) { - var el = field.dom - return el && !el.disabled && validator.dom.contains(el) - }).map(function (field) { - return dir.validate(field, true) - }) - var reasons = [] - Promise.all(promise).then(function (array) { - for (var i = 0, el; el = array[i++]; ) { - reasons = reasons.concat(el) - } - if (validator.deduplicateInValidateAll) { - var uniq = {} - reasons = reasons.filter(function (field) { - var el = field.dom - var uuid = el.uniqueID || (el.uniqueID = setTimeout('1')) - if (uniq[uuid]) { - return false - } else { - uniq[uuid] = true - return true - } - }) + return vmodel.$render = lastFn + + } + module.exports = initComponent + + function stringifyAnonymous(fn) { + return fn.toString().replace('anonymous', '') + .replace(/\s*\/\*\*\//g, '') + } + + + function fnTemplate() { + var shell = (XXXXX)(vm, local); + var shellRoot = shell[0] + var vtree = (YYYYY)(vm, local); + var component = vtree[0] + + //处理diff + + for (var i in shellRoot) { + if (i !== 'children' && i !== 'nodeName') { + if (i === 'props') { + avalon.mix(component.props, shellRoot.props) + } else { + component[i] = shellRoot[i] } - fn.call(validator.dom, reasons) //这里只放置未通过验证的组件 - }) - }, - addField: function (field) { - var validator = this - var node = field.dom - if (validator.validateInKeyup && (!field.isChanged && !field.debounceTime)) { - avalon.bind(node, 'keyup', function (e) { - dir.validate(field, 0, e) - }) - } - if (validator.validateInBlur) { - avalon.bind(node, 'blur', function (e) { - dir.validate(field, 0, e) - }) - } - if (validator.resetInFocus) { - avalon.bind(node, 'focus', function (e) { - validator.onReset.call(node, e, field) - }) } - }, - validate: function (field, isValidateAll, event) { - var promises = [] - var value = field.value - var elem = field.dom - var validator = field.validator - if (elem.disabled) - return - for (var ruleName in field.rules) { - var ruleValue = field.rules[ruleName] - if (ruleValue === false) - continue - var hook = avalon.validators[ruleName] - var resolve, reject - promises.push(new Promise(function (a, b) { - resolve = a - reject = b - })) - var next = function (a) { - if (field.norequired && value === '') { - a = true - } - if (a) { - resolve(true) - } else { - var reason = { - element: elem, - data: field.data, - message: elem.getAttribute('data-' + ruleName + '-message') || elem.getAttribute('data-message') || hook.message, - validateRule: ruleName, - getMessage: getMessage - } - resolve(reason) - } - } - field.data = {} - field.data[ruleName] = ruleValue - hook.get(value, field, next) + } + + + var soleSlot = ZZZZZ + var slots = avalon.collectSlots(shellRoot, soleSlot) + if (soleSlot && (!slots[soleSlot] || !slots[soleSlot].length)) { + slots[soleSlot] = [{ + nodeName: '#text', + nodeValue: vm[soleSlot], + dynamic: true + }] + } + avalon.insertSlots(vtree, slots) + + delete component.skipAttrs + delete component.skipContent + return vtree + + } + + function replaceSlot(vtree, slotName) { + for (var i = 0, el; el = vtree[i]; i++) { + if (el.nodeName === 'slot') { + var name = el.props.name || slotName + + vtree.splice(i, 1, { + nodeName: '#comment', + nodeValue: 'slot:' + name, + dynamic: true, + type: name + }, { + nodeName: '#comment', + nodeValue: 'slot-end:' + }) + i++ + } else if (el.children) { + replaceSlot(el.children, slotName) } - var reasons = [] - //如果promises不为空,说明经过验证拦截器 - var lastPromise = Promise.all(promises).then(function (array) { - for (var i = 0, el; el = array[i++]; ) { - if (typeof el === 'object') { - reasons.push(el) - } - } - if (!isValidateAll) { - if (reasons.length) { - validator.onError.call(elem, reasons, event) + } + } + + + avalon.insertSlots = function (vtree, slots) { + for (var i = 0, el; el = vtree[i]; i++) { + // console.log(el.type) + if (el.nodeName === '#comment' && slots[el.type]) { + // console.log(slots[el.type]) + var args = [i + 1, 0].concat(slots[el.type]) + vtree.splice.apply(vtree, args) + i += slots[el.type].length + } else if (el.children) { + avalon.insertSlots(el.children, slots) + } + } + } + + avalon.collectSlots = function (node, soleSlot) { + var slots = {} + if (soleSlot) { + slots[soleSlot] = node.children + slots.__sole__ = soleSlot + } else { + node.children.forEach(function (el, i) { + var name = el.props && el.props.slot + if (el.forExpr) { + slots[name] = node.children.slice(i, i + 2) + } else { + if (Array.isArray(slots[name])) { + slots[name].push(el) } else { - validator.onSuccess.call(elem, reasons, event) + slots[name] = [el] } - validator.onComplete.call(elem, reasons, event) } - return reasons }) - return lastPromise } - }) + return slots + } - var rformat = /\\?{{([^{}]+)\}}/gm - function getMessage() { - var data = this.data || {} - return this.message.replace(rformat, function (_, name) { - return data[name] == null ? '' : data[name] - }) + //必须以字母开头,结尾以字母或数字结束,中间至少出现一次"-", + //并且不能大写字母,特殊符号,"_","$",汉字 + var rcustomTag = /^[a-z]([a-z\d]+\-)+[a-z\d]+$/ + + function isCustomTag(type) { + return rcustomTag.test(type) || avalon.components[type] } - dir.defaults = { - addField: dir.addField, //供内部使用,收集此元素底下的所有ms-duplex的域对象 - onError: avalon.noop, - onSuccess: avalon.noop, - onComplete: avalon.noop, - onManual: avalon.noop, - onReset: avalon.noop, - onValidateAll: avalon.noop, - validateInBlur: true, //@config {Boolean} true,在blur事件中进行验证,触发onSuccess, onError, onComplete回调 - validateInKeyup: true, //@config {Boolean} true,在keyup事件中进行验证,触发onSuccess, onError, onComplete回调 - validateAllInSubmit: true, //@config {Boolean} true,在submit事件中执行onValidateAll回调 - resetInFocus: true, //@config {Boolean} true,在focus事件中执行onReset回调, - deduplicateInValidateAll: false //@config {Boolean} false,在validateAll回调中对reason数组根据元素节点进行去重 + + function mixinHooks(target, option, overwrite) { + for (var k in option) { + var v = option[k] + //如果是生命周期钩子,总是不断收集 + if (componentEvents[k]) { + if (k in target) { + target[k].push(v) + } else { + target[k] = [option[k]] + } + } else { + if (overwrite) { + target[k] = v + } + } + } } /***/ }, - /* 64 */ + /* 69 */ /***/ function(module, exports) { - avalon.directive('rules', { + /** + * + $$skipArray:是系统级通用的不可监听属性 + $skipArray: 是当前对象特有的不可监听属性 + + 不同点是 + $$skipArray被hasOwnProperty后返回false + $skipArray被hasOwnProperty后返回true + */ + + module.exports = avalon.oneObject('$id,$render,$track,$element,$watch,$fire,$events,$model,$skipArray,$accessors,$hashcode,$run,$wait,__proxy__,__data__,__const__') + + /***/ }, + /* 70 */ + /***/ function(module, exports, __webpack_require__) { + + var support = __webpack_require__(71) + var Cache = __webpack_require__(30) + var update = __webpack_require__(45) + + avalon.directive('effect', { + priority: 5, diff: function (copy, src, name) { - var neo = copy[name] - if (neo && Object.prototype.toString.call(neo) === '[object Object]') { - src[name] = neo.$model || neo - var field = src.dom && src.dom.__ms_duplex__ - if (field) { - field.rules = copy[name] + var copyObj = copy[name] + copyObj = copy.$model || copyObj + if (typeof copyObj === 'string') { + var is = copyObj + copyObj = { + is: is } + + } else if (Array.isArray(copyObj)) { + copyObj = avalon.mix.apply({}, copyObj) } - } - }) - function isRegExp(value) { - return avalon.type(value) === 'regexp' - } - var rmail = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/i - var rurl = /^(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/ - function isCorrectDate(value) { - if (typeof value === "string" && value) { //是字符串但不能是空字符 - var arr = value.split("-") //可以被-切成3份,并且第1个是4个字符 - if (arr.length === 3 && arr[0].length === 4) { - var year = ~~arr[0] //全部转换为非负整数 - var month = ~~arr[1] - 1 - var date = ~~arr[2] - var d = new Date(year, month, date) - return d.getFullYear() === year && d.getMonth() === month && d.getDate() === date - } - } - return false - } - avalon.shadowCopy(avalon.validators, { - pattern: { - message: '必须匹配{{pattern}}这样的格式', - get: function (value, field, next) { - var elem = field.element - var data = field.data - if (!isRegExp(data.pattern)) { - var h5pattern = elem.getAttribute("pattern") - data.pattern = new RegExp('^(?:' + h5pattern + ')$') + + copyObj.action = copyObj.action || 'enter' + if (Object(copyObj) === copyObj) { + if (!src.dynamic[name] || diffObj(copyObj, src[name] || {})) { + src[name] = copyObj + update(src, this.update, 'afterChange') } - next(data.pattern.test(value)) - return value } - }, - digits: { - message: '必须整数', - get: function (value, field, next) {//整数 - next(/^\-?\d+$/.test(value)) - return value + if (copy !== src) { + delete copy[name] } }, - number: { - message: '必须数字', - get: function (value, field, next) {//数值 - next(isFinite(value)) - return value + update: function (dom, vdom, parent, option) { + if(!dom || dom.nodeType !== 1){ + return } - }, - required: { - message: '必须填写', - get: function (value, field, next) { - next(value !== "") - return value + + if (dom.animating) { + return } - }, - equalto: { - message: '密码输入不一致', - get: function (value, field, next) { - var id = String(field.data.equalto) - var other = avalon(document.getElementById(id)).val() || "" - next(value === other) - return value + dom.animating = true + var localeOption = vdom['ms-effect'] + if (!vdom.dynamic['ms-effect']) { + var a = localeOption.cb || avalon.noop + localeOption.cb = [function () { + vdom.dynamic['ms-effect'] = 1 + localeOption.cb = a + }].concat(a) } - }, - date: { - message: '日期格式不正确', - get: function (value, field, next) { - var data = field.data - if (avalon.type(data.date) === 'regexp') { - next(data.date.test(value)) - } else { - next(isCorrectDate(value)) - } - return value + var type = localeOption.is + option = option || {} + if (!type) {//如果没有指定类型 + return avalon.warn('need is option') } - }, - url: { - message: 'URL格式不正确', - get: function (value, field, next) { - next(rurl.test(value)) - return value + var effects = avalon.effects + if (support.css && !effects[type]) { + avalon.effect(type, {}) } - }, - email: { - message: 'email格式不正确', - get: function (value, field, next) { - next(rmail.test(value)) - return value + var globalOption = effects[type] + if (!globalOption) {//如果没有定义特效 + return avalon.warn(type + ' effect is undefined') } - }, - minlength: { - message: '最少输入{{minlength}}个字', - get: function (value, field, next) { - var num = parseInt(field.data.minlength, 10) - next(value.length >= num) - return value + var action = option.action || localeOption.action + var Effect = avalon.Effect + if (typeof Effect.prototype[action] !== 'function') { + return avalon.warn(action + ' action is undefined') } - }, - maxlength: { - message: '最多输入{{maxlength}}个字', - get: function (value, field, next) { - var num = parseInt(field.data.maxlength, 10) - next(value.length <= num) - return value + var effect = new Effect(dom) + var finalOption = avalon.mix(option, globalOption, localeOption) + if (finalOption.queue) { + animationQueue.push(function () { + effect[action](finalOption) + }) + callNextAnimation() + } else { + setTimeout(function () { + effect[action](finalOption) + }, 4) } - }, - min: { - message: '输入值不能小于{{min}}', - get: function (value, field, next) { - var num = parseInt(field.data.min, 10) - next(parseFloat(value) >= num) - return value + } + }) + function diffObj(a, b) { + for (var i in a) { + if (a[i] !== b[i]) + return true + } + return false + } + + var animationQueue = [] + function callNextAnimation() { + if (animationQueue.lock) + return + var fn = animationQueue[0] + if (fn) { + callNextAnimation.lock = true + fn() + } + } + + avalon.effects = {} + //这里定义CSS动画 + + + avalon.effect = function (name, definition) { + avalon.effects[name] = definition || {} + if (support.css) { + if (!definition.enterClass) { + definition.enterClass = name + '-enter' } - }, - max: { - message: '输入值不能大于{{max}}', - get: function (value, field, next) { - var num = parseInt(field.data.max, 10) - next(parseFloat(value) <= num) - return value + if (!definition.enterActiveClass) { + definition.enterActiveClass = definition.enterClass + '-active' } - }, - chs: { - message: '必须是中文字符', - get: function (value, field, next) { - next(/^[\u4e00-\u9fa5]+$/.test(value)) - return value + if (!definition.leaveClass) { + definition.leaveClass = name + '-leave' + } + if (!definition.leaveActiveClass) { + definition.leaveActiveClass = definition.leaveClass + '-active' } } - }) + if (!definition.action) { + definition.action = 'enter' + } + } - /***/ }, - /* 65 */ - /***/ function(module, exports, __webpack_require__) { - var update = __webpack_require__(45) - //ms-imporant ms-controller ms-for ms-widget ms-effect ms-if ... - avalon.directive('if', { - priority: 6, - diff: function (copy, src, name, copys, sources, index) { - var cur = !!copy[name] - src[name] = cur - update(src, this.update) + var Effect = function (el) { + this.el = el + } + avalon.Effect = Effect + Effect.prototype = { + enter: createAction('Enter'), + leave: createAction('Leave'), + move: createAction('Move') + } - }, - update: function (dom, vdom, parent) { - var show = vdom['ms-if'] - if (vdom.dynamic['ms-if']) { - vdom.dynamic['ms-if'] = vdom.nodeName + var rsecond = /\d+s$/ + function toMillisecond(str) { + var ratio = rsecond.test(str) ? 1000 : 1 + return parseFloat(str) * ratio + } + + function execHooks(options, name, el) { + var list = options[name] + list = Array.isArray(list) ? list : typeof list === 'function' ? [list] : [] + list.forEach(function (fn) { + fn && fn(el) + }) + } + var staggerCache = new Cache(128) + + function createAction(action) { + var lower = action.toLowerCase() + return function (option) { + var elem = this.el + var $el = avalon(elem) + var enterAnimateDone + var staggerTime = isFinite(option.stagger) ? option.stagger * 1000 : 0 + if (staggerTime) { + if (option.staggerKey) { + var stagger = staggerCache.get(option.staggerKey) || + staggerCache.put(option.staggerKey, { + count: 0, + items: 0 + }) + stagger.count++ + stagger.items++ + } } - if (show) { - if (vdom.nodeName === '#comment') { - vdom.nodeName = vdom.dynamic['ms-if'] - delete vdom.nodeValue - var comment = vdom.comment - if (!comment) { - return + var staggerIndex = stagger && stagger.count || 0 + var animationDone = function (e) { + var isOk = e !== false + elem.animating = void 0 + enterAnimateDone = true + var dirWord = isOk ? 'Done' : 'Abort' + execHooks(option, 'on' + action + dirWord, elem) + avalon.unbind(elem, support.transitionEndEvent) + avalon.unbind(elem, support.animationEndEvent) + if (stagger) { + if (--stagger.items === 0) { + stagger.count = 0 } - parent = comment.parentNode - if (parent) - parent.replaceChild(dom, comment) - delete vdom.comment - avalon.applyEffect(dom, vdom, { - hook: 'onEnterDone' - }) } - } else { - - //要移除元素节点,在对应位置上插入注释节点 - if (!vdom.comment) { - vdom.comment = document.createComment('if') + if (option.queue) { + animationQueue.lock = false + animationQueue.shift() + callNextAnimation() } - vdom.nodeName = '#comment' - vdom.nodeValue = 'if' - avalon.applyEffect(dom, vdom, { - hook: 'onLeaveDone', - cb: function () { - //去掉注释节点临时添加的ms-effect - //https://github.com/RubyLouvre/avalon/issues/1577 - //这里必须设置nodeValue为ms-if,否则会在节点对齐算法中出现乱删节点的BUG - parent = parent || dom.parentNode - if (!parent) { - return - } - parent.replaceChild(vdom.comment, dom) - } + } + execHooks(option, 'onBefore' + action, elem) + + if (option[lower]) { + option[lower](elem, function (ok) { + animationDone(ok !== false) }) + } else if (support.css) { + $el.addClass(option[lower + 'Class']) + if (lower === 'leave') { + $el.removeClass(option.enterClass + ' ' + option.enterActiveClass) + } else if (lower === 'enter') { + $el.removeClass(option.leaveClass + ' ' + option.leaveActiveClass) + } + + $el.bind(support.transitionEndEvent, animationDone) + $el.bind(support.animationEndEvent, animationDone) + setTimeout(function () { + enterAnimateDone = avalon.root.offsetWidth === NaN + $el.addClass(option[lower + 'ActiveClass']) + var computedStyles = window.getComputedStyle(elem) + var tranDuration = computedStyles[support.transitionDuration] + var animDuration = computedStyles[support.animationDuration] + var time = toMillisecond(tranDuration) || toMillisecond(animDuration) + if (!time === 0) { + animationDone(false) + } else if (!staggerTime) { + setTimeout(function () { + if (!enterAnimateDone) { + animationDone(false) + } + }, time + 130) + } + }, 17 + staggerTime * staggerIndex)// = 1000/60 + } + } + } + + avalon.applyEffect = function (node, vnode, opts) { + var cb = opts.cb + var curEffect = vnode['ms-effect'] + if (curEffect && node && node.nodeType === 1) { + var hook = opts.hook + var old = curEffect[hook] + if (cb) { + if (Array.isArray(old)) { + old.push(cb) + } else if (old) { + curEffect[hook] = [old, cb] + } else { + curEffect[hook] = [cb] + } } - } - }) - - + getAction(opts) + avalon.directives.effect.update(node, vnode, 0, avalon.shadowCopy({}, opts)) - /***/ }, - /* 66 */ - /***/ function(module, exports, __webpack_require__) { + } else if (cb) { + cb(node) + } + } - var update = __webpack_require__(45) + function getAction(opts) { + if (!opts.acton) { + opts.action = opts.hook.replace(/^on/, '').replace(/Done$/, '').toLowerCase() + } + } - var rforAs = /\s+as\s+([$\w]+)/ - var rident = /^[$a-zA-Z_][$a-zA-Z0-9_]*$/ - var rinvalid = /^(null|undefined|NaN|window|this|\$index|\$id)$/ - var rargs = /[$\w]+/g - function getTraceKey(item) { - var type = typeof item - return item && type === 'object' ? item.$hashcode : type + ':' + item - } - avalon._each = function (obj, fn, local, vnodes) { - var repeat = [] - vnodes.push(repeat) - var arr = (fn + '').slice(0, 40).match(rargs) + /***/ }, + /* 71 */ + /***/ function(module, exports) { - arr.shift() + /** + * ------------------------------------------------------------ + * 检测浏览器对CSS动画的支持与API名 + * ------------------------------------------------------------ + */ + if (avalon.browser) { + var supportTransition = false + var supportAnimation = false + var supportCSS = false + var transitionEndEvent + var animationEndEvent + var transitionDuration = avalon.cssName('transition-duration') + var animationDuration = avalon.cssName('animation-duration') - if (Array.isArray(obj)) { - for (var i = 0; i < obj.length; i++) { - iterator(i, obj[i], local, fn, arr[0], arr[1], repeat, true) + var checker = { + TransitionEvent: 'transitionend', + WebKitTransitionEvent: 'webkitTransitionEnd', + OTransitionEvent: 'oTransitionEnd', + otransitionEvent: 'otransitionEnd' + } + var window = avalon.window + var tran + //有的浏览器同时支持私有实现与标准写法,比如webkit支持前两种,Opera支持1、3、4 + for (var name in checker) { + if (window[name]) { + tran = checker[name] + break } - } else { - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - iterator(i, obj[i], local, fn, arr[0], arr[1], repeat) - } + try { + var a = document.createEvent(name) + tran = checker[name] + break + } catch (e) { } } - } + if (typeof tran === 'string') { + supportTransition = true + supportCSS = true + transitionEndEvent = tran + } - function iterator(index, item, vars, fn, k1, k2, repeat, isArray) { - var key = isArray ? getTraceKey(item) : index - var local = {} - local[k1] = index - local[k2] = item - for (var k in vars) { - if (!(k in local)) { - local[k] = vars[k] + //animationend有两个可用形态 + //IE10+, Firefox 16+ & Opera 12.1+: animationend + //Chrome/Safari: webkitAnimationEnd + //http://blogs.msdn.com/b/davrous/archive/2011/12/06/introduction-to-css3-animat ions.aspx + //IE10也可以使用MSAnimationEnd监听,但是回调里的事件 type依然为animationend + // el.addEventListener('MSAnimationEnd', function(e) { + // alert(e.type)// animationend!!! + // }) + checker = { + 'AnimationEvent': 'animationend', + 'WebKitAnimationEvent': 'webkitAnimationEnd' + } + var ani + for (name in checker) { + if (window[name]) { + ani = checker[name] + break } } - fn(index, item, key, local, repeat) + if (typeof ani === 'string') { + supportAnimation = true + supportCSS = true + animationEndEvent = ani + } + } + module.exports = { + transition: supportTransition, + animation: supportAnimation, + css: supportCSS, + transitionEndEvent: transitionEndEvent, + animationEndEvent: animationEndEvent, + transitionDuration: transitionDuration, + animationDuration: animationDuration } + /***/ }, + /* 72 */ + /***/ function(module, exports, __webpack_require__) { - avalon.directive('for', { - priority: 3, - parse: function (copy, src, binding) { - var str = src.forExpr, aliasAs - str = str.replace(rforAs, function (a, b) { - if (!rident.test(b) || rinvalid.test(b)) { - avalon.error('alias ' + b + ' is invalid --- must be a valid JS identifier which is not a reserved name.') - } else { - aliasAs = b - } - return '' - }) + + avalon.lexer = __webpack_require__(73) + avalon.diff = __webpack_require__(77) + avalon.batch = __webpack_require__(78) + // dispatch与patch 为内置模块 + var vdom2body = __webpack_require__(38) + var rquoteEscapes = /\\\\(['"])/g + function render(vtree, local) { + var _body = Array.isArray(vtree) ? vdom2body(vtree) : vtree + var _local = [] + if (local) { + for (var i in local) { + _local.push('var ' + i + ' = __local__['+avalon.quote(i)+']') + } + } + //处理 props: {"ms-effect": "{is:\\'star\\',action:@action}" 的情况 + _body = _body.replace(rquoteEscapes,"$1") + var body = '__local__ = __local__ || {};\n' + + _local.join(';\n')+'\n' + _body + + try{ + var fn = Function('__vmodel__', '__local__', body) + }catch(e){ + avalon.warn(_body, 'render parse error') + } + return fn + } - var arr = str.split(' in ') - var assign = 'var loop = ' + avalon.parseExpr(arr[1]) + ' \n' - var alias = aliasAs ? 'var ' + aliasAs + ' = loop\n' : '' - var kv = arr[0].match(rargs) + avalon.render = render - if (kv.length === 1) {//确保avalon._each的回调有三个参数 - kv.unshift('$key') - } - kv.push('traceKey', '__local__', 'vnodes') - src.$append = assign + alias + 'avalon._each(loop,function(' - + kv.join(', ') + '){\n' - + (aliasAs ? '__local__[' + avalon.quote(aliasAs) + ']=loop\n' : '') - + 'vnodes.push({\nnodeName: "#document-fragment",\nindex: arguments[0],\nkey: traceKey,\n' + - 'children: new function(){\n var vnodes = []\n' + module.exports = avalon - }, - diff: function (copy, src, cpList, spList, index) { - //将curRepeat转换成一个个可以比较的component,并求得compareText - //如果这个元素没有插入 - if (avalon.callArray) { - if (src.list && src.forExpr.indexOf(avalon.callArray) === -1) { - return - } - } else { - if (src.list) { - return - } - } - var srcRepeat = spList[index + 1] - var curRepeat = cpList[index + 1] - var end = spList[index + 2] - //preRepeat不为空时 - var cache = src.cache || {} - //for指令只做添加删除操作 - var i, c, p - var removes = [] - if (!srcRepeat.length) {//一维数组最开始初始化时 - src.action = 'init' + /***/ }, + /* 73 */ + /***/ function(module, exports, __webpack_require__) { - /* eslint-disable no-cond-assign */ - spList[index + 1] = curRepeat - curRepeat.forEach(function (c, i) { - srcRepeat[i] = c - saveInCache(cache, c) - }) - src.cache = cache - } else if (srcRepeat === curRepeat) { - curRepeat.forEach(function (c) { - saveInCache(cache, c) - }) - src.cache = cache - var noUpdate = true - } else { - src.action = 'update' - var newCache = {} - /* eslint-disable no-cond-assign */ - var fuzzy = [] - for (i = 0; c = curRepeat[i]; i++) { - var p = isInCache(cache, c.key) - if (p) { - p.oldIndex = p.index - p.index = c.index - saveInCache(newCache, p) - } else { - //如果找不到就进行模糊搜索 - fuzzy.push(c) - } - } - for (var i = 0, c; c = fuzzy[i]; i++) { - p = fuzzyMatchCache(cache, c.key) - if (p) { - p.oldIndex = p.index - p.index = c.index - p.key = c.key - } else { - p = c - srcRepeat.push(p) - } + /** + * ------------------------------------------------------------ + * avalon2.1.1的新式lexer + * 将字符串变成一个虚拟DOM树,方便以后进一步变成模板函数 + * 此阶段只会生成VElement,VText,VComment + * ------------------------------------------------------------ + */ + var avalon = __webpack_require__(4) + + __webpack_require__(74) + var voidTag = __webpack_require__(37) + var addTbody = __webpack_require__(75) + var fixPlainTag = __webpack_require__(76) + var plainTag = avalon.oneObject('script,style,textarea,xmp,noscript,option,template') - saveInCache(newCache, p) - } - srcRepeat.sort(function (a, b) { - return a.index - b.index - }) + var ropenTag = /^<([-A-Za-z0-9_]+)\s*([^>]*?)(\/?)>/ + var rendTag = /^<\/([^>]+)>/ + //https://github.com/rviscomi/trunk8/blob/master/trunk8.js + //判定里面有没有内容 + var rcontent = /\S/ + var rfill = /\?\?\d+/g + var rlineSp = /\n\s*/g + var rnowhite = /\S+/g + var number = 1 + var stringPool = {} - src.cache = newCache - for (var i in cache) { - p = cache[i] - p.action = 'leave' - avalon.Array.remove(srcRepeat, p) - removes.push(p) - if (p.arr) { - p.arr.forEach(function (m) { - m.action = 'leave' - removes.push(m) - }) - delete p.arr + function dig(a) { + var key = '??' + number++ + stringPool[key] = a + return key + } + function fill(a) { + var val = stringPool[a] + return val + } + + + function lexer(str) { + stringPool = {} + str = clearString(str) + var stack = [] + stack.last = function () { + return stack[stack.length - 1] + } + var ret = [] + + var breakIndex = 100000 + do { + var node = false + if (str.charAt(0) !== '<') {//处理文本节点 + var i = str.indexOf('<') + i = i === -1 ? str.length : i + var nodeValue = str.slice(0, i).replace(rfill, fill) + str = str.slice(i) + node = { + nodeName: '#text', + nodeValue: nodeValue + } + if (rcontent.test(nodeValue)) { + collectNodes(node, stack, ret)//不收集空白节点 + } + } + if (!node) { + var i = str.indexOf('') + if (l === -1) { + avalon.error('注释节点没有闭合' + str) + } + var nodeValue = str.slice(4, l).replace(rfill, fill) + str = str.slice(l + 3) + node = { + nodeName: '#comment', + nodeValue: nodeValue } + collectNodes(node, stack, ret) } } - if (removes.length > 1) { - removes.sort(function (a, b) { - return a.index - b.index - }) - } - src.removes = removes - var cb = avalon.caches[src.wid] - var vm = copy.vmodel - if (end && cb) { - end.afterChange = [function (dom) { - cb.call(vm, { - type: 'rendered', - target: dom, - signature: src.signature - }) - }] - } - if (!noUpdate) { - src.list = srcRepeat - update(src, this.update) - } - return true + if (!node) { + var match = str.match(ropenTag)//处理元素节点开始部分 + //console.log(match) + if (match) { + var nodeName = match[1].toLowerCase() + var isVoidTag = voidTag[nodeName] || match[3] === '\/' + node = { + nodeName: nodeName, + props: {}, + children: [], + isVoidTag: isVoidTag + } - }, - update: function (dom, vdom, parent) { - if (vdom.action === 'init') { - var b = parent - parent = document.createDocumentFragment() - } - var before = dom - var signature = vdom.signature + var attrs = match[2] + if (attrs) { + collectProps(attrs, node.props) + } + collectNodes(node, stack, ret) + str = str.slice(match[0].length) + if (isVoidTag) { + node.end = true + } else { + stack.push(node) + if (plainTag[nodeName]) { + var index = str.indexOf('') + var innerHTML = str.slice(0, index).trim() + str = str.slice(index) - for (var i = 0, item; item = vdom.removes[i++]; ) { - if (item.dom) { + fixPlainTag(node, nodeName, nomalString(innerHTML)) - delete item.split - if (vdom.hasEffect) { - !function (obj) { - var nodes = moveItem(obj) - var children = obj.children.concat() - obj.children.length = 0 - applyEffects(nodes, children, { - hook: 'onLeaveDone', - staggerKey: signature + 'leave', - cb: function (node) { - if (node.parentNode) { - node.parentNode.removeChild(node) - } - } - }) - }(item) - } else { - moveItem(item, 'add') + } } - } } - vdom.list.forEach(function (el, i) { - if (el.action === 'leave') - return - if (!el.dom) { - el.dom = avalon.domize(el) - } - var f = el.dom - if (el.oldIndex === void 0) { - if (vdom.hasEffect) - var nodes = avalon.slice(f.childNodes) - if (i === 0 && vdom.action === 'init') { - parent.appendChild(f) - } else { - parent.insertBefore(f, before.nextSibling) - } - if (vdom.hasEffect) { - applyEffects(nodes, el.children, { - hook: 'onEnterDone', - staggerKey: signature + 'enter' - }) + if (!node) { + var match = str.match(rendTag)//处理元素节点结束部分 + if (match) { + var nodeName = match[1].toLowerCase() + var last = stack.last() + if (!last) { + avalon.error(match[0] + '前面缺少<' + nodeName + '>') + } else if (last.nodeName !== nodeName) { + avalon.error(last.nodeName + '没有闭合') } - } else if (el.index !== el.oldIndex) { - var nodes = moveItem(el, 'add') - parent.insertBefore(el.dom, before.nextSibling) - vdom.hasEffect && applyEffects(nodes, el.children, { - hook: 'onMoveDone', - staggerKey: signature + 'move' - }) + node = stack.pop() + node.end = true + str = str.slice(match[0].length) } - before = el.split - }) - if (vdom.action === 'init') { - b.insertBefore(parent, dom.nextSibling) } - } - }) + if (!node || --breakIndex === 0) { + break + } + if (node.end) { + fixTbodyAndRepeat(node, stack, ret) + delete node.end + } - function moveItem(item, addToFragment) { - var nodes = item.children.map(function (el) { - return el['ms-if'] ? el.comment : el.dom - }) - if (addToFragment) { - nodes.forEach(function (el) { - item.dom.appendChild(el) - }) - } - return nodes - } + } while (str.length); + return ret - avalon.domize = function (a) { - return avalon.vdomAdaptor(a, 'toDOM') } + module.exports = lexer - var rfuzzy = /^(string|number|boolean)/ - var rkfuzzy = /^_*(string|number|boolean)/ - function fuzzyMatchCache(cache) { - var key - for (var id in cache) { - var key = id - break + + function fixTbodyAndRepeat(node, stack, ret) { + var nodeName = node.nodeName + var props = node.props + if (nodeName === 'table') { + addTbody(node.children) } - if (key) { - return isInCache(cache, key) + var forExpr = props['ms-for'] + //tr两旁的注释节点还会在addTbody中挪一下位置 + if (forExpr) { + delete props['ms-for'] + var p = stack.last() + var arr = p ? p.children : ret + arr.splice(arr.length - 1, 1, { + nodeName: '#comment', + nodeValue: 'ms-for:' + forExpr, + type: nodeName + }, node, { + nodeName: '#comment', + nodeValue: 'ms-for-end:', + type: nodeName + }) + } } - // 新位置: 旧位置 - function isInCache(cache, id) { - var c = cache[id] - if (c) { - var arr = c.arr - if (arr) { - var r = arr.pop() - if (!arr.length) { - c.arr = 0 + + function collectNodes(node, stack, ret) { + var p = stack.last() + if (p) { + p.children.push(node) + } else { + ret.push(node) + } + } + + function collectProps(attrs, props) { + attrs.replace(rnowhite, function (prop) { + var arr = prop.split('=') + var name = arr[0] + var value = arr[1] || '' + if (name.charAt(0) === ':') { + name = 'ms-' + name.slice(1) + } + if (value) { + if (value.indexOf('??') === 0) { + value = nomalString(value). + replace(rlineSp, ''). + // replace(/\"/g, "'"). + slice(1, -1) } - return r } - delete cache[id] - return c - } + if (!(name in props)) { + props[name] = value + } + }) + } - //[1,1,1] number1 number1_ number1__ - function saveInCache(cache, component) { - var trackId = component.key - if (!cache[trackId]) { - cache[trackId] = component - } else { - var c = cache[trackId] - var arr = c.arr || (c.arr = []) - arr.push(component) + function nomalString(str) { + return avalon.unescapeHTML(str.replace(rfill, fill)) + } + + function clearString(str) { + var array = readString(str) + for (var i = 0, n = array.length; i < n; i++) { + str = str.replace(array[i], dig) } + return str } - var applyEffects = function (nodes, vnodes, opts) { - vnodes.forEach(function (vdom, i) { - avalon.applyEffect(nodes[i], vdom, opts) - }) + function readString(str) { + var end, s = 0 + var ret = [] + for (var i = 0, n = str.length; i < n; i++) { + var c = str.charAt(i) + if (!end) { + if (c === "'") { + end = "'" + s = i + } else if (c === '"') { + end = '"' + s = i + } + } else { + if (c === '\\') { + i += 1 + continue + } + if (c === end) { + ret.push(str.slice(s, i + 1)) + end = false + } + } + } + return ret } /***/ }, - /* 67 */ + /* 74 */ /***/ function(module, exports, __webpack_require__) { - var update = __webpack_require__(45) - //var reconcile = require('../strategy/reconcile') - var tryInitComponent = __webpack_require__(68) + + var rmsForStart = /^\s*ms\-for\:/ + var rmsForEnd = /^\s*ms\-for\-end/ + var vdom2body = __webpack_require__(38) + avalon.speedUp = function (array) { + hasDirectives(array) + return array + } - avalon.component = function (name, definition) { - //这是定义组件的分支,并将列队中的同类型对象移除 - if (!avalon.components[name]) { - avalon.components[name] = definition - }//这里没有返回值 + var hasDirectives = function (arr) { + var nodes = [], hasDir = false + for (var i = 0; i < arr.length; i++) { + var el = arr[i] + var isComment = el.nodeName === '#comment' + if (isComment && rmsForStart.test(el.nodeValue)) { + hasDir = true//在startRepeat节点前添加一个数组,收集后面的节点 + nodes.push(el) + var old = nodes + nodes = [] + nodes.list = old + nodes.start = el + } else if (isComment && rmsForEnd.test(el.nodeValue)) { + var old = nodes + nodes = old.list + var start = old.start + delete old.list + delete old.start + nodes.push(old, el) + el.dynamic = true + var uuid = start.signature || (start.signature = avalon.makeHashCode('for')) + el.signature = uuid + + start.forExpr = start.nodeValue.replace(/ms\-for:\s*/, '') + if (old.length === 1) { + var element = old[0] + if (element.props) { + if(element.props.slot){ + start.props = '{slot: "'+element.props.slot+'"}' + } + var cb = element.props['data-for-rendered'] + if (cb) { + delete element.props['data-for-rendered'] + var wid = cb + ':cb' + if (!avalon.caches[wid]) { + avalon.caches[wid] = Function('return ' + avalon.parseExpr(cb, 'on'))() + } + start.wid = wid + } + } + } + for (var j = 0; j < old.length; j++) { + var el = old[j] + var elem = el.dom + if (elem && elem.parentNode) {//移除真实节点 + elem.parentNode.removeChild(elem) + } + } + start.hasEffect = hasEffect(old) + hasDirectives(old) + if (!avalon.caches[uuid]) { + avalon.caches[uuid] = vdom2body(old, true) + } + old.length = 0 + } else { + if (hasDirective(el)) { + hasDir = true + } + nodes.push(el) + } + } + arr.length = 0 + arr.push.apply(arr, nodes) + return hasDir } - avalon.directive('widget', { - priority: 4, - parse: function (copy, src, binding) { - src.props.wid = src.props.wid || avalon.makeHashCode('w') - //将渲染函数的某一部分存起来,渲在c方法中转换为函数 - copy[binding.name] = avalon.parseExpr(binding) - copy.template = src.template - copy.vmodel = '__vmodel__' - copy.local = '__local__' - }, - define: function () { - return avalon.mediatorFactory.apply(this, arguments) - }, - diff: function (copy, src, name, copyList, srcList, index) { - var a = copy[name] - if (Object(a) === a) { - //有三个地方可以设置is, 属性,标签名,配置对象 - var is = src.props.is || (/^ms\-/.test(src.nodeName) ? src.nodeName : 0) - if (!is) {//开始大费周章地获取组件的类型 - a = a.$model || a//安全的遍历VBscript - if (Array.isArray(a)) {//转换成对象 - a.unshift({})// 防止污染旧数据 - avalon.mix.apply(0, a) - a = a.shift() + function hasDirective(node) { + + var nodeName = node.nodeName + switch (nodeName) { + case '#text': + if (avalon.config.rexpr.test(node.nodeValue)) { + return node.dynamic = true + } else { + return false + } + case '#comment': + if (node.dynamic) { + return true + } + return false + case void 0: + return true + default: + var props = node.props || {} + if ('ms-skip' in props) { + node.skipContent = true + return false + } + var flag = false + if (nodeName === 'input') { + if (!props.type) { + props.type = 'text' + } + } else if (nodeName === 'select') { + var postfix = props.hasOwnProperty('multiple') ? 'multiple' : 'one' + props.type = nodeName + '-' + postfix + } else if (nodeName.indexOf('ms-') === 0) { + if (!props['ms-widget']) { + props.is = nodeName + props['ms-widget'] = '{is:"' + nodeName + '"}' + } + } + var childDir = false + if (props['ms-widget']) { + childDir = true + delDir(props, 'html', 'widget') + delDir(props, 'text', 'widget') + var clone = avalon.mix({}, node) + var cprops = avalon.mix({}, node.props) + delete cprops['ms-widget'] + delete clone.isVoidTag + clone.nodeName = "cheng" + clone.props = cprops + node.template = avalon.vdomAdaptor(clone, 'toHTML') + if (!node.isVoidTag) + node.children = [] + } + if (props['ms-text']) { + childDir = true + delDir(props, 'html', 'text') + if (!node.isVoidTag) { + node.children = [] + } + } + if (props['ms-html']) { + childDir = true + if (!node.isVoidTag) { + node.children = [] } - is = a.is } - var vmName = 'component-vm:' + is - - src.props.is = is - src.vmodel = copy.vmodel - //如果组件没有初始化,那么先初始化(生成对应的vm,$render) - if (!src[vmName]) { - if (!tryInitComponent(src, copy[name], copy.local, copy.template)) { - //替换成注释节点 - src.nodeValue = 'unresolved component placeholder' - copyList[index] = src - update(src, this.mountComment) - return + var hasProps = false + for (var i in props) { + hasProps = true + if (i.indexOf('ms-') === 0) { + flag = true + node.dynamic = {} + break } } - - //如果已经存在于avalon.scopes - var comVm = src[vmName] - var scope = avalon.scopes[comVm.$id] - if (scope && scope.vmodel) { - var com = scope.vmodel.$element - if (src.dom !== com) { - var component = com.vtree[0] - srcList[index] = copyList[index] = component - src.com = com - if (!component.skipContent) { - component.skipContent = 'optimize' - } - - update(src, this.replaceCachedComponent) - - update(component, function () { - if (component.skipContent === 'optimize') { - component.skipContent = true - } - }, 'afterChange') - return - } + if (hasProps) { + node.props = props } - var render = comVm.$render - var tree = render(comVm, copy.local) - var component = tree[0] - if (component && isComponentReady(component)) { - component.local = copy.local - Array( - vmName, - 'component-html:' + is, - 'component-ready:' + is, - 'dom', 'dynamic' - ).forEach(function (name) { - component[name] = src[name] - }) - component.vmodel = comVm - copyList[index] = component - // 如果与ms-if配合使用, 会跑这分支 - if (src.comment && src.nodeValue) { - component.dom = src.comment + if (node.children) { + var r = hasDirectives(node.children) + if (r) { + delete node.skipContent + return true } - if (src.nodeName !== component.nodeName) { - srcList[index] = component - update(component, this.mountComponent) + if (!childDir) { + node.skipContent = true } else { - update(src, this.updateComponent) + delete node.skipContent } - } else { - - src.nodeValue = 'unresolved component placeholder' - copyList[index] = { - nodeValue: 'unresolved component placeholder', - nodeName: '#comment' + } + return flag + } + } + + function delDir(props, a, b) { + if (props['ms-' + a]) { + avalon.warn(a, '指令不能与', b, '指令共存于同一个元素') + delete props['ms-' + a] + } + } + + function hasEffect(arr) { + for (var i = 0, el; el = arr[i++]; ) { + if (el.props && el.props['ms-effect']) { + return true + } + } + return false + } + + + /***/ }, + /* 75 */ + /***/ function(module, exports) { + + + //如果直接将tr元素写table下面,那么浏览器将将它们(相邻的那几个),放到一个动态创建的tbody底下 + module.exports = function addTbody(nodes) { + var tbody, needAddTbody = false, count = 0, start = 0, n = nodes.length + for (var i = 0; i < n; i++) { + var node = nodes[i] + if (!tbody) { + if ((node.type || node.nodeName) === 'tr') { + //收集tr及tr两旁的注释节点 + tbody = { + nodeName: 'tbody', + children: [] } - update(src, this.mountComment) + tbody.children.push(node) + if (node.type) { + delete node.type + } + needAddTbody = true + if (start === 0) + start = i + nodes[i] = tbody } } else { - if (src.props.is === copy.props.is) { - update(src, this.updateComponent) + if (node.nodeName !== 'tr' && node.children) { + tbody = false + } else { + tbody.children.push(node) + count++ + nodes[i] = 0 } } - }, - replaceCachedComponent: function (dom, vdom, parent) { - var com = vdom.com - parent.replaceChild(com, dom) - vdom.dom = com - delete vdom.com - }, - mountComment: function (dom, vdom, parent) { - var comment = document.createComment(vdom.nodeValue) - vdom.dom = comment - parent.replaceChild(comment, dom) - }, - updateComponent: function (dom, vdom) { - var vm = vdom["component-vm:" + vdom.props.is] - var viewChangeObservers = vm.$events.onViewChange - if (viewChangeObservers && viewChangeObservers.length) { - update(vdom, viewChangeHandle, 'afterChange') - } - }, - mountComponent: function (dom, vdom, parent) { - delete vdom.dom - var com = avalon.vdomAdaptor(vdom, 'toDOM') - - var is = vdom.props.is - var vm = vdom['component-vm:' + is] - vm.$fire('onInit', { - type: 'init', - vmodel: vm, - is: is - }) - - parent.replaceChild(com, dom) - - vdom.dom = vm.$element = com - com.vtree = [vdom] - avalon.onComponentDispose(com) - vdom['component-ready:' + is] = true - //-------------- - avalon.scopes[vm.$id] = { - vmodel: vm, - top: vdom.vmodel, - local: vdom.local - } - //-------------- - update(vdom, function () { - vm.$fire('onReady', { - type: 'ready', - target: com, - vmodel: vm, - is: is - }) - }, 'afterChange') + } - update(vdom, function () { - vdom[ 'component-html:' + is] = avalon.vdomAdaptor(vdom, 'toHTML') - }, 'afterChange') + if (needAddTbody) { + for (i = start; i < n; i++) { + if (nodes[i] === 0) { + nodes.splice(i, 1) + i-- + count-- + if (count === 0) { + break + } + } + } } - }) + } - function viewChangeHandle(dom, vdom) { - var is = vdom.props.is - var vm = vdom['component-vm:' + is] - var html = 'component-html:' + is - var preHTML = vdom[html] - var curHTML = avalon.vdomAdaptor(vdom, 'toHTML') - if (preHTML !== curHTML) { - vdom[html] = curHTML - vm.$fire('onViewChange', { - type: 'viewchange', - target: dom, - vmodel: vm, - is: is - }) + /***/ }, + /* 76 */ + /***/ function(module, exports) { + + /* + *  修正容器元素 + */ + + function fixPlainTag(node, nodeName, innerHTML) { + switch (nodeName) { + case 'style': + case 'script': + case 'noscript': + case 'template': + case 'xmp': + node.children = [ + { + nodeName: '#text', + skipContent: true, + nodeValue: innerHTML + } + ] + break + case 'textarea': + var props = node.props + props.type = 'textarea' + props.value = innerHTML + node.children = [] + break + case 'option': + node.children = [{ + nodeName: '#text', + nodeValue: trimHTML(innerHTML) + }] + break } + } + //专门用于处理option标签里面的标签 + var rtrimHTML = /<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi + function trimHTML(v) { + return String(v).replace(rtrimHTML, '').trim() + } + module.exports = fixPlainTag - function isComponentReady(vnode) { - var isReady = true - try { - hasUnresolvedComponent(vnode) - } catch (e) { - isReady = false + /***/ }, + /* 77 */ + /***/ function(module, exports) { + + /** + * ------------------------------------------------------------ + * diff 对比新旧两个虚拟DOM树,根据directive中的diff方法为新虚拟DOM树 + * 添加change, afterChange更新钩子 + * ------------------------------------------------------------ + */ + var emptyArr = [] + // 防止被引用 + var emptyObj = function () { + return { + children: [], props: {} } - return isReady } + var directives = avalon.directives + var rbinding = /^ms-(\w+)-?(.*)/ - function hasUnresolvedComponent(vnode) { - vnode.children.forEach(function (el) { - if (el.nodeName === '#comment') { - if (el.nodeValue === 'unresolved component placeholder') { - throw 'unresolved' - } - } else if (el.children) { - hasUnresolvedComponent(el) - } - }) - } + function diff(copys, sources) { + for (var i = 0; i < copys.length; i++) { + var copy = copys[i] + var src = sources[i] || copys[i] + switch (copy.nodeName) { + case '#text': + if (copy.dynamic) { + var curValue = copy.nodeValue + '' + if (curValue !== src.nodeValue) { + src.nodeValue = curValue + if (src.dom) { + src.dom.nodeValue = curValue + } + } + } + break + case '#comment': + if (copy.forExpr) {//比较循环区域的元素位置 + directives['for'].diff(copy, src, copys, sources, i) + } else if (src.afterChange) { + execHooks(src, src.afterChange) + } + break + case void(0): + diff(copy, src)//比较循环区域的内容 + break + case '#document-fragment': + diff(copy.children, src.children)//比较循环区域的内容 + break + default: + if (copy.dynamic) { + var index = i + if (copy['ms-widget']) { + avalon.directives['widget'].diff(copy, src, 'ms-widget', copys, sources, index) + copy = copys[i] + src = sources[i] || emptyObj() + delete copy['ms-widget'] + } - /***/ }, - /* 68 */ - /***/ function(module, exports, __webpack_require__) { + if ('ms-if' in copy) { + avalon.directives['if'].diff(copy, src, 'ms-if', copys, sources, index) + copy = copys[i] + src = sources[i] || emptyObj() + delete copy['ms-if'] + } + diffProps(copy, src) + } - var skipArray = __webpack_require__(69) + if (/^\w/.test(copy.nodeName) && !copy.skipContent && !copy.isVoidTag) { + diff(copy.children, src.children || []) + } - var legalTags = {wbr: 1, xmp: 1, template: 1} - var events = 'onInit,onReady,onViewChange,onDispose' - var componentEvents = avalon.oneObject(events) - var immunity = events.split(',').concat('is', 'define') - var onceWarn = true - function initComponent(src, rawOption, local, template) { - var tag = src.nodeName - var is = src.props.is - //判定用户传入的标签名是否符合规格 - if (!legalTags[tag] && !isCustomTag(tag)) { - avalon.warn(tag + '不合适做组件的标签') - return - } - //开始初始化组件 - var hooks = {} - //用户只能操作顶层VM - //只有$id,is的对象就是emptyOption - if (!rawOption) { - options = [] - } else { - var options = [].concat(rawOption) - options.forEach(function (a) { - if (a && typeof a === 'object') { - mixinHooks(hooks, (a.$model || a), true) - } - }) - } - var definition = avalon.components[is] - //如果连组件的定义都没有加载回来,应该立即返回 - if (!definition) { - return + if (src.afterChange) { + execHooks(src, src.afterChange) + } + break + } } + } - - //得到组件在顶层vm的配置对象名 - if (!hooks.$id && onceWarn) { - avalon.warn('warning!', is, '组件最好在ms-widget配置对象中指定全局不重复的$id以提高性能!\n', - '若在ms-for循环中可以利用 ($index,el) in @array 中的$index拼写你的$id\n', - '如 ms-widget="{is:\'ms-button\',$id:\'btn\'+$index}"' - ) - onceWarn = false - } - var define = hooks.define - define = define || avalon.directives.widget.define - //生成组件VM - var $id = hooks.$id || src.props.wid || 'w' + (new Date - 0) - var defaults = avalon.mix(true, {}, definition.defaults) - mixinHooks(hooks, defaults, false)//src.vmodel, - var skipProps = immunity.concat() - function sweeper(a, b) { - skipProps.forEach(function (k) { - delete a[k] - delete b[k] - }) + function execHooks(el, hooks) { + if (hooks.length) { + for (var hook, i = 0; hook = hooks[i++]; ) { + hook(el.dom, el) + } } + delete el.afterChange + } - sweeper.isWidget = true - var vmodel = define.apply(sweeper, [src.vmodel, defaults].concat(options)) - if (!avalon.modern) {//增强对IE的兼容 - for (var i in vmodel) { - if (!skipArray[i] && typeof vmodel[i] === 'function') { - vmodel[i] = vmodel[i].bind(vmodel) + function diffProps(copy, source) { + var directives = avalon.directives + try { + for (var name in copy) { + var match = name.match(rbinding) + var type = match && match[1] + if (directives[type]) { + directives[type].diff(copy, source, name) } } + + } catch (e) { + avalon.warn(type, e, e.stack || e.message, 'diffProps error') } + } + avalon.diff = diff + avalon.diffProps = diffProps + module.exports = diff - vmodel.$id = $id - avalon.vmodels[$id] = vmodel - //绑定组件的生命周期钩子 - for (var e in componentEvents) { - if (hooks[e]) { - hooks[e].forEach(function (fn) { - vmodel.$watch(e, fn) - }) - } - } - // 生成外部的渲染函数 - // template保存着最原始的组件容器信息 - // 我们先将它转换成虚拟DOM,如果是xmp, template, - // 它们内部是一个纯文本节点, 需要继续转换为虚拟DOM - var shell = avalon.lexer(template) + /***/ }, + /* 78 */ + /***/ function(module, exports, __webpack_require__) { + + /** + * ------------------------------------------------------------ + * batch 同时对N个视图进行全量更新 + * ------------------------------------------------------------ + */ - var shellRoot = shell[0] - shellRoot.children = shellRoot.children || [] - shellRoot.props.is = is - shellRoot.props.wid = $id - avalon.speedUp(shell) + var reconcile = __webpack_require__(53) - var render = avalon.render(shell, local) + //如果正在更新一个子树,那么将它放到 + var needRenderIds = [] + var renderingID = false + avalon.suspendUpdate = 0 - //生成内部的渲染函数 - var finalTemplate = definition.template.trim() - if (typeof definition.getTemplate === 'function') { - finalTemplate = definition.getTemplate(vmodel, finalTemplate) + function batchUpdate(id) { + if (renderingID) { + return avalon.Array.ensure(needRenderIds, id) + } else { + renderingID = id } - var vtree = avalon.lexer(finalTemplate) - - if (vtree.length > 1) { - avalon.error('组件必须用一个元素包起来') + var scope = avalon.scopes[id] + if (!scope || !document.nodeName || avalon.suspendUpdate) { + return renderingID = null + } + var vm = scope.vmodel + var dom = vm.$element + var source = dom.vtree || [] + var renderFn = vm.$render + var copy = renderFn(scope.vmodel, scope.local) + if (scope.isTemp) { + //在最开始时,替换作用域的所有节点,确保虚拟DOM与真实DOM是对齐的 + delete avalon.scopes[id] + } + + + avalon.diff(copy, source) + + + var index = needRenderIds.indexOf(renderingID) + renderingID = 0 + if (index > -1) { + var removed = needRenderIds.splice(index, 1) + return batchUpdate(removed[0]) } - var soleSlot = definition.soleSlot - replaceSlot(vtree, soleSlot) - avalon.speedUp(vtree) - var render2 = avalon.render(vtree) + var more = needRenderIds.shift() + if (more) { + batchUpdate(more) + } + } - //生成最终的组件渲染函数 - var str = fnTemplate + '' - var zzzzz = soleSlot ? avalon.quote(soleSlot) : "null" - str = str. - replace('XXXXX', stringifyAnonymous(render)). - replace('YYYYY', stringifyAnonymous(render2)). - replace('ZZZZZ', zzzzz) - var begin = str.indexOf('{') + 1 - var end = str.lastIndexOf("}") - var lastFn = Function('vm', 'local', str.slice(begin, end)) - vmodel.$render = lastFn + module.exports = avalon.batch = batchUpdate - src['component-vm:' + is] = vmodel - return vmodel.$render = lastFn + /***/ }, + /* 79 */, + /* 80 */ + /***/ function(module, exports) { + function inDomTree(el) { + while (el) { + if (el.nodeType === 9) { + return true + } + el = el.parentNode + } + return false } - module.exports = initComponent - function stringifyAnonymous(fn) { - return fn.toString().replace('anonymous', '') - .replace(/\s*\/\*\*\//g, '') + function fireDisposeHook(el) { + if (el.nodeType === 1 && el.getAttribute('wid') && !inDomTree(el)) { + var wid = el.getAttribute('wid') + var docker = avalon.scopes[ wid ] + if (!docker) + return + var vm = docker.vmodel + docker.vmodel.$fire("onDispose", { + type: 'dispose', + target: el, + vmodel: vm + }) + if (docker && !el.getAttribute('cached')) { + delete docker.vmodel + delete avalon.scopes[ wid ] + var is = el.getAttribute('is') + var v = el.vtree + detachEvents(v) + if (v) { + v[0][is + '-mount'] = false + v[0]['component-ready:' + is] = false + } + } + return false + } } - - - function fnTemplate() { - var shell = (XXXXX)(vm, local); - var shellRoot = shell[0] - var vtree = (YYYYY)(vm, local); - var component = vtree[0] - - //处理diff - - for (var i in shellRoot) { - if (i !== 'children' && i !== 'nodeName') { - if (i === 'props') { - avalon.mix(component.props, shellRoot.props) - } else { - component[i] = shellRoot[i] + var rtag = /^\w/ + function detachEvents(arr) { + for (var i in arr) { + var el = arr[i] + if (rtag.test(el.nodeName)) { + for (var i in el) { + if (i.indexOf('ms-on') === 0) { + delete el[i] + } + } + if (el.children) { + detachEvents(el.children) } } } - - - var soleSlot = ZZZZZ - var slots = avalon.collectSlots(shellRoot, soleSlot) - if (soleSlot && (!slots[soleSlot] || !slots[soleSlot].length)) { - slots[soleSlot] = [{ - nodeName: '#text', - nodeValue: vm[soleSlot], - dynamic: true - }] + } + function fireDisposeHookDelay(a) { + setTimeout(function () { + fireDisposeHook(a) + }, 4) + } + function fireDisposeHooks(nodes) { + for (var i = 0, el; el = nodes[i++]; ) { + fireDisposeHook(el) } - avalon.insertSlots(vtree, slots) + } + module.exports = { + fireDisposeHookDelay: fireDisposeHookDelay, + fireDisposeHooks: fireDisposeHooks, + fireDisposeHook: fireDisposeHook + } - delete component.skipAttrs - delete component.skipContent - return vtree + /***/ }, + /* 81 */, + /* 82 */, + /* 83 */ + /***/ function(module, exports, __webpack_require__) { - } + + var $$midway = {} + var $$skipArray = __webpack_require__(69) + var dispatch = __webpack_require__(84) + var $emit = dispatch.$emit + var $watch = dispatch.$watch + /* + * initEvents + * isSkip + * modelAdaptor + * makeAccessor + */ - function replaceSlot(vtree, slotName) { - for (var i = 0, el; el = vtree[i]; i++) { - if (el.nodeName === 'slot') { - var name = el.props.name || slotName + function initEvents($vmodel, heirloom) { + heirloom.__vmodel__ = $vmodel + var hide = $$midway.hideProperty - vtree.splice(i, 1, { - nodeName: '#comment', - nodeValue: 'slot:' + name, - dynamic: true, - type: name - }, { - nodeName: '#comment', - nodeValue: 'slot-end:' - }) - i++ - } else if (el.children) { - replaceSlot(el.children, slotName) + hide($vmodel, '$events', heirloom) + hide($vmodel, '$watch', function () { + if (arguments.length === 2) { + return $watch.apply($vmodel, arguments) + } else { + throw '$watch方法参数不对' } - } + }) + hide($vmodel, '$fire', function (expr, a, b) { + var list = $vmodel.$events[expr] + $emit(list, $vmodel, expr, a, b) + }) } + var rskip = /function|window|date|regexp|element/i - avalon.insertSlots = function (vtree, slots) { - for (var i = 0, el; el = vtree[i]; i++) { - // console.log(el.type) - if (el.nodeName === '#comment' && slots[el.type]) { - // console.log(slots[el.type]) - var args = [i + 1, 0].concat(slots[el.type]) - vtree.splice.apply(vtree, args) - i += slots[el.type].length - } else if (el.children) { - avalon.insertSlots(el.children, slots) + function isSkip(key, value, skipArray) { + // 判定此属性能否转换访问器 + return key.charAt(0) === '$' || + skipArray[key] || + (rskip.test(avalon.type(value))) || + (value && value.nodeName && value.nodeType > 0) + } + + function modelAdaptor(definition, old, heirloom, options) { + //如果数组转换为监控数组 + if (Array.isArray(definition)) { + return $$midway.arrayFactory(definition, old, heirloom, options) + } else if (Object(definition) === definition && typeof definition !== 'function') { + //如果此属性原来就是一个VM,拆分里面的访问器属性 + if (old && old.$id) { + ++avalon.suspendUpdate + //1.5带来的优化方案 + if (old.$track !== Object.keys(definition).sort().join(';;')) { + var vm = $$midway.slaveFactory(old, definition, heirloom, options) + } else { + vm = old + } + for (var i in definition) { + if ($$skipArray[i]) + continue + vm[i] = definition[i] + } + --avalon.suspendUpdate + return vm + } else { + vm = $$midway.masterFactory(definition, heirloom, options) + return vm } + } else { + return definition } } + $$midway.modelAdaptor = modelAdaptor - avalon.collectSlots = function (node, soleSlot) { - var slots = {} - if (soleSlot) { - slots[soleSlot] = node.children - slots.__sole__ = soleSlot - } else { - node.children.forEach(function (el, i) { - var name = el.props && el.props.slot - if (el.forExpr) { - slots[name] = node.children.slice(i, i + 2) - } else { - if (Array.isArray(slots[name])) { - slots[name].push(el) - } else { - slots[name] = [el] + + function makeAccessor(sid, spath, heirloom) { + var old = NaN + function get() { + return old + } + get.heirloom = heirloom + return { + get: get, + set: function (val) { + if (old === val) { + return + } + var vm = heirloom.__vmodel__ + if (val && typeof val === 'object') { + val = $$midway.modelAdaptor(val, old, heirloom, { + pathname: spath, + id: sid + }) + } + var older = old + old = val + if (this.$hashcode && vm ) { + vm.$events.$$dirty$$ = true + if(vm.$events.$$wait$$) + return + //★★确保切换到新的events中(这个events可能是来自oldProxy) + if (heirloom !== vm.$events) { + get.heirloom = vm.$events + } + + //如果这个属性是组件配置对象中的属性,那么它需要触发组件的回调 + emitWidget(get.$decompose, spath, val, older) + //触发普通属性的回调 + if (spath.indexOf('*') === -1) { + $emit(get.heirloom[spath], vm, spath, val, older) } + //如果这个属性是数组元素上的属性 + emitArray(sid+'', vm, spath, val, older) + //如果这个属性存在通配符 + emitWildcard(get.heirloom, vm, spath, val, older) + vm.$events.$$dirty$$ = false + batchUpdateView(vm.$id) } - }) + }, + enumerable: true, + configurable: true } - return slots } - - //必须以字母开头,结尾以字母或数字结束,中间至少出现一次"-", - //并且不能大写字母,特殊符号,"_","$",汉字 - var rcustomTag = /^[a-z]([a-z\d]+\-)+[a-z\d]+$/ - - function isCustomTag(type) { - return rcustomTag.test(type) || avalon.components[type] + function batchUpdateView(id) { + avalon.rerenderStart = new Date + var dotIndex = id.indexOf('.') + if (dotIndex > 0) { + avalon.batch(id.slice(0, dotIndex)) + } else { + avalon.batch(id) + } } - function mixinHooks(target, option, overwrite) { - for (var k in option) { - var v = option[k] - //如果是生命周期钩子,总是不断收集 - if (componentEvents[k]) { - if (k in target) { - target[k].push(v) - } else { - target[k] = [option[k]] - } - } else { - if (overwrite) { - target[k] = v - } + var rtopsub = /([^.]+)\.(.+)/ + function emitArray(sid, vm, spath, val, older) { + if (sid.indexOf('.*.') > 0) { + var arr = sid.match(rtopsub) + var top = avalon.vmodels[ arr[1] ] + if (top) { + var path = arr[2] + $emit(top.$events[ path ], vm, spath, val, older) } } } - /***/ }, - /* 69 */ - /***/ function(module, exports) { - - /** - * - $$skipArray:是系统级通用的不可监听属性 - $skipArray: 是当前对象特有的不可监听属性 - - 不同点是 - $$skipArray被hasOwnProperty后返回false - $skipArray被hasOwnProperty后返回true - */ - - module.exports = avalon.oneObject('$id,$render,$track,$element,$watch,$fire,$events,$model,$skipArray,$accessors,$hashcode,$run,$wait,__proxy__,__data__,__const__') - - /***/ }, - /* 70 */ - /***/ function(module, exports, __webpack_require__) { - - var support = __webpack_require__(71) - var Cache = __webpack_require__(30) - var update = __webpack_require__(45) - - avalon.directive('effect', { - priority: 5, - diff: function (copy, src, name) { - var copyObj = copy[name] - copyObj = copy.$model || copyObj - if (typeof copyObj === 'string') { - var is = copyObj - copyObj = { - is: is - } - - } else if (Array.isArray(copyObj)) { - copyObj = avalon.mix.apply({}, copyObj) - } - - copyObj.action = copyObj.action || 'enter' - if (Object(copyObj) === copyObj) { - if (!src.dynamic[name] || diffObj(copyObj, src[name] || {})) { - src[name] = copyObj - update(src, this.update, 'afterChange') - } - } - if (copy !== src) { - delete copy[name] - } - }, - update: function (dom, vdom, parent, option) { - if(!dom || dom.nodeType !== 1){ - return - } - - if (dom.animating) { - return - } - dom.animating = true - var localeOption = vdom['ms-effect'] - if (!vdom.dynamic['ms-effect']) { - var a = localeOption.cb || avalon.noop - localeOption.cb = [function () { - vdom.dynamic['ms-effect'] = 1 - localeOption.cb = a - }].concat(a) - } - var type = localeOption.is - option = option || {} - if (!type) {//如果没有指定类型 - return avalon.warn('need is option') - } - var effects = avalon.effects - if (support.css && !effects[type]) { - avalon.effect(type, {}) - } - var globalOption = effects[type] - if (!globalOption) {//如果没有定义特效 - return avalon.warn(type + ' effect is undefined') - } - var action = option.action || localeOption.action - var Effect = avalon.Effect - if (typeof Effect.prototype[action] !== 'function') { - return avalon.warn(action + ' action is undefined') - } - var effect = new Effect(dom) - var finalOption = avalon.mix(option, globalOption, localeOption) - if (finalOption.queue) { - animationQueue.push(function () { - effect[action](finalOption) - }) - callNextAnimation() + function emitWidget(whole, spath, val, older) { + if (whole && whole[spath]) { + var wvm = whole[spath] + if (!wvm.$hashcode) { + delete whole[spath] } else { - setTimeout(function () { - effect[action](finalOption) - }, 4) + var wpath = spath.replace(/^[^.]+\./, '') + if (wpath !== spath) { + $emit(wvm.$events[wpath], wvm, wpath, val, older) + } } } - }) - function diffObj(a, b) { - for (var i in a) { - if (a[i] !== b[i]) - return true - } - return false } - var animationQueue = [] - function callNextAnimation() { - if (animationQueue.lock) - return - var fn = animationQueue[0] - if (fn) { - callNextAnimation.lock = true - fn() + function emitWildcard(obj, vm, spath, val, older) { + if (obj.__fuzzy__) { + obj.__fuzzy__.replace(avalon.rword, function (expr) { + var list = obj[expr] + var reg = list.reg + if (reg && reg.test(spath)) { + $emit(list, vm, spath, val, older) + } + return expr + }) } } - avalon.effects = {} - //这里定义CSS动画 - - avalon.effect = function (name, definition) { - avalon.effects[name] = definition || {} - if (support.css) { - if (!definition.enterClass) { - definition.enterClass = name + '-enter' - } - if (!definition.enterActiveClass) { - definition.enterActiveClass = definition.enterClass + '-active' - } - if (!definition.leaveClass) { - definition.leaveClass = name + '-leave' - } - if (!definition.leaveActiveClass) { - definition.leaveActiveClass = definition.leaveClass + '-active' - } + function define(definition) { + var $id = definition.$id + if (!$id && avalon.config.debug) { + avalon.warn('vm.$id must be specified') } - if (!definition.action) { - definition.action = 'enter' + if (avalon.vmodels[$id]) { + throw Error('error:[' + $id + '] had defined!') } - } - - - var Effect = function (el) { - this.el = el - } - avalon.Effect = Effect - Effect.prototype = { - enter: createAction('Enter'), - leave: createAction('Leave'), - move: createAction('Move') - } + var vm = $$midway.masterFactory(definition, {}, { + pathname: '', + id: $id, + master: true + }) - var rsecond = /\d+s$/ - function toMillisecond(str) { - var ratio = rsecond.test(str) ? 1000 : 1 - return parseFloat(str) * ratio - } + return avalon.vmodels[$id] = vm - function execHooks(options, name, el) { - var list = options[name] - list = Array.isArray(list) ? list : typeof list === 'function' ? [list] : [] - list.forEach(function (fn) { - fn && fn(el) - }) } - var staggerCache = new Cache(128) - function createAction(action) { - var lower = action.toLowerCase() - return function (option) { - var elem = this.el - var $el = avalon(elem) - var enterAnimateDone - var staggerTime = isFinite(option.stagger) ? option.stagger * 1000 : 0 - if (staggerTime) { - if (option.staggerKey) { - var stagger = staggerCache.get(option.staggerKey) || - staggerCache.put(option.staggerKey, { - count: 0, - items: 0 - }) - stagger.count++ - stagger.items++ - } + function arrayFactory(array, old, heirloom, options) { + if (old && old.splice) { + var args = [0, old.length].concat(array) + ++avalon.suspendUpdate + avalon.callArray = options.pathname + + old.splice.apply(old, args) + --avalon.suspendUpdate + return old + } else { + for (var i in __array__) { + array[i] = __array__[i] } - var staggerIndex = stagger && stagger.count || 0 - var animationDone = function (e) { - var isOk = e !== false - elem.animating = void 0 - enterAnimateDone = true - var dirWord = isOk ? 'Done' : 'Abort' - execHooks(option, 'on' + action + dirWord, elem) - avalon.unbind(elem, support.transitionEndEvent) - avalon.unbind(elem, support.animationEndEvent) - if (stagger) { - if (--stagger.items === 0) { - stagger.count = 0 + + array.notify = function (a, b, c, d) { + var vm = heirloom.__vmodel__ + if (vm) { + var path = a === null || a === void 0 ? + options.pathname : + options.pathname + '.' + a + vm.$fire(path, b, c) + if (!d && !heirloom.$$wait$$ && !avalon.suspendUpdate ) { + avalon.callArray = path + batchUpdateView(vm.$id) + delete avalon.callArray } } - if (option.queue) { - animationQueue.lock = false - animationQueue.shift() - callNextAnimation() - } } - execHooks(option, 'onBefore' + action, elem) - if (option[lower]) { - option[lower](elem, function (ok) { - animationDone(ok !== false) - }) - } else if (support.css) { - $el.addClass(option[lower + 'Class']) - if (lower === 'leave') { - $el.removeClass(option.enterClass + ' ' + option.enterActiveClass) - } else if (lower === 'enter') { - $el.removeClass(option.leaveClass + ' ' + option.leaveActiveClass) - } + var hashcode = avalon.makeHashCode('$') + options.array = true + options.hashcode = hashcode + options.id = options.id || hashcode + $$midway.initViewModel(array, heirloom, {}, {}, options) - $el.bind(support.transitionEndEvent, animationDone) - $el.bind(support.animationEndEvent, animationDone) - setTimeout(function () { - enterAnimateDone = avalon.root.offsetWidth === NaN - $el.addClass(option[lower + 'ActiveClass']) - var computedStyles = window.getComputedStyle(elem) - var tranDuration = computedStyles[support.transitionDuration] - var animDuration = computedStyles[support.animationDuration] - var time = toMillisecond(tranDuration) || toMillisecond(animDuration) - if (!time === 0) { - animationDone(false) - } else if (!staggerTime) { - setTimeout(function () { - if (!enterAnimateDone) { - animationDone(false) - } - }, time + 130) - } - }, 17 + staggerTime * staggerIndex)// = 1000/60 + for (var j = 0, n = array.length; j < n; j++) { + array[j] = modelAdaptor(array[j], 0, {}, { + id: array.$id + '.*', + master: true + }) } + return array } } + $$midway.arrayFactory = arrayFactory - avalon.applyEffect = function (node, vnode, opts) { - var cb = opts.cb - var curEffect = vnode['ms-effect'] - if (curEffect && node && node.nodeType === 1) { - var hook = opts.hook - var old = curEffect[hook] - if (cb) { - if (Array.isArray(old)) { - old.push(cb) - } else if (old) { - curEffect[hook] = [old, cb] - } else { - curEffect[hook] = [cb] + var __array__ = { + set: function (index, val) { + if (((index >>> 0) === index) && this[index] !== val) { + if (index > this.length) { + throw Error(index + 'set方法的第一个参数不能大于原数组长度') } + this.splice(index, 1, val) } - getAction(opts) - avalon.directives.effect.update(node, vnode, 0, avalon.shadowCopy({}, opts)) - - } else if (cb) { - cb(node) + }, + contains: function (el) { //判定是否包含 + return this.indexOf(el) !== -1 + }, + ensure: function (el) { + if (!this.contains(el)) { //只有不存在才push + this.push(el) + } + return this + }, + pushArray: function (arr) { + return this.push.apply(this, arr) + }, + remove: function (el) { //移除第一个等于给定值的元素 + return this.removeAt(this.indexOf(el)) + }, + removeAt: function (index) { //移除指定索引上的元素 + if ((index >>> 0) === index) { + return this.splice(index, 1) + } + return [] + }, + clear: function () { + this.removeAll() + return this } } + avalon.define = define - function getAction(opts) { - if (!opts.acton) { - opts.action = opts.hook.replace(/^on/, '').replace(/Done$/, '').toLowerCase() - } + module.exports = { + $$midway: $$midway, + $$skipArray: $$skipArray, + isSkip: isSkip, + __array__: __array__, + initEvents: initEvents, + makeAccessor: makeAccessor, + modelAdaptor: modelAdaptor } - - /***/ }, - /* 71 */ + /* 84 */ /***/ function(module, exports) { + /** * ------------------------------------------------------------ - * 检测浏览器对CSS动画的支持与API名 + * 属性监听系统 * ------------------------------------------------------------ */ - if (avalon.browser) { - var supportTransition = false - var supportAnimation = false - var supportCSS = false - var transitionEndEvent - var animationEndEvent - var transitionDuration = avalon.cssName('transition-duration') - var animationDuration = avalon.cssName('animation-duration') - var checker = { - TransitionEvent: 'transitionend', - WebKitTransitionEvent: 'webkitTransitionEnd', - OTransitionEvent: 'oTransitionEnd', - otransitionEvent: 'otransitionEnd' - } - var window = avalon.window - var tran - //有的浏览器同时支持私有实现与标准写法,比如webkit支持前两种,Opera支持1、3、4 - for (var name in checker) { - if (window[name]) { - tran = checker[name] - break - } - try { - var a = document.createEvent(name) - tran = checker[name] - break - } catch (e) { - } - } - if (typeof tran === 'string') { - supportTransition = true - supportCSS = true - transitionEndEvent = tran - } + function adjustVm(vm, expr) { + var toppath = expr.split(".")[0], other + try { + if (vm.hasOwnProperty(toppath)) { + if (vm.$accessors) { + other = vm.$accessors[toppath].get.heirloom.__vmodel__ + } else { + other = Object.getOwnPropertyDescriptor(vm, toppath).get.heirloom.__vmodel__ + } - //animationend有两个可用形态 - //IE10+, Firefox 16+ & Opera 12.1+: animationend - //Chrome/Safari: webkitAnimationEnd - //http://blogs.msdn.com/b/davrous/archive/2011/12/06/introduction-to-css3-animat ions.aspx - //IE10也可以使用MSAnimationEnd监听,但是回调里的事件 type依然为animationend - // el.addEventListener('MSAnimationEnd', function(e) { - // alert(e.type)// animationend!!! - // }) - checker = { - 'AnimationEvent': 'animationend', - 'WebKitAnimationEvent': 'webkitAnimationEnd' - } - var ani - for (name in checker) { - if (window[name]) { - ani = checker[name] - break } + } catch (e) { } - if (typeof ani === 'string') { - supportAnimation = true - supportCSS = true - animationEndEvent = ani - } - } - module.exports = { - transition: supportTransition, - animation: supportAnimation, - css: supportCSS, - transitionEndEvent: transitionEndEvent, - animationEndEvent: animationEndEvent, - transitionDuration: transitionDuration, - animationDuration: animationDuration + return other || vm } - /***/ }, - /* 72 */ - /***/ function(module, exports, __webpack_require__) { - - - avalon.lexer = __webpack_require__(73) - avalon.diff = __webpack_require__(77) - avalon.batch = __webpack_require__(78) - // dispatch与patch 为内置模块 - var vdom2body = __webpack_require__(38) - var rquoteEscapes = /\\\\(['"])/g - function render(vtree, local) { - var _body = Array.isArray(vtree) ? vdom2body(vtree) : vtree - var _local = [] - if (local) { - for (var i in local) { - _local.push('var ' + i + ' = __local__['+avalon.quote(i)+']') + function toRegExp(expr) { + var arr = expr.split('.') + return new RegExp("^" + arr.map(function (el) { + return el === '*' ? '(?:[^.]+)' : el + }).join('\\.') + '$', 'i') + } + function addFuzzy(add, obj, expr) { + if (add) { + if (obj.__fuzzy__) { + if (obj.__fuzzy__.indexOf(',' + expr) === -1) { + obj.__fuzzy__ += ',' + expr + } + } else { + obj.__fuzzy__ = expr } } - //处理 props: {"ms-effect": "{is:\\'star\\',action:@action}" 的情况 - _body = _body.replace(rquoteEscapes,"$1") - var body = '__local__ = __local__ || {};\n' + - _local.join(';\n')+'\n' + _body - - try{ - var fn = Function('__vmodel__', '__local__', body) - }catch(e){ - avalon.warn(_body, 'render parse error') - } - return fn } - avalon.render = render - - module.exports = avalon + function $watch(expr, callback) { + var fuzzy = expr.indexOf('.*') > 0 || expr === '*' + var vm = fuzzy ? this : $watch.adjust(this, expr) + var hive = this.$events + var list = hive[expr] || (hive[expr] = []) + if (fuzzy) { + list.reg = list.reg || toRegExp(expr) + } + addFuzzy(fuzzy, hive, expr) + if (vm !== this) { + addFuzzy(fuzzy, this.$events, expr) + } + avalon.Array.ensure(list, callback) - /***/ }, - /* 73 */ - /***/ function(module, exports, __webpack_require__) { + return function () { + avalon.Array.remove(list, callback) + } + } + $watch.adjust = adjustVm /** - * ------------------------------------------------------------ - * avalon2.1.1的新式lexer - * 将字符串变成一个虚拟DOM树,方便以后进一步变成模板函数 - * 此阶段只会生成VElement,VText,VComment - * ------------------------------------------------------------ + * $fire 方法的内部实现 + * + * @param {Array} list 订阅者数组 + * @param {Component} vm + * @param {String} path 监听属性名或路径 + * @param {Any} a 当前值 + * @param {Any} b 过去值 + * @param {Number} i 如果抛错,让下一个继续执行 + * @returns {undefined} */ - var avalon = __webpack_require__(4) + function $emit(list, vm, path, a, b, i) { + if (list && list.length) { + try { + for (i = i || list.length - 1; i >= 0; i--) { + var callback = list[i] + callback.call(vm, a, b, path) + } + } catch (e) { + if (i - 1 > 0) + $emit(list, vm, path, a, b, i - 1) + avalon.log(e, path) + } - __webpack_require__(74) - var voidTag = __webpack_require__(37) - var addTbody = __webpack_require__(75) - var fixPlainTag = __webpack_require__(76) - var plainTag = avalon.oneObject('script,style,textarea,xmp,noscript,option,template') + } + } - var ropenTag = /^<([-A-Za-z0-9_]+)\s*([^>]*?)(\/?)>/ - var rendTag = /^<\/([^>]+)>/ - //https://github.com/rviscomi/trunk8/blob/master/trunk8.js - //判定里面有没有内容 - var rcontent = /\S/ - var rfill = /\?\?\d+/g - var rlineSp = /\n\s*/g - var rnowhite = /\S+/g - var number = 1 - var stringPool = {} - function dig(a) { - var key = '??' + number++ - stringPool[key] = a - return key - } - function fill(a) { - var val = stringPool[a] - return val + module.exports = { + $emit: $emit, + $watch: $watch, + adjustVm: adjustVm } - function lexer(str) { - stringPool = {} - str = clearString(str) - var stack = [] - stack.last = function () { - return stack[stack.length - 1] - } - var ret = [] + /***/ }, + /* 85 */, + /* 86 */, + /* 87 */ + /***/ function(module, exports, __webpack_require__) { - var breakIndex = 100000 - do { - var node = false - if (str.charAt(0) !== '<') {//处理文本节点 - var i = str.indexOf('<') - i = i === -1 ? str.length : i - var nodeValue = str.slice(0, i).replace(rfill, fill) - str = str.slice(i) - node = { - nodeName: '#text', - nodeValue: nodeValue - } - if (rcontent.test(nodeValue)) { - collectNodes(node, stack, ret)//不收集空白节点 - } - } - if (!node) { - var i = str.indexOf('') - if (l === -1) { - avalon.error('注释节点没有闭合' + str) - } - var nodeValue = str.slice(4, l).replace(rfill, fill) - str = str.slice(l + 3) - node = { - nodeName: '#comment', - nodeValue: nodeValue - } - collectNodes(node, stack, ret) - } + __webpack_require__(3) + __webpack_require__(88) + __webpack_require__(6) + module.exports = __webpack_require__(7) - } - if (!node) { - var match = str.match(ropenTag)//处理元素节点开始部分 - //console.log(match) - if (match) { - var nodeName = match[1].toLowerCase() - var isVoidTag = voidTag[nodeName] || match[3] === '\/' - node = { - nodeName: nodeName, - props: {}, - children: [], - isVoidTag: isVoidTag - } - var attrs = match[2] - if (attrs) { - collectProps(attrs, node.props) - } - collectNodes(node, stack, ret) - str = str.slice(match[0].length) - if (isVoidTag) { - node.end = true - } else { - stack.push(node) - if (plainTag[nodeName]) { - var index = str.indexOf('') - var innerHTML = str.slice(0, index).trim() - str = str.slice(index) + /***/ }, + /* 88 */ + /***/ function(module, exports, __webpack_require__) { - fixPlainTag(node, nodeName, nomalString(innerHTML)) + //这里放置存在异议的方法 + var avalon = __webpack_require__(4) + var tos = avalon.inspect + var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ + var rarraylike = /(Array|List|Collection|Map|Arguments)\]$/ - } - } - } - } - if (!node) { - var match = str.match(rendTag)//处理元素节点结束部分 - if (match) { - var nodeName = match[1].toLowerCase() - var last = stack.last() - if (!last) { - avalon.error(match[0] + '前面缺少<' + nodeName + '>') - } else if (last.nodeName !== nodeName) { - avalon.error(last.nodeName + '没有闭合') - } - node = stack.pop() - node.end = true - str = str.slice(match[0].length) - } - } - if (!node || --breakIndex === 0) { - break - } - if (node.end) { - fixTbodyAndRepeat(node, stack, ret) - delete node.end - } + // avalon.type + var class2type = {} + 'Boolean Number String Function Array Date RegExp Object Error'.replace(avalon.rword, function (name) { + class2type['[object ' + name + ']'] = name.toLowerCase() + }) - } while (str.length); + avalon.type = function (obj) { //取得目标的类型 + if (obj == null) { + return String(obj) + } + // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function + return typeof obj === 'object' || typeof obj === 'function' ? + class2type[tos.call(obj)] || 'object' : + typeof obj + } + + avalon.quote = JSON.stringify - return ret + avalon.isFunction = function (fn) { + return typeof fn === 'function' } - module.exports = lexer + avalon.isWindow = function (obj) { + return rwindow.test(tos.call(obj)) + } - function fixTbodyAndRepeat(node, stack, ret) { - var nodeName = node.nodeName - var props = node.props - if (nodeName === 'table') { - addTbody(node.children) + /*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ + avalon.isPlainObject = function (obj) { + // 简单的 typeof obj === 'object'检测,会致使用isPlainObject(window)在opera下通不过 + return tos.call(obj) === '[object Object]' && + Object.getPrototypeOf(obj) === Object.prototype + } + + //与jQuery.extend方法,可用于浅拷贝,深拷贝 + avalon.mix = avalon.fn.mix = function () { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false + + // 如果第一个参数为布尔,判定是否深拷贝 + if (typeof target === 'boolean') { + deep = target + target = arguments[1] || {} + i++ } - var forExpr = props['ms-for'] - //tr两旁的注释节点还会在addTbody中挪一下位置 - if (forExpr) { - delete props['ms-for'] - var p = stack.last() - var arr = p ? p.children : ret - arr.splice(arr.length - 1, 1, { - nodeName: '#comment', - nodeValue: 'ms-for:' + forExpr, - type: nodeName - }, node, { - nodeName: '#comment', - nodeValue: 'ms-for-end:', - type: nodeName - }) + //确保接受方为一个复杂的数据类型 + if (typeof target !== 'object' && typeof target !== 'function') { + target = {} } - } + //如果只有一个参数,那么新成员添加于mix所在的对象上 + if (i === length) { + target = this + i-- + } + for (; i < length; i++) { + //只处理非空参数 + if ((options = arguments[i]) != null) { + for (name in options) { + src = target[name] + try { + copy = options[name] //当options为VBS对象时报错 + } catch (e) { + continue + } + // 防止环引用 + if (target === copy) { + continue + } + if (deep && copy && (avalon.isPlainObject(copy) || (copyIsArray = Array.isArray(copy)))) { - function collectNodes(node, stack, ret) { - var p = stack.last() - if (p) { - p.children.push(node) - } else { - ret.push(node) - } - } + if (copyIsArray) { + copyIsArray = false + clone = src && Array.isArray(src) ? src : [] - function collectProps(attrs, props) { - attrs.replace(rnowhite, function (prop) { - var arr = prop.split('=') - var name = arr[0] - var value = arr[1] || '' - if (name.charAt(0) === ':') { - name = 'ms-' + name.slice(1) - } - if (value) { - if (value.indexOf('??') === 0) { - value = nomalString(value). - replace(rlineSp, ''). - // replace(/\"/g, "'"). - slice(1, -1) + } else { + clone = src && avalon.isPlainObject(src) ? src : {} + } + + target[name] = avalon.mix(deep, clone, copy) + } else if (copy !== void 0) { + target[name] = copy + } } } - if (!(name in props)) { - props[name] = value - } - }) - - } - function nomalString(str) { - return avalon.unescapeHTML(str.replace(rfill, fill)) + } + return target } - function clearString(str) { - var array = readString(str) - for (var i = 0, n = array.length; i < n; i++) { - str = str.replace(array[i], dig) + /*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ + function isArrayLike(obj) { + if (obj && typeof obj === 'object') { + var n = obj.length, + str = tos.call(obj) + if (rarraylike.test(str)) { + return true + } else if (str === '[object Object]' && n === (n >>> 0)) { + return true //由于ecma262v5能修改对象属性的enumerable,因此不能用propertyIsEnumerable来判定了 + } } - return str + return false } - function readString(str) { - var end, s = 0 - var ret = [] - for (var i = 0, n = str.length; i < n; i++) { - var c = str.charAt(i) - if (!end) { - if (c === "'") { - end = "'" - s = i - } else if (c === '"') { - end = '"' - s = i + + avalon.each = function (obj, fn) { + if (obj) { //排除null, undefined + var i = 0 + if (isArrayLike(obj)) { + for (var n = obj.length; i < n; i++) { + if (fn(i, obj[i]) === false) + break } } else { - if (c === '\\') { - i += 1 - continue - } - if (c === end) { - ret.push(str.slice(s, i + 1)) - end = false + for (i in obj) { + if (obj.hasOwnProperty(i) && fn(i, obj[i]) === false) { + break + } } } } - return ret + } + + module.exports = { + avalon: avalon, + isArrayLike: isArrayLike } + + /***/ }, - /* 74 */ + /* 89 */ /***/ function(module, exports, __webpack_require__) { + /** + * 虚拟DOM的4大构造器 + */ + var VText = __webpack_require__(16) + var VComment = __webpack_require__(18) + var VElement = __webpack_require__(90) + var VFragment = __webpack_require__(20) + + avalon.vdomAdaptor = function (obj, method) { + if (!obj) {//obj在ms-for循环里面可能是null + return method === "toHTML" ? '' : document.createDocumentFragment() + } + switch (obj.nodeName) { + case '#text': + return VText.prototype[method].call(obj) + case '#comment': + return VComment.prototype[method].call(obj) + case '#document-fragment': + return VFragment.prototype[method].call(obj) + case void(0): + return (new VFragment(obj))[method]() + default: + return VElement.prototype[method].call(obj) + } + } + + module.exports = { + VText: VText, + VComment: VComment, + VElement: VElement, + VFragment: VFragment + } + + + /***/ }, + /* 90 */ + /***/ function(module, exports) { + - var rmsForStart = /^\s*ms\-for\:/ - var rmsForEnd = /^\s*ms\-for\-end/ - var vdom2body = __webpack_require__(38) - avalon.speedUp = function (array) { - hasDirectives(array) - return array + function VElement(type, props, children) { + this.nodeName = type + this.props = props + this.children = children + + } + function skipFalseAndFunction(a) { + return a !== false && (Object(a) !== a) } - var hasDirectives = function (arr) { - var nodes = [], hasDir = false - for (var i = 0; i < arr.length; i++) { - var el = arr[i] - var isComment = el.nodeName === '#comment' - if (isComment && rmsForStart.test(el.nodeValue)) { - hasDir = true//在startRepeat节点前添加一个数组,收集后面的节点 - nodes.push(el) - var old = nodes - nodes = [] - nodes.list = old - nodes.start = el - } else if (isComment && rmsForEnd.test(el.nodeValue)) { - var old = nodes - nodes = old.list - var start = old.start - delete old.list - delete old.start - nodes.push(old, el) - el.dynamic = true - var uuid = start.signature || (start.signature = avalon.makeHashCode('for')) - el.signature = uuid - - start.forExpr = start.nodeValue.replace(/ms\-for:\s*/, '') - if (old.length === 1) { - var element = old[0] - if (element.props) { - if(element.props.slot){ - start.props = '{slot: "'+element.props.slot+'"}' - } - var cb = element.props['data-for-rendered'] - if (cb) { - delete element.props['data-for-rendered'] - var wid = cb + ':cb' - if (!avalon.caches[wid]) { - avalon.caches[wid] = Function('return ' + avalon.parseExpr(cb, 'on'))() - } - start.wid = wid - } + + function createSVG(type) { + return document.createElementNS('http://www.w3.org/2000/svg', type) + } + var svgTags = avalon.oneObject('circle,defs,ellipse,image,line,' + + 'path,polygon,polyline,rect,symbol,text,use,g,svg') + + + var rvml = /^\w+\:\w+/ + if (avalon.browser) { + var supportTemplate = 'content' in document.createElement('template') + } + VElement.prototype = { + constructor: VElement, + toDOM: function () { + if (this.dom) + return this.dom + var dom, tagName = this.nodeName + if (avalon.modern && svgTags[tagName]) { + dom = createSVG(tagName) + } else { + dom = document.createElement(tagName) + } + var props = this.props || {} + var wid = props['ms-important'] || + props['ms-controller'] || this.wid + if (wid) { + var scope = avalon.scopes[wid] + var element = scope && scope.vmodel && scope.vmodel.$element + if (element) { + var oldVdom = element.vtree[0] + if (oldVdom.children) { + this.children = oldVdom.children } + return element } - for (var j = 0; j < old.length; j++) { - var el = old[j] - var elem = el.dom - if (elem && elem.parentNode) {//移除真实节点 - elem.parentNode.removeChild(elem) + } + for (var i in props) { + var val = props[i] + if (skipFalseAndFunction(val)) { + dom.setAttribute(i, val + '') + } + } + var c = this.children || [] + var template = c[0] ? c[0].nodeValue : '' + switch (this.nodeName) { + case 'xmp': + case 'script': + case 'style': + case 'noscript': + dom.innerHTML = template + break + case 'template': + if (supportTemplate) { + dom.innerHTML = template + } else { + dom.textContent = template } + break + default: + if (!this.isVoidTag) { + this.children.forEach(function (c) { + c && dom.appendChild(avalon.vdomAdaptor(c, 'toDOM')) + }) + } + break + } + return this.dom = dom + }, + toHTML: function () { + var arr = [] + var props = this.props || {} + for (var i in props) { + var val = props[i] + if (skipFalseAndFunction(val)) { + arr.push(i + '=' + avalon.quote(props[i] + '')) } - start.hasEffect = hasEffect(old) - hasDirectives(old) - if (!avalon.caches[uuid]) { - avalon.caches[uuid] = vdom2body(old, true) - } - old.length = 0 - } else { - if (hasDirective(el)) { - hasDir = true - } - nodes.push(el) } + arr = arr.length ? ' ' + arr.join(' ') : '' + var str = '<' + this.nodeName + arr + if (this.isVoidTag) { + return str + '/>' + } + str += '>' + if (this.children) { + str += this.children.map(function (c) { + return c ? avalon.vdomAdaptor(c, 'toHTML') : '' + }).join('') + } + return str + '' } - arr.length = 0 - arr.push.apply(arr, nodes) - return hasDir } + module.exports = VElement + /***/ }, + /* 91 */ + /***/ function(module, exports, __webpack_require__) { - function hasDirective(node) { + + /********************************************************************* + * DOM Api * + * shim,class,data,css,val,html,event,ready * + **********************************************************************/ + + __webpack_require__(92) + __webpack_require__(93) + __webpack_require__(94) + __webpack_require__(95) + __webpack_require__(96) + __webpack_require__(29) + __webpack_require__(97) + __webpack_require__(98) - var nodeName = node.nodeName - switch (nodeName) { - case '#text': - if (avalon.config.rexpr.test(node.nodeValue)) { - return node.dynamic = true - } else { - return false - } - case '#comment': - if (node.dynamic) { - return true - } - return false - case void 0: - return true - default: - var props = node.props || {} - if ('ms-skip' in props) { - node.skipContent = true - return false - } - var flag = false - if (nodeName === 'input') { - if (!props.type) { - props.type = 'text' - } - } else if (nodeName === 'select') { - var postfix = props.hasOwnProperty('multiple') ? 'multiple' : 'one' - props.type = nodeName + '-' + postfix - } else if (nodeName.indexOf('ms-') === 0) { - if (!props['ms-widget']) { - props.is = nodeName - props['ms-widget'] = '{is:"' + nodeName + '"}' - } - } - var childDir = false - if (props['ms-widget']) { - childDir = true - delDir(props, 'html', 'widget') - delDir(props, 'text', 'widget') - var clone = avalon.mix({}, node) - var cprops = avalon.mix({}, node.props) - delete cprops['ms-widget'] - delete clone.isVoidTag - clone.nodeName = "cheng" - clone.props = cprops - node.template = avalon.vdomAdaptor(clone, 'toHTML') - if (!node.isVoidTag) - node.children = [] - } - if (props['ms-text']) { - childDir = true - delDir(props, 'html', 'text') - if (!node.isVoidTag) { - node.children = [] - } - } - if (props['ms-html']) { - childDir = true - if (!node.isVoidTag) { - node.children = [] - } - } - var hasProps = false - for (var i in props) { - hasProps = true - if (i.indexOf('ms-') === 0) { - flag = true - node.dynamic = {} - break - } - } - if (hasProps) { - node.props = props - } - if (node.children) { - var r = hasDirectives(node.children) - if (r) { - delete node.skipContent - return true - } - if (!childDir) { - node.skipContent = true - } else { - delete node.skipContent - } - } - return flag + module.exports = avalon + + + /***/ }, + /* 92 */ + /***/ function(module, exports, __webpack_require__) { + + //safari5+是把contains方法放在Element.prototype上而不是Node.prototype + var avalon = __webpack_require__(4) + if (typeof Node === 'function') { + if (!document.contains) { + Node.prototype.contains = function (arg) { + return !!(this.compareDocumentPosition(arg) & 16) + } } } - function delDir(props, a, b) { - if (props['ms-' + a]) { - avalon.warn(a, '指令不能与', b, '指令共存于同一个元素') - delete props['ms-' + a] + avalon.contains = function (root, el) { + try { + while ((el = el.parentNode)) + if (el === root) + return true + return false + } catch (e) { + return false } } - function hasEffect(arr) { - for (var i = 0, el; el = arr[i++]; ) { - if (el.props && el.props['ms-effect']) { - return true + /***/ }, + /* 93 */ + /***/ function(module, exports, __webpack_require__) { + + var avalon = __webpack_require__(4) + var rnowhite = /\S+/g + 'add,remove'.replace(avalon.rword, function (method) { + avalon.fn[method + 'Class'] = function (cls) { + var el = this[0] || {} + //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26 + if (cls && typeof cls === 'string' && el.nodeType === 1) { + cls.replace(rnowhite, function (c) { + el.classList[method](c) + }) } + return this } - return false - } + }) + + avalon.fn.mix({ + hasClass: function (cls) { + var el = this[0] || {} + //IE10+, chrome8+, firefox3.6+, safari5.1+,opera11.5+支持classList, + //chrome24+,firefox26+支持classList2.0 + return el.nodeType === 1 && el.classList.contains(cls) + }, + toggleClass: function (value, stateVal) { + var isBool = typeof stateVal === 'boolean' + var me = this + String(value).replace(rnowhite, function (c) { + var state = isBool ? stateVal : !me.hasClass(c) + me[state ? 'addClass' : 'removeClass'](c) + }) + return this + } + }) + /***/ }, - /* 75 */ - /***/ function(module, exports) { + /* 94 */ + /***/ function(module, exports, __webpack_require__) { - - //如果直接将tr元素写table下面,那么浏览器将将它们(相邻的那几个),放到一个动态创建的tbody底下 - module.exports = function addTbody(nodes) { - var tbody, needAddTbody = false, count = 0, start = 0, n = nodes.length - for (var i = 0; i < n; i++) { - var node = nodes[i] - if (!tbody) { - if ((node.type || node.nodeName) === 'tr') { - //收集tr及tr两旁的注释节点 - tbody = { - nodeName: 'tbody', - children: [] + var avalon = __webpack_require__(4) + var propMap = __webpack_require__(25) + var rsvg = /^\[object SVG\w*Element\]$/ + + function attrUpdate(node, vnode) { + var attrs = vnode.changeAttr + if (attrs) { + vnode.dynamic['ms-attr'] = 1 + for (var attrName in attrs) { + var val = attrs[attrName] + // switch + if (attrName === 'src' && window.chrome && node.tagName === 'EMBED') { + node[attrName] = val + var parent = node.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 + var comment = document.createComment('ms-src') + parent.replaceChild(comment, node) + parent.replaceChild(node, comment) + } else if (attrName.indexOf('data-') == 0) { + node.setAttribute(attrName, val) + } else { + var propName = propMap[attrName] || attrName + if (typeof node[propName] === 'boolean') { + //布尔属性必须使用el.xxx = true|false方式设值 + //如果为false, IE全系列下相当于setAttribute(xxx,''), + //会影响到样式,需要进一步处理 + node[propName] = !!val } - tbody.children.push(node) - if (node.type) { - delete node.type + if (val === false) { + node.removeAttribute(attrName) + continue } - needAddTbody = true - if (start === 0) - start = i - nodes[i] = tbody - } - } else { - if (node.nodeName !== 'tr' && node.children) { - tbody = false - } else { - tbody.children.push(node) - count++ - nodes[i] = 0 - } - } - } - if (needAddTbody) { - for (i = start; i < n; i++) { - if (nodes[i] === 0) { - nodes.splice(i, 1) - i-- - count-- - if (count === 0) { - break + //SVG只能使用setAttribute(xxx, yyy), VML只能使用node.xxx = yyy , + //HTML的固有属性必须node.xxx = yyy + var isInnate = rsvg.test(node) ? false : attrName in node.cloneNode(false) + if (isInnate) { + node[propName] = val + '' + } else { + node.setAttribute(attrName, val) } } } } + vnode.changeAttr = null } + avalon.parseJSON = JSON.parse + avalon.fn.attr = function (name, value) { + if (arguments.length === 2) { + this[0].setAttribute(name, value) + return this + } else { + return this[0].getAttribute(name) + } + } - /***/ }, - /* 76 */ - /***/ function(module, exports) { - /* - *  修正容器元素 - */ + module.exports = attrUpdate - function fixPlainTag(node, nodeName, innerHTML) { - switch (nodeName) { - case 'style': - case 'script': - case 'noscript': - case 'template': - case 'xmp': - node.children = [ - { - nodeName: '#text', - skipContent: true, - nodeValue: innerHTML - } - ] - break - case 'textarea': - var props = node.props - props.type = 'textarea' - props.value = innerHTML - node.children = [] - break - case 'option': - node.children = [{ - nodeName: '#text', - nodeValue: trimHTML(innerHTML) - }] - break - } + /***/ }, + /* 95 */ + /***/ function(module, exports, __webpack_require__) { + + var avalon = __webpack_require__(4) + var root = avalon.root + var camelize = avalon.camelize + var cssHooks = avalon.cssHooks + var prefixes = ['', '-webkit-', '-o-', '-moz-', '-ms-'] + var cssMap = { + 'float': 'cssFloat' } - //专门用于处理option标签里面的标签 - var rtrimHTML = /<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?>|<\/\w+>/gi - function trimHTML(v) { - return String(v).replace(rtrimHTML, '').trim() + avalon.cssNumber = avalon.oneObject('animationIterationCount,columnCount,order,flex,flexGrow,flexShrink,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom') + + avalon.cssName = function (name, host, camelCase) { + if (cssMap[name]) { + return cssMap[name] + } + host = host || root.style + for (var i = 0, n = prefixes.length; i < n; i++) { + camelCase = camelize(prefixes[i] + name) + if (camelCase in host) { + return (cssMap[name] = camelCase) + } + } + return null } - module.exports = fixPlainTag - /***/ }, - /* 77 */ - /***/ function(module, exports) { + avalon.fn.css = function (name, value) { + if (avalon.isPlainObject(name)) { + for (var i in name) { + avalon.css(this, i, name[i]) + } + } else { + var ret = avalon.css(this, name, value) + } + return ret !== void 0 ? ret : this + } - /** - * ------------------------------------------------------------ - * diff 对比新旧两个虚拟DOM树,根据directive中的diff方法为新虚拟DOM树 - * 添加change, afterChange更新钩子 - * ------------------------------------------------------------ - */ - var emptyArr = [] - // 防止被引用 - var emptyObj = function () { + avalon.fn.position = function () { + var offsetParent, offset, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + } + if (!elem) { + return parentOffset + } + if (this.css("position") === "fixed") { + offset = elem.getBoundingClientRect() + } else { + offsetParent = this.offsetParent() //得到真正的offsetParent + offset = this.offset() // 得到正确的offsetParent + if (offsetParent[0].tagName !== "HTML") { + parentOffset = offsetParent.offset() + } + parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true) + parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true) + + // Subtract offsetParent scroll positions + parentOffset.top -= offsetParent.scrollTop() + parentOffset.left -= offsetParent.scrollLeft() + } return { - children: [], props: {} + top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true), + left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true) } } - var directives = avalon.directives - var rbinding = /^ms-(\w+)-?(.*)/ - function diff(copys, sources) { - for (var i = 0; i < copys.length; i++) { - var copy = copys[i] - var src = sources[i] || copys[i] - switch (copy.nodeName) { - case '#text': - if (copy.dynamic) { - var curValue = copy.nodeValue + '' - if (curValue !== src.nodeValue) { - src.nodeValue = curValue - if (src.dom) { - src.dom.nodeValue = curValue - } - } - } - break - case '#comment': - if (copy.forExpr) {//比较循环区域的元素位置 - directives['for'].diff(copy, src, copys, sources, i) - } else if (src.afterChange) { - execHooks(src, src.afterChange) - } - break - case void(0): - diff(copy, src)//比较循环区域的内容 - break - case '#document-fragment': - diff(copy.children, src.children)//比较循环区域的内容 - break - default: - if (copy.dynamic) { - var index = i - if (copy['ms-widget']) { - avalon.directives['widget'].diff(copy, src, 'ms-widget', copys, sources, index) - copy = copys[i] - src = sources[i] || emptyObj() - delete copy['ms-widget'] - } + avalon.fn.offsetParent = function () { + var offsetParent = this[0].offsetParent + while (offsetParent && avalon.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent + } + return avalon(offsetParent || root) + } - if ('ms-if' in copy) { - avalon.directives['if'].diff(copy, src, 'ms-if', copys, sources, index) - copy = copys[i] - src = sources[i] || emptyObj() - delete copy['ms-if'] - } - diffProps(copy, src) - } - if (/^\w/.test(copy.nodeName) && !copy.skipContent && !copy.isVoidTag) { - diff(copy.children, src.children || []) - } + cssHooks["@:set"] = function (node, name, value) { + node.style[name] = value + } - if (src.afterChange) { - execHooks(src, src.afterChange) - } - break + cssHooks["@:get"] = function (node, name) { + if (!node || !node.style) { + throw new Error("getComputedStyle要求传入一个节点 " + node) + } + var ret, computed = getComputedStyle(node) + if (computed) { + ret = name === "filter" ? computed.getPropertyValue(name) : computed[name] + if (ret === "") { + ret = node.style[name] //其他浏览器需要我们手动取内联样式 } } + return ret } - function execHooks(el, hooks) { - if (hooks.length) { - for (var hook, i = 0; hook = hooks[i++]; ) { - hook(el.dom, el) + cssHooks["opacity:get"] = function (node) { + var ret = cssHooks["@:get"](node, "opacity") + return ret === "" ? "1" : ret + } + + "top,left".replace(avalon.rword, function (name) { + cssHooks[name + ":get"] = function (node) { + var computed = cssHooks["@:get"](node, name) + return /px$/.test(computed) ? computed : + avalon(node).position()[name] + "px" + } + }) + + var cssShow = { + position: "absolute", + visibility: "hidden", + display: "block" + } + var rdisplayswap = /^(none|table(?!-c[ea]).+)/ + + function showHidden(node, array) { + //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html + if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0 + var styles = getComputedStyle(node, null) + if (rdisplayswap.test(styles["display"])) { + var obj = { + node: node + } + for (var name in cssShow) { + obj[name] = styles[name] + node.style[name] = cssShow[name] + } + array.push(obj) + } + var parent = node.parentNode + if (parent && parent.nodeType === 1) { + showHidden(parent, array) } } - delete el.afterChange } - function diffProps(copy, source) { - var directives = avalon.directives + avalon.each({ + Width: "width", + Height: "height" + }, function (name, method) { + var clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name + cssHooks[method + ":get"] = function (node, which, override) { + var boxSizing = -4 + if (typeof override === "number") { + boxSizing = override + } + which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"] + var ret = node[offsetProp] // border-box 0 + if (boxSizing === 2) { // margin-box 2 + return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true) + } + if (boxSizing < 0) { // padding-box -2 + ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true) + } + if (boxSizing === -4) { // content-box -4 + ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true) + } + return ret + } + cssHooks[method + "&get"] = function (node) { + var hidden = [] + showHidden(node, hidden) + var val = cssHooks[method + ":get"](node) + for (var i = 0, obj; obj = hidden[i++]; ) { + node = obj.node + for (var n in obj) { + if (typeof obj[n] === "string") { + node.style[n] = obj[n] + } + } + } + return val + } + avalon.fn[method] = function (value) { //会忽视其display + var node = this[0] + if (arguments.length === 0) { + if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替 + return node["inner" + name] + } + if (node.nodeType === 9) { //取得页面尺寸 + var doc = node.documentElement + //FF chrome html.scrollHeight< body.scrollHeight + //IE 标准模式 : html.scrollHeight> body.scrollHeight + //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点? + return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp]) + } + return cssHooks[method + "&get"](node) + } else { + return this.css(method, value) + } + } + avalon.fn["inner" + name] = function () { + return cssHooks[method + ":get"](this[0], void 0, -2) + } + avalon.fn["outer" + name] = function (includeMargin) { + return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0) + } + }) + + avalon.fn.offset = function () { //取得距离页面左右角的坐标 + var node = this[0] try { - for (var name in copy) { - var match = name.match(rbinding) - var type = match && match[1] - if (directives[type]) { - directives[type].diff(copy, source, name) + var rect = node.getBoundingClientRect() + // Make sure element is not hidden (display: none) or disconnected + // https://github.com/jquery/jquery/pull/2043/files#r23981494 + if (rect.width || rect.height || node.getClientRects().length) { + var doc = node.ownerDocument + var root = doc.documentElement + var win = doc.defaultView + return { + top: rect.top + win.pageYOffset - root.clientTop, + left: rect.left + win.pageXOffset - root.clientLeft } } - } catch (e) { - avalon.warn(type, e, e.stack || e.message, 'diffProps error') + return { + left: 0, + top: 0 + } } } - avalon.diff = diff - avalon.diffProps = diffProps - module.exports = diff + avalon.each({ + scrollLeft: "pageXOffset", + scrollTop: "pageYOffset" + }, function (method, prop) { + avalon.fn[method] = function (val) { + var node = this[0] || {}, + win = getWindow(node), + top = method === "scrollTop" + if (!arguments.length) { + return win ? win[prop] : node[method] + } else { + if (win) { + win.scrollTo(!top ? val : win[prop], top ? val : win[prop]) + } else { + node[method] = val + } + } + } + }) + + function getWindow(node) { + return node.window || node.defaultView || false + } /***/ }, - /* 78 */ + /* 96 */ /***/ function(module, exports, __webpack_require__) { - - /** - * ------------------------------------------------------------ - * batch 同时对N个视图进行全量更新 - * ------------------------------------------------------------ - */ - - var reconcile = __webpack_require__(53) - - //如果正在更新一个子树,那么将它放到 - var needRenderIds = [] - var renderingID = false - avalon.suspendUpdate = 0 + var avalon = __webpack_require__(4) - function batchUpdate(id) { - if (renderingID) { - return avalon.Array.ensure(needRenderIds, id) - } else { - renderingID = id - } - var scope = avalon.scopes[id] - if (!scope || !document.nodeName || avalon.suspendUpdate) { - return renderingID = null - } - var vm = scope.vmodel - var dom = vm.$element - var source = dom.vtree || [] - var renderFn = vm.$render - var copy = renderFn(scope.vmodel, scope.local) - if (scope.isTemp) { - //在最开始时,替换作用域的所有节点,确保虚拟DOM与真实DOM是对齐的 - delete avalon.scopes[id] - } - - - avalon.diff(copy, source) - - - var index = needRenderIds.indexOf(renderingID) - renderingID = 0 - if (index > -1) { - var removed = needRenderIds.splice(index, 1) - return batchUpdate(removed[0]) + function getValType(elem) { + var ret = elem.tagName.toLowerCase() + return ret === 'input' && /checkbox|radio/.test(elem.type) ? 'checked' : ret + } + var valHooks = { + 'select:get': function self(node, ret, index, singleton) { + var nodes = node.children, value, + getter = valHooks['option:get'] + index = ret ? index : node.selectedIndex + singleton = ret ? singleton : node.type === 'select-one' || index < 0 + ret = ret || [] + for (var i = 0, el; el = nodes[i++]; ) { + if (!el.disabled) { + switch (el.nodeName.toLowerCase()) { + case 'option': + if ((el.selected || el.index === index)) { + value = el.value + if (singleton) { + return value + } else { + ret.push(value) + } + } + break + case 'optgroup': + value = self(el, ret, index, singleton) + if (typeof value === 'string') { + return value + } + break + } + } + } + return singleton ? null : ret + }, + 'select:set': function (node, values, optionSet) { + values = [].concat(values) //强制转换为数组 + for (var i = 0, el; el = node.options[i++]; ) { + if ((el.selected = values.indexOf(el.value) > -1)) { + optionSet = true + } + } + if (!optionSet) { + node.selectedIndex = -1 + } } + } - var more = needRenderIds.shift() - if (more) { - batchUpdate(more) + avalon.fn.val = function (value) { + var node = this[0] + if (node && node.nodeType === 1) { + var get = arguments.length === 0 + var access = get ? ':get' : ':set' + var fn = valHooks[getValType(node) + access] + if (fn) { + var val = fn(node, value) + } else if (get) { + return (node.value || '').replace(/\r/g, '') + } else { + node.value = value + } } + return get ? val : this } - - - module.exports = avalon.batch = batchUpdate - - /***/ }, - /* 79 */ + /* 97 */ /***/ function(module, exports, __webpack_require__) { - var ret = __webpack_require__(80) - var fireDisposeHook = ret.fireDisposeHook - var fireDisposeHooks = ret.fireDisposeHooks - var fireDisposeHookDelay = ret.fireDisposeHookDelay + var avalon = __webpack_require__(4) + var document = avalon.document + var window = avalon.window + var root = avalon.root + var getShortID = __webpack_require__(6).getShortID + var canBubbleUp = __webpack_require__(33) - //http://stackoverflow.com/questions/11425209/are-dom-mutation-observers-slower-than-dom-mutation-events - //http://stackoverflow.com/questions/31798816/simple-mutationobserver-version-of-domnoderemovedfromdocument - function byMutationEvent(dom) { - dom.addEventListener("DOMNodeRemovedFromDocument", function () { - fireDisposeHookDelay(dom) - }) - } - //用于IE8+, firefox - function byRewritePrototype() { - if (byRewritePrototype.execute) { - return - } - //https://www.web-tinker.com/article/20618.html?utm_source=tuicool&utm_medium=referral - //IE6-8虽然暴露了Element.prototype,但无法重写已有的DOM API - byRewritePrototype.execute = true - var p = Node.prototype - function rewite(name, fn) { - var cb = p[name] - p[name] = function (a, b) { - return fn.call(this, cb, a, b) + var eventHooks = avalon.eventHooks + /*绑定事件*/ + avalon.bind = function (elem, type, fn) { + if (elem.nodeType === 1) { + var value = elem.getAttribute('avalon-events') || '' + //如果是使用ms-on-*绑定的回调,其uuid格式为e12122324, + //如果是使用bind方法绑定的回调,其uuid格式为_12 + var uuid = getShortID(fn) + var hook = eventHooks[type] + if (hook) { + type = hook.type || type + if (hook.fix) { + fn = hook.fix(elem, fn) + fn.uuid = uuid + } } - } - rewite('removeChild', function (fn, a, b) { - fn.call(this, a, b) - if (a.nodeType === 1) { - fireDisposeHookDelay(a) + //fix 移动端浏览器:click不触发的BUG + if(type === 'click' && !elem.onclick){ + elem.onclick = '' } - return a - }) - - rewite('replaceChild', function (fn, a, b) { - fn.call(this, a, b) - if (a.nodeType === 1) { - fireDisposeHookDelay(a) + var key = type + ':' + uuid + avalon.eventListeners[fn.uuid] = fn + if (value.indexOf(type + ':') === -1) {//同一种事件只绑定一次 + if (canBubbleUp[type] || focusBlur[type]) { + delegateEvent(type) + } else { + nativeBind(elem, type, dispatch) + } } - return a - }) - //访问器属性需要用getOwnPropertyDescriptor处理 - var ep = Element.prototype, oldSetter - function newSetter(html) { - var all = avalon.slice(this.getElementsByTagName('*')) - oldSetter.call(this, html) - fireDisposeHooks(all) + var keys = value.split(',') + if (keys[0] === '') { + keys.shift() + } + if (keys.indexOf(key) === -1) { + keys.push(key) + elem.setAttribute('avalon-events', keys.join(',')) + //将令牌放进avalon-events属性中 + } + + } else { + nativeBind(elem, type, fn) } - try { - var obj = Object.getOwnPropertyDescriptor(ep, 'innerHTML') - var oldSetter = obj.set - obj.set = newSetter - Object.defineProperty(ep, 'innerHTML', obj) - } catch (e) { - //safari 9.1.2使用Object.defineProperty重写innerHTML会抛 - // Attempting to change the setter of an unconfigurable property. - if (ep && ep.__lookupSetter__) { - oldSetter = ep.__lookupSetter__('innerHTML') - ep.__defineSetter__('innerHTML', newSetter) + return fn //兼容之前的版本 + } + + avalon.unbind = function (elem, type, fn) { + if (elem.nodeType === 1) { + var value = elem.getAttribute('avalon-events') || '' + switch (arguments.length) { + case 1: + nativeUnBind(elem, type, dispatch) + elem.removeAttribute('avalon-events') + break + case 2: + value = value.split(',').filter(function (str) { + return str.indexOf(type + ':') === -1 + }).join(',') + + elem.setAttribute('avalon-events', value) + break + default: + var search = type + ':' + fn.uuid + value = value.split(',').filter(function (str) { + return str !== search + }).join(',') + elem.setAttribute('avalon-events', value) + delete avalon.eventListeners[fn.uuid] + break } + } else { + nativeUnBind(elem, type, fn) } + } - rewite('appendChild', function (fn, a) { - fn.call(this, a) - if (a.nodeType === 1 && this.nodeType === 11) { - fireDisposeHookDelay(a) + var typeRegExp = {} + function collectHandlers(elem, type, handlers) { + var value = elem.getAttribute('avalon-events') + if (value && (elem.disabled !== true || type !== 'click')) { + var uuids = [] + var reg = typeRegExp[type] || (typeRegExp[type] = new RegExp('\\b' + type + '\\:([^,\\s]+)', 'g')) + value.replace(reg, function (a, b) { + uuids.push(b) + return a + }) + if (uuids.length) { + handlers.push({ + elem: elem, + uuids: uuids + }) } - return a - }) + } + elem = elem.parentNode + var g = avalon.gestureEvents || {} + if (elem && elem.getAttribute && (canBubbleUp[type] || g[type])) { + collectHandlers(elem, type, handlers) + } - rewite('insertBefore', function (fn, a, b) { - fn.call(this, a, b) - if (a.nodeType === 1 && this.nodeType === 11) { - fireDisposeHookDelay(a) - } - return a - }) } - //用于IE6~8 - var checkDisposeNodes = [] - var checkID = 0 - function byPolling(dom) { - avalon.Array.ensure(checkDisposeNodes, dom) - if (!checkID) { - checkID = setInterval(function () { - for (var i = 0, el; el = checkDisposeNodes[i]; ) { - if (false === fireDisposeHook(el)) { - avalon.Array.removeAt(checkDisposeNodes, i) - } else { - i++ - } + var rhandleHasVm = /^e/ + var stopImmediate = false + + function dispatch(event) { + event = new avEvent(event) + var type = event.type + var elem = event.target + var handlers = [] + collectHandlers(elem, type, handlers) + var i = 0, j, uuid, handler + while ((handler = handlers[i++]) && !event.cancelBubble) { + var host = event.currentTarget = handler.elem + j = 0 + while ((uuid = handler.uuids[ j++ ])) { + if (stopImmediate) { + stopImmediate = false + break } - if (checkDisposeNodes.length == 0) { - clearInterval(checkID) - checkID = 0 + var fn = avalon.eventListeners[uuid] + if (fn) { + var vm = rhandleHasVm.test(uuid) ? handler.elem._ms_context_ : 0 + if (vm && vm.$hashcode === false) { + return avalon.unbind(elem, type, fn) + } + var ret = fn.call(vm || elem, event, host._ms_local) + if (ret === false) { + event.preventDefault() + event.stopPropagation() + } } - }, 700) + } } } + var focusBlur = { + focus: true, + blur: true + } + var nativeBind = function (el, type, fn, capture) { + el.addEventListener(type, fn, capture) + } + var nativeUnBind = function (el, type, fn) { + el.removeEventListener(type, fn) + } - module.exports = function onComponentDispose(dom) { - if (window.chrome && window.MutationEvent) { - byMutationEvent(dom) - } else if (avalon.modern && typeof window.Node === 'function') { - byRewritePrototype(dom) - } else { - byPolling(dom) + function delegateEvent(type) { + var value = root.getAttribute('delegate-events') || '' + if (value.indexOf(type) === -1) { + var arr = value.match(avalon.rword) || [] + arr.push(type) + root.setAttribute('delegate-events', arr.join(',')) + nativeBind(root, type, dispatch, !!focusBlur[type]) } } - - - /***/ }, - /* 80 */ - /***/ function(module, exports) { - - function inDomTree(el) { - while (el) { - if (el.nodeType === 9) { - return true + var rconstant = /^[A-Z_]+$/ + function avEvent(event) { + if (event.originalEvent) { + return this + } + for (var i in event) { + if (!rconstant.test(i) && typeof event[i] !== 'function') { + this[i] = event[i] } - el = el.parentNode } - return false + this.timeStamp = new Date() - 0 + this.originalEvent = event } - - function fireDisposeHook(el) { - if (el.nodeType === 1 && el.getAttribute('wid') && !inDomTree(el)) { - var wid = el.getAttribute('wid') - var docker = avalon.scopes[ wid ] - if (!docker) - return - var vm = docker.vmodel - docker.vmodel.$fire("onDispose", { - type: 'dispose', - target: el, - vmodel: vm - }) - if (docker && !el.getAttribute('cached')) { - delete docker.vmodel - delete avalon.scopes[ wid ] - var is = el.getAttribute('is') - var v = el.vtree - detachEvents(v) - if (v) { - v[0][is + '-mount'] = false - v[0]['component-ready:' + is] = false - } + avEvent.prototype = { + preventDefault: function () { + var e = this.originalEvent || {} + this.returnValue = false + if (e.preventDefault) { + e.preventDefault() } - return false + }, + stopPropagation: function () { + var e = this.originalEvent || {} + this.cancelBubble = true + if (e.stopPropagation) { + e.stopPropagation() + } + }, + stopImmediatePropagation: function () { + stopImmediate = true + this.stopPropagation() + }, + toString: function () { + return '[object Event]'//#1619 } } - var rtag = /^\w/ - function detachEvents(arr) { - for (var i in arr) { - var el = arr[i] - if (rtag.test(el.nodeName)) { - for (var i in el) { - if (i.indexOf('ms-on') === 0) { - delete el[i] + + avalon.fireDom = function (elem, type, opts) { + var hackEvent = document.createEvent('Events') + hackEvent.initEvent(type, true, true) + avalon.shadowCopy(hackEvent, opts) + elem.dispatchEvent(hackEvent) + } + + + //针对firefox, chrome修正mouseenter, mouseleave(chrome30+) + if (!('onmouseenter' in root)) { + avalon.each({ + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }, function (origType, fixType) { + eventHooks[origType] = { + type: fixType, + fn: function (elem, fn) { + return function (e) { + var t = e.relatedTarget + if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) { + delete e.type + e.type = origType + return fn.call(this, e) + } } } - if (el.children) { - detachEvents(el.children) + } + }) + } + //针对IE9+, w3c修正animationend + avalon.each({ + AnimationEvent: 'animationend', + WebKitAnimationEvent: 'webkitAnimationEnd' + }, function (construct, fixType) { + if (window[construct] && !eventHooks.animationend) { + eventHooks.animationend = { + type: fixType + } + } + }) + + if (document.onmousewheel === void 0) { + /* IE6-11 chrome mousewheel wheelDetla 下 -120 上 120 + firefox DOMMouseScroll detail 下3 上-3 + firefox wheel detlaY 下3 上-3 + IE9-11 wheel deltaY 下40 上-40 + chrome wheel deltaY 下100 上-100 */ + eventHooks.mousewheel = { + type: 'wheel', + fn: function (elem, fn) { + return function (e) { + e.wheelDeltaY = e.wheelDelta = e.deltaY > 0 ? -120 : 120 + e.wheelDeltaX = 0 + Object.defineProperty(e, 'type', { + value: 'mousewheel' + }) + fn.call(this, e) } } } } - function fireDisposeHookDelay(a) { - setTimeout(function () { - fireDisposeHook(a) - }, 4) - } - function fireDisposeHooks(nodes) { - for (var i = 0, el; el = nodes[i++]; ) { - fireDisposeHook(el) + + avalon.fn.bind = function (type, fn, phase) { + if (this[0]) { //此方法不会链 + return avalon.bind(this[0], type, fn, phase) } } - module.exports = { - fireDisposeHookDelay: fireDisposeHookDelay, - fireDisposeHooks: fireDisposeHooks, - fireDisposeHook: fireDisposeHook - } - - /***/ }, - /* 81 */ - /***/ function(module, exports, __webpack_require__) { - - /** - * ------------------------------------------------------------ - * avalon基于纯净的Object.defineProperties的vm工厂 - * masterFactory,slaveFactory,mediatorFactory, ArrayFactory - * ------------------------------------------------------------ - */ - - var share = __webpack_require__(82) - var createViewModel = __webpack_require__(86) - var isSkip = share.isSkip - var toJson = share.toJson - var $$midway = share.$$midway - var $$skipArray = share.$$skipArray - - var makeAccessor = share.makeAccessor - var initViewModel = share.initViewModel - var modelAccessor = share.modelAccessor - var modelAdaptor = share.modelAdaptor - var makeHashCode = avalon.makeHashCode - - - //一个vm总是为Observer的实例 - function Observer() { + avalon.fn.unbind = function (type, fn, phase) { + if (this[0]) { + avalon.unbind(this[0], type, fn, phase) + } + return this } - function masterFactory(definition, heirloom, options) { - - var $skipArray = {} - if (definition.$skipArray) {//收集所有不可监听属性 - $skipArray = avalon.oneObject(definition.$skipArray) - delete definition.$skipArray - } - var keys = {} - options = options || {} - heirloom = heirloom || {} - var accessors = {} - var hashcode = makeHashCode('$') - var pathname = options.pathname || '' - options.id = options.id || hashcode - options.hashcode = options.hashcode || hashcode - var key, sid, spath - for (key in definition) { - if ($$skipArray[key]) - continue - var val = keys[key] = definition[key] - if (!isSkip(key, val, $skipArray)) { - sid = options.id + '.' + key - spath = pathname ? pathname + '.' + key : key - accessors[key] = makeAccessor(sid, spath, heirloom) - } - } + /***/ }, + /* 98 */ + /***/ function(module, exports, __webpack_require__) { - accessors.$model = modelAccessor - var $vmodel = new Observer() - $vmodel = createViewModel($vmodel, accessors, definition) + var avalon = __webpack_require__(4) + var scan = __webpack_require__(35) + var document = avalon.document - for (key in keys) { - //对普通监控属性或访问器属性进行赋值 - $vmodel[key] = keys[key] + var readyList = [], isReady + var fireReady = function (fn) { + isReady = true - //删除系统属性 - if (key in $skipArray) { - delete keys[key] - } else { - keys[key] = true - } + while (fn = readyList.shift()) { + fn(avalon) } - initViewModel($vmodel, heirloom, keys, accessors, options) - - return $vmodel } - $$midway.masterFactory = masterFactory - var empty = {} - function slaveFactory(before, after, heirloom, options) { - var keys = {} - var skips = {} - var accessors = {} - heirloom = heirloom || {} - var pathname = options.pathname - var resue = before.$accessors || {} - var key, sid, spath - for (key in after) { - if ($$skipArray[key]) - continue - keys[key] = true//包括可监控与不可监控的 - if (!isSkip(key, after[key], empty)) { - if (resue[key]) { - accessors[key] = resue[key] - } else { - sid = options.id + '.' + key - spath = pathname ? pathname + '.' + key : key - accessors[key] = makeAccessor(sid, spath, heirloom) - } - } else { - skips[key] = after[key] - delete after[key] - } + avalon.ready = function (fn) { + if (!isReady) { + readyList.push(fn) + } else { + fn(avalon) } + } - options.hashcode = before.$hashcode || makeHashCode('$') - accessors.$model = modelAccessor - var $vmodel = new Observer() - $vmodel = createViewModel($vmodel, accessors, skips) + avalon.ready(function () { + scan(document.body) + }) - for (key in skips) { - $vmodel[key] = skips[key] + new function () { + if (!avalon.browser) + return + if (document.readyState === 'complete') { + setTimeout(fireReady) //如果在domReady之外加载 + } else { + document.addEventListener('DOMContentLoaded', fireReady) } - initViewModel($vmodel, heirloom, keys, accessors, options) + avalon.bind(window, 'load', fireReady) - return $vmodel } - $$midway.slaveFactory = slaveFactory - function mediatorFactory(before, after) { - var keys = {}, key - var accessors = {}//新vm的访问器 - var unresolve = {}//需要转换的属性集合 - var heirloom = {} - var arr = avalon.slice(arguments) - var $skipArray = {} - var isWidget = typeof this === 'function' && this.isWidget - var config - var configName - for (var i = 0; i < arr.length; i++) { - var obj = arr[i] - //收集所有键值对及访问器属性 - var $accessors = obj.$accessors - for (var key in obj) { - if (!obj.hasOwnProperty(key)) { - continue - } - var cur = obj[key] - if (key === '$skipArray') {//处理$skipArray - if (Array.isArray(cur)) { - cur.forEach(function (el) { - $skipArray[el] = 1 - }) - } - continue - } - if (isWidget && arr.indexOf(cur) !== -1) {//处理配置对象 - config = cur - configName = key - continue - } - keys[key] = cur - if (accessors[key] && avalon.isObject(cur)) {//处理子vm - delete accessors[key] - } - if ($accessors && $accessors[key]) { - accessors[key] = $accessors[key] - } else if (typeof keys[key] !== 'function') { - unresolve[key] = 1 - } - } - } + /***/ }, + /* 99 */ + /***/ function(module, exports, __webpack_require__) { + __webpack_require__(44) + __webpack_require__(46) + //处理属性样式 + __webpack_require__(100) + __webpack_require__(48) + __webpack_require__(49) + //处理内容 + __webpack_require__(50) + __webpack_require__(51) + __webpack_require__(52) + //需要用到事件的 + __webpack_require__(54) + __webpack_require__(55) + __webpack_require__(101) + __webpack_require__(63) + __webpack_require__(64) - if (typeof this === 'function') { - this(keys, unresolve) - } - for (key in unresolve) { - //系统属性跳过,已经有访问器的属性跳过 - if ($$skipArray[key] || accessors[key]) - continue - if (!isSkip(key, keys[key], $skipArray)) { - - accessors[key] = makeAccessor(before.$id, key, heirloom) - accessors[key].set(keys[key]) - } - } + //处理逻辑 + __webpack_require__(65) + __webpack_require__(66) - var $vmodel = new Observer() - $vmodel = createViewModel($vmodel, accessors, keys) - for (key in keys) { - if (!accessors[key]) {//添加不可监控的属性 - - $vmodel[key] = keys[key] - } - //用于通过配置对象触发组件的$watch回调 - if (isWidget && config && accessors[key] && config.hasOwnProperty(key)) { - var GET = accessors[key].get - // GET.heirloom = heirloom - if (!GET.$decompose) { - GET.$decompose = {} - } - GET.$decompose[configName + '.' + key] = $vmodel - } + __webpack_require__(67) + __webpack_require__(70) - if (key in $$skipArray) { - delete keys[key] - } else { - keys[key] = true - } + /***/ }, + /* 100 */ + /***/ function(module, exports, __webpack_require__) { - } + + var attrUpdate = __webpack_require__(94) + var update = __webpack_require__(45) - initViewModel($vmodel, heirloom, keys, accessors, { - id: before.$id, - hashcode: makeHashCode('$'), - master: true - }) + avalon.directive('attr', { + diff: function (copy, src, name) { + var a = copy[name] + var p = src[name] + if (a && typeof a === 'object') { + if (Array.isArray(a)) {//转换成对象 + a = avalon.mix.apply({}, a) + } + if (!src.dynamic[name] || !p) {//如果一开始为空 + src.changeAttr = src[name] = a + } else { + var patch = {} + var hasChange = false + for (var i in a) {//diff差异点 + if (a[i] !== p[i]) { + hasChange = true + patch[i] = a[i] + } + } + if (hasChange) { + src[name] = a + src.changeAttr = patch + } + } + if (src.changeAttr) { + update(src, this.update) + } + } + if (copy !== src) { + delete copy[name]//释放内存 + } + }, + //dom, vnode + update: attrUpdate + }) - return $vmodel - } + /***/ }, + /* 101 */ + /***/ function(module, exports, __webpack_require__) { - $$midway.mediatorFactory = avalon.mediatorFactory = mediatorFactory + + var update = __webpack_require__(45) + var evaluatorPool = __webpack_require__(42) + var stringify = __webpack_require__(40) - var __array__ = share.__array__ + var rchangeFilter = /\|\s*change\b/ + var rcheckedType = /^(?:checkbox|radio)$/ + var rdebounceFilter = /\|\s*debounce(?:\(([^)]+)\))?/ + var updateModelByEvent = __webpack_require__(102) + var updateModelByValue = __webpack_require__(60) + var updateModel = __webpack_require__(58) + var updateView = __webpack_require__(103) + var addValidateField = __webpack_require__(62) + var duplexDir = 'ms-duplex' + avalon.directive('duplex', { + priority: 2000, + parse: function (copy, src, binding) { + var expr = binding.expr + var etype = src.props.type + //处理数据转换器 + var parsers = binding.param, dtype + var isChecked = false + parsers = parsers ? parsers.split('-').map(function (a) { + if (a === 'checked') { + isChecked = true + } + return a + }) : [] - var ap = Array.prototype - var _splice = ap.splice - function notifySize(array, size) { - if (array.length !== size) { - array.notify('length', array.length, size, true) - } - } + if (rcheckedType.test(etype) && isChecked) { + //如果是radio, checkbox,判定用户使用了checked格式函数没有 + parsers = [] + dtype = 'radio' + } - __array__.removeAll = function (all) { //移除N个元素 - var size = this.length - if (Array.isArray(all)) { - for (var i = this.length - 1; i >= 0; i--) { - if (all.indexOf(this[i]) !== -1) { - _splice.call(this, i, 1) + if (!/input|textarea|select/.test(src.nodeName)) { + if ('contenteditable' in src.props) { + dtype = 'contenteditable' } + } else if (!dtype) { + dtype = src.nodeName === 'select' ? 'select' : + etype === 'checkbox' ? 'checkbox' : + etype === 'radio' ? 'radio' : + 'input' } - } else if (typeof all === 'function') { - for (i = this.length - 1; i >= 0; i--) { - var el = this[i] - if (all(el, i)) { - _splice.call(this, i, 1) + var isChanged = false, debounceTime = 0 + //判定是否使用了 change debounce 过滤器 + if (dtype === 'input' || dtype === 'contenteditable') { + var isString = true + if (rchangeFilter.test(expr)) { + isChanged = true + } + if (!isChanged) { + var match = expr.match(rdebounceFilter) + if (match) { + debounceTime = parseInt(match[1], 10) || 300 + } } } - } else { - _splice.call(this, 0, this.length) - - } - if (!avalon.modern) { - this.$model = toJson(this) - } - notifySize(this, size) - this.notify() - } - - - var __method__ = ['push', 'pop', 'shift', 'unshift', 'splice'] - - __method__.forEach(function (method) { - var original = ap[method] - __array__[method] = function (a, b) { - // 继续尝试劫持数组元素的属性 - var args = [], size = this.length - if (method === 'splice' && Object(this[0]) === this[0]) { - var old = this.slice(a, b) - var neo = ap.slice.call(arguments, 2) - var args = [a, b] - for (var j = 0, jn = neo.length; j < jn; j++) { - var item = old[j] + var changed = copy.props['data-duplex-changed'] + var get = avalon.parseExpr(binding, 'duplex')// 输出原始数据 + var quoted = parsers.map(function (a) { + return avalon.quote(a) + }) + copy[duplexDir] = stringify({ + type: dtype, //这个决定绑定什么事件 + vmodel: '__vmodel__', + local: '__local__', + debug: avalon.quote(binding.name + '=' + binding.expr), + isChecked: isChecked, //用于radio与checked + parsers: '[' + quoted + ']', //各种转换器的名字 + isString: !!isString, //这个决定是否需要转换为字符串 + isChanged: isChanged, //这个决定同步的频数 + debounceTime: debounceTime, //这个决定同步的频数 + get: get, // + set: evaluatorPool.get('duplex:set:' + expr), + callback: changed ? avalon.parseExpr(changed, 'on') : 'avalon.noop' + }) + }, + diff: function (copy, src) { + if (!src.dynamic[duplexDir]) { + //第一次为原始虚拟DOM添加duplexData + var data = src[duplexDir] = copy[duplexDir] + data.parse = parseValue + } else { + data = src[duplexDir] + } + if (copy !== src) {//释放内存 + copy[duplexDir] = null + } - args[j + 2] = modelAdaptor(neo[j], item, (item && item.$events || {}), { - id: this.$id + '.*', - master: true - }) + var curValue = data.get(data.vmodel) + var preValue = data.value + if (data.isString) {//减少不必要的视图渲染 + curValue = data.parse(curValue) + curValue += '' + if (curValue === preValue) { + return } - - } else { - for (var i = 0, n = arguments.length; i < n; i++) { - args[i] = modelAdaptor(arguments[i], 0, {}, { - id: this.$id + '.*', - master: true - }) + } else if (Array.isArray(curValue)) { + var hack = true + if (curValue + '' === data.arrayHack) { + return } } - var result = original.apply(this, args) - if (!avalon.modern) { - this.$model = toJson(this) + data.value = curValue + //如果是curValue是一个数组,当我们改变vm中的数组, + //那么这个data.value也是跟着改变,因此必须保持一份副本才能用于比较 + if (hack) { + data.arayHack = curValue + '' } - notifySize(this, size) - this.notify() - return result + update(src, this.update, 'afterChange') + }, + update: function (dom, vdom) { + if (dom && dom.nodeType === 1) { + vdom.dynamic[duplexDir] = 1 + if (!dom.__ms_duplex__) { + dom.__ms_duplex__ = avalon.mix(vdom[duplexDir],{dom:dom}) + //绑定事件 + updateModelByEvent(dom, vdom) + //添加验证 + addValidateField(dom, vdom) + } + var data = dom.__ms_duplex__ + data.dom = dom + //如果不支持input.value的Object.defineProperty的属性支持, + //需要通过轮询同步, chrome 42及以下版本需要这个hack + if (data.isString + && !avalon.msie + && updateModelByValue === false + && !dom.valueHijack) { + dom.valueHijack = updateModel + var intervalID = setInterval(function () { + if (!avalon.contains(avalon.root, dom)) { + clearInterval(intervalID) + } else { + dom.valueHijack({type: 'poll'}) + } + }, 30) + } + //更新视图 + updateView[data.type].call(data) + } + } }) - 'sort,reverse'.replace(avalon.rword, function (method) { - __array__[method] = function () { - ap[method].apply(this, arguments) - if (!avalon.modern) { - this.$model = toJson(this) + function parseValue(val) { + for (var i = 0, k; k = this.parsers[i++]; ) { + var fn = avalon.parsers[k] + if (fn) { + val = fn.call(this, val) } - this.notify() - return this } - }) + return val + } - module.exports = avalon - //使用这个来扁平化数据 https://github.com/gaearon/normalizr - //使用Promise https://github.com/stefanpenner/es6-promise - //使用这个AJAX库 https://github.com/matthew-andrews/isomorphic-fetch /***/ }, - /* 82 */ + /* 102 */ /***/ function(module, exports, __webpack_require__) { - var share = __webpack_require__(83) - var canHideProperty = __webpack_require__(85) - var initEvents = share.initEvents - - /* - * toJson - * hideProperty - * initViewModel + /* + * 通过绑定事件同步vmodel + * 总共有三种方式同步视图 + * 1. 各种事件 input, change, click, propertychange, keydown... + * 2. value属性重写 + * 3. 定时器轮询 */ + var updateModel = __webpack_require__(58) + var markID = __webpack_require__(6).getShortID + var msie = avalon.msie + var window = avalon.window + var document = avalon.document - function toJson(val) { - var xtype = avalon.type(val) - if (xtype === 'array') { - var array = [] - for (var i = 0; i < val.length; i++) { - array[i] = toJson(val[i]) - } - return array - } else if (xtype === 'object') { - var obj = {} - for (i in val) { - if (i === '__proxy__' || i === '__data__' || i === '__const__') - continue - if (val.hasOwnProperty(i)) { - var value = val[i] - obj[i] = value && value.nodeType ? value : toJson(value) + function updateModelByEvent(node, vnode) { + var events = {} + var data = vnode['ms-duplex'] + data.update = updateModel + //添加需要监听的事件 + switch (data.type) { + case 'radio': + case 'checkbox': + events.click = updateModel + break + case 'select': + events.change = updateModel + break + case 'contenteditable': + if (data.isChanged) { + events.blur = updateModel + } else { + + if (window.webkitURL) { + // http://code.metager.de/source/xref/WebKit/LayoutTests/fast/events/ + // https://bugs.webkit.org/show_bug.cgi?id=110742 + events.webkitEditableContentChanged = updateModel + } else if (window.MutationEvent) { + events.DOMCharacterDataModified = updateModel + } + events.input = updateModel } - } - return obj + break + case 'input': + if (data.isChanged) { + events.change = updateModel + } else { + events.input = updateModel + + //https://github.com/RubyLouvre/avalon/issues/1368#issuecomment-220503284 + events.compositionstart = openComposition + events.compositionend = closeComposition + if(avalon.msie){ + events.keyup = updateModelKeyDown + } + } + break } - return val - } - function hideProperty(host, name, value) { - if (canHideProperty) { - Object.defineProperty(host, name, { - value: value, - writable: true, - enumerable: false, - configurable: true - }) - } else { - host[name] = value + if (/password|text/.test(vnode.props.type)) { + events.focus = openCaret //判定是否使用光标修正功能 + events.blur = closeCaret + data.getCaret = getCaret + data.setCaret = setCaret + } + + for (var name in events) { + avalon.bind(node, name, events[name]) } } - var modelAccessor = { - get: function () { - return toJson(this) - }, - set: avalon.noop, - enumerable: false, - configurable: true + + function updateModelKeyDown(e) { + var key = e.keyCode + // ignore + // command modifiers arrows + if (key === 91 || (15 < key && key < 19) || (37 <= key && key <= 40)) + return + updateModel.call(this, e) } - function initViewModel($vmodel, heirloom, keys, accessors, options) { + function openCaret() { + this.caret = true + } - if (options.array) { - if (avalon.modern) { - Object.defineProperty($vmodel, '$model', modelAccessor) - } else { - $vmodel.$model = toJson($vmodel) - } - } else { - function hasOwnKey(key) { - return keys[key] === true - } - hideProperty($vmodel, '$accessors', accessors) - hideProperty($vmodel, 'hasOwnProperty', hasOwnKey) - hideProperty($vmodel, '$track', Object.keys(keys).sort().join(';;')) - } - hideProperty($vmodel, '$id', options.id) - hideProperty($vmodel, '$hashcode', options.hashcode) - if (options.master === true) { - hideProperty($vmodel, '$run', function () { - run.call($vmodel) - }) - hideProperty($vmodel, '$wait', function () { - wait.call($vmodel) - }) - hideProperty($vmodel, '$element', null) - hideProperty($vmodel, '$render', 0) - initEvents($vmodel, heirloom) + function closeCaret() { + this.caret = false + } + function openComposition() { + this.composing = true + } + + function closeComposition(e) { + this.composing = false + var elem = this + setTimeout(function(){ + updateModel.call(elem, e) + }, 0) + + } + + + markID(openCaret) + markID(closeCaret) + markID(openComposition) + markID(closeComposition) + markID(updateModelKeyDown) + markID(updateModel) + + + function getCaret(field) { + var start = NaN + if (field.setSelectionRange) { + start = field.selectionStart } + return start } - function wait() { - this.$events.$$wait$$ = true + function setCaret(field, pos) { + if (!field.value || field.readOnly) + return + field.selectionStart = pos + field.selectionEnd = pos } - function run() { - var host = this.$events - delete host.$$wait$$ - if (host.$$dirty$$) { - delete host.$$dirty$$ - avalon.rerenderStart = new Date - var id = this.$id - var dotIndex = id.indexOf('.') - if (dotIndex > 0) { - avalon.batch(id.slice(0, dotIndex)) + + module.exports = updateModelByEvent + + /***/ }, + /* 103 */ + /***/ function(module, exports) { + + + var updateView = { + input: function () {//处理单个value值处理 + this.dom.value = this.value + }, + radio: function () {//处理单个checked属性 + var checked + if (this.isChecked) { + checked = !!this.value } else { - avalon.batch(id) + checked = this.value + '' === this.dom.value } - } - } - - share.$$midway.initViewModel = initViewModel + var dom = this.dom - share.$$midway.hideProperty = hideProperty + dom.checked = checked - var mixin = { - toJson: toJson, - initViewModel: initViewModel, - modelAccessor: modelAccessor - } - for (var i in share) { - mixin[i] = share[i] + }, + checkbox: function () {//处理多个checked属性 + var checked = false + var dom = this.dom + var value = dom.value + for (var i = 0; i < this.value.length; i++) { + var el = this.value[i] + if (el + '' === value) { + checked = true + } + } + dom.checked = checked + }, + select: function () {//处理子级的selected属性 + var a = Array.isArray(this.value) ? + this.value.map(String) : this.value + '' + avalon(this.dom).val(a) + }, + contenteditable: function () {//处理单个innerHTML + this.dom.innerHTML = this.value + this.update.call(this.dom) + } } - module.exports = mixin + module.exports = updateView /***/ }, - /* 83 */ + /* 104 */ /***/ function(module, exports, __webpack_require__) { - - var $$midway = {} - var $$skipArray = __webpack_require__(69) - var dispatch = __webpack_require__(84) - var $emit = dispatch.$emit - var $watch = dispatch.$watch - /* - * initEvents - * isSkip - * modelAdaptor - * makeAccessor + /** + * ------------------------------------------------------------ + * avalon基于纯净的Object.defineProperties的vm工厂 + * masterFactory,slaveFactory,mediatorFactory, ArrayFactory + * ------------------------------------------------------------ */ + var share = __webpack_require__(105) + var isSkip = share.isSkip + var $$midway = share.$$midway + var $$skipArray = share.$$skipArray + delete $$skipArray.$accessors + delete $$skipArray.__data__ + delete $$skipArray.__proxy__ + delete $$skipArray.__const__ - function initEvents($vmodel, heirloom) { - heirloom.__vmodel__ = $vmodel - var hide = $$midway.hideProperty + var makeAccessor = share.makeAccessor + var initViewModel = share.initViewModel + var modelAccessor = share.modelAccessor + var modelAdaptor = share.modelAdaptor + var makeHashCode = avalon.makeHashCode - hide($vmodel, '$events', heirloom) - hide($vmodel, '$watch', function () { - if (arguments.length === 2) { - return $watch.apply($vmodel, arguments) - } else { - throw '$watch方法参数不对' - } - }) - hide($vmodel, '$fire', function (expr, a, b) { - var list = $vmodel.$events[expr] - $emit(list, $vmodel, expr, a, b) - }) + + //一个vm总是为Observer的实例 + function Observer() { } + function masterFactory(definition, heirloom, options) { - var rskip = /function|window|date|regexp|element/i + var $skipArray = {} + if (definition.$skipArray) {//收集所有不可监听属性 + $skipArray = avalon.oneObject(definition.$skipArray) + delete definition.$skipArray + } - function isSkip(key, value, skipArray) { - // 判定此属性能否转换访问器 - return key.charAt(0) === '$' || - skipArray[key] || - (rskip.test(avalon.type(value))) || - (value && value.nodeName && value.nodeType > 0) - } + var keys = {} + options = options || {} + heirloom = heirloom || {} + var accessors = {} + var hashcode = makeHashCode('$') + var pathname = options.pathname || '' + options.id = options.id || hashcode + options.hashcode = hashcode + var key, sid, spath + for (key in definition) { + if ($$skipArray[key]) + continue + var val = keys[key] = definition[key] + if (!isSkip(key, val, $skipArray)) { + sid = options.id + '.' + key + spath = pathname ? pathname + '.' + key : key + accessors[key] = makeAccessor(sid, spath, heirloom) + } + } - function modelAdaptor(definition, old, heirloom, options) { - //如果数组转换为监控数组 - if (Array.isArray(definition)) { - return $$midway.arrayFactory(definition, old, heirloom, options) - } else if (Object(definition) === definition && typeof definition !== 'function') { - //如果此属性原来就是一个VM,拆分里面的访问器属性 - if (old && old.$id) { - ++avalon.suspendUpdate - //1.5带来的优化方案 - if (old.$track !== Object.keys(definition).sort().join(';;')) { - var vm = $$midway.slaveFactory(old, definition, heirloom, options) - } else { - vm = old - } - for (var i in definition) { - if ($$skipArray[i]) - continue - vm[i] = definition[i] - } - --avalon.suspendUpdate - return vm + accessors.$model = modelAccessor + var $vmodel = new Observer() + Object.defineProperties($vmodel, accessors) + + for (key in keys) { + //对普通监控属性或访问器属性进行赋值 + $vmodel[key] = keys[key] + //删除系统属性 + if (key in $skipArray) { + delete keys[key] } else { - vm = $$midway.masterFactory(definition, heirloom, options) - return vm + keys[key] = true } - } else { - return definition } - } - $$midway.modelAdaptor = modelAdaptor + initViewModel($vmodel, heirloom, keys, accessors, options) + return $vmodel + } + $$midway.masterFactory = masterFactory - function makeAccessor(sid, spath, heirloom) { - var old = NaN - function get() { - return old - } - get.heirloom = heirloom - return { - get: get, - set: function (val) { - if (old === val) { - return - } - var vm = heirloom.__vmodel__ - if (val && typeof val === 'object') { - val = $$midway.modelAdaptor(val, old, heirloom, { - pathname: spath, - id: sid - }) - } - var older = old - old = val - if (this.$hashcode && vm ) { - vm.$events.$$dirty$$ = true - if(vm.$events.$$wait$$) - return - //★★确保切换到新的events中(这个events可能是来自oldProxy) - if (heirloom !== vm.$events) { - get.heirloom = vm.$events - } - - //如果这个属性是组件配置对象中的属性,那么它需要触发组件的回调 - emitWidget(get.$decompose, spath, val, older) - //触发普通属性的回调 - if (spath.indexOf('*') === -1) { - $emit(get.heirloom[spath], vm, spath, val, older) - } - //如果这个属性是数组元素上的属性 - emitArray(sid+'', vm, spath, val, older) - //如果这个属性存在通配符 - emitWildcard(get.heirloom, vm, spath, val, older) - vm.$events.$$dirty$$ = false - batchUpdateView(vm.$id) - } - }, - enumerable: true, - configurable: true + var empty = {} + function slaveFactory(before, after, heirloom, options) { + var keys = {} + var accessors = {} + var pathname = options.pathname + heirloom = heirloom || {} + var key, sid, spath + for (key in after) { + if ($$skipArray[key]) + continue + keys[key] = after[key] + if (!isSkip(key, after[key], empty)) { + var accessor = Object.getOwnPropertyDescriptor(before, key) + if (accessor && accessor.get) { + accessors[key] = accessor + } else { + sid = options.id + '.' + key + spath = pathname ? pathname + '.' + key : key + accessors[key] = makeAccessor(sid, spath, heirloom) + } + } } - } - - function batchUpdateView(id) { - avalon.rerenderStart = new Date - var dotIndex = id.indexOf('.') - if (dotIndex > 0) { - avalon.batch(id.slice(0, dotIndex)) - } else { - avalon.batch(id) + for (key in before) { + delete before[key] } - } - var rtopsub = /([^.]+)\.(.+)/ - function emitArray(sid, vm, spath, val, older) { - if (sid.indexOf('.*.') > 0) { - var arr = sid.match(rtopsub) - var top = avalon.vmodels[ arr[1] ] - if (top) { - var path = arr[2] - $emit(top.$events[ path ], vm, spath, val, older) + options.hashcode = before.$hashcode || makeHashCode('$') + accessors.$model = modelAccessor + var $vmodel = before + Object.defineProperties($vmodel, accessors) + + for (key in keys) { + if (!accessors[key]) {//添加不可监控的属性 + $vmodel[key] = keys[key] } + keys[key] = true } + initViewModel($vmodel, heirloom, keys, accessors, options) + + return $vmodel } - function emitWidget(whole, spath, val, older) { - if (whole && whole[spath]) { - var wvm = whole[spath] - if (!wvm.$hashcode) { - delete whole[spath] - } else { - var wpath = spath.replace(/^[^.]+\./, '') - if (wpath !== spath) { - $emit(wvm.$events[wpath], wvm, wpath, val, older) + $$midway.slaveFactory = slaveFactory + + function mediatorFactory(before, after) { + var keys = {} + var accessors = {} + var unresolve = {} + var heirloom = {} + var $skipArray = {} + var arr = avalon.slice(arguments) + var config + var configName + var isWidget = typeof this === 'function' && this.isWidget + for (var i = 0; i < arr.length; i++) { + var obj = arr[i] + //收集所有键值对及访问器属性 + for (var key in obj) { + var cur = obj[key] + if (key === '$skipArray') { + if (Array.isArray(cur)) { + cur.forEach(function (el) { + $skipArray[el] = 1 + }) + } + continue } - } - } - } + if (isWidget && arr.indexOf(cur) !== -1) {//处理配置对象 + config = cur + configName = key + continue + } + var accessor = Object.getOwnPropertyDescriptor(obj, key) - function emitWildcard(obj, vm, spath, val, older) { - if (obj.__fuzzy__) { - obj.__fuzzy__.replace(avalon.rword, function (expr) { - var list = obj[expr] - var reg = list.reg - if (reg && reg.test(spath)) { - $emit(list, vm, spath, val, older) + keys[key] = cur + + if (accessors[key] && avalon.isObject(cur)) {//处理子vm + delete accessors[key] } - return expr - }) + if (accessor.set) { + accessors[key] = accessor + } else if (typeof keys[key] !== 'function') { + unresolve[key] = 1 + } + } + } + if (typeof this === 'function') { + this(keys, unresolve) + } + for (key in unresolve) { + if ($$skipArray[key] || accessors[key]) + continue + if (!isSkip(key, keys[key], $skipArray)) { + accessors[key] = makeAccessor(before.$id + '.' + key, key, heirloom) + accessors[key].set(keys[key]) + } } - } + var $vmodel = new Observer() + Object.defineProperties($vmodel, accessors) - function define(definition) { - var $id = definition.$id - if (!$id && avalon.config.debug) { - avalon.warn('vm.$id must be specified') - } - if (avalon.vmodels[$id]) { - throw Error('error:[' + $id + '] had defined!') + for (key in keys) { + if (!accessors[key]) {//添加不可监控的属性 + $vmodel[key] = keys[key] + } + if (isWidget && config && accessors[key] && config.hasOwnProperty(key)) { + var GET = accessors[key].get + if (!GET.$decompose) { + GET.$decompose = {} + } + GET.$decompose[configName + '.' + key] = $vmodel + } + keys[key] = true } - var vm = $$midway.masterFactory(definition, {}, { - pathname: '', - id: $id, + + initViewModel($vmodel, heirloom, keys, accessors, { + id: before.$id, + hashcode: makeHashCode("$"), master: true }) - return avalon.vmodels[$id] = vm + return $vmodel + } + + $$midway.mediatorFactory = avalon.mediatorFactory = mediatorFactory + var __array__ = share.__array__ + var ap = Array.prototype + var _splice = ap.splice + function notifySize(array, size) { + if (array.length !== size) { + array.notify('length', array.length, size, true) + } } - function arrayFactory(array, old, heirloom, options) { - if (old && old.splice) { - var args = [0, old.length].concat(array) - ++avalon.suspendUpdate - avalon.callArray = options.pathname - - old.splice.apply(old, args) - --avalon.suspendUpdate - return old - } else { - for (var i in __array__) { - array[i] = __array__[i] + __array__.removeAll = function (all) { //移除N个元素 + var size = this.length + if (Array.isArray(all)) { + for (var i = this.length - 1; i >= 0; i--) { + if (all.indexOf(this[i]) !== -1) { + _splice.call(this, i, 1) + } } - - array.notify = function (a, b, c, d) { - var vm = heirloom.__vmodel__ - if (vm) { - var path = a === null || a === void 0 ? - options.pathname : - options.pathname + '.' + a - vm.$fire(path, b, c) - if (!d && !heirloom.$$wait$$ && !avalon.suspendUpdate ) { - avalon.callArray = path - batchUpdateView(vm.$id) - delete avalon.callArray - } + } else if (typeof all === 'function') { + for (i = this.length - 1; i >= 0; i--) { + var el = this[i] + if (all(el, i)) { + _splice.call(this, i, 1) } } + } else { + _splice.call(this, 0, this.length) - var hashcode = avalon.makeHashCode('$') - options.array = true - options.hashcode = hashcode - options.id = options.id || hashcode - $$midway.initViewModel(array, heirloom, {}, {}, options) - - for (var j = 0, n = array.length; j < n; j++) { - array[j] = modelAdaptor(array[j], 0, {}, { - id: array.$id + '.*', - master: true - }) - } - return array } + + notifySize(this, size) + this.notify() } - $$midway.arrayFactory = arrayFactory - var __array__ = { - set: function (index, val) { - if (((index >>> 0) === index) && this[index] !== val) { - if (index > this.length) { - throw Error(index + 'set方法的第一个参数不能大于原数组长度') + + var __method__ = ['push', 'pop', 'shift', 'unshift', 'splice'] + __method__.forEach(function (method) { + var original = ap[method] + __array__[method] = function (a, b) { + // 继续尝试劫持数组元素的属性 + var args = [], size = this.length + if (method === 'splice' && Object(this[0]) === this[0]) { + var old = this.slice(a, b) + var neo = ap.slice.call(arguments, 2) + var args = [a, b] + for (var j = 0, jn = neo.length; j < jn; j++) { + var item = old[j] + args[j + 2] = modelAdaptor(neo[j], item, (item && item.$events || {}), { + id: this.$id + '.*', + master: true + }) + } + } else { + for (var i = 0, n = arguments.length; i < n; i++) { + args[i] = modelAdaptor(arguments[i], 0, {}, { + id: this.$id + '.*', + master: true + }) } - this.splice(index, 1, val) - } - }, - contains: function (el) { //判定是否包含 - return this.indexOf(el) !== -1 - }, - ensure: function (el) { - if (!this.contains(el)) { //只有不存在才push - this.push(el) - } - return this - }, - pushArray: function (arr) { - return this.push.apply(this, arr) - }, - remove: function (el) { //移除第一个等于给定值的元素 - return this.removeAt(this.indexOf(el)) - }, - removeAt: function (index) { //移除指定索引上的元素 - if ((index >>> 0) === index) { - return this.splice(index, 1) } - return [] - }, - clear: function () { - this.removeAll() + + var result = original.apply(this, args) + + notifySize(this, size) + this.notify() + + return result + } + }) + + 'sort,reverse'.replace(avalon.rword, function (method) { + __array__[method] = function () { + ap[method].apply(this, arguments) + this.notify() return this } - } - avalon.define = define + }) + + + module.exports = avalon - module.exports = { - $$midway: $$midway, - $$skipArray: $$skipArray, - isSkip: isSkip, - __array__: __array__, - initEvents: initEvents, - makeAccessor: makeAccessor, - modelAdaptor: modelAdaptor - } /***/ }, - /* 84 */ - /***/ function(module, exports) { + /* 105 */ + /***/ function(module, exports, __webpack_require__) { - - /** - * ------------------------------------------------------------ - * 属性监听系统 - * ------------------------------------------------------------ - */ + var share = __webpack_require__(83) + var initEvents = share.initEvents - function adjustVm(vm, expr) { - var toppath = expr.split(".")[0], other - try { - if (vm.hasOwnProperty(toppath)) { - if (vm.$accessors) { - other = vm.$accessors[toppath].get.heirloom.__vmodel__ - } else { - other = Object.getOwnPropertyDescriptor(vm, toppath).get.heirloom.__vmodel__ + function toJson(val) { + var xtype = avalon.type(val) + if (xtype === 'array') { + var array = [] + for (var i = 0; i < val.length; i++) { + array[i] = toJson(val[i]) + } + return array + } else if (xtype === 'object') { + var obj = {} + for (i in val) { + if (val.hasOwnProperty(i)) { + var value = val[i] + obj[i] = value && value.nodeType ? value : toJson(value) } - } - } catch (e) { + return obj } - return other || vm + return val } - function toRegExp(expr) { - var arr = expr.split('.') - return new RegExp("^" + arr.map(function (el) { - return el === '*' ? '(?:[^.]+)' : el - }).join('\\.') + '$', 'i') + function hideProperty(host, name, value) { + Object.defineProperty(host, name, { + value: value, + writable: true, + enumerable: false, + configurable: true + }) } - function addFuzzy(add, obj, expr) { - if (add) { - if (obj.__fuzzy__) { - if (obj.__fuzzy__.indexOf(',' + expr) === -1) { - obj.__fuzzy__ += ',' + expr - } - } else { - obj.__fuzzy__ = expr - } - } + + var modelAccessor = { + get: function () { + return toJson(this) + }, + set: avalon.noop, + enumerable: false, + configurable: true } - function $watch(expr, callback) { - var fuzzy = expr.indexOf('.*') > 0 || expr === '*' - var vm = fuzzy ? this : $watch.adjust(this, expr) - var hive = this.$events - var list = hive[expr] || (hive[expr] = []) - if (fuzzy) { - list.reg = list.reg || toRegExp(expr) - } - addFuzzy(fuzzy, hive, expr) - if (vm !== this) { - addFuzzy(fuzzy, this.$events, expr) - } + share.$$midway.hideProperty = hideProperty - avalon.Array.ensure(list, callback) + function initViewModel($vmodel, heirloom, keys, accessors, options) { - return function () { - avalon.Array.remove(list, callback) + if (options.array) { + Object.defineProperty($vmodel, '$model', modelAccessor) + } else { + function hasOwnKey(key) { + return keys[key] === true + } + hideProperty($vmodel, 'hasOwnProperty', hasOwnKey) } + hideProperty($vmodel, '$id', options.id) + hideProperty($vmodel, '$hashcode', options.hashcode) + hideProperty($vmodel, '$track', Object.keys(keys).sort().join(';;')) + if (options.master === true) { + hideProperty($vmodel, '$run', run) + hideProperty($vmodel, '$wait', wait) + hideProperty($vmodel, '$element', null) + hideProperty($vmodel, '$render', 0) + initEvents($vmodel, heirloom) + } + } + function wait() { + this.$events.$$wait$$ = true } - $watch.adjust = adjustVm - /** - * $fire 方法的内部实现 - * - * @param {Array} list 订阅者数组 - * @param {Component} vm - * @param {String} path 监听属性名或路径 - * @param {Any} a 当前值 - * @param {Any} b 过去值 - * @param {Number} i 如果抛错,让下一个继续执行 - * @returns {undefined} - */ - function $emit(list, vm, path, a, b, i) { - if (list && list.length) { - try { - for (i = i || list.length - 1; i >= 0; i--) { - var callback = list[i] - callback.call(vm, a, b, path) - } - } catch (e) { - if (i - 1 > 0) - $emit(list, vm, path, a, b, i - 1) - avalon.log(e, path) + function run() { + var host = this.$events + delete host.$$wait$$ + if (host.$$dirty$$) { + delete host.$$dirty$$ + avalon.rerenderStart = new Date + var id = this.$id + var dotIndex = id.indexOf('.') + if (dotIndex > 0) { + avalon.batch(id.slice(0, dotIndex)) + } else { + avalon.batch(id) } - } } + share.$$midway.initViewModel = initViewModel - module.exports = { - $emit: $emit, - $watch: $watch, - adjustVm: adjustVm + var mixin = { + toJson: toJson, + initViewModel: initViewModel, + modelAccessor: modelAccessor } - - - /***/ }, - /* 85 */ - /***/ function(module, exports) { - - //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 - //标准浏览器使用__defineGetter__, __defineSetter__实现 - var flag = true - try { - Object.defineProperty({}, '_', { - value: 'x' - }) - } catch (e) { - flag = false + for (var i in share) { + mixin[i] = share[i] } - module.exports = flag + module.exports = mixin + /***/ }, - /* 86 */ + /* 106 */ /***/ function(module, exports, __webpack_require__) { - - var canHideProperty = __webpack_require__(85) - var $$skipArray = __webpack_require__(69) - - - var defineProperties = Object.defineProperties - var defineProperty - - var expose = new Date() - 0 + var ret = __webpack_require__(80) + var fireDisposeHook = ret.fireDisposeHook + var fireDisposeHooks = ret.fireDisposeHooks + var fireDisposeHookDelay = ret.fireDisposeHookDelay - if (!canHideProperty) { - if ('__defineGetter__' in avalon) { - defineProperty = function (obj, prop, desc) { - if ('value' in desc) { - obj[prop] = desc.value - } - if ('get' in desc) { - obj.__defineGetter__(prop, desc.get) - } - if ('set' in desc) { - obj.__defineSetter__(prop, desc.set) - } - return obj + //用于IE8+, firefox + function byRewritePrototype() { + if (byRewritePrototype.execute) { + return + } + //https://www.web-tinker.com/article/20618.html?utm_source=tuicool&utm_medium=referral + //IE6-8虽然暴露了Element.prototype,但无法重写已有的DOM API + byRewritePrototype.execute = true + var p = Node.prototype + function rewite(name, fn) { + var cb = p[name] + p[name] = function (a, b) { + return fn.call(this, cb, a, b) } - defineProperties = function (obj, descs) { - for (var prop in descs) { - if (descs.hasOwnProperty(prop)) { - defineProperty(obj, prop, descs[prop]) - } - } - return obj + } + rewite('removeChild', function (fn, a, b) { + fn.call(this, a, b) + if (a.nodeType === 1) { + fireDisposeHookDelay(a) + } + return a + }) + + rewite('replaceChild', function (fn, a, b) { + fn.call(this, a, b) + if (a.nodeType === 1) { + fireDisposeHookDelay(a) } + return a + }) + //访问器属性需要用getOwnPropertyDescriptor处理 + var ep = Element.prototype + function newSetter(html) { + var all = avalon.slice(this.getElementsByTagName('*')) + oldSetter.call(this, html) + fireDisposeHooks(all) } - if (avalon.msie) { - var VBClassPool = {} - window.execScript([// jshint ignore:line - 'Function parseVB(code)', - '\tExecuteGlobal(code)', - 'End Function' //转换一段文本为VB代码 - ].join('\n'), 'VBScript'); - - function VBMediator(instance, accessors, name, value) {// jshint ignore:line - var accessor = accessors[name] - if (arguments.length === 4) { - accessor.set.call(instance, value) - } else { - return accessor.get.call(instance) - } - } - defineProperties = function (name, accessors, properties) { - // jshint ignore:line - var buffer = [] - buffer.push( - '\r\n\tPrivate [__data__], [__proxy__]', - '\tPublic Default Function [__const__](d' + expose + ', p' + expose + ')', - '\t\tSet [__data__] = d' + expose + ': set [__proxy__] = p' + expose, - '\t\tSet [__const__] = Me', //链式调用 - '\tEnd Function') - //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好 - var uniq = { - __proxy__: true, - __data__: true, - __const__: true - } - - //添加访问器属性 - for (name in accessors) { - uniq[name] = true - buffer.push( - //由于不知对方会传入什么,因此set, let都用上 - '\tPublic Property Let [' + name + '](val' + expose + ')', //setter - '\t\tCall [__proxy__](Me,[__data__], "' + name + '", val' + expose + ')', - '\tEnd Property', - '\tPublic Property Set [' + name + '](val' + expose + ')', //setter - '\t\tCall [__proxy__](Me,[__data__], "' + name + '", val' + expose + ')', - '\tEnd Property', - '\tPublic Property Get [' + name + ']', //getter - '\tOn Error Resume Next', //必须优先使用set语句,否则它会误将数组当字符串返回 - '\t\tSet[' + name + '] = [__proxy__](Me,[__data__],"' + name + '")', - '\tIf Err.Number <> 0 Then', - '\t\t[' + name + '] = [__proxy__](Me,[__data__],"' + name + '")', - '\tEnd If', - '\tOn Error Goto 0', - '\tEnd Property') - - } - for (name in properties) { - if (uniq[name] !== true) { - uniq[name] = true - buffer.push('\tPublic [' + name + ']') - } - } - for (name in $$skipArray) { - if (uniq[name] !== true) { - uniq[name] = true - buffer.push('\tPublic [' + name + ']') - } - } - buffer.push('\tPublic [' + 'hasOwnProperty' + ']') - buffer.push('End Class') - var body = buffer.join('\r\n') - var className = VBClassPool[body] - if (!className) { - className = avalon.makeHashCode('VBClass') - - window.parseVB('Class ' + className + body) - window.parseVB([ - 'Function ' + className + 'Factory(a, b)', //创建实例并传入两个关键的参数 - '\tDim o', - '\tSet o = (New ' + className + ')(a, b)', - '\tSet ' + className + 'Factory = o', - 'End Function' - ].join('\r\n')) - VBClassPool[body] = className - } - var ret = window[className + 'Factory'](accessors, VBMediator) //得到其产品 - return ret //得到其产品 + + try { + var obj = Object.getOwnPropertyDescriptor(ep, 'innerHTML') + var oldSetter = obj.set + obj.set = newSetter + Object.defineProperty(ep, 'innerHTML', obj) + } catch (e) { + //safari 9.1.2使用Object.defineProperty重写innerHTML会抛 + // Attempting to change the setter of an unconfigurable property. + if (ep && ep.__lookupSetter__) { + oldSetter = ep.__lookupSetter__('innerHTML') + ep.__defineSetter__('innerHTML', newSetter) } } + + + rewite('appendChild', function (fn, a) { + fn.call(this, a) + if (a.nodeType === 1 && this.nodeType === 11) { + fireDisposeHookDelay(a) + } + return a + }) + + rewite('insertBefore', function (fn, a, b) { + fn.call(this, a, b) + if (a.nodeType === 1 && this.nodeType === 11) { + fireDisposeHookDelay(a) + } + return a + }) + } + + module.exports = function onComponentDispose(dom) { + byRewritePrototype(dom) } - module.exports = defineProperties /***/ } diff --git a/karma.conf.js b/karma.conf.js index 590ede409..662f0821c 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -5,7 +5,12 @@ module.exports = function (config) { files: [ {pattern: 'node_modules/chai/chai.js', include: true}, 'dist/avalon.test.js', - // 'karma/reconcile.js', + // 'karma/reconcile.js', + 'karma/seed/core.js', + 'karma/seed/browser.js', + 'karma/seed/lang.js', + 'karma/seed/cache.js', + 'karma/seed/config.js', 'karma/$watch.js', 'karma/other.js', 'karma/directives/text.js', @@ -24,7 +29,6 @@ module.exports = function (config) { 'karma/directives/widget.js' ], exclude: [], - reporters: ['mocha'], mochaReporter: { output: 'autowatch', colors: { @@ -34,6 +38,17 @@ module.exports = function (config) { error: 'bgRed' } }, + reporters: ['mocha', 'coverage'], + preprocessors: { + // source files, that you wanna generate coverage for + // do not include tests or libraries + // (these files will be instrumented by Istanbul) + 'dist/avalon.test.js': ['coverage'] + }, + coverageReporter: { + type: 'html', + dir: 'coverage/' + }, port: 9858, colors: true, logLevel: config.LOG_INFO, @@ -44,6 +59,8 @@ module.exports = function (config) { singleRun: false, plugins: [ 'karma-mocha', + + 'karma-coverage', 'karma-mocha-reporter', 'karma-firefox-launcher', 'karma-safari-launcher', diff --git a/karma/seed/browser.js b/karma/seed/browser.js new file mode 100644 index 000000000..5979eef9d --- /dev/null +++ b/karma/seed/browser.js @@ -0,0 +1,43 @@ + +describe('测试browser文件的API', function () { + + it('document', function () { + expect(typeof avalon.document).to.equal('object') + }) + + + it('window', function () { + expect(avalon).have.property('window') + }) + + + it('root', function () { + expect(avalon).to.have.property('root') + }) + + + it('avalonDiv', function () { + expect(avalon).to.have.property('avalonDiv') + }) + + + it('avalonFragment', function () { + expect(avalon).to.have.property('avalonFragment') + }) + + + it('msie', function () { + expect(avalon.msie).to.be.a('number') + }) + + + it('modern', function () { + expect(avalon.modern).to.be.a('boolean') + }) + + + it('browser', function () { + expect(avalon.browser).to.be.a('boolean') + }) + +}) \ No newline at end of file diff --git a/karma/seed/cache.js b/karma/seed/cache.js new file mode 100644 index 000000000..d5c114661 --- /dev/null +++ b/karma/seed/cache.js @@ -0,0 +1,24 @@ +describe('测试cache文件的API', function () { + describe('LRU', function () { + var Cache = avalon.cache + var cache = new Cache(3) + it('test', function () { + + expect(cache.get).to.be.a('function') + expect(cache.put).to.be.a('function') + expect(cache.shift).to.be.a('function') + var e = cache.put('aa', 'bb') + expect(e).to.equal('bb') + expect(cache.limit).to.equal(3) + expect(cache.size).to.equal(1) + cache.put('eee', 'bb') + cache.put('ddd', '111') + cache.get('aa') + cache.put('fff', '111') + cache.get('aa') + cache.put('999', '111') + expect(cache.size).to.equal(3) + expect(cache.get('eee')).to.equal(void 0) + }) + }) +}) \ No newline at end of file diff --git a/karma/seed/config.js b/karma/seed/config.js new file mode 100644 index 000000000..9f27460be --- /dev/null +++ b/karma/seed/config.js @@ -0,0 +1,14 @@ +describe('测试config文件的API', function () { + describe('config', function () { + it('test', function () { + expect(avalon.config).to.be.a('function') + expect(avalon.config).to.have.all.keys( + [ + 'openTag', 'closeTag', 'rexpr', 'plugins', 'debug' + ]) + expect(avalon.config.openTag).to.equal('{{') + expect(avalon.config.closeTag).to.equal('}}') + expect(avalon.config.plugins.interpolate).to.be.a('function') + }) + }) +}) diff --git a/karma/seed/core.js b/karma/seed/core.js new file mode 100644 index 000000000..54b48fb9b --- /dev/null +++ b/karma/seed/core.js @@ -0,0 +1,80 @@ +var expect = chai.expect +function heredoc(fn) { + return fn.toString().replace(/^[^\/]+\/\*!?\s?/, ''). + replace(/\*\/[^\/]+$/, '').trim().replace(/>\s*<') +} +describe('core', function () { + it('new avalon', function () { + var body = document.body + var el = avalon(body) + expect(el).to.be.instanceof(avalon) + expect(el[0]).to.be.equal(body) + expect(el.element).to.be.equal(body) + }) + it('fn', function () { + expect(avalon.fn).to.equal(avalon.prototype) + }) + it('noop', function () { + + expect(avalon.noop).to.a('function') + + }) + it('shadowCopy', function () { + var a = {} + var b = {aa: 1, bb: 1} + expect(avalon.shadowCopy).to.be.a('function') + expect(avalon.shadowCopy(a, b)).to.be.eql(b) + }) + it('rword', function () { + + expect(avalon.rword).to.be.a('regexp') + }) + it('inspect', function () { + var a = avalon.inspect + expect(a).to.be.a('function') + expect(a).to.be.equal(Object.prototype.toString) + expect(a.call('')).to.equal('[object String]') + expect(a.call([])).to.equal('[object Array]') + expect(a.call(1)).to.equal('[object Number]') + expect(a.call(new Date())).to.equal('[object Date]') + expect(a.call(/test/)).to.equal('[object RegExp]') + }) + + it('ohasOwn', function () { + + expect(avalon.ohasOwn).to.be.a('function') + expect(avalon.ohasOwn).to.be.equal(Object.prototype.hasOwnProperty) + }) + it('log', function () { + + expect(avalon.log).to.be.a('function') + }) + it('warn', function () { + + expect(avalon.warn).to.be.a('function') + }) + it('error', function () { + + expect(avalon.error).to.be.a('function') + }) + it('oneObject', function () { + + expect(avalon.oneObject).to.be.a('function') + expect(avalon.oneObject('aa,bb,cc')).to.eql({ + aa: 1, + bb: 1, + cc: 1 + }) + expect(avalon.oneObject([1, 2, 3], false)).to.eql({ + 1: false, + 2: false, + 3: false + }) + }) +}) + + + + + + diff --git a/karma/seed/lang.js b/karma/seed/lang.js new file mode 100644 index 000000000..e7eb1eb63 --- /dev/null +++ b/karma/seed/lang.js @@ -0,0 +1,175 @@ +describe('测试lang.compact/modern文件的API', function () { + describe('quote', function () { + it('test', function () { + expect(avalon.quote).to.be.a('function') + }) + }) + + describe('type', function () { + it('test', function () { + var fn = avalon.type + expect(fn(/e\d+/)).to.equal('regexp') + expect(fn('sss')).to.equal('string') + expect(fn(111)).to.equal('number') + expect(fn(new Error)).to.equal('error') + expect(fn(Date)).to.equal('function') + expect(fn(new Date)).to.equal('date') + expect(fn({})).to.equal('object') + expect(fn(null)).to.equal('null') + expect(fn(void 0)).to.equal('undefined') + }) + }) + + describe('isFunction', function () { + it('test', function () { + expect(avalon.isFunction(avalon.noop)).to.equal(true) + }) + }) + + describe('isWindow', function () { + it('test', function () { + expect(avalon.isWindow(avalon.document)).to.equal(false) + }) + }) + + describe('isPlainObject', function () { + it('test', function () { + expect(avalon.isPlainObject({})).to.be.true + expect(avalon.isPlainObject(new Object)).to.be.true + }) + }) + + describe('mix', function () { + it('test', function () { + expect(avalon.mix).to.be.a('function') + + }) + }) + + describe('each', function () { + it('test', function () { + expect(avalon.each).to.be.a('function') + + }) + }) + + +}) + +describe('测试lang.share文件的API', function () { + describe('caches', function () { + it('test', function () { + expect(avalon.caches).to.be.a('object') + }) + }) + describe('components', function () { + it('test', function () { + expect(avalon.components).to.be.a('object') + }) + }) + describe('scopes', function () { + it('test', function () { + expect(avalon.scopes).to.be.a('object') + }) + }) + describe('directives', function () { + it('test', function () { + expect(avalon.directives).to.be.a('object') + }) + }) + describe('filters', function () { + it('test', function () { + expect(avalon.filters).to.be.a('object') + }) + }) + describe('vmodels', function () { + it('test', function () { + expect(avalon.vmodels).to.be.a('object') + }) + }) + describe('parsers', function () { + it('test', function () { + expect(avalon.vmodels).to.be.a('object') + }) + }) + describe('eventHooks', function () { + it('test', function () { + expect(avalon.eventHooks).to.be.a('object') + }) + }) + describe('cssHooks', function () { + it('test', function () { + expect(avalon.cssHooks).to.be.a('object') + }) + }) + + describe('slice', function () { + it('test', function () { + expect(avalon.slice([1, 2, 3, 4], 1, 2)).to.eql([2]) + }) + }) + + describe('version', function () { + it('test', function () { + expect(/\d/.test(avalon.version)).to.be.true + }) + }) + describe('css', function () { + it('test', function () { + expect(avalon.css).to.be.a('function') + }) + }) + describe('directive', function () { + it('test', function () { + expect(avalon.directive).to.be.a('function') + }) + }) + + describe('isObject', function () { + it('test', function () { + expect(avalon.isObject({})).to.be.true + expect(avalon.isObject(avalon.noop)).to.be.false + }) + }) + + describe('range', function () { + it('test', function () { + expect(avalon.range).to.be.a('function') + }) + }) + + describe('hyphen', function () { + it('test', function () { + expect(avalon.hyphen).to.be.a('function') + }) + }) + + describe('camelize', function () { + it('test', function () { + expect(avalon.camelize).to.be.a('function') + }) + }) + + describe('makeHashCode', function () { + it('test', function () { + expect(avalon.makeHashCode).to.be.a('function') + }) + }) + + describe('escapeRegExp', function () { + it('test', function () { + expect(avalon.escapeRegExp).to.be.a('function') + }) + }) + + describe('Array', function () { + it('test', function () { + expect(avalon.Array).to.be.a('object') + expect(avalon.Array).to.have.all.keys(['merge', 'ensure', 'remove', 'removeAt']) + + }) + }) + + + +}) \ No newline at end of file diff --git a/package.json b/package.json index 81432120c..9e6529acf 100644 --- a/package.json +++ b/package.json @@ -19,8 +19,9 @@ "eslint-loader": "1.3.0", "karma": "^0.13.19", "karma-browserstack-launcher": "^1.0.1", + "karma-chai": "^0.1.0", "karma-chrome-launcher": "^0.2.2", - "karma-coverage": "^0.5.3", + "karma-coverage": "^0.5.5", "karma-firefox-launcher": "^0.1.7", "karma-junit-reporter": "^0.3.8", "karma-mocha": "^0.1.8", diff --git a/src/avalon.test.js b/src/avalon.test.js index a02a23cf0..fe92b86bd 100644 --- a/src/avalon.test.js +++ b/src/avalon.test.js @@ -1,5 +1,6 @@ var avalon = require('../dist/avalon.modern') +avalon.cache = require('./seed/cache') require('../components/button/index') require('../components/panel/index') diff --git a/src/seed/__test__.js b/src/seed/__test__.js deleted file mode 100644 index 84ecdce93..000000000 --- a/src/seed/__test__.js +++ /dev/null @@ -1,366 +0,0 @@ - -var file = process.env.modern === true ? 'modern' : 'compact' - -var avalon = require("./" + file) -var isArrayLike = require('./lang.' + file).isArrayLike -var share = require('./lang.share') -var expect = require('chai').expect - -describe('测试core文件的API', function () { - - describe('init', function () { - it('test', function () { - expect(avalon.init).to.be.a('function') - }) - }) - - describe('fn', function () { - it('test', function () { - expect(avalon.fn).to.equal(avalon.prototype) - }) - }) - - describe('shadowCopy', function () { - it('test', function () { - var obj = {} - avalon.mix(obj, { - a: 1, - b: 2 - }) - expect(obj).to.eql({a: 1, b: 2}) - }) - }) - - describe('noop', function () { - it('test', function () { - expect(avalon.noop).to.be.a('function') - }) - }) - describe('rword', function () { - it('test', function () { - expect(avalon.rword).to.be.a('regexp') - }) - }) - - describe('inspect', function () { - it('test', function () { - var a = avalon.inspect - expect(a).to.equal(Object.prototype.toString) - - expect(a.call('')).to.equal('[object String]') - expect(a.call([])).to.equal('[object Array]') - expect(a.call(1)).to.equal('[object Number]') - expect(a.call(new Date())).to.equal('[object Date]') - expect(a.call(/test/)).to.equal('[object RegExp]') - }) - }) - - describe('ohasOwn', function () { - it('test', function () { - expect(avalon.ohasOwn).to.equal(Object.prototype.hasOwnProperty) - }) - }) - - describe('log', function () { - it('test', function () { - expect(avalon.log).to.be.a('function') - }) - }) - describe('warn', function () { - it('test', function () { - expect(avalon.warn).to.be.a('function') - }) - }) - describe('error', function () { - it('test', function () { - expect(avalon.error).to.be.a('function') - }) - }) - - describe('oneObject', function () { - it('test', function () { - expect(avalon.oneObject('aa,bb,cc')).to.eql({ - aa: 1, - bb: 1, - cc: 1 - }) - expect(avalon.oneObject([1, 2, 3], false)).to.eql({ - 1: false, - 2: false, - 3: false - }) - }) - }) - -}) - -describe('测试browser文件的API', function () { - - describe('document', function () { - it('test', function () { - expect(avalon.document).to.be.a('object') - }) - }) - - describe('window', function () { - it('test', function () { - expect(avalon).have.property('window') - }) - }) - - describe('root', function () { - it('test', function () { - expect(avalon).to.have.property('root') - }) - }) - - describe('avalonDiv', function () { - it('test', function () { - expect(avalon).to.have.property('avalonDiv') - }) - }) - - describe('avalonFragment', function () { - it('test', function () { - expect(avalon).to.have.property('avalonFragment') - }) - }) - - describe('msie', function () { - it('test', function () { - expect(avalon.msie).to.be.a('number') - }) - }) - - describe('modern', function () { - it('test', function () { - expect(avalon.modern).to.be.a('boolean') - }) - }) - - -}) - -describe('测试lang.compact/modern文件的API', function () { - describe('quote', function () { - it('test', function () { - expect(avalon.quote).to.be.a('function') - }) - }) - - describe('type', function () { - it('test', function () { - var fn = avalon.type - expect(fn(/e\d+/)).to.equal('regexp') - expect(fn('sss')).to.equal('string') - expect(fn(111)).to.equal('number') - expect(fn(new Error)).to.equal('error') - expect(fn(Date)).to.equal('function') - expect(fn(new Date)).to.equal('date') - expect(fn({})).to.equal('object') - expect(fn(null)).to.equal('null') - expect(fn(void 0)).to.equal('undefined') - }) - }) - - describe('isFunction', function () { - it('test', function () { - expect(avalon.isFunction(avalon.noop)).to.equal(true) - }) - }) - - describe('isWindow', function () { - it('test', function () { - expect(avalon.isWindow(avalon.document)).to.equal(false) - }) - }) - - describe('isPlainObject', function () { - it('test', function () { - expect(avalon.isPlainObject({})).to.be.true - expect(avalon.isPlainObject(new Object)).to.be.true - }) - }) - - describe('mix', function () { - it('test', function () { - expect(avalon.mix).to.be.a('function') - - }) - }) - - describe('each', function () { - it('test', function () { - expect(avalon.each).to.be.a('function') - - }) - }) - - describe('isArrayLike', function () { - it('test', function () { - expect(avalon.isArrayLike).to.be.a('undefined') - expect(isArrayLike).to.be.a('function') - }) - }) -}) - -describe('测试lang.share文件的API', function () { - describe('caches', function () { - it('test', function () { - expect(avalon.caches).to.be.a('object') - }) - }) - describe('components', function () { - it('test', function () { - expect(avalon.components).to.be.a('object') - }) - }) - describe('resolvedComponents', function () { - it('test', function () { - expect(avalon.resolvedComponents).to.be.a('object') - }) - }) - describe('directives', function () { - it('test', function () { - expect(avalon.directives).to.be.a('object') - }) - }) - describe('filters', function () { - it('test', function () { - expect(avalon.filters).to.be.a('object') - }) - }) - describe('vmodels', function () { - it('test', function () { - expect(avalon.vmodels).to.be.a('object') - }) - }) - describe('parsers', function () { - it('test', function () { - expect(avalon.vmodels).to.be.a('object') - }) - }) - describe('eventHooks', function () { - it('test', function () { - expect(avalon.eventHooks).to.be.a('object') - }) - }) - describe('cssHooks', function () { - it('test', function () { - expect(avalon.cssHooks).to.be.a('object') - }) - }) - - describe('slice', function () { - it('test', function () { - expect(avalon.slice([1, 2, 3, 4], 1, 2)).to.eql([2]) - }) - }) - - describe('version', function () { - it('test', function () { - expect(/\d/.test(avalon.version)).to.be.true - }) - }) - describe('css', function () { - it('test', function () { - expect(avalon.css).to.be.a('function') - }) - }) - describe('directive', function () { - it('test', function () { - expect(avalon.directive).to.be.a('function') - }) - }) - - describe('isObject', function () { - it('test', function () { - expect(avalon.isObject({})).to.be.true - expect(avalon.isObject(avalon.noop)).to.be.false - }) - }) - - describe('range', function () { - it('test', function () { - expect(avalon.range).to.be.a('function') - }) - }) - - describe('hyphen', function () { - it('test', function () { - expect(avalon.hyphen).to.be.a('function') - }) - }) - - describe('camelize', function () { - it('test', function () { - expect(avalon.camelize).to.be.a('function') - }) - }) - - describe('makeHashCode', function () { - it('test', function () { - expect(avalon.makeHashCode).to.be.a('function') - }) - }) - - describe('escapeRegExp', function () { - it('test', function () { - expect(avalon.escapeRegExp).to.be.a('function') - }) - }) - - describe('Array', function () { - it('test', function () { - expect(avalon.Array).to.be.a('object') - expect(avalon.Array).to.have.all.keys(['merge', 'ensure', 'remove', 'removeAt']) - - }) - }) - - describe('getLongID', function () { - it('test', function () { - var obj = {} - share.getLongID(obj) - expect(obj.uuid).to.match(/e\d{8,11}/) - }) - }) - - describe('getShortID', function () { - it('test', function () { - var obj = {} - share.getShortID(obj) - expect(obj.uuid).to.match(/_\d{1,2}/) - }) - }) - -}) - -describe('测试config文件的API', function () { - describe('config', function () { - it('test', function () { - expect(avalon.config).to.be.a('function') - expect(avalon.config).to.have.all.keys( - [ - 'openTag', 'closeTag', 'rexpr', 'plugins', 'debug' - ]) - expect(avalon.config.openTag).to.equal('{{') - expect(avalon.config.closeTag).to.equal('}}') - expect(avalon.config.plugins.interpolate).to.be.a('function') - }) - }) -}) - -describe('测试cache文件的API', function () { - describe('LRU', function () { - var Cache = require('./cache') - var cache = new Cache(125) - it('test', function () { - - expect(cache.get).to.be.a('function') - expect(cache.put).to.be.a('function') - expect(cache.shift).to.be.a('function') - - }) - }) -}) \ No newline at end of file diff --git a/src/seed/browser.js b/src/seed/browser.js index 4ff670ee7..370d0706a 100644 --- a/src/seed/browser.js +++ b/src/seed/browser.js @@ -15,22 +15,25 @@ var browser = { outerHTML: 'x' }, msie: NaN, + browser: false, modern: true, avalonDiv: {}, avalonFragment: null } window.avalon = avalon - +/* istanbul ignore if */ if (window.location && window.navigator && window.window) { var doc = window.document browser.browser = true browser.document = doc - browser.modern = window.dispatchEvent browser.root = doc.documentElement browser.avalonDiv = doc.createElement('div') browser.avalonFragment = doc.createDocumentFragment() if (window.VBArray) { browser.msie = doc.documentMode || (window.XMLHttpRequest ? 7 : 6) + browser.modern = browser.msie > 8 + } else { + browser.modern = true } } diff --git a/src/seed/config.js b/src/seed/config.js index fdde275f7..a6b772eaf 100644 --- a/src/seed/config.js +++ b/src/seed/config.js @@ -1,6 +1,7 @@ var avalon = require('./core') function kernel(settings) { for (var p in settings) { + /* istanbul ignore if */ if (!avalon.ohasOwn.call(settings, p)) continue var val = settings[p] @@ -22,12 +23,14 @@ var plugins = { var openTag = array[0] var closeTag = array[1] /*eslint-disable */ + /* istanbul ignore if */ if (openTag === closeTag) { throw new SyntaxError('openTag!==closeTag') } var test = openTag + 'test' + closeTag var div = avalon.avalonDiv div.innerHTML = test + /* istanbul ignore if */ if (div.innerHTML !== test && div.innerHTML.indexOf('<') > -1) { throw new SyntaxError('此定界符不合法') }