From 71495350d0efd6e68332384c08b8871c3d79091f Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 28 Oct 2015 10:23:32 +0800 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20mq=20=E4=B8=AD?= =?UTF-8?q?=E7=9A=84=20import=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mq/scripts/mq.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mq/scripts/mq.py b/mq/scripts/mq.py index 7ba83fba..411b1033 100644 --- a/mq/scripts/mq.py +++ b/mq/scripts/mq.py @@ -10,7 +10,7 @@ from judge.judger.result import result from submission.models import Submission from problem.models import Problem from utils.cache import get_cache_redis -from contest.models import ContestProblem, Contest, ContestSubmission, CONTEST_UNDERWAY, ContestRank +from contest.models import ContestProblem, Contest, CONTEST_UNDERWAY, ContestRank from account.models import User logger = logging.getLogger("app_info") From 3d6daf0d7c9d369cfe19039ec1e9e25fcc50c082 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 28 Oct 2015 11:10:31 +0800 Subject: [PATCH 2/7] =?UTF-8?q?=E6=96=87=E4=BB=B6=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E8=BF=9B=E5=BA=A6=E5=8F=AA=E6=98=BE=E7=A4=BA=E6=95=B4=E6=95=B0?= =?UTF-8?q?=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/problem/addProblem.js | 2 +- .../src/js/app/admin/problem/editProblem.js | 2 +- static/src/js/lib/avalon/avalon.js | 5844 ----------------- template/src/admin/problem/add_problem.html | 2 +- template/src/admin/problem/edit_problem.html | 2 +- 5 files changed, 4 insertions(+), 5848 deletions(-) delete mode 100644 static/src/js/lib/avalon/avalon.js diff --git a/static/src/js/app/admin/problem/addProblem.js b/static/src/js/app/admin/problem/addProblem.js index 61cb9401..d8f89289 100644 --- a/static/src/js/app/admin/problem/addProblem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -145,7 +145,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }, function (file, percentage) { - vm.uploadProgress = percentage; + vm.uploadProgress = praseInt(percentage * 100); }); var tagAutoCompleteList = []; diff --git a/static/src/js/app/admin/problem/editProblem.js b/static/src/js/app/admin/problem/editProblem.js index 4b936e02..923dd854 100644 --- a/static/src/js/app/admin/problem/editProblem.js +++ b/static/src/js/app/admin/problem/editProblem.js @@ -147,7 +147,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }, function (file, percentage) { - vm.uploadProgress = percentage; + vm.uploadProgress = parseInt(percentage * 100); } ); diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js deleted file mode 100644 index 48c20211..00000000 --- a/static/src/js/lib/avalon/avalon.js +++ /dev/null @@ -1,5844 +0,0 @@ -/*================================================== - Copyright (c) 2013-2015 司徒正美 and other contributors - http://www.cnblogs.com/rubylouvre/ - https://github.com/RubyLouvre - http://weibo.com/jslouvre/ - - Released under the MIT license - avalon.shim.js 1.5.4 built in 2015.10.18 - support IE6+ and other browsers - ==================================================*/ -(function(global, factory) { - - if (typeof module === "object" && typeof module.exports === "object") { - // For CommonJS and CommonJS-like environments where a proper `window` - // is present, execute the factory and get avalon. - // For environments that do not have a `window` with a `document` - // (such as Node.js), expose a factory as module.exports. - // This accentuates the need for the creation of a real `window`. - // e.g. var avalon = require("avalon")(window); - module.exports = global.document ? factory(global, true) : function(w) { - if (!w.document) { - throw new Error("Avalon requires a window with a document") - } - return factory(w) - } - } else { - factory(global) - } - -// Pass this if window is not defined yet -}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ - -/********************************************************************* - * 全局变量及方法 * - **********************************************************************/ -var expose = new Date() - 0 -//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function -var DOC = window.document -var head = DOC.getElementsByTagName("head")[0] //HEAD元素 -var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG -ifGroup.innerHTML = "X" -ifGroup.setAttribute("ms-skip", "1") -ifGroup.className = "avalonHide" -var rnative = /\[native code\]/ //判定是否原生函数 -function log() { - if (window.console && avalon.config.debug) { - // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log - Function.apply.call(console.log, console, arguments) - } -} - - -var subscribers = "$" + expose - -var stopRepeatAssign = false -var nullObject = {} //作用类似于noop,只用于代码防御,千万不要在它上面添加属性 -var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach -var rw20g = /\w+/g -var rcomplexType = /^(?:object|array)$/ -var rsvg = /^\[object SVG\w*Element\]$/ -var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ -var oproto = Object.prototype -var ohasOwn = oproto.hasOwnProperty -var serialize = oproto.toString -var ap = Array.prototype -var aslice = ap.slice -var W3C = window.dispatchEvent -var root = DOC.documentElement -var avalonFragment = DOC.createDocumentFragment() -var cinerator = DOC.createElement("div") -var class2type = {} -"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { - class2type["[object " + name + "]"] = name.toLowerCase() -}) -function scpCompile(array){ - return Function.apply(noop,array) -} -function noop(){} - -function oneObject(array, val) { - if (typeof array === "string") { - array = array.match(rword) || [] - } - var result = {}, - value = val !== void 0 ? val : 1 - for (var i = 0, n = array.length; i < n; i++) { - result[array[i]] = value - } - return result -} - -//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript -var generateID = function (prefix) { - prefix = prefix || "avalon" - return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) -} -function IE() { - if (window.VBArray) { - var mode = document.documentMode - return mode ? mode : window.XMLHttpRequest ? 7 : 6 - } else { - return NaN - } -} -var IEVersion = IE() - -avalon = function (el) { //创建jQuery式的无new 实例化结构 - return new avalon.init(el) -} - - -/*视浏览器情况采用最快的异步回调*/ -avalon.nextTick = new function () {// jshint ignore:line - var tickImmediate = window.setImmediate - var tickObserver = window.MutationObserver - if (tickImmediate) { - return tickImmediate.bind(window) - } - - var queue = [] - function callback() { - var n = queue.length - for (var i = 0; i < n; i++) { - queue[i]() - } - queue = queue.slice(n) - } - - if (tickObserver) { - var node = document.createTextNode("avalon") - new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line - var bool = false - return function (fn) { - queue.push(fn) - bool = !bool - node.data = bool - } - } - - - return function (fn) { - setTimeout(fn, 4) - } -}// jshint ignore:line -/********************************************************************* - * avalon的静态方法定义区 * - **********************************************************************/ -avalon.init = function (el) { - this[0] = this.element = el -} -avalon.fn = avalon.prototype = avalon.init.prototype - -avalon.type = function (obj) { //取得目标的类型 - if (obj == null) { - return String(obj) - } - // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function - return typeof obj === "object" || typeof obj === "function" ? - class2type[serialize.call(obj)] || "object" : - typeof obj -} - -var isFunction = typeof alert === "object" ? function (fn) { - try { - return /^\s*\bfunction\b/.test(fn + "") - } catch (e) { - return false - } -} : function (fn) { - return serialize.call(fn) === "[object Function]" -} -avalon.isFunction = isFunction - -avalon.isWindow = function (obj) { - if (!obj) - return false - // 利用IE678 window == document为true,document == window竟然为false的神奇特性 - // 标准浏览器及IE9,IE10等使用 正则检测 - return obj == obj.document && obj.document != obj //jshint ignore:line -} - -function isWindow(obj) { - return rwindow.test(serialize.call(obj)) -} -if (isWindow(window)) { - avalon.isWindow = isWindow -} -var enu -for (enu in avalon({})) { - break -} -var enumerateBUG = enu !== "0" //IE6下为true, 其他为false -/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ -avalon.isPlainObject = function (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) -} -if (rnative.test(Object.getPrototypeOf)) { - avalon.isPlainObject = function (obj) { - // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 - return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto - } -} -//与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" && !isFunction(target)) { - 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)))) { - - 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 -} - -function _number(a, len) { //用于模拟slice, splice的效果 - a = Math.floor(a) || 0 - return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); -} -avalon.mix({ - rword: rword, - subscribers: subscribers, - version: 1.54, - ui: {}, - log: log, - slice: W3C ? function (nodes, start, end) { - return aslice.call(nodes, start, end) - } : function (nodes, start, end) { - var ret = [] - var len = nodes.length - if (end === void 0) - end = len - if (typeof end === "number" && isFinite(end)) { - start = _number(start, len) - end = _number(end, len) - for (var i = start; i < end; ++i) { - ret[i - start] = nodes[i] - } - } - return ret - }, - noop: noop, - /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ - error: function (str, e) { - throw (e || Error)(str) - }, - /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ - oneObject: oneObject, - /* avalon.range(10) - => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - avalon.range(1, 11) - => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - avalon.range(0, 30, 5) - => [0, 5, 10, 15, 20, 25] - avalon.range(0, -10, -1) - => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] - avalon.range(0) - => []*/ - range: function (start, end, step) { // 用于生成整数数组 - step || (step = 1) - if (end == null) { - end = start || 0 - start = 0 - } - var index = -1, - length = Math.max(0, Math.ceil((end - start) / step)), - result = new Array(length) - while (++index < length) { - result[index] = start - start += step - } - return result - }, - eventHooks: {}, - /*绑定事件*/ - bind: function (el, type, fn, phase) { - var hooks = avalon.eventHooks - var hook = hooks[type] - if (typeof hook === "object") { - type = hook.type || type - phase = hook.phase || !!phase - fn = hook.fn ? hook.fn(el, fn) : fn - } - var callback = W3C ? fn : function (e) { - fn.call(el, fixEvent(e)); - } - if (W3C) { - el.addEventListener(type, callback, phase) - } else { - el.attachEvent("on" + type, callback) - } - return callback - }, - /*卸载事件*/ - unbind: function (el, type, fn, phase) { - var hooks = avalon.eventHooks - var hook = hooks[type] - var callback = fn || noop - if (typeof hook === "object") { - type = hook.type || type - phase = hook.phase || !!phase - } - if (W3C) { - el.removeEventListener(type, callback, phase) - } else { - el.detachEvent("on" + type, callback) - } - }, - /*读写删除元素节点的样式*/ - css: function (node, name, value) { - if (node instanceof avalon) { - node = node[0] - } - var prop = /[_-]/.test(name) ? camelize(name) : name, - fn - name = avalon.cssName(prop) || prop - if (value === void 0 || typeof value === "boolean") { //获取样式 - fn = cssHooks[prop + ":get"] || cssHooks["@:get"] - if (name === "background") { - name = "backgroundColor" - } - var val = fn(node, name) - return value === true ? parseFloat(val) || 0 : val - } else if (value === "") { //请除样式 - node.style[name] = "" - } else { //设置样式 - if (value == null || value !== value) { - return - } - if (isFinite(value) && !avalon.cssNumber[prop]) { - value += "px" - } - fn = cssHooks[prop + ":set"] || cssHooks["@:set"] - fn(node, name, value) - } - }, - /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ - 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 - } - } - } - } - }, - //收集元素的data-{{prefix}}-*属性,并转换为对象 - getWidgetData: function (elem, prefix) { - var raw = avalon(elem).data() - var result = {} - for (var i in raw) { - if (i.indexOf(prefix) === 0) { - result[i.replace(prefix, "").replace(/\w/, function (a) { - return a.toLowerCase() - })] = raw[i] - } - } - return result - }, - Array: { - /*只有当前数组不存在此元素时只添加它*/ - ensure: function (target, item) { - if (target.indexOf(item) === -1) { - return target.push(item) - } - }, - /*移除数组中指定位置的元素,返回布尔表示成功与否*/ - removeAt: function (target, index) { - return !!target.splice(index, 1).length - }, - /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ - remove: function (target, item) { - var index = target.indexOf(item) - if (~index) - return avalon.Array.removeAt(target, index) - return false - } - } -}) - -var bindingHandlers = avalon.bindingHandlers = {} -var bindingExecutors = avalon.bindingExecutors = {} - -var directives = avalon.directives = {} -avalon.directive = function (name, obj) { - bindingHandlers[name] = obj.init = (obj.init || noop) - bindingExecutors[name] = obj.update = (obj.update || noop) - - return directives[name] = obj -} -/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ -function isArrayLike(obj) { - if (!obj) - return false - var n = obj.length - if (n === (n >>> 0)) { //检测length属性是否为非负整数 - var type = serialize.call(obj).slice(8, -1) - if (/(?:regexp|string|function|window|global)$/i.test(type)) - return false - if (type === "Array") - return true - try { - if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 - return /^\s?function/.test(obj.item || obj.callee) - } - return true - } catch (e) { //IE的NodeList直接抛错 - return !obj.window //IE6-8 window - } - } - return false -} - - -// https://github.com/rsms/js-lru -var Cache = new function() {// jshint ignore:line - function LRU(maxLength) { - this.size = 0 - this.limit = maxLength - 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) { - this.tail.newer = entry - entry.older = this.tail - } else { - this.head = entry - } - this.tail = entry - if (this.size === this.limit) { - this.shift() - } else { - this.size++ - } - return value - } - - p.shift = function() { - var entry = this.head - if (entry) { - this.head = this.head.newer - this.head.older = - entry.newer = - entry.older = - this._keymap[entry.key] = void 0 - delete this._keymap[entry.key] //#1029 - } - } - p.get = function(key) { - var entry = this._keymap[key] - if (entry === void 0) - return - if (entry === this.tail) { - return entry.value - } - // HEAD--------------TAIL - // <.older .newer> - // <--- add direction -- - // A B C E - if (entry.newer) { - if (entry === this.head) { - this.head = entry.newer - } - entry.newer.older = entry.older // C <-- E. - } - if (entry.older) { - entry.older.newer = entry.newer // C. --> E - } - entry.newer = void 0 // D --x - entry.older = this.tail // D. --> E - if (this.tail) { - this.tail.newer = entry // E. <-- D - } - this.tail = entry - return entry.value - } - return LRU -}// jshint ignore:line - -/********************************************************************* - * javascript 底层补丁 * - **********************************************************************/ -if (!"司徒正美".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 (!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 (!Array.isArray) { - Array.isArray = function (a) { - return serialize.call(a) === "[object Array]" - } -} - -if (!noop.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) - } - } -} - -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 (!rnative.test([].map)) { - avalon.mix(ap, { - //定位操作,返回数组中第一个等于给定参数的元素的索引值。 - 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') - }) -} -/********************************************************************* - * DOM 底层补丁 * - **********************************************************************/ - -function fixContains(root, el) { - try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 - while ((el = el.parentNode)) - if (el === root) - return true - return false - } catch (e) { - return false - } -} -avalon.contains = fixContains -//IE6-11的文档对象没有contains -if (!DOC.contains) { - DOC.contains = function (b) { - return fixContains(DOC, b) - } -} - -function outerHTML() { - return new XMLSerializer().serializeToString(this) -} - -if (window.SVGElement) { - //safari5+是把contains方法放在Element.prototype上而不是Node.prototype - if (!DOC.createTextNode("x").contains) { - Node.prototype.contains = function (arg) {//IE6-8没有Node对象 - return !!(this.compareDocumentPosition(arg) & 16) - } - } - var svgns = "http://www.w3.org/2000/svg" - var svg = DOC.createElementNS(svgns, "svg") - svg.innerHTML = '' - if (!rsvg.test(svg.firstChild)) { // #409 - function enumerateNode(node, targetNode) {// jshint ignore:line - if (node && node.childNodes) { - var nodes = node.childNodes - for (var i = 0, el; el = nodes[i++]; ) { - if (el.tagName) { - var svg = DOC.createElementNS(svgns, - el.tagName.toLowerCase()) - ap.forEach.call(el.attributes, function (attr) { - svg.setAttribute(attr.name, attr.value) //复制属性 - })// jshint ignore:line - // 递归处理子节点 - enumerateNode(el, svg) - targetNode.appendChild(svg) - } - } - } - } - Object.defineProperties(SVGElement.prototype, { - "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 - enumerable: true, - configurable: true, - get: outerHTML, - set: function (html) { - var tagName = this.tagName.toLowerCase(), - par = this.parentNode, - frag = avalon.parseHTML(html) - // 操作的svg,直接插入 - if (tagName === "svg") { - par.insertBefore(frag, this) - // svg节点的子节点类似 - } else { - var newFrag = DOC.createDocumentFragment() - enumerateNode(frag, newFrag) - par.insertBefore(newFrag, this) - } - par.removeChild(this) - } - }, - "innerHTML": { - enumerable: true, - configurable: true, - get: function () { - var s = this.outerHTML - var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") - var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") - return s.replace(ropen, "").replace(rclose, "") - }, - set: function (html) { - if (avalon.clearHTML) { - avalon.clearHTML(this) - var frag = avalon.parseHTML(html) - enumerateNode(frag, this) - } - } - } - }) - } -} -if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML - HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); -} - - -//============================= event binding ======================= -var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ -function fixEvent(event) { - var ret = {} - for (var i in event) { - ret[i] = event[i] - } - var target = ret.target = event.srcElement - if (event.type.indexOf("key") === 0) { - ret.which = event.charCode != null ? event.charCode : event.keyCode - } else if (rmouseEvent.test(event.type)) { - var doc = target.ownerDocument || DOC - var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement - ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) - ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) - ret.wheelDeltaY = ret.wheelDelta - ret.wheelDeltaX = 0 - } - ret.timeStamp = new Date() - 0 - ret.originalEvent = event - ret.preventDefault = function () { //阻止默认行为 - event.returnValue = false - } - ret.stopPropagation = function () { //阻止事件在DOM树中的传播 - event.cancelBubble = true - } - return ret -} - -var eventHooks = avalon.eventHooks -//针对firefox, chrome修正mouseenter, mouseleave -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(elem, e) - } - } - } - } - }) -} -//针对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 DOC.createElement("input"))) { - eventHooks.input = { - type: "propertychange", - deel: function (elem, fn) { - return function (e) { - if (e.propertyName === "value") { - e.type = "input" - return fn.call(elem, e) - } - } - } - } -} -if (DOC.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 = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" - var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" - eventHooks.mousewheel = { - type: fixWheelType, - fn: function (elem, fn) { - return function (e) { - e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 - e.wheelDeltaX = 0 - if (Object.defineProperty) { - Object.defineProperty(e, "type", { - value: "mousewheel" - }) - } - fn.call(elem, e) - } - } - } -} - - - -/********************************************************************* - * 配置系统 * - **********************************************************************/ - -function kernel(settings) { - for (var p in settings) { - if (!ohasOwn.call(settings, p)) - continue - var val = settings[p] - if (typeof kernel.plugins[p] === "function") { - kernel.plugins[p](val) - } else if (typeof kernel[p] === "object") { - avalon.mix(kernel[p], val) - } else { - kernel[p] = val - } - } - return this -} -var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g - -function escapeRegExp(target) { - //http://stevenlevithan.com/regex/xregexp/ - //将字符串安全格式化为正则表达式的源码 - return (target + "").replace(rregexp, "\\$&") -} - -var plugins = { - interpolate: function (array) { - openTag = array[0] - closeTag = array[1] - if (openTag === closeTag) { - throw new SyntaxError("openTag!==closeTag") - var test = openTag + "test" + closeTag - cinerator.innerHTML = test - if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { - throw new SyntaxError("此定界符不合法") - } - cinerator.innerHTML = "" - } - kernel.openTag = openTag - kernel.closeTag = closeTag - var o = escapeRegExp(openTag), - c = escapeRegExp(closeTag) - rexpr = new RegExp(o + "(.*?)" + c) - rexprg = new RegExp(o + "(.*?)" + c, "g") - rbind = new RegExp(o + ".*?" + c + "|\\sms-") - } -} -kernel.async =true -kernel.debug = true -kernel.plugins = plugins -kernel.plugins['interpolate'](["{{", "}}"]) -kernel.paths = {} -kernel.shim = {} -kernel.maxRepeatSize = 100 -avalon.config = kernel -function $watch(expr, binding) { - var $events = this.$events || (this.$events = {}) - var queue = $events[expr] || ($events[expr] = []) - if (typeof binding === "function") { - var backup = binding - backup.uniqueNumber = Math.random() - binding = { - element: root, - type: "user-watcher", - handler: noop, - vmodels: [this], - expr: expr, - uniqueNumber: backup.uniqueNumber - } - binding.wildcard = /\*/.test(expr) - } - - if (!binding.update) { - if (/\w\.*\B/.test(expr)) { - binding.getter = noop - var host = this - binding.update = function () { - var args = this.fireArgs || [] - if (args[2]) - binding.handler.apply(host, args) - delete this.fireArgs - } - queue.sync = true - avalon.Array.ensure(queue, binding) - } else { - avalon.injectBinding(binding) - } - if (backup) { - binding.handler = backup - } - } else if (!binding.oneTime) { - avalon.Array.ensure(queue, binding) - } - return function () { - binding.update = binding.getter = binding.handler = noop - binding.element = DOC.createElement("a") - } -} - -function $emit(key, args) { - var event = this.$events - if (event && event[key]) { - if (args) { - args[2] = key - } - notifySubscribers(event[key], args) - var parent = this.$up - if (parent) { - if (this.$pathname) { - $emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡 - } - - $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 - } - } else { - parent = this.$up - if (parent) { - var path = this.$pathname + "." + key - var arr = path.split(".") - if (arr.indexOf("*") === -1) { - $emit.call(parent, path, args)//以确切的值往上冒泡 - arr[1] = "*" - $emit.call(parent, arr.join("."), args)//以确切的值往上冒泡 - } else { - $emit.call(parent, path, args)//以确切的值往上冒泡 - } - } - } -} - - -function collectDependency(el, key) { - do { - if (el.$watch) { - var e = el.$events || (el.$events = {}) - var array = e[key] || (e[key] = []) - dependencyDetection.collectDependency(array) - return - } - el = el.$up - if (el) { - key = el.$pathname + "." + key - } else { - break - } - - } while (true) -} - - -function notifySubscribers(subs, args) { - if (!subs) - return - if (new Date() - beginTime > 444 && typeof subs[0] === "object") { - rejectDisposeQueue() - } - var users = [], renders = [] - for (var i = 0, sub; sub = subs[i++]; ) { - if (sub.type === "user-watcher") { - users.push(sub) - } else { - renders.push(sub) - } - - } - if (kernel.async) { - buffer.render()//1 - for (i = 0; sub = renders[i++]; ) { - if (sub.update) { - var uuid = getUid(sub) - if (!buffer.queue[uuid]) { - buffer.queue[uuid] = 1 - buffer.queue.push(sub) - } - } - } - } else { - for (i = 0; sub = renders[i++]; ) { - if (sub.update) { - sub.update()//最小化刷新DOM树 - } - } - } - for (i = 0; sub = users[i++]; ) { - if (args && args[2] === sub.expr || sub.wildcard) { - sub.fireArgs = args - } - sub.update() - } -} -//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) -var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 -avalon.define = function (source) { - var $id = source.$id - if (!$id) { - log("warning: vm必须指定$id") - } - var vmodel = modelFactory(source) - vmodel.$id = $id - return VMODELS[$id] = vmodel -} - -//一些不需要被监听的属性 -var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors") -var defineProperty = Object.defineProperty -var canHideOwn = true -//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 -//标准浏览器使用__defineGetter__, __defineSetter__实现 -try { - defineProperty({}, "_", { - value: "x" - }) - var defineProperties = Object.defineProperties -} catch (e) { - canHideOwn = false -} - -function modelFactory(source, options) { - options = options || {} - options.watch = true - return observeObject(source, options) -} - -//监听对象属性值的变化(注意,数组元素不是数组的属性),通过对劫持当前对象的访问器实现 -//监听对象或数组的结构变化, 对对象的键值对进行增删重排, 或对数组的进行增删重排,都属于这范畴 -// 通过比较前后代理VM顺序实现 -function Component() { -} - -function observeObject(source, options) { - if (!source || (source.$id && source.$accessors)) { - return source - } - //source为原对象,不能是元素节点或null - //options,可选,配置对象,里面有old, force, watch这三个属性 - options = options || nullObject - var force = options.force || nullObject - var old = options.old - var oldAccessors = old && old.$accessors || nullObject - var $vmodel = new Component() //要返回的对象, 它在IE6-8下可能被偷龙转凤 - var accessors = {} //监控属性 - var hasOwn = {} - var skip = [] - var simple = [] - var $skipArray = {} - if (source.$skipArray) { - $skipArray = oneObject(source.$skipArray) - delete source.$skipArray - } - //处理计算属性 - var computed = source.$computed - if (computed) { - delete source.$computed - for (var name in computed) { - hasOwn[name] = true; - (function (key, value) { - var old - accessors[key] = { - get: function () { - return old = value.get.call(this) - }, - set: function (x) { - if (typeof value.set === "function") { - var older = old - value.set.call(this, x) - var newer = this[key] - if (this.$fire && (newer !== older)) { - this.$fire(key, newer, older) - } - } - }, - enumerable: true, - configurable: true - } - })(name, computed[name])// jshint ignore:line - } - } - - - for (name in source) { - var value = source[name] - if (!$$skipArray[name]) - hasOwn[name] = true - if (typeof value === "function" || (value && value.nodeType) || - (!force[name] && (name.charAt(0) === "$" || $$skipArray[name] || $skipArray[name]))) { - skip.push(name) - } else if (isComputed(value)) { - log("warning:计算属性建议放在$computed对象中统一定义"); - (function (key, value) { - var old - accessors[key] = { - get: function () { - return old = value.get.call(this) - }, - set: function (x) { - if (typeof value.set === "function") { - var older = old - value.set.call(this, x) - var newer = this[key] - if (this.$fire && (newer !== older)) { - this.$fire(key, newer, older) - } - } - }, - enumerable: true, - configurable: true - } - })(name, value)// jshint ignore:line - } else { - simple.push(name) - if (oldAccessors[name]) { - accessors[name] = oldAccessors[name] - } else { - accessors[name] = makeGetSet(name, value) - } - } - } - - - accessors["$model"] = $modelDescriptor - $vmodel = defineProperties($vmodel, accessors, source) - function trackBy(name) { - return hasOwn[name] === true - } - skip.forEach(function (name) { - $vmodel[name] = source[name] - }) - - /* jshint ignore:start */ - hideProperty($vmodel, "$id", "anonymous") - hideProperty($vmodel, "$up", old ? old.$up : null) - hideProperty($vmodel, "$track", Object.keys(hasOwn)) - hideProperty($vmodel, "$active", false) - hideProperty($vmodel, "$pathname", old ? old.$pathname : "") - hideProperty($vmodel, "$accessors", accessors) - hideProperty($vmodel, "hasOwnProperty", trackBy) - if (options.watch) { - hideProperty($vmodel, "$watch", function () { - return $watch.apply($vmodel, arguments) - }) - hideProperty($vmodel, "$fire", function (path, a) { - if(path.indexOf("all!") === 0 ){ - var ee = path.slice(4) - for(var i in avalon.vmodels){ - var v = avalon.vmodels[i] - v.$fire && v.$fire.apply(v, [ee, a]) - } - }else{ - $emit.call($vmodel, path, [a]) - } - }) - } - /* jshint ignore:end */ - - //必须设置了$active,$events - simple.forEach(function (name) { - var val = $vmodel[name] = source[name] - if (val && typeof val === "object") { - val.$up = $vmodel - val.$pathname = name - } - $emit.call($vmodel, name) - }) - for (name in computed) { - value = $vmodel[name] - } - $vmodel.$active = true - return $vmodel -} -/* - 新的VM拥有如下私有属性 - $id: vm.id - $events: 放置$watch回调与绑定对象 - $watch: 增强版$watch - $fire: 触发$watch回调 - $track:一个数组,里面包含用户定义的所有键名 - $active:boolean,false时防止依赖收集 - $model:返回一个纯净的JS对象 - $accessors:放置所有读写器的数据描述对象 - $up:返回其上级对象 - $pathname:返回此对象在上级对象的名字,注意,数组元素的$pathname为空字符串 - ============================= - $skipArray:用于指定不可监听的属性,但VM生成是没有此属性的 - */ -function isComputed(val) {//speed up! - if (val && typeof val === "object") { - for (var i in val) { - if (i !== "get" && i !== "set") { - return false - } - } - return typeof val.get === "function" - } -} -function makeGetSet(key, value) { - var childVm - value = NaN - return { - get: function () { - if (this.$active) { - collectDependency(this, key) - } - return value - }, - set: function (newVal) { - if (value === newVal) - return - var oldValue = value - childVm = observe(newVal, value) - if (childVm) { - value = childVm - } else { - childVm = void 0 - value = newVal - } - - if (Object(childVm) === childVm) { - childVm.$pathname = key - childVm.$up = this - } - if (this.$active) { - $emit.call(this, key, [value, oldValue]) - } - }, - enumerable: true, - configurable: true - } -} - -function observe(obj, old, hasReturn, watch) { - if (Array.isArray(obj)) { - return observeArray(obj, old, watch) - } else if (avalon.isPlainObject(obj)) { - if (old) { - var keys = getKeys(obj) - var keys2 = getKeys(old) - if (keys.join(";") === keys2.join(";")) { - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - old[i] = obj[i] - } - } - return old - } - old.$active = false - } - return observeObject(obj, { - old: old, - watch: watch - }) - } - if (hasReturn) { - return obj - } -} -var getKeys = rnative.test(Object.key) ? Object.key : function (a) { - var ret = [] - for (var i in a) { - if (a.hasOwnProperty(i) && !$$skipArray[i]) { - ret.push(i) - } - } - return ret -} -function observeArray(array, old, watch) { - if (old) { - var args = [0, old.length].concat(array) - old.splice.apply(old, args) - return old - } else { - for (var i in newProto) { - array[i] = newProto[i] - } - - hideProperty(array, "$up", null) - hideProperty(array, "$pathname", "") - hideProperty(array, "$track", createTrack(array.length)) - - array._ = observeObject({ - length: NaN - }, { - watch: true - }) - array._.length = array.length - array._.$watch("length", function (a, b) { - $emit.call(array.$up, array.$pathname + ".length", [a, b]) - }) - if (watch) { - hideProperty(array, "$watch", function () { - return $watch.apply(array, arguments) - }) - } - - if (W3C) { - Object.defineProperty(array, "$model", $modelDescriptor) - } else { - array.$model = toJson(array) - } - for (var j = 0, n = array.length; j < n; j++) { - var el = array[j] = observe(array[j], 0, 1, 1) - if (Object(el) === el) {//#1077 - el.$up = array - } - } - - return array - } -} - -function hideProperty(host, name, value) { - if (canHideOwn) { - Object.defineProperty(host, name, { - value: value, - writable: true, - enumerable: false, - configurable: true - }) - } else { - host[name] = value - } -} - -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) - } - } - return obj - } - return val -} - -var $modelDescriptor = { - get: function () { - return toJson(this) - }, - set: noop, - enumerable: false, - configurable: true -} - - -//===================修复浏览器对Object.defineProperties的支持================= -if (!canHideOwn) { - 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 - } - defineProperties = function (obj, descs) { - for (var prop in descs) { - if (descs.hasOwnProperty(prop)) { - defineProperty(obj, prop, descs[prop]) - } - } - return obj - } - } - if (IEVersion) { - 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 = {} - - //添加访问器属性 - 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 = generateID("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 //得到其产品 - } - } -} - -/********************************************************************* - * 监控数组(与ms-each, ms-repeat配合使用) * - **********************************************************************/ - -var arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice'] -var arrayProto = Array.prototype -var newProto = { - notify: function () { - $emit.call(this.$up, this.$pathname) - }, - set: function (index, val) { - if (((index >>> 0) === index) && this[index] !== val) { - if (index > this.length) { - throw Error(index + "set方法的第一个参数不能大于原数组长度") - } - $emit.call(this.$up, this.$pathname + ".*", [val, this[index]]) - 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 [] - }, - size: function () { //取得数组长度,这个函数可以同步视图,length不能 - return this._.length - }, - removeAll: function (all) { //移除N个元素 - if (Array.isArray(all)) { - for (var i = this.length - 1; i >= 0; i--) { - if (all.indexOf(this[i]) !== -1) { - _splice.call(this.$track, i, 1) - _splice.call(this, i, 1) - - } - } - } else if (typeof all === "function") { - for (i = this.length - 1; i >= 0; i--) { - var el = this[i] - if (all(el, i)) { - _splice.call(this.$track, i, 1) - _splice.call(this, i, 1) - - } - } - } else { - _splice.call(this.$track, 0, this.length) - _splice.call(this, 0, this.length) - - } - if (!W3C) { - this.$model = toJson(this) - } - this.notify() - this._.length = this.length - }, - clear: function () { - return this.removeAll() - } -} -var _splice = arrayProto.splice -arrayMethods.forEach(function (method) { - var original = arrayProto[method] - newProto[method] = function () { - // 继续尝试劫持数组元素的属性 - var args = [] - for (var i = 0, n = arguments.length; i < n; i++) { - args[i] = observe(arguments[i], 0, 1, 1) - } - var result = original.apply(this, args) - addTrack(this.$track, method, args) - if (!W3C) { - this.$model = toJson(this) - } - this.notify() - this._.length = this.length - return result - } -}) - -"sort,reverse".replace(rword, function (method) { - newProto[method] = function () { - var oldArray = this.concat() //保持原来状态的旧数组 - var newArray = this - var mask = Math.random() - var indexes = [] - var hasSort = false - arrayProto[method].apply(newArray, arguments) //排序 - for (var i = 0, n = oldArray.length; i < n; i++) { - var neo = newArray[i] - var old = oldArray[i] - if (neo === old) { - indexes.push(i) - } else { - var index = oldArray.indexOf(neo) - indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 - oldArray[index] = mask //屏蔽已经找过的元素 - hasSort = true - } - } - if (hasSort) { - sortByIndex(this.$track, indexes) - if (!W3C) { - this.$model = toJson(this) - } - this.notify() - } - return this - } -}) - -function sortByIndex(array, indexes) { - var map = {}; - for (var i = 0, n = indexes.length; i < n; i++) { - map[i] = array[i] - var j = indexes[i] - if (j in map) { - array[i] = map[j] - delete map[j] - } else { - array[i] = array[j] - } - } -} - -function createTrack(n) { - var ret = [] - for (var i = 0; i < n; i++) { - ret[i] = generateID("$proxy$each") - } - return ret -} - -function addTrack(track, method, args) { - switch (method) { - case 'push': - case 'unshift': - args = createTrack(args.length) - break - case 'splice': - if (args.length > 2) { - // 0, 5, a, b, c --> 0, 2, 0 - // 0, 5, a, b, c, d, e, f, g--> 0, 0, 3 - var del = args[1] - var add = args.length - 2 - // args = [args[0], Math.max(del - add, 0)].concat(createTrack(Math.max(add - del, 0))) - args = [args[0], args[1]].concat(createTrack(args.length - 2)) - } - break - } - Array.prototype[method].apply(track, args) -} -/********************************************************************* - * 依赖调度系统 * - **********************************************************************/ -//检测两个对象间的依赖关系 -var dependencyDetection = (function () { - var outerFrames = [] - var currentFrame - return { - begin: function (binding) { - //accessorObject为一个拥有callback的对象 - outerFrames.push(currentFrame) - currentFrame = binding - }, - end: function () { - currentFrame = outerFrames.pop() - }, - collectDependency: function (array) { - if (currentFrame) { - //被dependencyDetection.begin调用 - currentFrame.callback(array) - } - } - }; -})() -//将绑定对象注入到其依赖项的订阅数组中 -var roneval = /^on$/ - -function returnRandom() { - return new Date() - 0 -} - -avalon.injectBinding = function (binding) { - - binding.handler = binding.handler || directives[binding.type].update || noop - binding.update = function () { - var begin = false - if (!binding.getter) { - begin = true - dependencyDetection.begin({ - callback: function (array) { - injectDependency(array, binding) - } - }) - binding.getter = parseExpr(binding.expr, binding.vmodels, binding) - binding.observers.forEach(function (a) { - a.v.$watch(a.p, binding) - }) - delete binding.observers - } - try { - var args = binding.fireArgs, a, b - delete binding.fireArgs - if (!args) { - if (binding.type === "on") { - a = binding.getter + "" - } else { - a = binding.getter.apply(0, binding.args) - } - } else { - a = args[0] - b = args[1] - - } - b = typeof b === "undefined" ? binding.oldValue : b - if (binding._filters) { - a = filters.$filter.apply(0, [a].concat(binding._filters)) - } - if (binding.signature) { - var xtype = avalon.type(a) - if (xtype !== "array" && xtype !== "object") { - throw Error("warning:" + binding.expr + "只能是对象或数组") - } - binding.xtype = xtype - var vtrack = getProxyIds(binding.proxies || [], xtype) - var mtrack = a.$track || (xtype === "array" ? createTrack(a.length) : - Object.keys(a)) - binding.track = mtrack - if (vtrack !== mtrack.join(";")) { - binding.handler(a, b) - binding.oldValue = 1 - } - } else if (Array.isArray(a) ? a.length !== (b && b.length) : false) { - binding.handler(a, b) - binding.oldValue = a.concat() - } else if (!("oldValue" in binding) || a !== b) { - binding.handler(a, b) - binding.oldValue = a - } - } catch (e) { - delete binding.getter - log("warning:exception throwed in [avalon.injectBinding] ", e) - var node = binding.element - if (node && node.nodeType === 3) { - node.nodeValue = openTag + (binding.oneTime ? "::" : "") + binding.expr + closeTag - } - } finally { - begin && dependencyDetection.end() - - } - } - binding.update() -} - - -//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 -function injectDependency(list, binding) { - if (binding.oneTime) - return - if (list && avalon.Array.ensure(list, binding) && binding.element) { - injectDisposeQueue(binding, list) - if (new Date() - beginTime > 444) { - rejectDisposeQueue() - } - } -} - - -function getProxyIds(a, isArray) { - var ret = [] - for (var i = 0, el; el = a[i++]; ) { - ret.push(isArray ? el.$id : el.$key) - } - return ret.join(";") -} - -/********************************************************************* - * 定时GC回收机制 * - **********************************************************************/ -var disposeCount = 0 -var disposeQueue = avalon.$$subscribers = [] -var beginTime = new Date() -var oldInfo = {} - -function getUid(data) { //IE9+,标准浏览器 - if (!data.uniqueNumber) { - var elem = data.element - if (elem) { - if (elem.nodeType !== 1) { - //如果是注释节点,则data.pos不存在,当一个元素下有两个注释节点就会出问题 - data.uniqueNumber = data.type + "-" + getUid(elem.parentNode) + "-" + (++disposeCount) - } else { - data.uniqueNumber = data.name + "-" + getUid(elem) - } - } else { - data.uniqueNumber = ++disposeCount - } - } - return data.uniqueNumber -} - -//添加到回收列队中 -function injectDisposeQueue(data, list) { - var lists = data.lists || (data.lists = []) - var uuid = getUid(data) - avalon.Array.ensure(lists, list) - list.$uuid = list.$uuid || generateID() - if (!disposeQueue[uuid]) { - disposeQueue[uuid] = 1 - disposeQueue.push(data) - } -} - -function rejectDisposeQueue(data) { - - var i = disposeQueue.length - var n = i - var allTypes = [] - var iffishTypes = {} - var newInfo = {} - //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 - while (data = disposeQueue[--i]) { - var type = data.type - if (newInfo[type]) { - newInfo[type]++ - } else { - newInfo[type] = 1 - allTypes.push(type) - } - } - var diff = false - allTypes.forEach(function (type) { - if (oldInfo[type] !== newInfo[type]) { - iffishTypes[type] = 1 - diff = true - } - }) - i = n - if (diff) { - while (data = disposeQueue[--i]) { - if (data.element === null) { - disposeQueue.splice(i, 1) - continue - } - if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 - disposeQueue.splice(i, 1) - delete disposeQueue[data.uniqueNumber] - var lists = data.lists - for (var k = 0, list; list = lists[k++]; ) { - avalon.Array.remove(lists, list) - avalon.Array.remove(list, data) - } - disposeData(data) - } - } - } - oldInfo = newInfo - beginTime = new Date() -} - -function disposeData(data) { - delete disposeQueue[data.uniqueNumber] // 先清除,不然无法回收了 - data.element = null - data.rollback && data.rollback() - for (var key in data) { - data[key] = null - } -} - -function shouldDispose(el) { - try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 - var fireError = el.parentNode.nodeType - } catch (e) { - return true - } - if (el.ifRemove) { - // 如果节点被放到ifGroup,才移除 - if (!root.contains(el.ifRemove) && (ifGroup === el.parentNode)) { - el.parentNode && el.parentNode.removeChild(el) - return true - } - } - return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) -} - - - -/************************************************************************ - * HTML处理(parseHTML, innerHTML, clearHTML) * - ************************************************************************/ -// We have to close these tags to support XHTML -var tagHooks = { - area: [1, "", ""], - param: [1, "", ""], - col: [2, "", "
"], - legend: [1, "
", "
"], - option: [1, ""], - thead: [1, "", "
"], - tr: [2, "", "
"], - td: [3, "", "
"], - g: [1, '', ''], - //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 - _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 -} -tagHooks.th = tagHooks.td -tagHooks.optgroup = tagHooks.option -tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead -String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { - tagHooks[tag] = tagHooks.g //处理SVG -}) -var rtagName = /<([\w:]+)/ //取得其tagName -var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig -var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig -var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) -var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 -var script = DOC.createElement("script") -var rhtml = /<|&#?\w+;/ -avalon.parseHTML = function (html) { - var fragment = avalonFragment.cloneNode(false) - if (typeof html !== "string") { - return fragment - } - if (!rhtml.test(html)) { - fragment.appendChild(DOC.createTextNode(html)) - return fragment - } - html = html.replace(rxhtml, "<$1>").trim() - var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), - //取得其标签名 - wrap = tagHooks[tag] || tagHooks._default, - wrapper = cinerator, - firstChild, neo - if (!W3C) { //fix IE - html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 - } - wrapper.innerHTML = wrap[1] + html + wrap[2] - var els = wrapper.getElementsByTagName("script") - if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 - for (var i = 0, el; el = els[i++];) { - if (scriptTypes[el.type]) { - //以偷龙转凤方式恢复执行脚本功能 - neo = script.cloneNode(false) //FF不能省略参数 - ap.forEach.call(el.attributes, function (attr) { - if (attr && attr.specified) { - neo[attr.name] = attr.value //复制其属性 - neo.setAttribute(attr.name, attr.value) - } - }) // jshint ignore:line - neo.text = el.text - el.parentNode.replaceChild(neo, el) //替换节点 - } - } - } - if (!W3C) { //fix IE - var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild - if (target && target.tagName === "TABLE" && tag !== "tbody") { - //IE6-7处理 --> , - // --> , - // -->
- for (els = target.childNodes, i = 0; el = els[i++];) { - if (el.tagName === "TBODY" && !el.innerHTML) { - target.removeChild(el) - break - } - } - } - els = wrapper.getElementsByTagName("br") - var n = els.length - while (el = els[--n]) { - if (el.className === "msNoScope") { - el.parentNode.removeChild(el) - } - } - for (els = wrapper.all, i = 0; el = els[i++];) { //fix VML - if (isVML(el)) { - fixVML(el) - } - } - } - //移除我们为了符合套嵌关系而添加的标签 - for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {} - while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! - fragment.appendChild(firstChild) - } - return fragment -} - -function isVML(src) { - var nodeName = src.nodeName - return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" -} - -function fixVML(node) { - if (node.currentStyle.behavior !== "url(#default#VML)") { - node.style.behavior = "url(#default#VML)" - node.style.display = "inline-block" - node.style.zoom = 1 //hasLayout - } -} -avalon.innerHTML = function (node, html) { - if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { - try { - node.innerHTML = html - return - } catch (e) {} - } - var a = this.parseHTML(html) - this.clearHTML(node).appendChild(a) -} -avalon.clearHTML = function (node) { - node.textContent = "" - while (node.firstChild) { - node.removeChild(node.firstChild) - } - return node -} - -/********************************************************************* - * avalon的原型方法定义区 * - **********************************************************************/ - -function hyphen(target) { - //转换为连字符线风格 - return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() -} - -function camelize(target) { - //提前判断,提高getStyle等的效率 - if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { - return target - } - //转换为驼峰风格 - return target.replace(/[-_][^-_]/g, function (match) { - return match.charAt(1).toUpperCase() - }) -} - -var fakeClassListMethods = { - _toString: function () { - var node = this.node - var cls = node.className - var str = typeof cls === "string" ? cls : cls.baseVal - return str.split(/\s+/).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 (rsvg.test(node)) { - //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 - node.setAttribute("class", cls) - } else { - node.className = cls - } - } //toggle存在版本差异,因此不使用它 -} - -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 -} - - -"add,remove".replace(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 && el.nodeType === 1) { - cls.replace(/\S+/g, 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 className, i = 0 - var classNames = String(value).split(/\s+/) - var isBool = typeof stateVal === "boolean" - while ((className = classNames[i++])) { - var state = isBool ? stateVal : !this.hasClass(className) - this[state ? "addClass" : "removeClass"](className) - } - return this - }, - attr: function (name, value) { - if (arguments.length === 2) { - this[0].setAttribute(name, value) - return this - } else { - return this[0].getAttribute(name) - } - }, - data: function (name, value) { - name = "data-" + hyphen(name || "") - switch (arguments.length) { - case 2: - this.attr(name, value) - return this - case 1: - var val = this.attr(name) - return parseData(val) - case 0: - var ret = {} - ap.forEach.call(this[0].attributes, function (attr) { - if (attr) { - name = attr.name - if (!name.indexOf("data-")) { - name = camelize(name.slice(5)) - ret[name] = parseData(attr.value) - } - } - }) - return ret - } - }, - removeData: function (name) { - name = "data-" + hyphen(name) - this[0].removeAttribute(name) - return this - }, - 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 - }, - position: function () { - var offsetParent, offset, - elem = this[0], - parentOffset = { - top: 0, - left: 0 - } - if (!elem) { - return - } - 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) - } - }, - offsetParent: function () { - var offsetParent = this[0].offsetParent - while (offsetParent && avalon.css(offsetParent, "position") === "static") { - offsetParent = offsetParent.offsetParent; - } - return avalon(offsetParent || root) - }, - bind: function (type, fn, phase) { - if (this[0]) { //此方法不会链 - return avalon.bind(this[0], type, fn, phase) - } - }, - unbind: function (type, fn, phase) { - if (this[0]) { - avalon.unbind(this[0], type, fn, phase) - } - return this - }, - 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 - } -}) - -function parseData(data) { - try { - if (typeof data === "object") - return data - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data - } catch (e) {} - return data -} -var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - 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 = window.JSON ? 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) - } - return data -} - -//生成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 - } - } - } -}) - -function getWindow(node) { - return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; -} -//=============================css相关======================= -var cssHooks = avalon.cssHooks = {} -var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] -var cssMap = { - "float": W3C ? "cssFloat" : "styleFloat" -} -avalon.cssNumber = 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 -} -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) {} -} -if (window.getComputedStyle) { - 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] //其他浏览器需要我们手动取内联样式 - } - } - 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 = !!window.XDomainRequest - 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(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 - 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) - } - } -} -"Width,Height".replace(rword, function (name) { //fix 481 - var method = name.toLowerCase(), - 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) { //取得窗口尺寸 - 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) - } - } - 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 - } -} - -//==================================val相关============================ - -function getValType(elem) { - var ret = elem.tagName.toLowerCase() - return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret -} -var roption = /^]+))?)*\s+value[\s=]/i -var valHooks = { - "option:get": IEVersion ? 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] - //旧式IE在reset后不会改变selected,需要改用i === index判定 - //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable - //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 - if ((option.selected || i === index) && !option.disabled) { - value = getter(option) - if (one) { - return value - } - //收集所有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 - } - } -} - -var keyMap = {} -var keys = ["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", /*保留字*/ - "arguments,let,yield,undefined"].join(",") -keys.replace(/\w+/g, function (a) { - keyMap[a] = true -}) -var ridentStart = /[a-z_$]/i -var rwhiteSpace = /[\s\uFEFF\xA0]/ -function getIdent(input, lastIndex) { - var result = [] - var subroutine = !!lastIndex - lastIndex = lastIndex || 0 - - //将表达式中的标识符抽取出来 - var state = "unknown" - var variable = "" - for (var i = 0; i < input.length; i++) { - var c = input.charAt(i) - if (c === "'" || c === '"') {//字符串开始 - if (state === "unknown") { - state = c - } else if (state === c) {//字符串结束 - state = "unknown" - } - } else if (c === "\\") { - if (state === "'" || state === '"') { - i++ - } - } else if (ridentStart.test(c)) {//碰到标识符 - if (state === "unknown") { - state = "variable" - variable = c - } else if (state === "maybePath") { - variable = result.pop() - variable += "." + c - state = "variable" - } else if (state === "variable") { - variable += c - } - } else if (/\w/.test(c)) { - if (state === "variable") { - variable += c - } - } else if (c === ".") { - if (state === "variable") { - if (variable) { - result.push(variable) - variable = "" - state = "maybePath" - } - } - } else if (c === "[") { - if (state === "variable" || state === "maybePath") { - if (variable) {//如果前面存在变量,收集它 - result.push(variable) - variable = "" - } - var lastLength = result.length - var last = result[lastLength - 1] - var innerResult = getIdent(input.slice(i), i) - if (innerResult.length) {//如果括号中存在变量,那么这里添加通配符 - result[lastLength - 1] = last + ".*" - result = innerResult.concat(result) - } else { //如果括号中的东西是确定的,直接转换为其子属性 - var content = input.slice(i + 1, innerResult.i) - try { - var text = (scpCompile(["return " + content]))() - result[lastLength - 1] = last + "." + text - } catch (e) { - } - } - state = "maybePath"//]后面可能还接东西 - i = innerResult.i - } - } else if (c === "]") { - if (subroutine) { - result.i = i + lastIndex - addVar(result, variable) - return result - } - } else if (rwhiteSpace.test(c) && c !== "\r" && c !== "\n") { - if (state === "variable") { - if (addVar(result, variable)) { - state = "maybePath" // aaa . bbb 这样的情况 - } - variable = "" - } - } else { - addVar(result, variable) - state = "unknown" - variable = "" - } - } - addVar(result, variable) - return result -} -function addVar(array, element) { - if (element && !keyMap[element]) { - array.push(element) - return true - } -} -function addAssign(vars, vmodel, name, binding) { - var ret = [], - prefix = " = " + name + "." - for (var i = vars.length, prop; prop = vars[--i]; ) { - var arr = prop.split("."), a - var first = arr[0] - while (a = arr.shift()) { - if (vmodel.hasOwnProperty(a)) { - ret.push(first + prefix + first) - - binding.observers.push({ - v: vmodel, - p: prop - }) - - vars.splice(i, 1) - } - } - } - return ret -} -var rproxy = /(\$proxy\$[a-z]+)\d+$/ -var variablePool = new Cache(218) -//缓存求值函数,以便多次利用 -var evaluatorPool = new Cache(128) - -function getVars(expr) { - expr = expr.trim() - var ret = variablePool.get(expr) - if (ret) { - return ret.concat() - } - var array = getIdent(expr) - var uniq = {} - var result = [] - for (var i = 0, el; el = array[i++]; ) { - if (!uniq[el]) { - uniq[el] = 1 - result.push(el) - } - } - return variablePool.put(expr, result).concat() -} - -function parseExpr(expr, vmodels, binding) { - var filters = binding.filters - if (typeof filters === "string" && filters.trim() && !binding._filters) { - binding._filters = parseFilter(filters.trim()) - } - - var vars = getVars(expr) - - var expose = new Date() - 0 - var assigns = [] - var names = [] - var args = [] - binding.observers = [] - for (var i = 0, sn = vmodels.length; i < sn; i++) { - if (vars.length) { - var name = "vm" + expose + "_" + i - names.push(name) - args.push(vmodels[i]) - assigns.push.apply(assigns, addAssign(vars, vmodels[i], name, binding)) - } - } - binding.args = args - var dataType = binding.type - var exprId = vmodels.map(function (el) { - return String(el.$id).replace(rproxy, "$1") - }) + expr + dataType - var getter = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 - if (getter) { - if (dataType === "duplex") { - var setter = evaluatorPool.get(exprId + "setter") - binding.setter = setter.apply(setter, binding.args) - } - return binding.getter = getter - } - - if (!assigns.length) { - assigns.push("fix" + expose) - } - - if (dataType === "duplex") { - var nameOne = {} - assigns.forEach(function (a) { - var arr = a.split("=") - nameOne[arr[0].trim()] = arr[1].trim() - }) - expr = expr.replace(/[\$\w]+/, function (a) { - return nameOne[a] ? nameOne[a] : a - }) - /* jshint ignore:start */ - var fn2 = scpCompile(names.concat("'use strict';" + - "return function(vvv){" + expr + " = vvv\n}\n")) - /* jshint ignore:end */ - evaluatorPool.put(exprId + "setter", fn2) - binding.setter = fn2.apply(fn2, binding.args) - } - - if (dataType === "on") { //事件绑定 - if (expr.indexOf("(") === -1) { - expr += ".call(this, $event)" - } else { - expr = expr.replace("(", ".call(this,") - } - names.push("$event") - expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") - var lastIndex = expr.lastIndexOf("\nreturn") - var header = expr.slice(0, lastIndex) - var footer = expr.slice(lastIndex) - expr = header + "\n" + footer - } else { - expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") - } - /* jshint ignore:start */ - getter = scpCompile(names.concat("'use strict';\nvar " + - assigns.join(",\n") + expr)) - /* jshint ignore:end */ - - return evaluatorPool.put(exprId, getter) - -} -//======== - -function stringifyExpr(code) { - var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况 - if (hasExpr) { - var array = scanExpr(code) - if (array.length === 1) { - return array[0].expr - } - return array.map(function (el) { - return el.type ? "(" + el.expr + ")" : quote(el.expr) - }).join(" + ") - } else { - return code - } -} - -avalon.parseExprProxy = parseExpr - -var rthimRightParentheses = /\)\s*$/ -var rthimOtherParentheses = /\)\s*\|/g -var rquoteFilterName = /\|\s*([$\w]+)/g -var rpatchBracket = /"\s*\["/g -var rthimLeftParentheses = /"\s*\(/g -function parseFilter(filters) { - filters = filters - .replace(rthimRightParentheses, "")//处理最后的小括号 - .replace(rthimOtherParentheses, function () {//处理其他小括号 - return "],|" - }) - .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 - return "[" + quote(b) - }) - .replace(rpatchBracket, function () { - return '"],["' - }) - .replace(rthimLeftParentheses, function () { - return '",' - }) + "]" - /* jshint ignore:start */ - return scpCompile(["return [" + filters + "]"])() - /* jshint ignore:end */ - -} -/********************************************************************* - * 编译系统 * - **********************************************************************/ -var meta = { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"': '\\"', - '\\': '\\\\' -} -var quote = window.JSON && JSON.stringify || function(str) { - return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { - var c = meta[a]; - return typeof c === 'string' ? c : - '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' -} -/********************************************************************* - * 扫描系统 * - **********************************************************************/ - -avalon.scan = function (elem, vmodel) { - elem = elem || root - var vmodels = vmodel ? [].concat(vmodel) : [] - scanTag(elem, vmodels) -} - -//http://www.w3.org/TR/html5/syntax.html#void-elements -var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) - -function checkScan(elem, callback, innerHTML) { - var id = setTimeout(function () { - var currHTML = elem.innerHTML - clearTimeout(id) - if (currHTML === innerHTML) { - callback() - } else { - checkScan(elem, callback, currHTML) - } - }) -} - - -function createSignalTower(elem, vmodel) { - var id = elem.getAttribute("avalonctrl") || vmodel.$id - elem.setAttribute("avalonctrl", id) - if (vmodel.$events) { - vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' - } -} - -var getBindingCallback = function (elem, name, vmodels) { - var callback = elem.getAttribute(name) - if (callback) { - for (var i = 0, vm; vm = vmodels[i++]; ) { - if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { - return vm[callback] - } - } - } -} - -function executeBindings(bindings, vmodels) { - for (var i = 0, binding; binding = bindings[i++]; ) { - binding.vmodels = vmodels - directives[binding.type].init(binding) - - avalon.injectBinding(binding) - if (binding.getter && binding.element.nodeType === 1) { //移除数据绑定,防止被二次解析 - //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 - binding.element.removeAttribute(binding.name) - } - } - bindings.length = 0 -} - -//https://github.com/RubyLouvre/avalon/issues/636 -var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { - var node = elem.firstChild, text - while (node) { - var aaa = node.nextSibling - if (node.nodeType === 3) { - if (text) { - text.nodeValue += node.nodeValue - elem.removeChild(node) - } else { - text = node - } - } else { - text = null - } - node = aaa - } -} : 0 -var roneTime = /^\s*::/ -var rmsAttr = /ms-(\w+)-?(.*)/ - -var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") -var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled,href,src") -function bindingSorter(a, b) { - return a.priority - b.priority -} - -function scanAttr(elem, vmodels, match) { - var scanNode = true - if (vmodels.length) { - var attributes = getAttributes ? getAttributes(elem) : elem.attributes - var bindings = [] - var uniq = {} - for (var i = 0, attr; attr = attributes[i++]; ) { - var name = attr.name - if (uniq[name]) {//IE8下ms-repeat,ms-with BUG - continue - } - uniq[name] = 1 - if (attr.specified) { - if (match = name.match(rmsAttr)) { - //如果是以指定前缀命名的 - var type = match[1] - var param = match[2] || "" - var value = attr.value - if (events[type]) { - param = type - type = "on" - } else if (obsoleteAttrs[type]) { - param = type - type = "attr" - name = "ms-" + type + "-" + param - log("warning!请改用" + name + "代替" + attr.name + "!") - } - if (directives[type]) { - var newValue = value.replace(roneTime, "") - var oneTime = value !== newValue - var binding = { - type: type, - param: param, - element: elem, - name: name, - expr: newValue, - oneTime: oneTime, - uniqueNumber: attr.name + "-" + getUid(elem), - //chrome与firefox下Number(param)得到的值不一样 #855 - priority: (directives[type].priority || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0) - } - if (type === "html" || type === "text") { - - var filters = getToken(value).filters - binding.expr = binding.expr.replace(filters, "") - - binding.filters = filters.replace(rhasHtml, function () { - binding.type = "html" - binding.group = 1 - return "" - }).trim() // jshint ignore:line - } else if (type === "duplex") { - var hasDuplex = name - } else if (name === "ms-if-loop") { - binding.priority += 100 - } else if (name === "ms-attr-value") { - var hasAttrValue = name - } - bindings.push(binding) - } - } - } - } - if (bindings.length) { - bindings.sort(bindingSorter) - //http://bugs.jquery.com/ticket/7071 - //在IE下对VML读取type属性,会让此元素所有属性都变成 - if (hasDuplex && hasAttrValue && elem.nodeName === "INPUT" && elem.type === "text") { - log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) - } - for (i = 0; binding = bindings[i]; i++) { - type = binding.type - if (rnoscanAttrBinding.test(type)) { - return executeBindings(bindings.slice(0, i + 1), vmodels) - } else if (scanNode) { - scanNode = !rnoscanNodeBinding.test(type) - } - } - - executeBindings(bindings, vmodels) - } - } - if (scanNode && !stopScan[elem.tagName] && (isWidget(elem) ? elem.msResolved : 1)) { - mergeTextNodes && mergeTextNodes(elem) - scanNodeList(elem, vmodels) //扫描子孙元素 - - } -} -var rnoscanAttrBinding = /^if|widget|repeat$/ -var rnoscanNodeBinding = /^each|with|html|include$/ -//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, -//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 -if (!W3C) { - var attrPool = new Cache(512) - var rattrs = /\s+([^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, - rquote = /^['"]/, - rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, - ramp = /&/g -//IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 -//
ddd
-// window.onload = function() { -// var body = document.body -// for (var i = 0, el; el = body.children[i++]; ) { -// avalon.log(el.outerHTML) -// } -// } -//依次输出
,
- var getAttributes = function (elem) { - var html = elem.outerHTML - //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 - if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) - //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 - var a = elem.getAttribute("ms-skip") - //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 - if (!elem.getAttributeNode) { - return log("warning " + elem.tagName + " no getAttributeNode method") - } - var b = elem.getAttributeNode("ms-important") - var c = elem.getAttributeNode("ms-controller") - if (typeof a === "string") { - return - } else if (node = b || c) { - var newVmodel = avalon.vmodels[node.value] - if (!newVmodel) { - return - } - //ms-important不包含父VM,ms-controller相反 - vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) - var name = node.name - elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 - avalon(elem).removeClass(name) - createSignalTower(elem, newVmodel) - } - - scanAttr(elem, vmodels) //扫描特性节点 -} - - - -var rhasHtml = /\|\s*html(?:\b|$)/, - r11a = /\|\|/g, - rlt = /</g, - rgt = />/g, - rstringLiteral = /(['"])(\\\1|.)+?\1/g - -function getToken(value) { - if (value.indexOf("|") > 0) { - var scapegoat = value.replace(rstringLiteral, function (_) { - return Array(_.length + 1).join("1") // jshint ignore:line - }) - var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 - if (index > -1) { - return { - type: "text", - filters: value.slice(index).trim(), - expr: value.slice(0, index) - } - } - } - return { - type: "text", - expr: value, - filters: "" - } -} - -function scanExpr(str) { - var tokens = [], - value, start = 0, - stop - do { - stop = str.indexOf(openTag, start) - if (stop === -1) { - break - } - value = str.slice(start, stop) - if (value) { // {{ 左边的文本 - tokens.push({ - expr: value - }) - } - start = stop + openTag.length - stop = str.indexOf(closeTag, start) - if (stop === -1) { - break - } - value = str.slice(start, stop) - if (value) { //处理{{ }}插值表达式 - tokens.push(getToken(value, start)) - } - start = stop + closeTag.length - } while (1) - value = str.slice(start) - if (value) { //}} 右边的文本 - tokens.push({ - expr: value - }) - } - return tokens -} - -function scanText(textNode, vmodels, index) { - var bindings = [], - tokens = scanExpr(textNode.data) - if (tokens.length) { - for (var i = 0, token; token = tokens[i++];) { - var node = DOC.createTextNode(token.expr) //将文本转换为文本节点,并替换原来的文本节点 - if (token.type) { - token.expr = token.expr.replace(roneTime, function () { - token.oneTime = true - return "" - }) // jshint ignore:line - token.element = node - token.filters = token.filters.replace(rhasHtml, function () { - token.type = "html" - return "" - }) // jshint ignore:line - token.pos = index * 1000 + i - bindings.push(token) //收集带有插值表达式的文本 - } - avalonFragment.appendChild(node) - } - textNode.parentNode.replaceChild(avalonFragment, textNode) - if (bindings.length) - executeBindings(bindings, vmodels) - } -} - - - -//使用来自游戏界的双缓冲技术,减少对视图的冗余刷新 -var Buffer = function () { - this.queue = [] -} -Buffer.prototype = { - render: function (isAnimate) { - if (!this.locked) { - this.locked = isAnimate ? root.offsetHeight + 10 : 1 - var me = this - avalon.nextTick(function () { - me.flush() - }) - } - }, - flush: function () { - for (var i = 0, sub; sub = this.queue[i++]; ) { - sub.update && sub.update() - } - this.locked = 0 - this.queue = [] - } -} - -var buffer = new Buffer() -var componentQueue = [] -var widgetList = [] -var componentHooks = { - $construct: function () { - return avalon.mix.apply(null, arguments) - }, - $ready: noop, - $init: noop, - $dispose: noop, - $container: null, - $childReady: noop, - $replace: false, - $extend: null, - $$template: function (str) { - return str - } -} - - -avalon.components = {} -avalon.component = function (name, opts) { - if (opts) { - avalon.components[name] = avalon.mix({}, componentHooks, opts) - } - for (var i = 0, obj; obj = componentQueue[i]; i++) { - if (name === obj.fullName) { - componentQueue.splice(i, 1) - i--; - - (function (host, hooks, elem, widget) { - - var dependencies = 1 - var library = host.library - var global = avalon.libraries[library] || componentHooks - - //===========收集各种配置======= - - var elemOpts = getOptionsFromTag(elem) - var vmOpts = getOptionsFromVM(host.vmodels, elemOpts.config || host.widget) - var $id = elemOpts.$id || elemOpts.identifier || generateID(widget) - delete elemOpts.config - delete elemOpts.$id - delete elemOpts.identifier - var componentDefinition = {} - - var parentHooks = avalon.components[hooks.$extend] - if (parentHooks) { - avalon.mix(true, componentDefinition, parentHooks) - componentDefinition = parentHooks.$construct.call(elem, componentDefinition, {}, {}) - } else { - avalon.mix(true, componentDefinition, hooks) - } - componentDefinition = avalon.components[name].$construct.call(elem, componentDefinition, vmOpts, elemOpts) - - componentDefinition.$refs = {} - componentDefinition.$id = $id - - //==========构建VM========= - var keepSolt = componentDefinition.$slot - var keepReplace = componentDefinition.$replace - var keepContainer = componentDefinition.$container - var keepTemplate = componentDefinition.$template - delete componentDefinition.$slot - delete componentDefinition.$replace - delete componentDefinition.$container - delete componentDefinition.$construct - - var vmodel = avalon.define(componentDefinition) || {} - elem.msResolved = 1 - vmodel.$init(vmodel, elem) - global.$init(vmodel, elem) - console.log("init") - var nodes = elem.childNodes - //收集插入点 - var slots = {}, snode - - for (var s = 0, el; el = nodes[s++]; ) { - var type = el.nodeType === 1 && el.getAttribute("slot") || keepSolt - if (type) { - if (slots[type]) { - slots[type].push(el) - } else { - slots[type] = [el] - } - } - } - - - if (vmodel.$$template) { - avalon.clearHTML(elem) - elem.innerHTML = vmodel.$$template(keepTemplate) - } - for (s in slots) { - if (vmodel.hasOwnProperty(s)) { - var ss = slots[s] - if (ss.length) { - var fragment = avalonFragment.cloneNode(true) - for (var ns = 0; snode = ss[ns++]; ) { - fragment.appendChild(snode) - } - vmodel[s] = fragment - } - slots[s] = null - } - } - slots = null - var child = elem.firstChild - if (keepReplace) { - child = elem.firstChild - elem.parentNode.replaceChild(child, elem) - child.msResolved = 1 - elem = host.element = child - } - if (keepContainer) { - keepContainer.appendChild(elem) - } - avalon.fireDom(elem, "datasetchanged", - {library: library, vm: vmodel, childReady: 1}) - var children = 0 - var removeFn = avalon.bind(elem, "datasetchanged", function (e) { - if (e.childReady && e.library === library) { - dependencies += e.childReady - if (vmodel !== e.vm) { - vmodel.$refs[e.vm.$id] = e.vm - if (e.childReady === -1) { - children++ - vmodel.$childReady(vmodel, elem, e) - } - e.stopPropagation() - } - } - console.log("dependencies "+dependencies) - if (dependencies === 0) { - var id1 = setTimeout(function () { - clearTimeout(id1) - - vmodel.$ready(vmodel, elem, host.vmodels) - global.$ready(vmodel, elem, host.vmodels) - }, children ? Math.max(children * 17, 100) : 17) - avalon.unbind(elem, "datasetchanged", removeFn) - //================== - host.rollback = function () { - try { - vmodel.$dispose(vmodel, elem) - global.$dispose(vmodel, elem) - } catch (e) { - } - delete avalon.vmodels[vmodel.$id] - } - injectDisposeQueue(host, widgetList) - if (window.chrome) { - elem.addEventListener("DOMNodeRemovedFromDocument", function () { - setTimeout(rejectDisposeQueue) - }) - } - - } - }) - scanTag(elem, [vmodel].concat(host.vmodels)) - - avalon.vmodels[vmodel.$id] = vmodel - avalon.log("添加组件VM: "+vmodel.$id) - if (!elem.childNodes.length) { - avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) - } else { - var id2 = setTimeout(function () { - clearTimeout(id2) - avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) - }, 17) - } - - - })(obj, avalon.components[name], obj.element, obj.widget)// jshint ignore:line - - - } - } -} - -avalon.fireDom = function (elem, type, opts) { - if (DOC.createEvent) { - var hackEvent = DOC.createEvent("Events"); - hackEvent.initEvent(type, true, true, opts) - avalon.mix(hackEvent, opts) - - elem.dispatchEvent(hackEvent) - } else if (root.contains(elem)) {//IE6-8触发事件必须保证在DOM树中,否则报"SCRIPT16389: 未指明的错误" - hackEvent = DOC.createEventObject() - avalon.mix(hackEvent, opts) - elem.fireEvent("on" + type, hackEvent) - } -} - - -function getOptionsFromVM(vmodels, pre) { - if (pre) { - for (var i = 0, v; v = vmodels[i++]; ) { - if (v.hasOwnProperty(pre) && typeof v[pre] === "object") { - var vmOptions = v[pre] - return vmOptions.$model || vmOptions - break - } - } - } - return {} -} - - - -avalon.libraries = [] -avalon.library = function (name, opts) { - if (DOC.namespaces) { - DOC.namespaces.add(name, 'http://www.w3.org/1999/xhtml'); - } - avalon.libraries[name] = avalon.mix({ - $init: noop, - $ready: noop, - $dispose: noop - }, opts || {}) -} - -avalon.library("ms") -/* -broswer nodeName scopeName localName -IE9 ONI:BUTTON oni button -IE10 ONI:BUTTON undefined oni:button -IE8 button oni undefined -chrome ONI:BUTTON undefined oni:button - -*/ -function isWidget(el) { //如果为自定义标签,返回UI库的名字 - if(el.scopeName && el.scopeName !== "HTML" ){ - return el.scopeName - } - var fullName = el.nodeName.toLowerCase() - var index = fullName.indexOf(":") - if (index > 0) { - return fullName.slice(0, index) - } -} -//各种MVVM框架在大型表格下的性能测试 -// https://github.com/RubyLouvre/avalon/issues/859 - - -var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", - "declare,disabled,defer,defaultChecked,defaultSelected", - "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", - "open,readOnly,selected" -].join(",") -var boolMap = {} -bools.replace(rword, function (name) { - boolMap[name.toLowerCase()] = name -}) - -var propMap = {//属性名映射 - "accept-charset": "acceptCharset", - "char": "ch", - "charoff": "chOff", - "class": "className", - "for": "htmlFor", - "http-equiv": "httpEquiv" -} - -var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", - "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", - "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" -].join(",") -anomaly.replace(rword, function (name) { - propMap[name.toLowerCase()] = name -}) - - -var attrDir = avalon.directive("attr", { - init: function (binding) { - //{{aaa}} --> aaa - //{{aaa}}/bbb.html --> (aaa) + "/bbb.html" - binding.expr = stringifyExpr(binding.expr.trim()) - if (binding.type === "include") { - var elem = binding.element - effectBinding(elem, binding) - binding.includeRendered = getBindingCallback(elem, "data-include-rendered", binding.vmodels) - binding.includeLoaded = getBindingCallback(elem, "data-include-loaded", binding.vmodels) - var outer = binding.includeReplace = !!avalon(elem).data("includeReplace") - if (avalon(elem).data("includeCache")) { - binding.templateCache = {} - } - binding.start = DOC.createComment("ms-include") - binding.end = DOC.createComment("ms-include-end") - if (outer) { - binding.element = binding.end - binding._element = elem - elem.parentNode.insertBefore(binding.start, elem) - elem.parentNode.insertBefore(binding.end, elem.nextSibling) - } else { - elem.insertBefore(binding.start, elem.firstChild) - elem.appendChild(binding.end) - } - } - }, - update: function (val) { - var elem = this.element - var attrName = this.param - if (attrName === "href" || attrName === "src") { - if (typeof val === "string" && !root.hasAttribute) { - val = val.replace(/&/g, "&") //处理IE67自动转义的问题 - } - elem[attrName] = val - if (window.chrome && elem.tagName === "EMBED") { - var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 - var comment = document.createComment("ms-src") - parent.replaceChild(comment, elem) - parent.replaceChild(elem, comment) - } - } else { - - // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc - // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 - // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 - var toRemove = (val === false) || (val === null) || (val === void 0) - if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 - attrName = propMap[attrName] - } - var bool = boolMap[attrName] - if (typeof elem[bool] === "boolean") { - elem[bool] = !!val //布尔属性必须使用el.xxx = true|false方式设值 - if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 - toRemove = true - } - } - if (toRemove) { - return elem.removeAttribute(attrName) - } - //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy - var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) - if (isInnate) { - elem[attrName] = val + "" - } else { - elem.setAttribute(attrName, val) - } - } - } -}) - - - -//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" -"title,alt,src,value,css,include,href".replace(rword, function (name) { - directives[name] = attrDir -}) - -//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" -//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html -avalon.directive("class", { - init: function (binding) { - var oldStyle = binding.param - var method = binding.type - if (!oldStyle || isFinite(oldStyle)) { - binding.param = "" //去掉数字 - directives.effect.init(binding) - } else { - log('ms-' + method + '-xxx="yyy"这种用法已经过时,请使用ms-' + method + '="xxx:yyy"') - binding.expr = '[' + quote(oldStyle) + "," + binding.expr + "]" - binding.oldStyle = oldStyle - } - if (method === "hover" || method === "active") { //确保只绑定一次 - if (!binding.hasBindEvent) { - var elem = binding.element - var $elem = avalon(elem) - var activate = "mouseenter" //在移出移入时切换类名 - var abandon = "mouseleave" - if (method === "active") { //在聚焦失焦中切换类名 - elem.tabIndex = elem.tabIndex || -1 - activate = "mousedown" - abandon = "mouseup" - var fn0 = $elem.bind("mouseleave", function () { - binding.toggleClass && $elem.removeClass(binding.newClass) - }) - } - } - - var fn1 = $elem.bind(activate, function () { - binding.toggleClass && $elem.addClass(binding.newClass) - }) - var fn2 = $elem.bind(abandon, function () { - binding.toggleClass && $elem.removeClass(binding.newClass) - }) - binding.rollback = function () { - $elem.unbind("mouseleave", fn0) - $elem.unbind(activate, fn1) - $elem.unbind(abandon, fn2) - } - binding.hasBindEvent = true - } - - }, - update: function (arr) { - var binding = this - var $elem = avalon(this.element) - binding.newClass = arr[0] - binding.toggleClass = !!arr[1] - if (binding.oldClass && binding.newClass !== binding.oldClass) { - $elem.removeClass(binding.oldClass) - } - binding.oldClass = binding.newClass - if (binding.type === "class") { - if (binding.oldStyle) { - $elem.toggleClass(binding.oldStyle, !!arr[1]) - } else { - $elem.toggleClass(binding.newClass, binding.toggleClass) - } - } - } -}) - -"hover,active".replace(rword, function (name) { - directives[name] = directives["class"] -}) - - -//ms-controller绑定已经在scanTag 方法中实现 -avalon.directive("css", { - init: directives.attr.init, - update: function (val) { - avalon(this.element).css(this.param, val) - } -}) - -avalon.directive("data", { - priority: 100, - update: function (val) { - var elem = this.element - var key = "data-" + this.param - if (val && typeof val === "object") { - elem[key] = val - } else { - elem.setAttribute(key, String(val)) - } - } -}) - -//双工绑定 -var rduplexType = /^(?:checkbox|radio)$/ -var rduplexParam = /^(?:radio|checked)$/ -var rnoduplexInput = /^(file|button|reset|submit|checkbox|radio|range)$/ -var duplexBinding = avalon.directive("duplex", { - priority: 2000, - init: function (binding, hasCast) { - var elem = binding.element - var vmodels = binding.vmodels - binding.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop - var params = [] - var casting = oneObject("string,number,boolean,checked") - if (elem.type === "radio" && binding.param === "") { - binding.param = "checked" - } - - - binding.param.replace(rw20g, function (name) { - if (rduplexType.test(elem.type) && rduplexParam.test(name)) { - if (name === "radio") - log("ms-duplex-radio已经更名为ms-duplex-checked") - name = "checked" - binding.isChecked = true - binding.xtype = "radio" - } - if (name === "bool") { - name = "boolean" - log("ms-duplex-bool已经更名为ms-duplex-boolean") - } else if (name === "text") { - name = "string" - log("ms-duplex-text已经更名为ms-duplex-string") - } - if (casting[name]) { - hasCast = true - } - avalon.Array.ensure(params, name) - }) - if (!hasCast) { - params.push("string") - } - binding.param = params.join("-") - if (!binding.xtype) { - binding.xtype = elem.tagName === "SELECT" ? "select" : - elem.type === "checkbox" ? "checkbox" : - elem.type === "radio" ? "radio" : - /^change/.test(elem.getAttribute("data-duplex-event")) ? "change" : - "input" - } - //===================绑定事件====================== - binding.bound = function (type, callback) { - if (elem.addEventListener) { - elem.addEventListener(type, callback, false) - } else { - elem.attachEvent("on" + type, callback) - } - var old = binding.rollback - binding.rollback = function () { - elem.avalonSetter = null - avalon.unbind(elem, type, callback) - old && old() - } - } - var composing = false - function callback(value) { - binding.changed.call(this, value, binding) - } - function compositionStart() { - composing = true - } - function compositionEnd() { - composing = false - } - var updateVModel = function () { - var val = elem.value //防止递归调用形成死循环 - if (composing || val === binding.oldValue) //处理中文输入法在minlengh下引发的BUG - return - var lastValue = binding.pipe(val, binding, "get") - try { - binding.setter(lastValue) - callback.call(elem, lastValue) - } catch (ex) { - log(ex) - } - } - switch (binding.xtype) { - case "radio": - binding.bound("click", function () { - var lastValue = binding.pipe(elem.value, binding, "get") - try { - binding.setter(lastValue) - callback.call(elem, lastValue) - } catch (ex) { - log(ex) - } - }) - break - case "checkbox": - binding.bound(W3C ? "change" : "click", function () { - var method = elem.checked ? "ensure" : "remove" - var array = binding.getter.apply(0, binding.vmodels) - if (!Array.isArray(array)) { - log("ms-duplex应用于checkbox上要对应一个数组") - array = [array] - } - var val = binding.pipe(elem.value, binding, "get") - avalon.Array[method](array, val) - callback.call(elem, array) - }) - break - case "change": - binding.bound("change", updateVModel) - break - case "input": - if (!IEVersion) { // W3C - binding.bound("input", updateVModel) - //非IE浏览器才用这个 - binding.bound("compositionstart", compositionStart) - binding.bound("compositionend", compositionEnd) - binding.bound("DOMAutoComplete", updateVModel) - } else { //onpropertychange事件无法区分是程序触发还是用户触发 - // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 - if (IEVersion > 8) { - binding.bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 - } else { - binding.bound("propertychange", function (e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 - if (e.propertyName === "value") { - updateVModel() - } - }) - } - binding.bound("dragend", function () { - setTimeout(function () { - updateVModel() - }, 17) - }) - //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html - //http://www.matts411.com/post/internet-explorer-9-oninput/ - } - break - case "select": - binding.bound("change", function () { - var val = avalon(elem).val() //字符串或字符串数组 - if (Array.isArray(val)) { - val = val.map(function (v) { - return binding.pipe(v, binding, "get") - }) - } else { - val = binding.pipe(val, binding, "get") - } - if (val + "" !== binding.oldValue) { - try { - binding.setter(val) - callback.call(elem, val) - } catch (ex) { - log(ex) - } - } - }) - binding.bound("datasetchanged", function (e) { - if (e.bubble === "selectDuplex") { - var value = binding._value - var curValue = Array.isArray(value) ? value.map(String) : value + "" - avalon(elem).val(curValue) - elem.oldValue = curValue + "" - binding.changed.call(elem, curValue) - } - }) - break - } - if (binding.xtype === "input" && !rnoduplexInput.test(elem.type)) { - if (elem.type !== "hidden") { - binding.bound("focus", function () { - elem.msFocus = true - }) - binding.bound("blur", function () { - elem.msFocus = false - }) - } - elem.avalonSetter = updateVModel //#765 - watchValueInTimer(function () { - if (elem.contains(elem)) { - if (!this.msFocus && binding.oldValue !== elem.value) { - updateVModel() - } - } else if (!elem.msRetain) { - return false - } - }) - } - - }, - update: function (value) { - var elem = this.element, binding = this, curValue - if (!this.init) { - for (var i in avalon.vmodels) { - var v = avalon.vmodels[i] - v.$fire("avalon-ms-duplex-init", binding) - } - var cpipe = binding.pipe || (binding.pipe = pipe) - cpipe(null, binding, "init") - this.init = 1 - } - switch (this.xtype) { - case "input": - case "change": - curValue = this.pipe(value, this, "set") //fix #673 - if (curValue !== this.oldValue) { - var fixCaret = false - if (elem.msFocus) { - var pos = getCaret(elem) - if (pos.start === pos.end) { - pos = pos.start - fixCaret = true - } - } - elem.value = this.oldValue = curValue - if (fixCaret) { - setCaret(element, pos, pos) - } - } - break - case "radio": - curValue = binding.isChecked ? !!value : value + "" === elem.value - if (IEVersion === 6) { - setTimeout(function () { - //IE8 checkbox, radio是使用defaultChecked控制选中状态, - //并且要先设置defaultChecked后设置checked - //并且必须设置延迟 - elem.defaultChecked = curValue - elem.checked = curValue - }, 31) - } else { - elem.checked = curValue - } - break - case "checkbox": - var array = [].concat(value) //强制转换为数组 - curValue = this.pipe(elem.value, this, "get") - elem.checked = array.indexOf(curValue) > -1 - break - case "select": - //必须变成字符串后才能比较 - binding._value = value - if(!elem.msHasEvent){ - elem.msHasEvent = "selectDuplex" - //必须等到其孩子准备好才触发 - }else{ - avalon.fireDom(elem, "datasetchanged", { - bubble: elem.msHasEvent - }) - } - break - } - if (binding.xtype !== "select") { - binding.changed.call(elem, curValue) - } - } -}) - -if (IEVersion) { - avalon.bind(DOC, "selectionchange", function (e) { - var el = DOC.activeElement - if (el && typeof el.avalonSetter === "function") { - el.avalonSetter() - } - }) -} - -function fixNull(val) { - return val == null ? "" : val -} -avalon.duplexHooks = { - checked: { - get: function (val, binding) { - return !binding.oldValue - } - }, - string: { - get: function (val) { //同步到VM - return val - }, - set: fixNull - }, - "boolean": { - get: function (val) { - return val === "true" - }, - set: fixNull - }, - number: { - get: function (val, binding) { - var number = parseFloat(val + "") - if (-val === -number) { - return number - } - - var arr = /strong|medium|weak/.exec(binding.element.getAttribute("data-duplex-number")) || ["medium"] - switch (arr[0]) { - case "strong": - return 0 - case "medium": - return val === "" ? "" : 0 - case "weak": - return val - } - }, - set: fixNull - } -} - -function pipe(val, binding, action) { - binding.param.replace(rw20g, function (name) { - var hook = avalon.duplexHooks[name] - if (hook && typeof hook[action] === "function") { - val = hook[action](val, binding) - } - }) - return val -} - -var TimerID, ribbon = [] - -avalon.tick = function (fn) { - if (ribbon.push(fn) === 1) { - TimerID = setInterval(ticker, 60) - } -} - -function ticker() { - for (var n = ribbon.length - 1; n >= 0; n--) { - var el = ribbon[n] - if (el() === false) { - ribbon.splice(n, 1) - } - } - if (!ribbon.length) { - clearInterval(TimerID) - } -} - -var watchValueInTimer = noop -new function () { // jshint ignore:line - 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) - if (!this.msFocus && this.avalonSetter && this.oldValue !== value) { - this.avalonSetter() - } - } - 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 - }) - } 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 - watchValueInTimer = avalon.tick - } -} // jshint ignore:line -function getCaret(ctrl, start, end) { - if (ctrl.setSelectionRange) { - start = ctrl.selectionStart - end = ctrl.selectionEnd - } else if (document.selection && document.selection.createRange) { - var range = document.selection.createRange() - start = 0 - range.duplicate().moveStart('character', -100000) - end = start + range.text.length - } - return { - start: start, - end: end - } -} -function setCaret(ctrl, begin, end) { - if (!ctrl.value || ctrl.readOnly) - return - if (ctrl.setSelectionRange) { - ctrl.selectionStart = begin - ctrl.selectionEnd = end - } else { - var range = ctrl.createTextRange() - range.collapse(true); - range.moveStart("character", begin) - range.moveEnd("character", end - begin) - range.select() - } -} - -avalon.directive("effect", { - priority: 5, - init: function (binding) { - var text = binding.expr, - className, - rightExpr - var colonIndex = text.replace(rexprg, function (a) { - return a.replace(/./g, "0") - }).indexOf(":") //取得第一个冒号的位置 - if (colonIndex === -1) { // 比如 ms-class/effect="aaa bbb ccc" 的情况 - className = text - rightExpr = true - } else { // 比如 ms-class/effect-1="ui-state-active:checked" 的情况 - className = text.slice(0, colonIndex) - rightExpr = text.slice(colonIndex + 1) - } - if (!rexpr.test(text)) { - className = quote(className) - } else { - className = stringifyExpr(className) - } - binding.expr = "[" + className + "," + rightExpr + "]" - }, - update: function (arr) { - var name = arr[0] - var elem = this.element - if (elem.getAttribute("data-effect-name") === name) { - return - } else { - elem.removeAttribute("data-effect-driver") - } - var inlineStyles = elem.style - var computedStyles = window.getComputedStyle ? window.getComputedStyle(elem) : null - var useAni = false - if (computedStyles && (supportTransition || supportAnimation)) { - - //如果支持CSS动画 - var duration = inlineStyles[transitionDuration] || computedStyles[transitionDuration] - if (duration && duration !== '0s') { - elem.setAttribute("data-effect-driver", "t") - useAni = true - } - - if (!useAni) { - - duration = inlineStyles[animationDuration] || computedStyles[animationDuration] - if (duration && duration !== '0s') { - elem.setAttribute("data-effect-driver", "a") - useAni = true - } - - } - } - - if (!useAni) { - if (avalon.effects[name]) { - elem.setAttribute("data-effect-driver", "j") - useAni = true - } - } - if (useAni) { - elem.setAttribute("data-effect-name", name) - } - } -}) - -avalon.effects = {} -avalon.effect = function (name, callbacks) { - avalon.effects[name] = callbacks -} - - - -var supportTransition = false -var supportAnimation = false - -var transitionEndEvent -var animationEndEvent -var transitionDuration = avalon.cssName("transition-duration") -var animationDuration = avalon.cssName("animation-duration") -new function () {// jshint ignore:line - var checker = { - 'TransitionEvent': 'transitionend', - 'WebKitTransitionEvent': 'webkitTransitionEnd', - 'OTransitionEvent': 'oTransitionEnd', - 'otransitionEvent': 'otransitionEnd' - } - 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 - transitionEndEvent = tran - } - - //大致上有两种选择 - //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; - } - } - if (typeof ani === "string") { - supportTransition = true - animationEndEvent = ani - } - -}() - -var effectPool = []//重复利用动画实例 -function effectFactory(el, opts) { - if (!el || el.nodeType !== 1) { - return null - } - if (opts) { - var name = opts.effectName - var driver = opts.effectDriver - } else { - name = el.getAttribute("data-effect-name") - driver = el.getAttribute("data-effect-driver") - } - if (!name || !driver) { - return null - } - - var instance = effectPool.pop() || new Effect() - instance.el = el - instance.driver = driver - instance.useCss = driver !== "j" - if (instance.useCss) { - opts && avalon(el).addClass(opts.effectClass) - instance.cssEvent = driver === "t" ? transitionEndEvent : animationEndEvent - } - instance.name = name - instance.callbacks = avalon.effects[name] || {} - - return instance - - -} - -function effectBinding(elem, binding) { - var name = elem.getAttribute("data-effect-name") - if (name) { - binding.effectName = name - binding.effectDriver = elem.getAttribute("data-effect-driver") - var stagger = +elem.getAttribute("data-effect-stagger") - binding.effectLeaveStagger = +elem.getAttribute("data-effect-leave-stagger") || stagger - binding.effectEnterStagger = +elem.getAttribute("data-effect-enter-stagger") || stagger - binding.effectClass = elem.className || NaN - } -} -function upperFirstChar(str) { - return str.replace(/^[\S]/g, function (m) { - return m.toUpperCase() - }) -} -var effectBuffer = new Buffer() -function Effect() { -}// 动画实例,做成类的形式,是为了共用所有原型方法 - -Effect.prototype = { - contrustor: Effect, - enterClass: function () { - return getEffectClass(this, "enter") - }, - leaveClass: function () { - return getEffectClass(this, "leave") - }, - // 共享一个函数 - actionFun: function (name, before, after) { - if (document.hidden) { - return - } - var me = this - var el = me.el - var isLeave = name === "leave" - name = isLeave ? "leave" : "enter" - var oppositeName = isLeave ? "enter" : "leave" - callEffectHook(me, "abort" + upperFirstChar(oppositeName)) - callEffectHook(me, "before" + upperFirstChar(name)) - if (!isLeave) - before(el) // 这里可能做插入DOM树的操作,因此必须在修改类名前执行 - var cssCallback = function (cancel) { - el.removeEventListener(me.cssEvent, me.cssCallback) - if (isLeave) { - before(el) //这里可能做移出DOM树操作,因此必须位于动画之后 - avalon(el).removeClass(me.cssClass) - } else { - if (me.driver === "a") { - avalon(el).removeClass(me.cssClass) - } - } - if (cancel !== true) { - callEffectHook(me, "after" + upperFirstChar(name)) - after && after(el) - } - me.dispose() - } - if (me.useCss) { - if (me.cssCallback) { //如果leave动画还没有完成,立即完成 - me.cssCallback(true) - } - - me.cssClass = getEffectClass(me, name) - me.cssCallback = cssCallback - - me.update = function () { - el.addEventListener(me.cssEvent, me.cssCallback) - if (!isLeave && me.driver === "t") {//transtion延迟触发 - avalon(el).removeClass(me.cssClass) - } - } - avalon(el).addClass(me.cssClass)//animation会立即触发 - - effectBuffer.render(true) - effectBuffer.queue.push(me) - - } else { - callEffectHook(me, name, cssCallback) - - } - }, - enter: function (before, after) { - this.actionFun.apply(this, ["enter"].concat(avalon.slice(arguments))) - - }, - leave: function (before, after) { - this.actionFun.apply(this, ["leave"].concat(avalon.slice(arguments))) - - }, - dispose: function () {//销毁与回收到池子中 - this.update = this.cssCallback = null - if (effectPool.unshift(this) > 100) { - effectPool.pop() - } - } - - -} - - -function getEffectClass(instance, type) { - var a = instance.callbacks[type + "Class"] - if (typeof a === "string") - return a - if (typeof a === "function") - return a() - return instance.name + "-" + type -} - - -function callEffectHook(effect, name, cb) { - var hook = effect.callbacks[name] - if (hook) { - hook.call(effect, effect.el, cb) - } -} - -var applyEffect = function (el, dir/*[before, [after, [opts]]]*/) { - var args = aslice.call(arguments, 0) - if (typeof args[2] !== "function") { - args.splice(2, 0, noop) - } - if (typeof args[3] !== "function") { - args.splice(3, 0, noop) - } - var before = args[2] - var after = args[3] - var opts = args[4] - var effect = effectFactory(el, opts) - if (!effect) { - before() - after() - return false - } else { - var method = dir ? 'enter' : 'leave' - effect[method](before, after) - } -} - -avalon.mix(avalon.effect, { - apply: applyEffect, - append: function (el, parent, after, opts) { - return applyEffect(el, 1, function () { - parent.appendChild(el) - }, after, opts) - }, - before: function (el, target, after, opts) { - return applyEffect(el, 1, function () { - target.parentNode.insertBefore(el, target) - }, after, opts) - }, - remove: function (el, parent, after, opts) { - return applyEffect(el, 0, function () { - if (el.parentNode === parent) - parent.removeChild(el) - }, after, opts) - } -}) - - -avalon.directive("html", { - update: function (val) { - var binding = this - var elem = this.element - var isHtmlFilter = elem.nodeType !== 1 - var parent = isHtmlFilter ? elem.parentNode : elem - if (!parent) - return - val = val == null ? "" : val - - if (elem.nodeType === 3) { - var signature = generateID("html") - parent.insertBefore(DOC.createComment(signature), elem) - binding.element = DOC.createComment(signature + ":end") - parent.replaceChild(binding.element, elem) - elem = binding.element - } - if (typeof val !== "object") {//string, number, boolean - var fragment = avalon.parseHTML(String(val)) - } else if (val.nodeType === 11) { //将val转换为文档碎片 - fragment = val - } else if (val.nodeType === 1 || val.item) { - var nodes = val.nodeType === 1 ? val.childNodes : val.item - fragment = avalonFragment.cloneNode(true) - while (nodes[0]) { - fragment.appendChild(nodes[0]) - } - } - - nodes = avalon.slice(fragment.childNodes) - //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 - if (isHtmlFilter) { - var endValue = elem.nodeValue.slice(0, -4) - while (true) { - var node = elem.previousSibling - if (!node || node.nodeType === 8 && node.nodeValue === endValue) { - break - } else { - parent.removeChild(node) - } - } - parent.insertBefore(fragment, elem) - } else { - avalon.clearHTML(elem).appendChild(fragment) - } - scanNodeArray(nodes, binding.vmodels) - } -}) - -avalon.directive("if", { - priority: 10, - update: function (val) { - var binding = this - var elem = this.element - var stamp = binding.stamp = +new Date() - var par - var after = function () { - if (stamp !== binding.stamp) - return - binding.recoverNode = null - } - if (binding.recoverNode) - binding.recoverNode() // 还原现场,有移动节点的都需要还原现场 - try { - if (!elem.parentNode) - return - par = elem.parentNode - } catch (e) { - return - } - if (val) { //插回DOM树 - function alway() {// jshint ignore:line - if (elem.getAttribute(binding.name)) { - elem.removeAttribute(binding.name) - scanAttr(elem, binding.vmodels) - } - binding.rollback = null - } - if (elem.nodeType === 8) { - var keep = binding.keep - var hasEffect = avalon.effect.apply(keep, 1, function () { - if (stamp !== binding.stamp) - return - elem.parentNode.replaceChild(keep, elem) - elem = binding.element = keep //这时可能为null - if (keep.getAttribute("_required")) {//#1044 - elem.required = true - elem.removeAttribute("_required") - } - if (elem.querySelectorAll) { - avalon.each(elem.querySelectorAll("[_required=true]"), function (el) { - el.required = true - el.removeAttribute("_required") - }) - } - alway() - }, after) - hasEffect = hasEffect === false - } - if (!hasEffect) - alway() - } else { //移出DOM树,并用注释节点占据原位置 - if (elem.nodeType === 1) { - if (elem.required === true) { - elem.required = false - elem.setAttribute("_required", "true") - } - try {// 如果不支持querySelectorAll或:required,可以直接无视 - avalon.each(elem.querySelectorAll(":required"), function (el) { - elem.required = false - el.setAttribute("_required", "true") - }) - } catch (e) { - } - - var node = binding.element = DOC.createComment("ms-if"), - pos = elem.nextSibling - binding.recoverNode = function () { - binding.recoverNode = null - if (node.parentNode !== par) { - par.insertBefore(node, pos) - binding.keep = elem - } - } - - avalon.effect.apply(elem, 0, function () { - binding.recoverNode = null - if (stamp !== binding.stamp) - return - elem.parentNode.replaceChild(node, elem) - binding.keep = elem //元素节点 - ifGroup.appendChild(elem) - binding.rollback = function () { - if (elem.parentNode === ifGroup) { - ifGroup.removeChild(elem) - } - } - }, after) - } - } - } -}) - - - -//ms-important绑定已经在scanTag 方法中实现 -var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img -var rnoscriptText = /([\s\S]+?)<\/noscript>/im - -var getXHR = function () { - return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line -} -//将所有远程加载的模板,以字符串形式存放到这里 -var templatePool = avalon.templateCache = {} - -function getTemplateContainer(binding, id, text) { - var div = binding.templateCache && binding.templateCache[id] - if (div) { - var dom = DOC.createDocumentFragment(), - firstChild - while (firstChild = div.firstChild) { - dom.appendChild(firstChild) - } - return dom - } - return avalon.parseHTML(text) - -} -function nodesToFrag(nodes) { - var frag = DOC.createDocumentFragment() - for (var i = 0, len = nodes.length; i < len; i++) { - frag.appendChild(nodes[i]) - } - return frag -} -avalon.directive("include", { - init: directives.attr.init, - update: function (val) { - var binding = this - var elem = this.element - var vmodels = binding.vmodels - var rendered = binding.includeRendered - var effectClass = binding.effectName && binding.effectClass // 是否开启动画 - var templateCache = binding.templateCache // 是否data-include-cache - var outer = binding.includeReplace // 是否data-include-replace - var loaded = binding.includeLoaded - var target = outer ? elem.parentNode : elem - var _ele = binding._element // data-include-replace binding.element === binding.end - - binding.recoverNodes = binding.recoverNodes || avalon.noop - var scanTemplate = function (text) { - var _stamp = binding._stamp = +(new Date()) // 过滤掉频繁操作 - if (loaded) { - var newText = loaded.apply(target, [text].concat(vmodels)) - if (typeof newText === "string") - text = newText - } - if (rendered) { - checkScan(target, function () { - rendered.call(target) - }, NaN) - } - var lastID = binding.includeLastID || "_default" // 默认 - - binding.includeLastID = val - var leaveEl = templateCache && templateCache[lastID] || DOC.createElement(elem.tagName || binding._element.tagName) // 创建一个离场元素 - if (effectClass) { - leaveEl.className = effectClass - target.insertBefore(leaveEl, binding.start) // 插入到start之前,防止被错误的移动 - } - - // cache or animate,移动节点 - (templateCache || {})[lastID] = leaveEl - var fragOnDom = binding.recoverNodes() // 恢复动画中的节点 - if (fragOnDom) { - target.insertBefore(fragOnDom, binding.end) - } - while (true) { - var node = binding.start.nextSibling - if (node && node !== leaveEl && node !== binding.end) { - leaveEl.appendChild(node) - } else { - break - } - } - // 元素退场 - avalon.effect.remove(leaveEl, target, function () { - if (templateCache) { // write cache - if (_stamp === binding._stamp) - ifGroup.appendChild(leaveEl) - } - }, binding) - - - var enterEl = target, - before = avalon.noop, - after = avalon.noop - - var fragment = getTemplateContainer(binding, val, text) - var nodes = avalon.slice(fragment.childNodes) - - if (outer && effectClass) { - enterEl = _ele - enterEl.innerHTML = "" // 清空 - enterEl.setAttribute("ms-skip", "true") - target.insertBefore(enterEl, binding.end.nextSibling) // 插入到bingding.end之后避免被错误的移动 - before = function () { - enterEl.insertBefore(fragment, null) // 插入节点 - } - after = function () { - binding.recoverNodes = avalon.noop - if (_stamp === binding._stamp) { - fragment = nodesToFrag(nodes) - target.insertBefore(fragment, binding.end) // 插入真实element - scanNodeArray(nodes, vmodels) - } - if (enterEl.parentNode === target) - target.removeChild(enterEl) // 移除入场动画元素 - } - binding.recoverNodes = function () { - binding.recoverNodes = avalon.noop - return nodesToFrag(nodes) - } - } else { - before = function () {// 新添加元素的动画 - target.insertBefore(fragment, binding.end) - scanNodeArray(nodes, vmodels) - } - } - - avalon.effect.apply(enterEl, "enter", before, after) - } - - if (binding.param === "src") { - if (typeof templatePool[val] === "string") { - avalon.nextTick(function () { - scanTemplate(templatePool[val]) - }) - } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 - templatePool[val].push(scanTemplate) - } else { - var xhr = getXHR() - xhr.onreadystatechange = function () { - if (xhr.readyState === 4) { - var s = xhr.status - if (s >= 200 && s < 300 || s === 304 || s === 1223) { - var text = xhr.responseText - for (var f = 0, fn; fn = templatePool[val][f++]; ) { - fn(text) - } - templatePool[val] = text - }else{ - log("ms-include load ["+ val +"] error") - } - } - } - templatePool[val] = [scanTemplate] - xhr.open("GET", val, true) - if ("withCredentials" in xhr) { - xhr.withCredentials = true - } - xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") - xhr.send(null) - } - } else { - //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) - //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ - var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) - if (el) { - if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML - xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 - xhr.open("GET", location, false) - xhr.send(null) - //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 - var noscripts = DOC.getElementsByTagName("noscript") - var array = (xhr.responseText || "").match(rnoscripts) || [] - var n = array.length - for (var i = 0; i < n; i++) { - var tag = noscripts[i] - if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 - tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug - tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] - } - } - } - avalon.nextTick(function () { - scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) - }) - } - } - } -}) - -var rdash = /\(([^)]*)\)/ -var onDir = avalon.directive("on", { - priority: 3000, - init: function (binding) { - var value = binding.expr - binding.type = "on" - var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 - if (typeof onDir[eventType + "Hook"] === "function") { - onDir[eventType + "Hook"](binding) - } - if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { - var matched = (value.match(rdash) || ["", ""])[1].trim() - if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 - value = value.replace(rdash, "") - } - } - binding.expr = value - }, - update: function (callback) { - var binding = this - var elem = this.element - callback = function (e) { - var fn = binding.getter || noop - return fn.apply(this, binding.args.concat(e)) - } - - var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 - if (eventType === "scan") { - callback.call(elem, { - type: eventType - }) - } else if (typeof binding.specialBind === "function") { - binding.specialBind(elem, callback) - } else { - var removeFn = avalon.bind(elem, eventType, callback) - } - binding.rollback = function () { - if (typeof binding.specialUnbind === "function") { - binding.specialUnbind() - } else { - avalon.unbind(elem, eventType, removeFn) - } - } - } -}) -avalon.directive("repeat", { - priority: 90, - init: function (binding) { - var type = binding.type - binding.cache = {} //用于存放代理VM - binding.enterCount = 0 - - var elem = binding.element - if (elem.nodeType === 1) { - elem.removeAttribute(binding.name) - effectBinding(elem, binding) - binding.param = binding.param || "el" - binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) - // binding.renderedCallback = - var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) - - var signature = generateID(type) - var start = DOC.createComment(signature + ":start") - var end = binding.element = DOC.createComment(signature + ":end") - binding.signature = signature - binding.start = start - binding.template = avalonFragment.cloneNode(false) - if (type === "repeat") { - var parent = elem.parentNode - parent.replaceChild(end, elem) - parent.insertBefore(start, end) - binding.template.appendChild(elem) - } else { - while (elem.firstChild) { - binding.template.appendChild(elem.firstChild) - } - elem.appendChild(start) - elem.appendChild(end) - parent = elem - } - binding.element = end - - if (rendered) { - var removeFn = avalon.bind(parent, "datasetchanged", function () { - rendered.apply(parent, parent.args) - avalon.unbind(parent, "datasetchanged", removeFn) - parent.msRendered = rendered - }) - } - } - }, - update: function (value, oldValue) { - var binding = this - var xtype = this.xtype - - this.enterCount += 1 - var init = !oldValue - if (init) { - binding.$outer = {} - var check0 = "$key" - var check1 = "$val" - if (xtype === "array") { - check0 = "$first" - check1 = "$last" - } - for (var i = 0, v; v = binding.vmodels[i++]; ) { - if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { - binding.$outer = v - break - } - } - } - var track = this.track - if (binding.sortedCallback) { //如果有回调,则让它们排序 - var keys2 = binding.sortedCallback.call(parent, track) - if (keys2 && Array.isArray(keys2)) { - track = keys2 - } - } - - var action = "move" - binding.$repeat = value - var fragments = [] - var transation = init && avalonFragment.cloneNode(false) - var proxies = [] - var param = this.param - var retain = avalon.mix({}, this.cache) - var elem = this.element - var length = track.length - - var parent = elem.parentNode - for (i = 0; i < length; i++) { - - var keyOrId = track[i] //array为随机数, object 为keyName - var proxy = retain[keyOrId] - if (!proxy) { - proxy = getProxyVM(this) - - if (xtype === "array") { - action = "add" - proxy.$id = keyOrId - - proxy[param] = value[i] //index - - } else { - action = "append" - proxy.$key = keyOrId - proxy.$val = value[keyOrId] //key - } - this.cache[keyOrId] = proxy - var node = proxy.$anchor || (proxy.$anchor = elem.cloneNode(false)) - node.nodeValue = this.signature - shimController(binding, transation, proxy, fragments, init && !binding.effectDriver) - decorateProxy(proxy, binding, xtype) - } else { -// if (xtype === "array") { -// proxy[param] = value[i] -// } - fragments.push({}) - retain[keyOrId] = true - } - - //重写proxy - if (this.enterCount === 1) {// 防止多次进入,导致位置不对 - proxy.$active = false - proxy.$oldIndex = proxy.$index - proxy.$active = true - proxy.$index = i - - } - - if (xtype === "array") { - proxy.$first = i === 0 - proxy.$last = i === length - 1 - // proxy[param] = value[i] - } else { - proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 - } - proxies.push(proxy) - } - this.proxies = proxies - if (init && !binding.effectDriver) { - parent.insertBefore(transation, elem) - fragments.forEach(function (fragment) { - scanNodeArray(fragment.nodes || [], fragment.vmodels) - //if(fragment.vmodels.length > 2) - fragment.nodes = fragment.vmodels = null - })// jshint ignore:line - } else { - - var staggerIndex = binding.staggerIndex = 0 - for (keyOrId in retain) { - if (retain[keyOrId] !== true) { - - action = "del" - removeItem(retain[keyOrId].$anchor, binding) - // avalon.log("删除", keyOrId) - // 相当于delete binding.cache[key] - proxyRecycler(this.cache, keyOrId, param) - retain[keyOrId] = null - } - } - - // console.log(effectEnterStagger) - for (i = 0; i < length; i++) { - proxy = proxies[i] - keyOrId = xtype === "array" ? proxy.$id : proxy.$key - var pre = proxies[i - 1] - var preEl = pre ? pre.$anchor : binding.start - if (!retain[keyOrId]) {//如果还没有插入到DOM树 - (function (fragment, preElement) { - var nodes = fragment.nodes - var vmodels = fragment.vmodels - if (nodes) { - staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { - parent.insertBefore(fragment.content, preElement.nextSibling) - scanNodeArray(nodes, vmodels) - animateRepeat(nodes, 1, binding) - }, staggerIndex) - } - fragment.nodes = fragment.vmodels = null - })(fragments[i], preEl)// jshint ignore:line - // avalon.log("插入") - - } else if (proxy.$index !== proxy.$oldIndex) { - (function (proxy2, preElement) { - staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { - var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 - var inserted = avalon.slice(curNode.childNodes) - parent.insertBefore(curNode, preElement.nextSibling) - animateRepeat(inserted, 1, binding) - }, staggerIndex) - })(proxy, preEl)// jshint ignore:line - - // avalon.log("移动", proxy.$oldIndex, "-->", proxy.$index) - } - } - - } - if (!value.$track) {//如果是非监控对象,那么就将其$events清空,阻止其持续监听 - for (keyOrId in this.cache) { - proxyRecycler(this.cache, keyOrId, param) - } - - } - - //repeat --> duplex - (function (args) { - parent.args = args - if (parent.msRendered) {//第一次事件触发,以后直接调用 - parent.msRendered.apply(parent, args) - } - })(kernel.newWatch ? arguments : [action]); - var id = setTimeout(function () { - clearTimeout(id) - //触发上层的select回调及自己的rendered回调 - avalon.fireDom(parent, "datasetchanged", { - bubble: parent.msHasEvent - }) - }) - this.enterCount -= 1 - - } - -}) - -"with,each".replace(rword, function (name) { - directives[name] = avalon.mix({}, directives.repeat, { - priority: 1400 - }) -}) - - -function animateRepeat(nodes, isEnter, binding) { - for (var i = 0, node; node = nodes[i++]; ) { - if (node.className === binding.effectClass) { - avalon.effect.apply(node, isEnter, noop, noop, binding) - } - } -} - -function mayStaggerAnimate(staggerTime, callback, index) { - if (staggerTime) { - setTimeout(callback, (++index) * staggerTime) - } else { - callback() - } - return index -} - - -function removeItem(node, binding) { - var fragment = avalonFragment.cloneNode(false) - var last = node - var breakText = last.nodeValue - var staggerIndex = binding && Math.max(+binding.staggerIndex, 0) - var nodes = avalon.slice(last.parentNode.childNodes) - var index = nodes.indexOf(last) - while (true) { - var pre = nodes[--index] //node.previousSibling - if (!pre || String(pre.nodeValue).indexOf(breakText) === 0) { - break - } - - if (binding && (pre.className === binding.effectClass)) { - node = pre; - (function (cur) { - binding.staggerIndex = mayStaggerAnimate(binding.effectLeaveStagger, function () { - avalon.effect.apply(cur, 0, noop, function () { - fragment.appendChild(cur) - }, binding) - }, staggerIndex) - })(pre);// jshint ignore:line - } else { - fragment.insertBefore(pre, fragment.firstChild) - } - } - fragment.appendChild(last) - return fragment -} - - -function shimController(data, transation, proxy, fragments, init) { - var content = data.template.cloneNode(true) - var nodes = avalon.slice(content.childNodes) - content.appendChild(proxy.$anchor) - init && transation.appendChild(content) - var nv = [proxy].concat(data.vmodels) - var fragment = { - nodes: nodes, - vmodels: nv, - content: content - } - fragments.push(fragment) -} -// {} --> {xx: 0, yy: 1, zz: 2} add -// {xx: 0, yy: 1, zz: 2} --> {xx: 0, yy: 1, zz: 2, uu: 3} -// [xx: 0, yy: 1, zz: 2} --> {xx: 0, zz: 1, yy: 2} - -function getProxyVM(binding) { - var agent = binding.xtype === "object" ? withProxyAgent : eachProxyAgent - var proxy = agent(binding) - var node = proxy.$anchor || (proxy.$anchor = binding.element.cloneNode(false)) - node.nodeValue = binding.signature - proxy.$outer = binding.$outer - return proxy -} - -var eachProxyPool = [] - -function eachProxyAgent(data, proxy) { - var itemName = data.param || "el" - for (var i = 0, n = eachProxyPool.length; i < n; i++) { - var candidate = eachProxyPool[i] - if (candidate && candidate.hasOwnProperty(itemName)) { - eachProxyPool.splice(i, 1) - proxy = candidate - break - } - } - if (!proxy) { - proxy = eachProxyFactory(itemName) - } - return proxy -} - -function eachProxyFactory(itemName) { - var source = { - $outer: {}, - $index: 0, - $oldIndex: 0, - $anchor: null, - //----- - $first: false, - $last: false, - $remove: avalon.noop - } - source[itemName] = NaN - - var force = { - $last: 1, - $first: 1, - $index: 1 - } - force[itemName] = 1 - var proxy = modelFactory(source, { - force: force - }) - proxy.$id = generateID("$proxy$each") - return proxy -} - -function decorateProxy(proxy, binding, type) { - if (type === "array") { - proxy.$remove = function () { - - binding.$repeat.removeAt(proxy.$index) - } - var param = binding.param - - - proxy.$watch(param, function (a) { - var index = proxy.$index - binding.$repeat[index] = a - }) - } else { - proxy.$watch("$val", function fn(a) { - binding.$repeat[proxy.$key] = a - }) - } -} - -var withProxyPool = [] - -function withProxyAgent() { - return withProxyPool.pop() || withProxyFactory() -} - -function withProxyFactory() { - var proxy = modelFactory({ - $key: "", - $val: NaN, - $index: 0, - $oldIndex: 0, - $outer: {}, - $anchor: null - }, { - force: { - $key: 1, - $val: 1, - $index: 1 - } - }) - proxy.$id = generateID("$proxy$with") - return proxy -} - - -function proxyRecycler(cache, key, param) { - var proxy = cache[key] - if (proxy) { - var proxyPool = proxy.$id.indexOf("$proxy$each") === 0 ? eachProxyPool : withProxyPool - proxy.$outer = {} - - for (var i in proxy.$events) { - var a = proxy.$events[i] - if (Array.isArray(a)) { - a.length = 0 - if (i === param) { - proxy[param] = NaN - - } else if (i === "$val") { - proxy.$val = NaN - } - } - } - - if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { - proxyPool.pop() - } - delete cache[key] - } -} -/********************************************************************* - * 各种指令 * - **********************************************************************/ -//ms-skip绑定已经在scanTag 方法中实现 -avalon.directive("text", { - update: function (value) { - var elem = this.element - value = value == null ? "" : value //不在页面上显示undefined null - if (elem.nodeType === 3) { //绑定在文本节点上 - try { //IE对游离于DOM树外的节点赋值会报错 - elem.data = value - } catch (e) { - } - } else { //绑定在特性节点上 - if ("textContent" in elem) { - elem.textContent = value - } else { - elem.innerText = value - } - } - } -}) -function parseDisplay(nodeName, val) { - //用于取得此类标签的默认display值 - var key = "_" + nodeName - if (!parseDisplay[key]) { - var node = DOC.createElement(nodeName) - root.appendChild(node) - if (W3C) { - val = getComputedStyle(node, null).display - } else { - val = node.currentStyle.display - } - root.removeChild(node) - parseDisplay[key] = val - } - return parseDisplay[key] -} - -avalon.parseDisplay = parseDisplay - -avalon.directive("visible", { - init: function (binding) { - effectBinding(binding.element, binding) - }, - update: function (val) { - var binding = this, elem = this.element, stamp - var noEffect = !this.effectName - if (!this.stamp) { - stamp = this.stamp = +new Date - if (val) { - elem.style.display = binding.display || "" - if (avalon(elem).css("display") === "none") { - elem.style.display = binding.display = parseDisplay(elem.nodeName) - } - } else { - elem.style.display = "none" - } - return - } - stamp = this.stamp = +new Date - if (val) { - avalon.effect.apply(elem, 1, function () { - if (stamp !== binding.stamp) - return - var driver = elem.getAttribute("data-effect-driver") || "a" - - if (noEffect) {//不用动画时走这里 - elem.style.display = binding.display || "" - } - // "a", "t" - if (driver === "a" || driver === "t") { - if (avalon(elem).css("display") === "none") { - elem.style.display = binding.display || parseDisplay(elem.nodeName) - } - } - }) - } else { - avalon.effect.apply(elem, 0, function () { - if (stamp !== binding.stamp) - return - elem.style.display = "none" - }) - } - } -}) - -/********************************************************************* - * 自带过滤器 * - **********************************************************************/ -var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim -var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g -var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig -var rsanitize = { - a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, - img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, - form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig -} -var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g -var rnoalphanumeric = /([^\#-~| |!])/g; - -function numberFormat(number, decimals, point, thousands) { - //form http://phpjs.org/functions/number_format/ - //number 必需,要格式化的数字 - //decimals 可选,规定多少个小数位。 - //point 可选,规定用作小数点的字符串(默认为 . )。 - //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 - number = (number + '') - .replace(/[^0-9+\-Ee.]/g, '') - var n = !isFinite(+number) ? 0 : +number, - prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), - sep = thousands || ",", - dec = point || ".", - s = '', - toFixedFix = function(n, prec) { - var k = Math.pow(10, prec) - return '' + (Math.round(n * k) / k) - .toFixed(prec) - } - // Fix for IE parseFloat(0.55).toFixed(0) = 0; - s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) - .split('.') - if (s[0].length > 3) { - s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) - } - if ((s[1] || '') - .length < prec) { - s[1] = s[1] || '' - s[1] += new Array(prec - s[1].length + 1) - .join('0') - } - return s.join(dec) -} - - -var filters = avalon.filters = { - uppercase: function(str) { - return str.toUpperCase() - }, - lowercase: function(str) { - return str.toLowerCase() - }, - truncate: function(str, length, truncation) { - //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 - length = length || 30 - truncation = typeof truncation === "string" ? truncation : "..." - return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) - }, - $filter: function(val) { - for (var i = 1, n = arguments.length; i < n; i++) { - var array = arguments[i] - var fn = avalon.filters[array[0]] - if (typeof fn === "function") { - var arr = [val].concat(array.slice(1)) - val = fn.apply(null, arr) - } - } - return val - }, - camelize: camelize, - //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet - // chrome - // chrome - // IE67chrome - // IE67chrome - // IE67chrome - sanitize: function(str) { - return str.replace(rscripts, "").replace(ropen, function(a, b) { - var match = a.toLowerCase().match(/<(\w+)\s/) - if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 - var reg = rsanitize[match[1]] - if (reg) { - a = a.replace(reg, function(s, name, value) { - var quote = value.charAt(0) - return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line - }) - } - } - return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 - }) - }, - escape: function(str) { - //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < - return String(str). - replace(/&/g, '&'). - replace(rsurrogate, function(value) { - var hi = value.charCodeAt(0) - var low = value.charCodeAt(1) - return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' - }). - replace(rnoalphanumeric, function(value) { - return '&#' + value.charCodeAt(0) + ';' - }). - replace(//g, '>') - }, - currency: function(amount, symbol, fractionSize) { - return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) - }, - number: numberFormat -} -/* - 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) - 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) - 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) - 'MMMM': Month in year (January-December) - 'MMM': Month in year (Jan-Dec) - 'MM': Month in year, padded (01-12) - 'M': Month in year (1-12) - 'dd': Day in month, padded (01-31) - 'd': Day in month (1-31) - 'EEEE': Day in Week,(Sunday-Saturday) - 'EEE': Day in Week, (Sun-Sat) - 'HH': Hour in day, padded (00-23) - 'H': Hour in day (0-23) - 'hh': Hour in am/pm, padded (01-12) - 'h': Hour in am/pm, (1-12) - 'mm': Minute in hour, padded (00-59) - 'm': Minute in hour (0-59) - 'ss': Second in minute, padded (00-59) - 's': Second in minute (0-59) - 'a': am/pm marker - 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) - format string can also be one of the following predefined localizable formats: - - 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) - 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) - 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) - 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 - 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) - 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) - 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) - 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) - */ -new function() {// jshint ignore:line - function toInt(str) { - return parseInt(str, 10) || 0 - } - - function padNumber(num, digits, trim) { - var neg = "" - if (num < 0) { - neg = '-' - num = -num - } - num = "" + num - while (num.length < digits) - num = "0" + num - if (trim) - num = num.substr(num.length - digits) - return neg + num - } - - function dateGetter(name, size, offset, trim) { - return function(date) { - var value = date["get" + name]() - if (offset > 0 || value > -offset) - value += offset - if (value === 0 && offset === -12) { - value = 12 - } - return padNumber(value, size, trim) - } - } - - function dateStrGetter(name, shortForm) { - return function(date, formats) { - var value = date["get" + name]() - var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() - return formats[get][value] - } - } - - function timeZoneGetter(date) { - var zone = -1 * date.getTimezoneOffset() - var paddedZone = (zone >= 0) ? "+" : "" - paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) - return paddedZone - } - //取得上午下午 - - function ampmGetter(date, formats) { - return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] - } - var DATE_FORMATS = { - yyyy: dateGetter("FullYear", 4), - yy: dateGetter("FullYear", 2, 0, true), - y: dateGetter("FullYear", 1), - MMMM: dateStrGetter("Month"), - MMM: dateStrGetter("Month", true), - MM: dateGetter("Month", 2, 1), - M: dateGetter("Month", 1, 1), - dd: dateGetter("Date", 2), - d: dateGetter("Date", 1), - HH: dateGetter("Hours", 2), - H: dateGetter("Hours", 1), - hh: dateGetter("Hours", 2, -12), - h: dateGetter("Hours", 1, -12), - mm: dateGetter("Minutes", 2), - m: dateGetter("Minutes", 1), - ss: dateGetter("Seconds", 2), - s: dateGetter("Seconds", 1), - sss: dateGetter("Milliseconds", 3), - EEEE: dateStrGetter("Day"), - EEE: dateStrGetter("Day", true), - a: ampmGetter, - Z: timeZoneGetter - } - var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ - var raspnetjson = /^\/Date\((\d+)\)\/$/ - filters.date = function(date, format) { - var locate = filters.date.locate, - text = "", - parts = [], - fn, match - format = format || "mediumDate" - format = locate[format] || format - if (typeof date === "string") { - if (/^\d+$/.test(date)) { - date = toInt(date) - } else if (raspnetjson.test(date)) { - date = +RegExp.$1 - } else { - var trimDate = date.trim() - var dateArray = [0, 0, 0, 0, 0, 0, 0] - var oDate = new Date(0) - //取得年月日 - trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { - var array = c.length === 4 ? [c, a, b] : [a, b, c] - dateArray[0] = toInt(array[0]) //年 - dateArray[1] = toInt(array[1]) - 1 //月 - dateArray[2] = toInt(array[2]) //日 - return "" - }) - var dateSetter = oDate.setFullYear - var timeSetter = oDate.setHours - trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { - dateArray[3] = toInt(a) //小时 - dateArray[4] = toInt(b) //分钟 - dateArray[5] = toInt(c) //秒 - if (d) { //毫秒 - dateArray[6] = Math.round(parseFloat("0." + d) * 1000) - } - return "" - }) - var tzHour = 0 - var tzMin = 0 - trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { - dateSetter = oDate.setUTCFullYear - timeSetter = oDate.setUTCHours - if (symbol) { - tzHour = toInt(symbol + c) - tzMin = toInt(symbol + d) - } - return "" - }) - - dateArray[3] -= tzHour - dateArray[4] -= tzMin - dateSetter.apply(oDate, dateArray.slice(0, 3)) - timeSetter.apply(oDate, dateArray.slice(3)) - date = oDate - } - } - if (typeof date === "number") { - date = new Date(date) - } - if (avalon.type(date) !== "date") { - return - } - while (format) { - match = rdateFormat.exec(format) - if (match) { - parts = parts.concat(match.slice(1)) - format = parts.pop() - } else { - parts.push(format) - format = null - } - } - parts.forEach(function(value) { - fn = DATE_FORMATS[value] - text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") - }) - return text - } - var locate = { - AMPMS: { - 0: "上午", - 1: "下午" - }, - DAY: { - 0: "星期日", - 1: "星期一", - 2: "星期二", - 3: "星期三", - 4: "星期四", - 5: "星期五", - 6: "星期六" - }, - MONTH: { - 0: "1月", - 1: "2月", - 2: "3月", - 3: "4月", - 4: "5月", - 5: "6月", - 6: "7月", - 7: "8月", - 8: "9月", - 9: "10月", - 10: "11月", - 11: "12月" - }, - SHORTDAY: { - "0": "周日", - "1": "周一", - "2": "周二", - "3": "周三", - "4": "周四", - "5": "周五", - "6": "周六" - }, - fullDate: "y年M月d日EEEE", - longDate: "y年M月d日", - medium: "yyyy-M-d H:mm:ss", - mediumDate: "yyyy-M-d", - mediumTime: "H:mm:ss", - "short": "yy-M-d ah:mm", - shortDate: "yy-M-d", - shortTime: "ah:mm" - } - locate.SHORTMONTH = locate.MONTH - filters.date.locate = locate -}// jshint ignore:line -/********************************************************************* - * DOMReady * - **********************************************************************/ - -var readyList = [], - isReady -var fireReady = function (fn) { - isReady = true - var require = avalon.require - if (require && require.checkDeps) { - modules["domReady!"].state = 4 - require.checkDeps() - } - while (fn = readyList.shift()) { - fn(avalon) - } -} - -function doScrollCheck() { - try { //IE下通过doScrollCheck检测DOM树是否建完 - root.doScroll("left") - fireReady() - } catch (e) { - setTimeout(doScrollCheck) - } -} - -if (DOC.readyState === "complete") { - setTimeout(fireReady) //如果在domReady之外加载 -} else if (W3C) { - DOC.addEventListener("DOMContentLoaded", fireReady) -} else { - DOC.attachEvent("onreadystatechange", function () { - if (DOC.readyState === "complete") { - fireReady() - } - }) - try { - var isTop = window.frameElement === null - } catch (e) {} - if (root.doScroll && isTop && window.external) { //fix IE iframe BUG - doScrollCheck() - } -} -avalon.bind(window, "load", fireReady) - -avalon.ready = function (fn) { - if (!isReady) { - readyList.push(fn) - } else { - fn(avalon) - } -} - -avalon.config({ - loader: true -}) - -avalon.ready(function () { - avalon.scan(DOC.body) -}) - - -// Register as a named AMD module, since avalon can be concatenated with other -// files that may use define, but not via a proper concatenation script that -// understands anonymous AMD modules. A named AMD is safest and most robust -// way to register. Lowercase avalon is used because AMD module names are -// derived from file names, and Avalon is normally delivered in a lowercase -// file name. Do this after creating the global so that if an AMD module wants -// to call noConflict to hide this version of avalon, it will work. - -// Note that for maximum portability, libraries that are not avalon should -// declare themselves as anonymous modules, and avoid setting a global if an -// AMD loader is present. avalon is a special case. For more information, see -// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon - if (typeof define === "function" && define.amd) { - define("avalon", [], function() { - return avalon - }) - } -// Map over avalon in case of overwrite - var _avalon = window.avalon - avalon.noConflict = function(deep) { - if (deep && window.avalon === avalon) { - window.avalon = _avalon - } - return avalon - } -// Expose avalon identifiers, even in AMD -// and CommonJS for browser emulators - if (noGlobal === void 0) { - window.avalon = avalon - } - return avalon - -})); \ No newline at end of file diff --git a/template/src/admin/problem/add_problem.html b/template/src/admin/problem/add_problem.html index 0a706ad9..5c6b430e 100644 --- a/template/src/admin/problem/add_problem.html +++ b/template/src/admin/problem/add_problem.html @@ -104,7 +104,7 @@ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
1.in 1.out 2.in 2.out
-

上传进度%

+

上传进度%

diff --git a/template/src/admin/problem/edit_problem.html b/template/src/admin/problem/edit_problem.html index b8a6fa8b..617573e7 100644 --- a/template/src/admin/problem/edit_problem.html +++ b/template/src/admin/problem/edit_problem.html @@ -110,7 +110,7 @@ 请将所有测试用例打包在一个文件中上传,所有文件要在压缩包的根目录,且输入输出文件名要以从1开始连续数字标识要对应例如:
1.in 1.out 2.in 2.out
-

上传进度%

+

上传进度%

编号
From 5b6b1bb60e11ea12899474cba5d10a56749a2a7d Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Wed, 28 Oct 2015 11:10:54 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20avalon=E4=B8=BA1.5.5?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E5=A4=8D=E4=BA=86=E9=83=A8=E5=88=86=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/lib/avalon/avalon.js | 5860 ++++++++++++++++++++++++++++ 1 file changed, 5860 insertions(+) create mode 100644 static/src/js/lib/avalon/avalon.js diff --git a/static/src/js/lib/avalon/avalon.js b/static/src/js/lib/avalon/avalon.js new file mode 100644 index 00000000..296c2457 --- /dev/null +++ b/static/src/js/lib/avalon/avalon.js @@ -0,0 +1,5860 @@ +/*================================================== + Copyright (c) 2013-2015 司徒正美 and other contributors + http://www.cnblogs.com/rubylouvre/ + https://github.com/RubyLouvre + http://weibo.com/jslouvre/ + + Released under the MIT license + avalon.shim.js 1.5.5 built in 2015.10.27 + support IE6+ and other browsers + ==================================================*/ +(function(global, factory) { + + if (typeof module === "object" && typeof module.exports === "object") { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get avalon. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var avalon = require("avalon")(window); + module.exports = global.document ? factory(global, true) : function(w) { + if (!w.document) { + throw new Error("Avalon requires a window with a document") + } + return factory(w) + } + } else { + factory(global) + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function(window, noGlobal){ + +/********************************************************************* + * 全局变量及方法 * + **********************************************************************/ +var expose = new Date() - 0 +//http://stackoverflow.com/questions/7290086/javascript-use-strict-and-nicks-find-global-function +var DOC = window.document +var head = DOC.getElementsByTagName("head")[0] //HEAD元素 +var ifGroup = head.insertBefore(document.createElement("avalon"), head.firstChild) //避免IE6 base标签BUG +ifGroup.innerHTML = "X" +ifGroup.setAttribute("ms-skip", "1") +ifGroup.className = "avalonHide" +var rnative = /\[native code\]/ //判定是否原生函数 +function log() { + if (window.console && avalon.config.debug) { + // http://stackoverflow.com/questions/8785624/how-to-safely-wrap-console-log + Function.apply.call(console.log, console, arguments) + } +} + + +var subscribers = "$" + expose + +var stopRepeatAssign = false +var nullObject = {} //作用类似于noop,只用于代码防御,千万不要在它上面添加属性 +var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach +var rw20g = /\w+/g +var rcomplexType = /^(?:object|array)$/ +var rsvg = /^\[object SVG\w*Element\]$/ +var rwindow = /^\[object (?:Window|DOMWindow|global)\]$/ +var oproto = Object.prototype +var ohasOwn = oproto.hasOwnProperty +var serialize = oproto.toString +var ap = Array.prototype +var aslice = ap.slice +var W3C = window.dispatchEvent +var root = DOC.documentElement +var avalonFragment = DOC.createDocumentFragment() +var cinerator = DOC.createElement("div") +var class2type = {} +"Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) { + class2type["[object " + name + "]"] = name.toLowerCase() +}) +function scpCompile(array){ + return Function.apply(noop,array) +} +function noop(){} + +function oneObject(array, val) { + if (typeof array === "string") { + array = array.match(rword) || [] + } + var result = {}, + value = val !== void 0 ? val : 1 + for (var i = 0, n = array.length; i < n; i++) { + result[array[i]] = value + } + return result +} + +//生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript +var generateID = function (prefix) { + prefix = prefix || "avalon" + return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix) +} +function IE() { + if (window.VBArray) { + var mode = document.documentMode + return mode ? mode : window.XMLHttpRequest ? 7 : 6 + } else { + return NaN + } +} +var IEVersion = IE() + +avalon = function (el) { //创建jQuery式的无new 实例化结构 + return new avalon.init(el) +} + + +/*视浏览器情况采用最快的异步回调*/ +avalon.nextTick = new function () {// jshint ignore:line + var tickImmediate = window.setImmediate + var tickObserver = window.MutationObserver + if (tickImmediate) { + return tickImmediate.bind(window) + } + + var queue = [] + function callback() { + var n = queue.length + for (var i = 0; i < n; i++) { + queue[i]() + } + queue = queue.slice(n) + } + + if (tickObserver) { + var node = document.createTextNode("avalon") + new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line + var bool = false + return function (fn) { + queue.push(fn) + bool = !bool + node.data = bool + } + } + + + return function (fn) { + setTimeout(fn, 4) + } +}// jshint ignore:line +/********************************************************************* + * avalon的静态方法定义区 * + **********************************************************************/ +avalon.init = function (el) { + this[0] = this.element = el +} +avalon.fn = avalon.prototype = avalon.init.prototype + +avalon.type = function (obj) { //取得目标的类型 + if (obj == null) { + return String(obj) + } + // 早期的webkit内核浏览器实现了已废弃的ecma262v4标准,可以将正则字面量当作函数使用,因此typeof在判定正则时会返回function + return typeof obj === "object" || typeof obj === "function" ? + class2type[serialize.call(obj)] || "object" : + typeof obj +} + +var isFunction = typeof alert === "object" ? function (fn) { + try { + return /^\s*\bfunction\b/.test(fn + "") + } catch (e) { + return false + } +} : function (fn) { + return serialize.call(fn) === "[object Function]" +} +avalon.isFunction = isFunction + +avalon.isWindow = function (obj) { + if (!obj) + return false + // 利用IE678 window == document为true,document == window竟然为false的神奇特性 + // 标准浏览器及IE9,IE10等使用 正则检测 + return obj == obj.document && obj.document != obj //jshint ignore:line +} + +function isWindow(obj) { + return rwindow.test(serialize.call(obj)) +} +if (isWindow(window)) { + avalon.isWindow = isWindow +} +var enu +for (enu in avalon({})) { + break +} +var enumerateBUG = enu !== "0" //IE6下为true, 其他为false +/*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/ +avalon.isPlainObject = function (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) +} +if (rnative.test(Object.getPrototypeOf)) { + avalon.isPlainObject = function (obj) { + // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过 + return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto + } +} +//与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" && !isFunction(target)) { + 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)))) { + + 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 +} + +function _number(a, len) { //用于模拟slice, splice的效果 + a = Math.floor(a) || 0 + return a < 0 ? Math.max(len + a, 0) : Math.min(a, len); +} +avalon.mix({ + rword: rword, + subscribers: subscribers, + version: 1.55, + ui: {}, + log: log, + slice: W3C ? function (nodes, start, end) { + return aslice.call(nodes, start, end) + } : function (nodes, start, end) { + var ret = [] + var len = nodes.length + if (end === void 0) + end = len + if (typeof end === "number" && isFinite(end)) { + start = _number(start, len) + end = _number(end, len) + for (var i = start; i < end; ++i) { + ret[i - start] = nodes[i] + } + } + return ret + }, + noop: noop, + /*如果不用Error对象封装一下,str在控制台下可能会乱码*/ + error: function (str, e) { + throw (e || Error)(str) + }, + /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/ + oneObject: oneObject, + /* avalon.range(10) + => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] + avalon.range(1, 11) + => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + avalon.range(0, 30, 5) + => [0, 5, 10, 15, 20, 25] + avalon.range(0, -10, -1) + => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9] + avalon.range(0) + => []*/ + range: function (start, end, step) { // 用于生成整数数组 + step || (step = 1) + if (end == null) { + end = start || 0 + start = 0 + } + var index = -1, + length = Math.max(0, Math.ceil((end - start) / step)), + result = new Array(length) + while (++index < length) { + result[index] = start + start += step + } + return result + }, + eventHooks: {}, + /*绑定事件*/ + bind: function (el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + if (typeof hook === "object") { + type = hook.type || type + phase = hook.phase || !!phase + fn = hook.fn ? hook.fn(el, fn) : fn + } + var callback = W3C ? fn : function (e) { + fn.call(el, fixEvent(e)); + } + if (W3C) { + el.addEventListener(type, callback, phase) + } else { + el.attachEvent("on" + type, callback) + } + return callback + }, + /*卸载事件*/ + unbind: function (el, type, fn, phase) { + var hooks = avalon.eventHooks + var hook = hooks[type] + var callback = fn || noop + if (typeof hook === "object") { + type = hook.type || type + phase = hook.phase || !!phase + } + if (W3C) { + el.removeEventListener(type, callback, phase) + } else { + el.detachEvent("on" + type, callback) + } + }, + /*读写删除元素节点的样式*/ + css: function (node, name, value) { + if (node instanceof avalon) { + node = node[0] + } + var prop = /[_-]/.test(name) ? camelize(name) : name, + fn + name = avalon.cssName(prop) || prop + if (value === void 0 || typeof value === "boolean") { //获取样式 + fn = cssHooks[prop + ":get"] || cssHooks["@:get"] + if (name === "background") { + name = "backgroundColor" + } + var val = fn(node, name) + return value === true ? parseFloat(val) || 0 : val + } else if (value === "") { //请除样式 + node.style[name] = "" + } else { //设置样式 + if (value == null || value !== value) { + return + } + if (isFinite(value) && !avalon.cssNumber[prop]) { + value += "px" + } + fn = cssHooks[prop + ":set"] || cssHooks["@:set"] + fn(node, name, value) + } + }, + /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/ + 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 + } + } + } + } + }, + //收集元素的data-{{prefix}}-*属性,并转换为对象 + getWidgetData: function (elem, prefix) { + var raw = avalon(elem).data() + var result = {} + for (var i in raw) { + if (i.indexOf(prefix) === 0) { + result[i.replace(prefix, "").replace(/\w/, function (a) { + return a.toLowerCase() + })] = raw[i] + } + } + return result + }, + Array: { + /*只有当前数组不存在此元素时只添加它*/ + ensure: function (target, item) { + if (target.indexOf(item) === -1) { + return target.push(item) + } + }, + /*移除数组中指定位置的元素,返回布尔表示成功与否*/ + removeAt: function (target, index) { + return !!target.splice(index, 1).length + }, + /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/ + remove: function (target, item) { + var index = target.indexOf(item) + if (~index) + return avalon.Array.removeAt(target, index) + return false + } + } +}) + +var bindingHandlers = avalon.bindingHandlers = {} +var bindingExecutors = avalon.bindingExecutors = {} + +var directives = avalon.directives = {} +avalon.directive = function (name, obj) { + bindingHandlers[name] = obj.init = (obj.init || noop) + bindingExecutors[name] = obj.update = (obj.update || noop) + + return directives[name] = obj +} +/*判定是否类数组,如节点集合,纯数组,arguments与拥有非负整数的length属性的纯JS对象*/ +function isArrayLike(obj) { + if (!obj) + return false + var n = obj.length + if (n === (n >>> 0)) { //检测length属性是否为非负整数 + var type = serialize.call(obj).slice(8, -1) + if (/(?:regexp|string|function|window|global)$/i.test(type)) + return false + if (type === "Array") + return true + try { + if ({}.propertyIsEnumerable.call(obj, "length") === false) { //如果是原生对象 + return /^\s?function/.test(obj.item || obj.callee) + } + return true + } catch (e) { //IE的NodeList直接抛错 + return !obj.window //IE6-8 window + } + } + return false +} + + +// https://github.com/rsms/js-lru +var Cache = new function() {// jshint ignore:line + function LRU(maxLength) { + this.size = 0 + this.limit = maxLength + 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) { + this.tail.newer = entry + entry.older = this.tail + } else { + this.head = entry + } + this.tail = entry + if (this.size === this.limit) { + this.shift() + } else { + this.size++ + } + return value + } + + p.shift = function() { + var entry = this.head + if (entry) { + this.head = this.head.newer + this.head.older = + entry.newer = + entry.older = + this._keymap[entry.key] = void 0 + delete this._keymap[entry.key] //#1029 + } + } + p.get = function(key) { + var entry = this._keymap[key] + if (entry === void 0) + return + if (entry === this.tail) { + return entry.value + } + // HEAD--------------TAIL + // <.older .newer> + // <--- add direction -- + // A B C E + if (entry.newer) { + if (entry === this.head) { + this.head = entry.newer + } + entry.newer.older = entry.older // C <-- E. + } + if (entry.older) { + entry.older.newer = entry.newer // C. --> E + } + entry.newer = void 0 // D --x + entry.older = this.tail // D. --> E + if (this.tail) { + this.tail.newer = entry // E. <-- D + } + this.tail = entry + return entry.value + } + return LRU +}// jshint ignore:line + +/********************************************************************* + * javascript 底层补丁 * + **********************************************************************/ +if (!"司徒正美".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 (!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 (!Array.isArray) { + Array.isArray = function (a) { + return serialize.call(a) === "[object Array]" + } +} + +if (!noop.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) + } + } +} + +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 (!rnative.test([].map)) { + avalon.mix(ap, { + //定位操作,返回数组中第一个等于给定参数的元素的索引值。 + 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') + }) +} +/********************************************************************* + * DOM 底层补丁 * + **********************************************************************/ + +function fixContains(root, el) { + try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错 + while ((el = el.parentNode)) + if (el === root) + return true + return false + } catch (e) { + return false + } +} +avalon.contains = fixContains +//IE6-11的文档对象没有contains +if (!DOC.contains) { + DOC.contains = function (b) { + return fixContains(DOC, b) + } +} + +function outerHTML() { + return new XMLSerializer().serializeToString(this) +} + +if (window.SVGElement) { + //safari5+是把contains方法放在Element.prototype上而不是Node.prototype + if (!DOC.createTextNode("x").contains) { + Node.prototype.contains = function (arg) {//IE6-8没有Node对象 + return !!(this.compareDocumentPosition(arg) & 16) + } + } + var svgns = "http://www.w3.org/2000/svg" + var svg = DOC.createElementNS(svgns, "svg") + svg.innerHTML = '' + if (!rsvg.test(svg.firstChild)) { // #409 + function enumerateNode(node, targetNode) {// jshint ignore:line + if (node && node.childNodes) { + var nodes = node.childNodes + for (var i = 0, el; el = nodes[i++]; ) { + if (el.tagName) { + var svg = DOC.createElementNS(svgns, + el.tagName.toLowerCase()) + ap.forEach.call(el.attributes, function (attr) { + svg.setAttribute(attr.name, attr.value) //复制属性 + })// jshint ignore:line + // 递归处理子节点 + enumerateNode(el, svg) + targetNode.appendChild(svg) + } + } + } + } + Object.defineProperties(SVGElement.prototype, { + "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性 + enumerable: true, + configurable: true, + get: outerHTML, + set: function (html) { + var tagName = this.tagName.toLowerCase(), + par = this.parentNode, + frag = avalon.parseHTML(html) + // 操作的svg,直接插入 + if (tagName === "svg") { + par.insertBefore(frag, this) + // svg节点的子节点类似 + } else { + var newFrag = DOC.createDocumentFragment() + enumerateNode(frag, newFrag) + par.insertBefore(newFrag, this) + } + par.removeChild(this) + } + }, + "innerHTML": { + enumerable: true, + configurable: true, + get: function () { + var s = this.outerHTML + var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i") + var rclose = new RegExp("<\/" + this.nodeName + ">$", "i") + return s.replace(ropen, "").replace(rclose, "") + }, + set: function (html) { + if (avalon.clearHTML) { + avalon.clearHTML(this) + var frag = avalon.parseHTML(html) + enumerateNode(frag, this) + } + } + } + }) + } +} +if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML + HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML); +} + + +//============================= event binding ======================= +var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/ +function fixEvent(event) { + var ret = {} + for (var i in event) { + ret[i] = event[i] + } + var target = ret.target = event.srcElement + if (event.type.indexOf("key") === 0) { + ret.which = event.charCode != null ? event.charCode : event.keyCode + } else if (rmouseEvent.test(event.type)) { + var doc = target.ownerDocument || DOC + var box = doc.compatMode === "BackCompat" ? doc.body : doc.documentElement + ret.pageX = event.clientX + (box.scrollLeft >> 0) - (box.clientLeft >> 0) + ret.pageY = event.clientY + (box.scrollTop >> 0) - (box.clientTop >> 0) + ret.wheelDeltaY = ret.wheelDelta + ret.wheelDeltaX = 0 + } + ret.timeStamp = new Date() - 0 + ret.originalEvent = event + ret.preventDefault = function () { //阻止默认行为 + event.returnValue = false + } + ret.stopPropagation = function () { //阻止事件在DOM树中的传播 + event.cancelBubble = true + } + return ret +} + +var eventHooks = avalon.eventHooks +//针对firefox, chrome修正mouseenter, mouseleave +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(elem, e) + } + } + } + } + }) +} +//针对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 DOC.createElement("input"))) { + eventHooks.input = { + type: "propertychange", + deel: function (elem, fn) { + return function (e) { + if (e.propertyName === "value") { + e.type = "input" + return fn.call(elem, e) + } + } + } + } +} +if (DOC.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 = DOC.onwheel !== void 0 ? "wheel" : "DOMMouseScroll" + var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail" + eventHooks.mousewheel = { + type: fixWheelType, + fn: function (elem, fn) { + return function (e) { + e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120 + e.wheelDeltaX = 0 + if (Object.defineProperty) { + Object.defineProperty(e, "type", { + value: "mousewheel" + }) + } + fn.call(elem, e) + } + } + } +} + + + +/********************************************************************* + * 配置系统 * + **********************************************************************/ + +function kernel(settings) { + for (var p in settings) { + if (!ohasOwn.call(settings, p)) + continue + var val = settings[p] + if (typeof kernel.plugins[p] === "function") { + kernel.plugins[p](val) + } else if (typeof kernel[p] === "object") { + avalon.mix(kernel[p], val) + } else { + kernel[p] = val + } + } + return this +} +var openTag, closeTag, rexpr, rexprg, rbind, rregexp = /[-.*+?^${}()|[\]\/\\]/g + +function escapeRegExp(target) { + //http://stevenlevithan.com/regex/xregexp/ + //将字符串安全格式化为正则表达式的源码 + return (target + "").replace(rregexp, "\\$&") +} + +var plugins = { + interpolate: function (array) { + openTag = array[0] + closeTag = array[1] + if (openTag === closeTag) { + throw new SyntaxError("openTag!==closeTag") + var test = openTag + "test" + closeTag + cinerator.innerHTML = test + if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("<") > -1) { + throw new SyntaxError("此定界符不合法") + } + cinerator.innerHTML = "" + } + kernel.openTag = openTag + kernel.closeTag = closeTag + var o = escapeRegExp(openTag), + c = escapeRegExp(closeTag) + rexpr = new RegExp(o + "(.*?)" + c) + rexprg = new RegExp(o + "(.*?)" + c, "g") + rbind = new RegExp(o + ".*?" + c + "|\\sms-") + } +} +kernel.async =true +kernel.debug = true +kernel.plugins = plugins +kernel.plugins['interpolate'](["{{", "}}"]) +kernel.paths = {} +kernel.shim = {} +kernel.maxRepeatSize = 100 +avalon.config = kernel +function $watch(expr, binding) { + var $events = this.$events || (this.$events = {}) + + var queue = $events[expr] || ($events[expr] = []) + if (typeof binding === "function") { + var backup = binding + backup.uniqueNumber = Math.random() + binding = { + element: root, + type: "user-watcher", + handler: noop, + vmodels: [this], + expr: expr, + uniqueNumber: backup.uniqueNumber + } + binding.wildcard = /\*/.test(expr) + } + + if (!binding.update) { + if (/\w\.*\B/.test(expr)) { + binding.getter = noop + var host = this + binding.update = function () { + var args = this.fireArgs || [] + if (args[2]) + binding.handler.apply(host, args) + delete this.fireArgs + } + queue.sync = true + avalon.Array.ensure(queue, binding) + } else { + avalon.injectBinding(binding) + } + if (backup) { + binding.handler = backup + } + } else if (!binding.oneTime) { + avalon.Array.ensure(queue, binding) + } + return function () { + binding.update = binding.getter = binding.handler = noop + binding.element = DOC.createElement("a") + } +} +function $emit(key, args) { + var event = this.$events + if (event && event[key]) { + if (args) { + args[2] = key + } + var arr = event[key] + notifySubscribers(arr, args) + var parent = this.$up + if (parent) { + if (this.$pathname) { + $emit.call(parent, this.$pathname + "." + key, args)//以确切的值往上冒泡 + } + + $emit.call(parent, "*." + key, args)//以模糊的值往上冒泡 + } + } else { + parent = this.$up + + if(this.$ups ){ + for(var i in this.$ups){ + $emit.call(this.$ups[i], i+"."+key, args)//以确切的值往上冒泡 + } + return + } + if (parent) { + var p = this.$pathname + if (p === "") + p = "*" + var path = p + "." + key + arr = path.split(".") + if (arr.indexOf("*") === -1) { + $emit.call(parent, path, args)//以确切的值往上冒泡 + arr[1] = "*" + $emit.call(parent, arr.join("."), args)//以模糊的值往上冒泡 + } else { + $emit.call(parent, path, args)//以确切的值往上冒泡 + } + } + } +} + + +function collectDependency(el, key) { + do { + if (el.$watch) { + var e = el.$events || (el.$events = {}) + var array = e[key] || (e[key] = []) + dependencyDetection.collectDependency(array) + return + } + el = el.$up + if (el) { + key = el.$pathname + "." + key + } else { + break + } + + } while (true) +} + + +function notifySubscribers(subs, args) { + if (!subs) + return + if (new Date() - beginTime > 444 && typeof subs[0] === "object") { + rejectDisposeQueue() + } + var users = [], renders = [] + for (var i = 0, sub; sub = subs[i++]; ) { + if (sub.type === "user-watcher") { + users.push(sub) + } else { + renders.push(sub) + } + + } + if (kernel.async) { + buffer.render()//1 + for (i = 0; sub = renders[i++]; ) { + if (sub.update) { + var uuid = getUid(sub) + if (!buffer.queue[uuid]) { + buffer.queue[uuid] = 1 + buffer.queue.push(sub) + } + } + } + } else { + for (i = 0; sub = renders[i++]; ) { + if (sub.update) { + sub.update()//最小化刷新DOM树 + } + } + } + for (i = 0; sub = users[i++]; ) { + if (args && args[2] === sub.expr || sub.wildcard) { + sub.fireArgs = args + } + sub.update() + } +} +//avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM) +var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里 +avalon.define = function (source) { + var $id = source.$id + if (!$id) { + log("warning: vm必须指定$id") + } + var vmodel = modelFactory(source) + vmodel.$id = $id + return VMODELS[$id] = vmodel +} + +//一些不需要被监听的属性 +var $$skipArray = oneObject("$id,$watch,$fire,$events,$model,$skipArray,$active,$pathname,$up,$track,$accessors,$ups") +var defineProperty = Object.defineProperty +var canHideOwn = true +//如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8 +//标准浏览器使用__defineGetter__, __defineSetter__实现 +try { + defineProperty({}, "_", { + value: "x" + }) + var defineProperties = Object.defineProperties +} catch (e) { + canHideOwn = false +} + +function modelFactory(source, options) { + options = options || {} + options.watch = true + return observeObject(source, options) +} + +//监听对象属性值的变化(注意,数组元素不是数组的属性),通过对劫持当前对象的访问器实现 +//监听对象或数组的结构变化, 对对象的键值对进行增删重排, 或对数组的进行增删重排,都属于这范畴 +// 通过比较前后代理VM顺序实现 +function Component() { +} + +function observeObject(source, options) { + if (!source || (source.$id && source.$accessors)) { + return source + } + //source为原对象,不能是元素节点或null + //options,可选,配置对象,里面有old, force, watch这三个属性 + options = options || nullObject + var force = options.force || nullObject + var old = options.old + var oldAccessors = old && old.$accessors || nullObject + var $vmodel = new Component() //要返回的对象, 它在IE6-8下可能被偷龙转凤 + var accessors = {} //监控属性 + var hasOwn = {} + var skip = [] + var simple = [] + var $skipArray = {} + if (source.$skipArray) { + $skipArray = oneObject(source.$skipArray) + delete source.$skipArray + } + //处理计算属性 + var computed = source.$computed + if (computed) { + delete source.$computed + for (var name in computed) { + hasOwn[name] = true; + (function (key, value) { + var old + accessors[key] = { + get: function () { + return old = value.get.call(this) + }, + set: function (x) { + if (typeof value.set === "function") { + var older = old + value.set.call(this, x) + var newer = this[key] + if (this.$fire && (newer !== older)) { + this.$fire(key, newer, older) + } + } + }, + enumerable: true, + configurable: true + } + })(name, computed[name])// jshint ignore:line + } + } + + + for (name in source) { + var value = source[name] + if (!$$skipArray[name]) + hasOwn[name] = true + if (typeof value === "function" || (value && value.nodeType) || + (!force[name] && (name.charAt(0) === "$" || $$skipArray[name] || $skipArray[name]))) { + skip.push(name) + } else if (isComputed(value)) { + log("warning:计算属性建议放在$computed对象中统一定义"); + (function (key, value) { + var old + accessors[key] = { + get: function () { + return old = value.get.call(this) + }, + set: function (x) { + if (typeof value.set === "function") { + var older = old + value.set.call(this, x) + var newer = this[key] + if (this.$fire && (newer !== older)) { + this.$fire(key, newer, older) + } + } + }, + enumerable: true, + configurable: true + } + })(name, value)// jshint ignore:line + } else { + simple.push(name) + if (oldAccessors[name]) { + accessors[name] = oldAccessors[name] + } else { + accessors[name] = makeGetSet(name, value) + } + } + } + + + accessors["$model"] = $modelDescriptor + $vmodel = defineProperties($vmodel, accessors, source) + function trackBy(name) { + return hasOwn[name] === true + } + skip.forEach(function (name) { + $vmodel[name] = source[name] + }) + + /* jshint ignore:start */ + hideProperty($vmodel, "$ups", null) + hideProperty($vmodel, "$id", "anonymous") + hideProperty($vmodel, "$up", old ? old.$up : null) + hideProperty($vmodel, "$track", Object.keys(hasOwn)) + hideProperty($vmodel, "$active", false) + hideProperty($vmodel, "$pathname", old ? old.$pathname : "") + hideProperty($vmodel, "$accessors", accessors) + hideProperty($vmodel, "hasOwnProperty", trackBy) + if (options.watch) { + hideProperty($vmodel, "$watch", function () { + return $watch.apply($vmodel, arguments) + }) + hideProperty($vmodel, "$fire", function (path, a) { + if(path.indexOf("all!") === 0 ){ + var ee = path.slice(4) + for(var i in avalon.vmodels){ + var v = avalon.vmodels[i] + v.$fire && v.$fire.apply(v, [ee, a]) + } + }else{ + $emit.call($vmodel, path, [a]) + } + }) + } + /* jshint ignore:end */ + //必须设置了$active,$events + simple.forEach(function (name) { + var oldVal = old && old[name] + var val = $vmodel[name] = source[name] + if (val && typeof val === "object") { + val.$up = $vmodel + val.$pathname = name + } + $emit.call($vmodel, name, [val,oldVal]) + }) + for (name in computed) { + value = $vmodel[name] + } + $vmodel.$active = true + return $vmodel +} +/* + 新的VM拥有如下私有属性 + $id: vm.id + $events: 放置$watch回调与绑定对象 + $watch: 增强版$watch + $fire: 触发$watch回调 + $track:一个数组,里面包含用户定义的所有键名 + $active:boolean,false时防止依赖收集 + $model:返回一个纯净的JS对象 + $accessors:放置所有读写器的数据描述对象 + $up:返回其上级对象 + $pathname:返回此对象在上级对象的名字,注意,数组元素的$pathname为空字符串 + ============================= + $skipArray:用于指定不可监听的属性,但VM生成是没有此属性的 + */ +function isComputed(val) {//speed up! + if (val && typeof val === "object") { + for (var i in val) { + if (i !== "get" && i !== "set") { + return false + } + } + return typeof val.get === "function" + } +} +function makeGetSet(key, value) { + var childVm + value = NaN + return { + get: function () { + if (this.$active) { + collectDependency(this, key) + } + return value + }, + set: function (newVal) { + if (value === newVal) + return + var oldValue = value + childVm = observe(newVal, value) + if (childVm) { + value = childVm + } else { + childVm = void 0 + value = newVal + } + + if (Object(childVm) === childVm) { + childVm.$pathname = key + childVm.$up = this + } + if (this.$active) { + $emit.call(this, key, [value, oldValue]) + } + }, + enumerable: true, + configurable: true + } +} + +function observe(obj, old, hasReturn, watch) { + if (Array.isArray(obj)) { + return observeArray(obj, old, watch) + } else if (avalon.isPlainObject(obj)) { + if (old && typeof old === 'object') { + var keys = getKeys(obj) + var keys2 = getKeys(old) + if (keys.join(";") === keys2.join(";")) { + for (var i in obj) { + if (obj.hasOwnProperty(i)) { + old[i] = obj[i] + } + } + return old + } + old.$active = false + } + return observeObject(obj, { + old: old, + watch: watch + }) + } + if (hasReturn) { + return obj + } +} +var getKeys = rnative.test(Object.key) ? Object.key : function (a) { + var ret = [] + for (var i in a) { + if (a.hasOwnProperty(i) && !$$skipArray[i]) { + ret.push(i) + } + } + return ret +} +function observeArray(array, old, watch) { + if (old) { + var args = [0, old.length].concat(array) + old.splice.apply(old, args) + return old + } else { + for (var i in newProto) { + array[i] = newProto[i] + } + hideProperty(array, "$up", null) + hideProperty(array, "$pathname", "") + hideProperty(array, "$track", createTrack(array.length)) + + array._ = observeObject({ + length: NaN + }, { + watch: true + }) + array._.length = array.length + array._.$watch("length", function (a, b) { + $emit.call(array.$up, array.$pathname + ".length", [a, b]) + }) + if (watch) { + hideProperty(array, "$watch", function () { + return $watch.apply(array, arguments) + }) + } + + if (W3C) { + Object.defineProperty(array, "$model", $modelDescriptor) + } else { + array.$model = toJson(array) + } + for (var j = 0, n = array.length; j < n; j++) { + var el = array[j] = observe(array[j], 0, 1, 1) + if (Object(el) === el) {//#1077 + el.$up = array + } + } + + return array + } +} + +function hideProperty(host, name, value) { + if (canHideOwn) { + Object.defineProperty(host, name, { + value: value, + writable: true, + enumerable: false, + configurable: true + }) + } else { + host[name] = value + } +} + +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) + } + } + return obj + } + return val +} + +var $modelDescriptor = { + get: function () { + return toJson(this) + }, + set: noop, + enumerable: false, + configurable: true +} + + +//===================修复浏览器对Object.defineProperties的支持================= +if (!canHideOwn) { + 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 + } + defineProperties = function (obj, descs) { + for (var prop in descs) { + if (descs.hasOwnProperty(prop)) { + defineProperty(obj, prop, descs[prop]) + } + } + return obj + } + } + if (IEVersion) { + 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 = {} + + //添加访问器属性 + 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 = generateID("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 //得到其产品 + } + } +} + +/********************************************************************* + * 监控数组(与ms-each, ms-repeat配合使用) * + **********************************************************************/ + +var arrayMethods = ['push', 'pop', 'shift', 'unshift', 'splice'] +var arrayProto = Array.prototype +var newProto = { + notify: function () { + $emit.call(this.$up, this.$pathname) + }, + set: function (index, val) { + if (((index >>> 0) === index) && this[index] !== val) { + if (index > this.length) { + throw Error(index + "set方法的第一个参数不能大于原数组长度") + } + $emit.call(this.$up, this.$pathname + ".*", [val, this[index]]) + 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 [] + }, + size: function () { //取得数组长度,这个函数可以同步视图,length不能 + return this._.length + }, + removeAll: function (all) { //移除N个元素 + if (Array.isArray(all)) { + for (var i = this.length - 1; i >= 0; i--) { + if (all.indexOf(this[i]) !== -1) { + _splice.call(this.$track, i, 1) + _splice.call(this, i, 1) + + } + } + } else if (typeof all === "function") { + for (i = this.length - 1; i >= 0; i--) { + var el = this[i] + if (all(el, i)) { + _splice.call(this.$track, i, 1) + _splice.call(this, i, 1) + + } + } + } else { + _splice.call(this.$track, 0, this.length) + _splice.call(this, 0, this.length) + + } + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + this._.length = this.length + }, + clear: function () { + return this.removeAll() + } +} +var _splice = arrayProto.splice +arrayMethods.forEach(function (method) { + var original = arrayProto[method] + newProto[method] = function () { + // 继续尝试劫持数组元素的属性 + var args = [] + for (var i = 0, n = arguments.length; i < n; i++) { + args[i] = observe(arguments[i], 0, 1, 1) + } + var result = original.apply(this, args) + addTrack(this.$track, method, args) + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + this._.length = this.length + return result + } +}) + +"sort,reverse".replace(rword, function (method) { + newProto[method] = function () { + var oldArray = this.concat() //保持原来状态的旧数组 + var newArray = this + var mask = Math.random() + var indexes = [] + var hasSort = false + arrayProto[method].apply(newArray, arguments) //排序 + for (var i = 0, n = oldArray.length; i < n; i++) { + var neo = newArray[i] + var old = oldArray[i] + if (neo === old) { + indexes.push(i) + } else { + var index = oldArray.indexOf(neo) + indexes.push(index)//得到新数组的每个元素在旧数组对应的位置 + oldArray[index] = mask //屏蔽已经找过的元素 + hasSort = true + } + } + if (hasSort) { + sortByIndex(this.$track, indexes) + if (!W3C) { + this.$model = toJson(this) + } + this.notify() + } + return this + } +}) + +function sortByIndex(array, indexes) { + var map = {}; + for (var i = 0, n = indexes.length; i < n; i++) { + map[i] = array[i] + var j = indexes[i] + if (j in map) { + array[i] = map[j] + delete map[j] + } else { + array[i] = array[j] + } + } +} + +function createTrack(n) { + var ret = [] + for (var i = 0; i < n; i++) { + ret[i] = generateID("$proxy$each") + } + return ret +} + +function addTrack(track, method, args) { + switch (method) { + case 'push': + case 'unshift': + args = createTrack(args.length) + break + case 'splice': + if (args.length > 2) { + // 0, 5, a, b, c --> 0, 2, 0 + // 0, 5, a, b, c, d, e, f, g--> 0, 0, 3 + var del = args[1] + var add = args.length - 2 + // args = [args[0], Math.max(del - add, 0)].concat(createTrack(Math.max(add - del, 0))) + args = [args[0], args[1]].concat(createTrack(args.length - 2)) + } + break + } + Array.prototype[method].apply(track, args) +} +/********************************************************************* + * 依赖调度系统 * + **********************************************************************/ +//检测两个对象间的依赖关系 +var dependencyDetection = (function () { + var outerFrames = [] + var currentFrame + return { + begin: function (binding) { + //accessorObject为一个拥有callback的对象 + outerFrames.push(currentFrame) + currentFrame = binding + }, + end: function () { + currentFrame = outerFrames.pop() + }, + collectDependency: function (array) { + if (currentFrame) { + //被dependencyDetection.begin调用 + currentFrame.callback(array) + } + } + }; +})() +//将绑定对象注入到其依赖项的订阅数组中 +var roneval = /^on$/ + +function returnRandom() { + return new Date() - 0 +} + +avalon.injectBinding = function (binding) { + + binding.handler = binding.handler || directives[binding.type].update || noop + binding.update = function () { + var begin = false + if (!binding.getter) { + begin = true + dependencyDetection.begin({ + callback: function (array) { + injectDependency(array, binding) + } + }) + binding.getter = parseExpr(binding.expr, binding.vmodels, binding) + binding.observers.forEach(function (a) { + a.v.$watch(a.p, binding) + }) + delete binding.observers + } + try { + var args = binding.fireArgs, a, b + delete binding.fireArgs + if (!args) { + if (binding.type === "on") { + a = binding.getter + "" + } else { + a = binding.getter.apply(0, binding.args) + } + } else { + a = args[0] + b = args[1] + + } + b = typeof b === "undefined" ? binding.oldValue : b + if (binding._filters) { + a = filters.$filter.apply(0, [a].concat(binding._filters)) + } + if (binding.signature) { + var xtype = avalon.type(a) + if (xtype !== "array" && xtype !== "object") { + throw Error("warning:" + binding.expr + "只能是对象或数组") + } + binding.xtype = xtype + var vtrack = getProxyIds(binding.proxies || [], xtype) + var mtrack = a.$track || (xtype === "array" ? createTrack(a.length) : + Object.keys(a)) + binding.track = mtrack + if (vtrack !== mtrack.join(";")) { + binding.handler(a, b) + binding.oldValue = 1 + } + } else if (Array.isArray(a) ? a.length !== (b && b.length) : false) { + binding.handler(a, b) + binding.oldValue = a.concat() + } else if (!("oldValue" in binding) || a !== b) { + binding.handler(a, b) + binding.oldValue = a + } + } catch (e) { + delete binding.getter + log("warning:exception throwed in [avalon.injectBinding] ", e) + var node = binding.element + if (node && node.nodeType === 3) { + node.nodeValue = openTag + (binding.oneTime ? "::" : "") + binding.expr + closeTag + } + } finally { + begin && dependencyDetection.end() + + } + } + binding.update() +} + + +//将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组 +function injectDependency(list, binding) { + if (binding.oneTime) + return + if (list && avalon.Array.ensure(list, binding) && binding.element) { + injectDisposeQueue(binding, list) + if (new Date() - beginTime > 444) { + rejectDisposeQueue() + } + } +} + + +function getProxyIds(a, isArray) { + var ret = [] + for (var i = 0, el; el = a[i++]; ) { + ret.push(isArray ? el.$id : el.$key) + } + return ret.join(";") +} + +/********************************************************************* + * 定时GC回收机制 * + **********************************************************************/ +var disposeCount = 0 +var disposeQueue = avalon.$$subscribers = [] +var beginTime = new Date() +var oldInfo = {} + +function getUid(data) { //IE9+,标准浏览器 + if (!data.uniqueNumber) { + var elem = data.element + if (elem) { + if (elem.nodeType !== 1) { + //如果是注释节点,则data.pos不存在,当一个元素下有两个注释节点就会出问题 + data.uniqueNumber = data.type + "-" + getUid(elem.parentNode) + "-" + (++disposeCount) + } else { + data.uniqueNumber = data.name + "-" + getUid(elem) + } + } else { + data.uniqueNumber = ++disposeCount + } + } + return data.uniqueNumber +} + +//添加到回收列队中 +function injectDisposeQueue(data, list) { + var lists = data.lists || (data.lists = []) + var uuid = getUid(data) + avalon.Array.ensure(lists, list) + list.$uuid = list.$uuid || generateID() + if (!disposeQueue[uuid]) { + disposeQueue[uuid] = 1 + disposeQueue.push(data) + } +} + +function rejectDisposeQueue(data) { + + var i = disposeQueue.length + var n = i + var allTypes = [] + var iffishTypes = {} + var newInfo = {} + //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型 + while (data = disposeQueue[--i]) { + var type = data.type + if (newInfo[type]) { + newInfo[type]++ + } else { + newInfo[type] = 1 + allTypes.push(type) + } + } + var diff = false + allTypes.forEach(function (type) { + if (oldInfo[type] !== newInfo[type]) { + iffishTypes[type] = 1 + diff = true + } + }) + i = n + if (diff) { + while (data = disposeQueue[--i]) { + if (data.element === null) { + disposeQueue.splice(i, 1) + continue + } + if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树 + disposeQueue.splice(i, 1) + delete disposeQueue[data.uniqueNumber] + var lists = data.lists + for (var k = 0, list; list = lists[k++]; ) { + avalon.Array.remove(lists, list) + avalon.Array.remove(list, data) + } + disposeData(data) + } + } + } + oldInfo = newInfo + beginTime = new Date() +} + +function disposeData(data) { + delete disposeQueue[data.uniqueNumber] // 先清除,不然无法回收了 + data.element = null + data.rollback && data.rollback() + for (var key in data) { + data[key] = null + } +} + +function shouldDispose(el) { + try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错 + var fireError = el.parentNode.nodeType + } catch (e) { + return true + } + if (el.ifRemove) { + // 如果节点被放到ifGroup,才移除 + if (!root.contains(el.ifRemove) && (ifGroup === el.parentNode)) { + el.parentNode && el.parentNode.removeChild(el) + return true + } + } + return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el)) +} + + + +/************************************************************************ + * HTML处理(parseHTML, innerHTML, clearHTML) * + ************************************************************************/ +// We have to close these tags to support XHTML +var tagHooks = { + area: [1, "", ""], + param: [1, "", ""], + col: [2, "
编号
", "
"], + legend: [1, "
", "
"], + option: [1, ""], + thead: [1, "", "
"], + tr: [2, "", "
"], + td: [3, "", "
"], + g: [1, '', ''], + //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签 + _default: W3C ? [0, "", ""] : [1, "X
", "
"] //div可以不用闭合 +} +tagHooks.th = tagHooks.td +tagHooks.optgroup = tagHooks.option +tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead +String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) { + tagHooks[tag] = tagHooks.g //处理SVG +}) +var rtagName = /<([\w:]+)/ //取得其tagName +var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig +var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig +var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"]) +var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签 +var script = DOC.createElement("script") +var rhtml = /<|&#?\w+;/ +avalon.parseHTML = function (html) { + var fragment = avalonFragment.cloneNode(false) + if (typeof html !== "string") { + return fragment + } + if (!rhtml.test(html)) { + fragment.appendChild(DOC.createTextNode(html)) + return fragment + } + html = html.replace(rxhtml, "<$1>").trim() + var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(), + //取得其标签名 + wrap = tagHooks[tag] || tagHooks._default, + wrapper = cinerator, + firstChild, neo + if (!W3C) { //fix IE + html = html.replace(rcreate, "
$1") //在link style script等标签之前添加一个补丁 + } + wrapper.innerHTML = wrap[1] + html + wrap[2] + var els = wrapper.getElementsByTagName("script") + if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性 + for (var i = 0, el; el = els[i++];) { + if (scriptTypes[el.type]) { + //以偷龙转凤方式恢复执行脚本功能 + neo = script.cloneNode(false) //FF不能省略参数 + ap.forEach.call(el.attributes, function (attr) { + if (attr && attr.specified) { + neo[attr.name] = attr.value //复制其属性 + neo.setAttribute(attr.name, attr.value) + } + }) // jshint ignore:line + neo.text = el.text + el.parentNode.replaceChild(neo, el) //替换节点 + } + } + } + if (!W3C) { //fix IE + var target = wrap[1] === "X
" ? wrapper.lastChild.firstChild : wrapper.lastChild + if (target && target.tagName === "TABLE" && tag !== "tbody") { + //IE6-7处理 --> , + // --> , + // -->
+ for (els = target.childNodes, i = 0; el = els[i++];) { + if (el.tagName === "TBODY" && !el.innerHTML) { + target.removeChild(el) + break + } + } + } + els = wrapper.getElementsByTagName("br") + var n = els.length + while (el = els[--n]) { + if (el.className === "msNoScope") { + el.parentNode.removeChild(el) + } + } + for (els = wrapper.all, i = 0; el = els[i++];) { //fix VML + if (isVML(el)) { + fixVML(el) + } + } + } + //移除我们为了符合套嵌关系而添加的标签 + for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {} + while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上! + fragment.appendChild(firstChild) + } + return fragment +} + +function isVML(src) { + var nodeName = src.nodeName + return nodeName.toLowerCase() === nodeName && src.scopeName && src.outerText === "" +} + +function fixVML(node) { + if (node.currentStyle.behavior !== "url(#default#VML)") { + node.style.behavior = "url(#default#VML)" + node.style.display = "inline-block" + node.style.zoom = 1 //hasLayout + } +} +avalon.innerHTML = function (node, html) { + if (!W3C && (!rcreate.test(html) && !rnest.test(html))) { + try { + node.innerHTML = html + return + } catch (e) {} + } + var a = this.parseHTML(html) + this.clearHTML(node).appendChild(a) +} +avalon.clearHTML = function (node) { + node.textContent = "" + while (node.firstChild) { + node.removeChild(node.firstChild) + } + return node +} + +/********************************************************************* + * avalon的原型方法定义区 * + **********************************************************************/ + +function hyphen(target) { + //转换为连字符线风格 + return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase() +} + +function camelize(target) { + //提前判断,提高getStyle等的效率 + if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) { + return target + } + //转换为驼峰风格 + return target.replace(/[-_][^-_]/g, function (match) { + return match.charAt(1).toUpperCase() + }) +} + +var fakeClassListMethods = { + _toString: function () { + var node = this.node + var cls = node.className + var str = typeof cls === "string" ? cls : cls.baseVal + return str.split(/\s+/).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 (rsvg.test(node)) { + //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作 + node.setAttribute("class", cls) + } else { + node.className = cls + } + } //toggle存在版本差异,因此不使用它 +} + +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 +} + + +"add,remove".replace(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 && el.nodeType === 1) { + cls.replace(/\S+/g, 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 className, i = 0 + var classNames = String(value).split(/\s+/) + var isBool = typeof stateVal === "boolean" + while ((className = classNames[i++])) { + var state = isBool ? stateVal : !this.hasClass(className) + this[state ? "addClass" : "removeClass"](className) + } + return this + }, + attr: function (name, value) { + if (arguments.length === 2) { + this[0].setAttribute(name, value) + return this + } else { + return this[0].getAttribute(name) + } + }, + data: function (name, value) { + name = "data-" + hyphen(name || "") + switch (arguments.length) { + case 2: + this.attr(name, value) + return this + case 1: + var val = this.attr(name) + return parseData(val) + case 0: + var ret = {} + ap.forEach.call(this[0].attributes, function (attr) { + if (attr) { + name = attr.name + if (!name.indexOf("data-")) { + name = camelize(name.slice(5)) + ret[name] = parseData(attr.value) + } + } + }) + return ret + } + }, + removeData: function (name) { + name = "data-" + hyphen(name) + this[0].removeAttribute(name) + return this + }, + 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 + }, + position: function () { + var offsetParent, offset, + elem = this[0], + parentOffset = { + top: 0, + left: 0 + } + if (!elem) { + return + } + 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) + } + }, + offsetParent: function () { + var offsetParent = this[0].offsetParent + while (offsetParent && avalon.css(offsetParent, "position") === "static") { + offsetParent = offsetParent.offsetParent; + } + return avalon(offsetParent || root) + }, + bind: function (type, fn, phase) { + if (this[0]) { //此方法不会链 + return avalon.bind(this[0], type, fn, phase) + } + }, + unbind: function (type, fn, phase) { + if (this[0]) { + avalon.unbind(this[0], type, fn, phase) + } + return this + }, + 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 + } +}) + +function parseData(data) { + try { + if (typeof data === "object") + return data + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data + } catch (e) {} + return data +} +var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, + 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 = window.JSON ? 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) + } + return data +} + +//生成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 + } + } + } +}) + +function getWindow(node) { + return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false; +} +//=============================css相关======================= +var cssHooks = avalon.cssHooks = {} +var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"] +var cssMap = { + "float": W3C ? "cssFloat" : "styleFloat" +} +avalon.cssNumber = 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 +} +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) {} +} +if (window.getComputedStyle) { + 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] //其他浏览器需要我们手动取内联样式 + } + } + 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 = !!window.XDomainRequest + 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(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 + 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) + } + } +} +"Width,Height".replace(rword, function (name) { //fix 481 + var method = name.toLowerCase(), + 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) { //取得窗口尺寸 + 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) + } + } + 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 + } +} + +//==================================val相关============================ + +function getValType(elem) { + var ret = elem.tagName.toLowerCase() + return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret +} +var roption = /^]+))?)*\s+value[\s=]/i +var valHooks = { + "option:get": IEVersion ? 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] + //旧式IE在reset后不会改变selected,需要改用i === index判定 + //我们过滤所有disabled的option元素,但在safari5下,如果设置select为disable,那么其所有孩子都disable + //因此当一个元素为disable,需要检测其是否显式设置了disable及其父节点的disable情况 + if ((option.selected || i === index) && !option.disabled) { + value = getter(option) + if (one) { + return value + } + //收集所有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 + } + } +} + +var keyMap = {} +var keys = ["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", /*保留字*/ + "arguments,let,yield,undefined"].join(",") +keys.replace(/\w+/g, function (a) { + keyMap[a] = true +}) +var ridentStart = /[a-z_$]/i +var rwhiteSpace = /[\s\uFEFF\xA0]/ +function getIdent(input, lastIndex) { + var result = [] + var subroutine = !!lastIndex + lastIndex = lastIndex || 0 + + //将表达式中的标识符抽取出来 + var state = "unknown" + var variable = "" + for (var i = 0; i < input.length; i++) { + var c = input.charAt(i) + if (c === "'" || c === '"') {//字符串开始 + if (state === "unknown") { + state = c + } else if (state === c) {//字符串结束 + state = "unknown" + } + } else if (c === "\\") { + if (state === "'" || state === '"') { + i++ + } + } else if (ridentStart.test(c)) {//碰到标识符 + if (state === "unknown") { + state = "variable" + variable = c + } else if (state === "maybePath") { + variable = result.pop() + variable += "." + c + state = "variable" + } else if (state === "variable") { + variable += c + } + } else if (/\w/.test(c)) { + if (state === "variable") { + variable += c + } + } else if (c === ".") { + if (state === "variable") { + if (variable) { + result.push(variable) + variable = "" + state = "maybePath" + } + } + } else if (c === "[") { + if (state === "variable" || state === "maybePath") { + if (variable) {//如果前面存在变量,收集它 + result.push(variable) + variable = "" + } + var lastLength = result.length + var last = result[lastLength - 1] + var innerResult = getIdent(input.slice(i), i) + if (innerResult.length) {//如果括号中存在变量,那么这里添加通配符 + result[lastLength - 1] = last + ".*" + result = innerResult.concat(result) + } else { //如果括号中的东西是确定的,直接转换为其子属性 + var content = input.slice(i + 1, innerResult.i) + try { + var text = (scpCompile(["return " + content]))() + result[lastLength - 1] = last + "." + text + } catch (e) { + } + } + state = "maybePath"//]后面可能还接东西 + i = innerResult.i + } + } else if (c === "]") { + if (subroutine) { + result.i = i + lastIndex + addVar(result, variable) + return result + } + } else if (rwhiteSpace.test(c) && c !== "\r" && c !== "\n") { + if (state === "variable") { + if (addVar(result, variable)) { + state = "maybePath" // aaa . bbb 这样的情况 + } + variable = "" + } + } else { + addVar(result, variable) + state = "unknown" + variable = "" + } + } + addVar(result, variable) + return result +} +function addVar(array, element) { + if (element && !keyMap[element]) { + array.push(element) + return true + } +} +function addAssign(vars, vmodel, name, binding) { + var ret = [], + prefix = " = " + name + "." + for (var i = vars.length, prop; prop = vars[--i]; ) { + var arr = prop.split("."), a + var first = arr[0] + while (a = arr.shift()) { + if (vmodel.hasOwnProperty(a)) { + ret.push(first + prefix + first) + + binding.observers.push({ + v: vmodel, + p: prop + }) + + vars.splice(i, 1) + } + } + } + return ret +} +var rproxy = /(\$proxy\$[a-z]+)\d+$/ +var variablePool = new Cache(218) +//缓存求值函数,以便多次利用 +var evaluatorPool = new Cache(128) + +function getVars(expr) { + expr = expr.trim() + var ret = variablePool.get(expr) + if (ret) { + return ret.concat() + } + var array = getIdent(expr) + var uniq = {} + var result = [] + for (var i = 0, el; el = array[i++]; ) { + if (!uniq[el]) { + uniq[el] = 1 + result.push(el) + } + } + return variablePool.put(expr, result).concat() +} + +function parseExpr(expr, vmodels, binding) { + var filters = binding.filters + if (typeof filters === "string" && filters.trim() && !binding._filters) { + binding._filters = parseFilter(filters.trim()) + } + + var vars = getVars(expr) + + var expose = new Date() - 0 + var assigns = [] + var names = [] + var args = [] + binding.observers = [] + for (var i = 0, sn = vmodels.length; i < sn; i++) { + if (vars.length) { + var name = "vm" + expose + "_" + i + names.push(name) + args.push(vmodels[i]) + assigns.push.apply(assigns, addAssign(vars, vmodels[i], name, binding)) + } + } + binding.args = args + var dataType = binding.type + var exprId = vmodels.map(function (el) { + return String(el.$id).replace(rproxy, "$1") + }) + expr + dataType + var getter = evaluatorPool.get(exprId) //直接从缓存,免得重复生成 + if (getter) { + if (dataType === "duplex") { + var setter = evaluatorPool.get(exprId + "setter") + binding.setter = setter.apply(setter, binding.args) + } + return binding.getter = getter + } + + if (!assigns.length) { + assigns.push("fix" + expose) + } + + if (dataType === "duplex") { + var nameOne = {} + assigns.forEach(function (a) { + var arr = a.split("=") + nameOne[arr[0].trim()] = arr[1].trim() + }) + expr = expr.replace(/[\$\w]+/, function (a) { + return nameOne[a] ? nameOne[a] : a + }) + /* jshint ignore:start */ + var fn2 = scpCompile(names.concat("'use strict';" + + "return function(vvv){" + expr + " = vvv\n}\n")) + /* jshint ignore:end */ + evaluatorPool.put(exprId + "setter", fn2) + binding.setter = fn2.apply(fn2, binding.args) + } + + if (dataType === "on") { //事件绑定 + if (expr.indexOf("(") === -1) { + expr += ".call(this, $event)" + } else { + expr = expr.replace("(", ".call(this,") + } + names.push("$event") + expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") + var lastIndex = expr.lastIndexOf("\nreturn") + var header = expr.slice(0, lastIndex) + var footer = expr.slice(lastIndex) + expr = header + "\n" + footer + } else { + expr = "\nreturn " + expr + ";" //IE全家 Function("return ")出错,需要Function("return ;") + } + /* jshint ignore:start */ + getter = scpCompile(names.concat("'use strict';\nvar " + + assigns.join(",\n") + expr)) + /* jshint ignore:end */ + + return evaluatorPool.put(exprId, getter) + +} +//======== + +function normalizeExpr(code) { + var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况 + if (hasExpr) { + var array = scanExpr(code) + if (array.length === 1) { + return array[0].expr + } + return array.map(function (el) { + return el.type ? "(" + el.expr + ")" : quote(el.expr) + }).join(" + ") + } else { + return code + } +} + +avalon.normalizeExpr = normalizeExpr +avalon.parseExprProxy = parseExpr + +var rthimRightParentheses = /\)\s*$/ +var rthimOtherParentheses = /\)\s*\|/g +var rquoteFilterName = /\|\s*([$\w]+)/g +var rpatchBracket = /"\s*\["/g +var rthimLeftParentheses = /"\s*\(/g +function parseFilter(filters) { + filters = filters + .replace(rthimRightParentheses, "")//处理最后的小括号 + .replace(rthimOtherParentheses, function () {//处理其他小括号 + return "],|" + }) + .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字 + return "[" + quote(b) + }) + .replace(rpatchBracket, function () { + return '"],["' + }) + .replace(rthimLeftParentheses, function () { + return '",' + }) + "]" + /* jshint ignore:start */ + return scpCompile(["return [" + filters + "]"])() + /* jshint ignore:end */ + +} +/********************************************************************* + * 编译系统 * + **********************************************************************/ +var meta = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"': '\\"', + '\\': '\\\\' +} +var quote = window.JSON && JSON.stringify || function(str) { + return '"' + str.replace(/[\\\"\x00-\x1f]/g, function(a) { + var c = meta[a]; + return typeof c === 'string' ? c : + '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"' +} +/********************************************************************* + * 扫描系统 * + **********************************************************************/ + +avalon.scan = function (elem, vmodel) { + elem = elem || root + var vmodels = vmodel ? [].concat(vmodel) : [] + scanTag(elem, vmodels) +} + +//http://www.w3.org/TR/html5/syntax.html#void-elements +var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase()) + +function checkScan(elem, callback, innerHTML) { + var id = setTimeout(function () { + var currHTML = elem.innerHTML + clearTimeout(id) + if (currHTML === innerHTML) { + callback() + } else { + checkScan(elem, callback, currHTML) + } + }) +} + + +function createSignalTower(elem, vmodel) { + var id = elem.getAttribute("avalonctrl") || vmodel.$id + elem.setAttribute("avalonctrl", id) + if (vmodel.$events) { + vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]' + } +} + +var getBindingCallback = function (elem, name, vmodels) { + var callback = elem.getAttribute(name) + if (callback) { + for (var i = 0, vm; vm = vmodels[i++]; ) { + if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") { + return vm[callback] + } + } + } +} + +function executeBindings(bindings, vmodels) { + for (var i = 0, binding; binding = bindings[i++]; ) { + binding.vmodels = vmodels + directives[binding.type].init(binding) + + avalon.injectBinding(binding) + if (binding.getter && binding.element.nodeType === 1) { //移除数据绑定,防止被二次解析 + //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99 + binding.element.removeAttribute(binding.name) + } + } + bindings.length = 0 +} + +//https://github.com/RubyLouvre/avalon/issues/636 +var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) { + var node = elem.firstChild, text + while (node) { + var aaa = node.nextSibling + if (node.nodeType === 3) { + if (text) { + text.nodeValue += node.nodeValue + elem.removeChild(node) + } else { + text = node + } + } else { + text = null + } + node = aaa + } +} : 0 +var roneTime = /^\s*::/ +var rmsAttr = /ms-(\w+)-?(.*)/ + +var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit") +var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled,href,src") +function bindingSorter(a, b) { + return a.priority - b.priority +} + +function scanAttr(elem, vmodels, match) { + var scanNode = true + if (vmodels.length) { + var attributes = getAttributes ? getAttributes(elem) : elem.attributes + var bindings = [] + var uniq = {} + for (var i = 0, attr; attr = attributes[i++]; ) { + var name = attr.name + if (uniq[name]) {//IE8下ms-repeat,ms-with BUG + continue + } + uniq[name] = 1 + if (attr.specified) { + if (match = name.match(rmsAttr)) { + //如果是以指定前缀命名的 + var type = match[1] + var param = match[2] || "" + var value = attr.value + if (events[type]) { + param = type + type = "on" + } else if (obsoleteAttrs[type]) { + param = type + type = "attr" + name = "ms-" + type + "-" + param + log("warning!请改用" + name + "代替" + attr.name + "!") + } + if (directives[type]) { + var newValue = value.replace(roneTime, "") + var oneTime = value !== newValue + var binding = { + type: type, + param: param, + element: elem, + name: name, + expr: newValue, + oneTime: oneTime, + uniqueNumber: attr.name + "-" + getUid(elem), + //chrome与firefox下Number(param)得到的值不一样 #855 + priority: (directives[type].priority || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0) + } + if (type === "html" || type === "text") { + + var filters = getToken(value).filters + binding.expr = binding.expr.replace(filters, "") + + binding.filters = filters.replace(rhasHtml, function () { + binding.type = "html" + binding.group = 1 + return "" + }).trim() // jshint ignore:line + } else if (type === "duplex") { + var hasDuplex = name + } else if (name === "ms-if-loop") { + binding.priority += 100 + } else if (name === "ms-attr-value") { + var hasAttrValue = name + } + bindings.push(binding) + } + } + } + } + if (bindings.length) { + bindings.sort(bindingSorter) + //http://bugs.jquery.com/ticket/7071 + //在IE下对VML读取type属性,会让此元素所有属性都变成 + if (hasDuplex && hasAttrValue && elem.nodeName === "INPUT" && elem.type === "text") { + log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex) + } + for (i = 0; binding = bindings[i]; i++) { + type = binding.type + if (rnoscanAttrBinding.test(type)) { + return executeBindings(bindings.slice(0, i + 1), vmodels) + } else if (scanNode) { + scanNode = !rnoscanNodeBinding.test(type) + } + } + + executeBindings(bindings, vmodels) + } + } + if (scanNode && !stopScan[elem.tagName] && (isWidget(elem) ? elem.msResolved : 1)) { + mergeTextNodes && mergeTextNodes(elem) + scanNodeList(elem, vmodels) //扫描子孙元素 + + } +} +var rnoscanAttrBinding = /^if|widget|repeat$/ +var rnoscanNodeBinding = /^each|with|html|include$/ +//IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支, +//但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面 +if (!W3C) { + var attrPool = new Cache(512) + var rattrs = /\s+([^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g, + rquote = /^['"]/, + rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i, + ramp = /&/g +//IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点 +//
ddd
+// window.onload = function() { +// var body = document.body +// for (var i = 0, el; el = body.children[i++]; ) { +// avalon.log(el.outerHTML) +// } +// } +//依次输出
,
+ var getAttributes = function (elem) { + var html = elem.outerHTML + //处理IE6-8解析HTML5新标签的情况,及
等半闭合标签outerHTML为空的情况 + if (html.slice(0, 2) === " ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100) + //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后 + var a = elem.getAttribute("ms-skip") + //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形 + if (!elem.getAttributeNode) { + return log("warning " + elem.tagName + " no getAttributeNode method") + } + var b = elem.getAttributeNode("ms-important") + var c = elem.getAttributeNode("ms-controller") + if (typeof a === "string") { + return + } else if (node = b || c) { + var newVmodel = avalon.vmodels[node.value] + if (!newVmodel) { + return + } + //ms-important不包含父VM,ms-controller相反 + vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels) + var name = node.name + elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则 + avalon(elem).removeClass(name) + createSignalTower(elem, newVmodel) + } + + scanAttr(elem, vmodels) //扫描特性节点 +} + + + +var rhasHtml = /\|\s*html(?:\b|$)/, + r11a = /\|\|/g, + rlt = /</g, + rgt = />/g, + rstringLiteral = /(['"])(\\\1|.)+?\1/g + +function getToken(value) { + if (value.indexOf("|") > 0) { + var scapegoat = value.replace(rstringLiteral, function (_) { + return Array(_.length + 1).join("1") // jshint ignore:line + }) + var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或 + if (index > -1) { + return { + type: "text", + filters: value.slice(index).trim(), + expr: value.slice(0, index) + } + } + } + return { + type: "text", + expr: value, + filters: "" + } +} + +function scanExpr(str) { + var tokens = [], + value, start = 0, + stop + do { + stop = str.indexOf(openTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { // {{ 左边的文本 + tokens.push({ + expr: value + }) + } + start = stop + openTag.length + stop = str.indexOf(closeTag, start) + if (stop === -1) { + break + } + value = str.slice(start, stop) + if (value) { //处理{{ }}插值表达式 + tokens.push(getToken(value, start)) + } + start = stop + closeTag.length + } while (1) + value = str.slice(start) + if (value) { //}} 右边的文本 + tokens.push({ + expr: value + }) + } + return tokens +} + +function scanText(textNode, vmodels, index) { + var bindings = [], + tokens = scanExpr(textNode.data) + if (tokens.length) { + for (var i = 0, token; token = tokens[i++];) { + var node = DOC.createTextNode(token.expr) //将文本转换为文本节点,并替换原来的文本节点 + if (token.type) { + token.expr = token.expr.replace(roneTime, function () { + token.oneTime = true + return "" + }) // jshint ignore:line + token.element = node + token.filters = token.filters.replace(rhasHtml, function () { + token.type = "html" + return "" + }) // jshint ignore:line + token.pos = index * 1000 + i + bindings.push(token) //收集带有插值表达式的文本 + } + avalonFragment.appendChild(node) + } + textNode.parentNode.replaceChild(avalonFragment, textNode) + if (bindings.length) + executeBindings(bindings, vmodels) + } +} + + + +//使用来自游戏界的双缓冲技术,减少对视图的冗余刷新 +var Buffer = function () { + this.queue = [] +} +Buffer.prototype = { + render: function (isAnimate) { + if (!this.locked) { + this.locked = isAnimate ? root.offsetHeight + 10 : 1 + var me = this + avalon.nextTick(function () { + me.flush() + }) + } + }, + flush: function () { + for (var i = 0, sub; sub = this.queue[i++]; ) { + sub.update && sub.update() + } + this.locked = 0 + this.queue = [] + } +} + +var buffer = new Buffer() +var componentQueue = [] +var widgetList = [] +var componentHooks = { + $construct: function () { + return avalon.mix.apply(null, arguments) + }, + $ready: noop, + $init: noop, + $dispose: noop, + $container: null, + $childReady: noop, + $replace: false, + $extend: null, + $$template: function (str) { + return str + } +} + + +avalon.components = {} +avalon.component = function (name, opts) { + if (opts) { + avalon.components[name] = avalon.mix({}, componentHooks, opts) + } + for (var i = 0, obj; obj = componentQueue[i]; i++) { + if (name === obj.fullName) { + componentQueue.splice(i, 1) + i--; + + (function (host, hooks, elem, widget) { + + var dependencies = 1 + var library = host.library + var global = avalon.libraries[library] || componentHooks + + //===========收集各种配置======= + + var elemOpts = getOptionsFromTag(elem) + var vmOpts = getOptionsFromVM(host.vmodels, elemOpts.config || host.widget) + var $id = elemOpts.$id || elemOpts.identifier || generateID(widget) + delete elemOpts.config + delete elemOpts.$id + delete elemOpts.identifier + var componentDefinition = {} + + var parentHooks = avalon.components[hooks.$extend] + if (parentHooks) { + avalon.mix(true, componentDefinition, parentHooks) + componentDefinition = parentHooks.$construct.call(elem, componentDefinition, {}, {}) + } else { + avalon.mix(true, componentDefinition, hooks) + } + componentDefinition = avalon.components[name].$construct.call(elem, componentDefinition, vmOpts, elemOpts) + + componentDefinition.$refs = {} + componentDefinition.$id = $id + + //==========构建VM========= + var keepSlot = componentDefinition.$slot + var keepReplace = componentDefinition.$replace + var keepContainer = componentDefinition.$container + var keepTemplate = componentDefinition.$template + delete componentDefinition.$slot + delete componentDefinition.$replace + delete componentDefinition.$container + delete componentDefinition.$construct + + var vmodel = avalon.define(componentDefinition) || {} + elem.msResolved = 1 + vmodel.$init(vmodel, elem) + global.$init(vmodel, elem) + var nodes = elem.childNodes + //收集插入点 + var slots = {}, snode + for (var s = 0, el; el = nodes[s++]; ) { + var type = el.nodeType === 1 && el.getAttribute("slot") || keepSlot + if (type) { + if (slots[type]) { + slots[type].push(el) + } else { + slots[type] = [el] + } + } + } + + + if (vmodel.$$template) { + avalon.clearHTML(elem) + elem.innerHTML = vmodel.$$template(keepTemplate) + } + for (s in slots) { + if (vmodel.hasOwnProperty(s)) { + var ss = slots[s] + if (ss.length) { + var fragment = avalonFragment.cloneNode(true) + for (var ns = 0; snode = ss[ns++]; ) { + fragment.appendChild(snode) + } + vmodel[s] = fragment + } + slots[s] = null + } + } + slots = null + var child = elem.firstChild + if (keepReplace) { + child = elem.firstChild + elem.parentNode.replaceChild(child, elem) + child.msResolved = 1 + elem = host.element = child + } + if (keepContainer) { + keepContainer.appendChild(elem) + } + avalon.fireDom(elem, "datasetchanged", + {library: library, vm: vmodel, childReady: 1}) + var children = 0 + var removeFn = avalon.bind(elem, "datasetchanged", function (e) { + if (e.childReady && e.library === library) { + dependencies += e.childReady + if (vmodel !== e.vm) { + vmodel.$refs[e.vm.$id] = e.vm + if (e.childReady === -1) { + children++ + vmodel.$childReady(vmodel, elem, e) + } + e.stopPropagation() + } + } + if (dependencies === 0) { + var id1 = setTimeout(function () { + clearTimeout(id1) + + vmodel.$ready(vmodel, elem, host.vmodels) + global.$ready(vmodel, elem, host.vmodels) + }, children ? Math.max(children * 17, 100) : 17) + avalon.unbind(elem, "datasetchanged", removeFn) + //================== + host.rollback = function () { + try { + vmodel.$dispose(vmodel, elem) + global.$dispose(vmodel, elem) + } catch (e) { + } + delete avalon.vmodels[vmodel.$id] + } + injectDisposeQueue(host, widgetList) + if (window.chrome) { + elem.addEventListener("DOMNodeRemovedFromDocument", function () { + setTimeout(rejectDisposeQueue) + }) + } + + } + }) + scanTag(elem, [vmodel].concat(host.vmodels)) + + avalon.vmodels[vmodel.$id] = vmodel + if (!elem.childNodes.length) { + avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) + } else { + var id2 = setTimeout(function () { + clearTimeout(id2) + avalon.fireDom(elem, "datasetchanged", {library: library, vm: vmodel, childReady: -1}) + }, 17) + } + + + })(obj, avalon.components[name], obj.element, obj.widget)// jshint ignore:line + + + } + } +} + +avalon.fireDom = function (elem, type, opts) { + if (DOC.createEvent) { + var hackEvent = DOC.createEvent("Events"); + hackEvent.initEvent(type, true, true, opts) + avalon.mix(hackEvent, opts) + + elem.dispatchEvent(hackEvent) + } else if (root.contains(elem)) {//IE6-8触发事件必须保证在DOM树中,否则报"SCRIPT16389: 未指明的错误" + hackEvent = DOC.createEventObject() + avalon.mix(hackEvent, opts) + elem.fireEvent("on" + type, hackEvent) + } +} + + +function getOptionsFromVM(vmodels, pre) { + if (pre) { + for (var i = 0, v; v = vmodels[i++]; ) { + if (v.hasOwnProperty(pre) && typeof v[pre] === "object") { + var vmOptions = v[pre] + return vmOptions.$model || vmOptions + break + } + } + } + return {} +} + + + +avalon.libraries = [] +avalon.library = function (name, opts) { + if (DOC.namespaces) { + DOC.namespaces.add(name, 'http://www.w3.org/1999/xhtml'); + } + avalon.libraries[name] = avalon.mix({ + $init: noop, + $ready: noop, + $dispose: noop + }, opts || {}) +} + +avalon.library("ms") +/* +broswer nodeName scopeName localName +IE9 ONI:BUTTON oni button +IE10 ONI:BUTTON undefined oni:button +IE8 button oni undefined +chrome ONI:BUTTON undefined oni:button + +*/ +function isWidget(el) { //如果为自定义标签,返回UI库的名字 + if(el.scopeName && el.scopeName !== "HTML" ){ + return el.scopeName + } + var fullName = el.nodeName.toLowerCase() + var index = fullName.indexOf(":") + if (index > 0) { + return fullName.slice(0, index) + } +} +//各种MVVM框架在大型表格下的性能测试 +// https://github.com/RubyLouvre/avalon/issues/859 + + +var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls", + "declare,disabled,defer,defaultChecked,defaultSelected", + "contentEditable,isMap,loop,multiple,noHref,noResize,noShade", + "open,readOnly,selected" +].join(",") +var boolMap = {} +bools.replace(rword, function (name) { + boolMap[name.toLowerCase()] = name +}) + +var propMap = {//属性名映射 + "accept-charset": "acceptCharset", + "char": "ch", + "charoff": "chOff", + "class": "className", + "for": "htmlFor", + "http-equiv": "httpEquiv" +} + +var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan", + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight", + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign" +].join(",") +anomaly.replace(rword, function (name) { + propMap[name.toLowerCase()] = name +}) + + +var attrDir = avalon.directive("attr", { + init: function (binding) { + //{{aaa}} --> aaa + //{{aaa}}/bbb.html --> (aaa) + "/bbb.html" + binding.expr = normalizeExpr(binding.expr.trim()) + if (binding.type === "include") { + var elem = binding.element + effectBinding(elem, binding) + binding.includeRendered = getBindingCallback(elem, "data-include-rendered", binding.vmodels) + binding.includeLoaded = getBindingCallback(elem, "data-include-loaded", binding.vmodels) + var outer = binding.includeReplace = !!avalon(elem).data("includeReplace") + if (avalon(elem).data("includeCache")) { + binding.templateCache = {} + } + binding.start = DOC.createComment("ms-include") + binding.end = DOC.createComment("ms-include-end") + if (outer) { + binding.element = binding.end + binding._element = elem + elem.parentNode.insertBefore(binding.start, elem) + elem.parentNode.insertBefore(binding.end, elem.nextSibling) + } else { + elem.insertBefore(binding.start, elem.firstChild) + elem.appendChild(binding.end) + } + } + }, + update: function (val) { + var elem = this.element + var attrName = this.param + if (attrName === "href" || attrName === "src") { + if (typeof val === "string" && !root.hasAttribute) { + val = val.replace(/&/g, "&") //处理IE67自动转义的问题 + } + elem[attrName] = val + if (window.chrome && elem.tagName === "EMBED") { + var parent = elem.parentNode //#525 chrome1-37下embed标签动态设置src不能发生请求 + var comment = document.createComment("ms-src") + parent.replaceChild(comment, elem) + parent.replaceChild(elem, comment) + } + } else { + + // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc + // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名 + // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性 + var toRemove = (val === false) || (val === null) || (val === void 0) + if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射 + attrName = propMap[attrName] + } + var bool = boolMap[attrName] + if (typeof elem[bool] === "boolean") { + elem[bool] = !!val //布尔属性必须使用el.xxx = true|false方式设值 + if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理 + toRemove = true + } + } + if (toRemove) { + return elem.removeAttribute(attrName) + } + //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy + var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false) + if (isInnate) { + elem[attrName] = val + "" + } else { + elem.setAttribute(attrName, val) + } + } + } +}) + + + +//这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html" +"title,alt,src,value,css,include,href".replace(rword, function (name) { + directives[name] = attrDir +}) + +//根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag" +//http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html +avalon.directive("class", { + init: function (binding) { + var oldStyle = binding.param + var method = binding.type + if (!oldStyle || isFinite(oldStyle)) { + binding.param = "" //去掉数字 + directives.effect.init(binding) + } else { + log('ms-' + method + '-xxx="yyy"这种用法已经过时,请使用ms-' + method + '="xxx:yyy"') + binding.expr = '[' + quote(oldStyle) + "," + binding.expr + "]" + binding.oldStyle = oldStyle + } + if (method === "hover" || method === "active") { //确保只绑定一次 + if (!binding.hasBindEvent) { + var elem = binding.element + var $elem = avalon(elem) + var activate = "mouseenter" //在移出移入时切换类名 + var abandon = "mouseleave" + if (method === "active") { //在聚焦失焦中切换类名 + elem.tabIndex = elem.tabIndex || -1 + activate = "mousedown" + abandon = "mouseup" + var fn0 = $elem.bind("mouseleave", function () { + binding.toggleClass && $elem.removeClass(binding.newClass) + }) + } + } + + var fn1 = $elem.bind(activate, function () { + binding.toggleClass && $elem.addClass(binding.newClass) + }) + var fn2 = $elem.bind(abandon, function () { + binding.toggleClass && $elem.removeClass(binding.newClass) + }) + binding.rollback = function () { + $elem.unbind("mouseleave", fn0) + $elem.unbind(activate, fn1) + $elem.unbind(abandon, fn2) + } + binding.hasBindEvent = true + } + + }, + update: function (arr) { + var binding = this + var $elem = avalon(this.element) + binding.newClass = arr[0] + binding.toggleClass = !!arr[1] + if (binding.oldClass && binding.newClass !== binding.oldClass) { + $elem.removeClass(binding.oldClass) + } + binding.oldClass = binding.newClass + if (binding.type === "class") { + if (binding.oldStyle) { + $elem.toggleClass(binding.oldStyle, !!arr[1]) + } else { + $elem.toggleClass(binding.newClass, binding.toggleClass) + } + } + } +}) + +"hover,active".replace(rword, function (name) { + directives[name] = directives["class"] +}) + + +//ms-controller绑定已经在scanTag 方法中实现 +avalon.directive("css", { + init: directives.attr.init, + update: function (val) { + avalon(this.element).css(this.param, val) + } +}) + +avalon.directive("data", { + priority: 100, + update: function (val) { + var elem = this.element + var key = "data-" + this.param + if (val && typeof val === "object") { + elem[key] = val + } else { + elem.setAttribute(key, String(val)) + } + } +}) + +//双工绑定 +var rduplexType = /^(?:checkbox|radio)$/ +var rduplexParam = /^(?:radio|checked)$/ +var rnoduplexInput = /^(file|button|reset|submit|checkbox|radio|range)$/ +var duplexBinding = avalon.directive("duplex", { + priority: 2000, + init: function (binding, hasCast) { + var elem = binding.element + var vmodels = binding.vmodels + binding.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop + var params = [] + var casting = oneObject("string,number,boolean,checked") + if (elem.type === "radio" && binding.param === "") { + binding.param = "checked" + } + + + binding.param.replace(rw20g, function (name) { + if (rduplexType.test(elem.type) && rduplexParam.test(name)) { + if (name === "radio") + log("ms-duplex-radio已经更名为ms-duplex-checked") + name = "checked" + binding.isChecked = true + binding.xtype = "radio" + } + if (name === "bool") { + name = "boolean" + log("ms-duplex-bool已经更名为ms-duplex-boolean") + } else if (name === "text") { + name = "string" + log("ms-duplex-text已经更名为ms-duplex-string") + } + if (casting[name]) { + hasCast = true + } + avalon.Array.ensure(params, name) + }) + if (!hasCast) { + params.push("string") + } + binding.param = params.join("-") + if (!binding.xtype) { + binding.xtype = elem.tagName === "SELECT" ? "select" : + elem.type === "checkbox" ? "checkbox" : + elem.type === "radio" ? "radio" : + /^change/.test(elem.getAttribute("data-duplex-event")) ? "change" : + "input" + } + //===================绑定事件====================== + binding.bound = function (type, callback) { + if (elem.addEventListener) { + elem.addEventListener(type, callback, false) + } else { + elem.attachEvent("on" + type, callback) + } + var old = binding.rollback + binding.rollback = function () { + elem.avalonSetter = null + avalon.unbind(elem, type, callback) + old && old() + } + } + var composing = false + function callback(value) { + binding.changed.call(this, value, binding) + } + function compositionStart() { + composing = true + } + function compositionEnd() { + composing = false + } + var updateVModel = function () { + var val = elem.value //防止递归调用形成死循环 + if (composing || val === binding.oldValue || binding.pipe === null) //处理中文输入法在minlengh下引发的BUG + return + var lastValue = binding.pipe(val, binding, "get") + try { + binding.setter(lastValue) + callback.call(elem, lastValue) + } catch (ex) { + log(ex) + } + } + switch (binding.xtype) { + case "radio": + binding.bound("click", function () { + var lastValue = binding.pipe(elem.value, binding, "get") + try { + binding.setter(lastValue) + callback.call(elem, lastValue) + } catch (ex) { + log(ex) + } + }) + break + case "checkbox": + binding.bound(W3C ? "change" : "click", function () { + var method = elem.checked ? "ensure" : "remove" + var array = binding.getter.apply(0, binding.vmodels) + if (!Array.isArray(array)) { + log("ms-duplex应用于checkbox上要对应一个数组") + array = [array] + } + var val = binding.pipe(elem.value, binding, "get") + avalon.Array[method](array, val) + callback.call(elem, array) + }) + break + case "change": + binding.bound("change", updateVModel) + break + case "input": + if (!IEVersion) { // W3C + binding.bound("input", updateVModel) + //非IE浏览器才用这个 + binding.bound("compositionstart", compositionStart) + binding.bound("compositionend", compositionEnd) + binding.bound("DOMAutoComplete", updateVModel) + } else { //onpropertychange事件无法区分是程序触发还是用户触发 + // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为 + if (IEVersion > 8) { + binding.bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动 + } else { + binding.bound("propertychange", function (e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正 + if (e.propertyName === "value") { + updateVModel() + } + }) + } + binding.bound("dragend", function () { + setTimeout(function () { + updateVModel() + }, 17) + }) + //http://www.cnblogs.com/rubylouvre/archive/2013/02/17/2914604.html + //http://www.matts411.com/post/internet-explorer-9-oninput/ + } + break + case "select": + binding.bound("change", function () { + var val = avalon(elem).val() //字符串或字符串数组 + if (Array.isArray(val)) { + val = val.map(function (v) { + return binding.pipe(v, binding, "get") + }) + } else { + val = binding.pipe(val, binding, "get") + } + if (val + "" !== binding.oldValue) { + try { + binding.setter(val) + callback.call(elem, val) + } catch (ex) { + log(ex) + } + } + }) + binding.bound("datasetchanged", function (e) { + if (e.bubble === "selectDuplex") { + var value = binding._value + var curValue = Array.isArray(value) ? value.map(String) : value + "" + avalon(elem).val(curValue) + elem.oldValue = curValue + "" + callback.call(elem, curValue) + } + }) + break + } + if (binding.xtype === "input" && !rnoduplexInput.test(elem.type)) { + if (elem.type !== "hidden") { + binding.bound("focus", function () { + elem.msFocus = true + }) + binding.bound("blur", function () { + elem.msFocus = false + }) + } + elem.avalonSetter = updateVModel //#765 + watchValueInTimer(function () { + if (elem.contains(elem)) { + if (!this.msFocus && binding.oldValue !== elem.value) { + updateVModel() + } + } else if (!elem.msRetain) { + return false + } + }) + } + + }, + update: function (value) { + var elem = this.element, binding = this, curValue + if (!this.init) { + for (var i in avalon.vmodels) { + var v = avalon.vmodels[i] + v.$fire("avalon-ms-duplex-init", binding) + } + var cpipe = binding.pipe || (binding.pipe = pipe) + cpipe(null, binding, "init") + this.init = 1 + } + switch (this.xtype) { + case "input": + case "change": + curValue = this.pipe(value, this, "set") //fix #673 + if (curValue !== this.oldValue) { + var fixCaret = false + if (elem.msFocus) { + try { + var pos = getCaret(elem) + if (pos.start === pos.end) { + pos = pos.start + fixCaret = true + } + } catch (e) { + } + } + elem.value = this.oldValue = curValue + if (fixCaret) { + setCaret(elem, pos, pos) + } + } + break + case "radio": + curValue = binding.isChecked ? !!value : value + "" === elem.value + if (IEVersion === 6) { + setTimeout(function () { + //IE8 checkbox, radio是使用defaultChecked控制选中状态, + //并且要先设置defaultChecked后设置checked + //并且必须设置延迟 + elem.defaultChecked = curValue + elem.checked = curValue + }, 31) + } else { + elem.checked = curValue + } + break + case "checkbox": + var array = [].concat(value) //强制转换为数组 + curValue = this.pipe(elem.value, this, "get") + elem.checked = array.indexOf(curValue) > -1 + break + case "select": + //必须变成字符串后才能比较 + binding._value = value + if (!elem.msHasEvent) { + elem.msHasEvent = "selectDuplex" + //必须等到其孩子准备好才触发 + } else { + avalon.fireDom(elem, "datasetchanged", { + bubble: elem.msHasEvent + }) + } + break + } + if (binding.xtype !== "select") { + binding.changed.call(elem, curValue, binding) + } + } +}) + +if (IEVersion) { + avalon.bind(DOC, "selectionchange", function (e) { + var el = DOC.activeElement + if (el && typeof el.avalonSetter === "function") { + el.avalonSetter() + } + }) +} + +function fixNull(val) { + return val == null ? "" : val +} +avalon.duplexHooks = { + checked: { + get: function (val, binding) { + return !binding.oldValue + } + }, + string: { + get: function (val) { //同步到VM + return val + }, + set: fixNull + }, + "boolean": { + get: function (val) { + return val === "true" + }, + set: fixNull + }, + number: { + get: function (val, binding) { + var number = parseFloat(val + "") + if (-val === -number) { + return number + } + + var arr = /strong|medium|weak/.exec(binding.element.getAttribute("data-duplex-number")) || ["medium"] + switch (arr[0]) { + case "strong": + return 0 + case "medium": + return val === "" ? "" : 0 + case "weak": + return val + } + }, + set: fixNull + } +} + +function pipe(val, binding, action) { + binding.param.replace(rw20g, function (name) { + var hook = avalon.duplexHooks[name] + if (hook && typeof hook[action] === "function") { + val = hook[action](val, binding) + } + }) + return val +} + +var TimerID, ribbon = [] + +avalon.tick = function (fn) { + if (ribbon.push(fn) === 1) { + TimerID = setInterval(ticker, 60) + } +} + +function ticker() { + for (var n = ribbon.length - 1; n >= 0; n--) { + var el = ribbon[n] + if (el() === false) { + ribbon.splice(n, 1) + } + } + if (!ribbon.length) { + clearInterval(TimerID) + } +} + +var watchValueInTimer = noop +new function () { // jshint ignore:line + 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) + if (!this.msFocus && this.avalonSetter && this.oldValue !== value) { + this.avalonSetter() + } + } + 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 + }) + } 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 + watchValueInTimer = avalon.tick + } +} // jshint ignore:line +function getCaret(ctrl, start, end) { + if (ctrl.setSelectionRange) { + start = ctrl.selectionStart + end = ctrl.selectionEnd + } else if (document.selection && document.selection.createRange) { + var range = document.selection.createRange() + start = 0 - range.duplicate().moveStart('character', -100000) + end = start + range.text.length + } + return { + start: start, + end: end + } +} +function setCaret(ctrl, begin, end) { + if (!ctrl.value || ctrl.readOnly) + return + if (ctrl.createTextRange) {//IE6-9 + setTimeout(function () { + var range = ctrl.createTextRange() + range.collapse(true); + range.moveStart("character", begin) + // range.moveEnd("character", end) #1125 + range.select() + }, 17) + } else { + ctrl.selectionStart = begin + ctrl.selectionEnd = end + } +} +avalon.directive("effect", { + priority: 5, + init: function (binding) { + var text = binding.expr, + className, + rightExpr + var colonIndex = text.replace(rexprg, function (a) { + return a.replace(/./g, "0") + }).indexOf(":") //取得第一个冒号的位置 + if (colonIndex === -1) { // 比如 ms-class/effect="aaa bbb ccc" 的情况 + className = text + rightExpr = true + } else { // 比如 ms-class/effect-1="ui-state-active:checked" 的情况 + className = text.slice(0, colonIndex) + rightExpr = text.slice(colonIndex + 1) + } + if (!rexpr.test(text)) { + className = quote(className) + } else { + className = normalizeExpr(className) + } + binding.expr = "[" + className + "," + rightExpr + "]" + }, + update: function (arr) { + var name = arr[0] + var elem = this.element + if (elem.getAttribute("data-effect-name") === name) { + return + } else { + elem.removeAttribute("data-effect-driver") + } + var inlineStyles = elem.style + var computedStyles = window.getComputedStyle ? window.getComputedStyle(elem) : null + var useAni = false + if (computedStyles && (supportTransition || supportAnimation)) { + + //如果支持CSS动画 + var duration = inlineStyles[transitionDuration] || computedStyles[transitionDuration] + if (duration && duration !== '0s') { + elem.setAttribute("data-effect-driver", "t") + useAni = true + } + + if (!useAni) { + + duration = inlineStyles[animationDuration] || computedStyles[animationDuration] + if (duration && duration !== '0s') { + elem.setAttribute("data-effect-driver", "a") + useAni = true + } + + } + } + + if (!useAni) { + if (avalon.effects[name]) { + elem.setAttribute("data-effect-driver", "j") + useAni = true + } + } + if (useAni) { + elem.setAttribute("data-effect-name", name) + } + } +}) + +avalon.effects = {} +avalon.effect = function (name, callbacks) { + avalon.effects[name] = callbacks +} + + + +var supportTransition = false +var supportAnimation = false + +var transitionEndEvent +var animationEndEvent +var transitionDuration = avalon.cssName("transition-duration") +var animationDuration = avalon.cssName("animation-duration") +new function () {// jshint ignore:line + var checker = { + 'TransitionEvent': 'transitionend', + 'WebKitTransitionEvent': 'webkitTransitionEnd', + 'OTransitionEvent': 'oTransitionEnd', + 'otransitionEvent': 'otransitionEnd' + } + 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 + transitionEndEvent = tran + } + + //大致上有两种选择 + //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; + } + } + if (typeof ani === "string") { + supportTransition = true + animationEndEvent = ani + } + +}() + +var effectPool = []//重复利用动画实例 +function effectFactory(el, opts) { + if (!el || el.nodeType !== 1) { + return null + } + if (opts) { + var name = opts.effectName + var driver = opts.effectDriver + } else { + name = el.getAttribute("data-effect-name") + driver = el.getAttribute("data-effect-driver") + } + if (!name || !driver) { + return null + } + + var instance = effectPool.pop() || new Effect() + instance.el = el + instance.driver = driver + instance.useCss = driver !== "j" + if (instance.useCss) { + opts && avalon(el).addClass(opts.effectClass) + instance.cssEvent = driver === "t" ? transitionEndEvent : animationEndEvent + } + instance.name = name + instance.callbacks = avalon.effects[name] || {} + + return instance + + +} + +function effectBinding(elem, binding) { + var name = elem.getAttribute("data-effect-name") + if (name) { + binding.effectName = name + binding.effectDriver = elem.getAttribute("data-effect-driver") + var stagger = +elem.getAttribute("data-effect-stagger") + binding.effectLeaveStagger = +elem.getAttribute("data-effect-leave-stagger") || stagger + binding.effectEnterStagger = +elem.getAttribute("data-effect-enter-stagger") || stagger + binding.effectClass = elem.className || NaN + } +} +function upperFirstChar(str) { + return str.replace(/^[\S]/g, function (m) { + return m.toUpperCase() + }) +} +var effectBuffer = new Buffer() +function Effect() { +}// 动画实例,做成类的形式,是为了共用所有原型方法 + +Effect.prototype = { + contrustor: Effect, + enterClass: function () { + return getEffectClass(this, "enter") + }, + leaveClass: function () { + return getEffectClass(this, "leave") + }, + // 共享一个函数 + actionFun: function (name, before, after) { + if (document.hidden) { + return + } + var me = this + var el = me.el + var isLeave = name === "leave" + name = isLeave ? "leave" : "enter" + var oppositeName = isLeave ? "enter" : "leave" + callEffectHook(me, "abort" + upperFirstChar(oppositeName)) + callEffectHook(me, "before" + upperFirstChar(name)) + if (!isLeave) + before(el) //  这里可能做插入DOM树的操作,因此必须在修改类名前执行 + var cssCallback = function (cancel) { + el.removeEventListener(me.cssEvent, me.cssCallback) + if (isLeave) { + before(el) //这里可能做移出DOM树操作,因此必须位于动画之后 + avalon(el).removeClass(me.cssClass) + } else { + if (me.driver === "a") { + avalon(el).removeClass(me.cssClass) + } + } + if (cancel !== true) { + callEffectHook(me, "after" + upperFirstChar(name)) + after && after(el) + } + me.dispose() + } + if (me.useCss) { + if (me.cssCallback) { //如果leave动画还没有完成,立即完成 + me.cssCallback(true) + } + + me.cssClass = getEffectClass(me, name) + me.cssCallback = cssCallback + + me.update = function () { + el.addEventListener(me.cssEvent, me.cssCallback) + if (!isLeave && me.driver === "t") {//transtion延迟触发 + avalon(el).removeClass(me.cssClass) + } + } + avalon(el).addClass(me.cssClass)//animation会立即触发 + + effectBuffer.render(true) + effectBuffer.queue.push(me) + + } else { + callEffectHook(me, name, cssCallback) + + } + }, + enter: function (before, after) { + this.actionFun.apply(this, ["enter"].concat(avalon.slice(arguments))) + + }, + leave: function (before, after) { + this.actionFun.apply(this, ["leave"].concat(avalon.slice(arguments))) + + }, + dispose: function () {//销毁与回收到池子中 + this.update = this.cssCallback = null + if (effectPool.unshift(this) > 100) { + effectPool.pop() + } + } + + +} + + +function getEffectClass(instance, type) { + var a = instance.callbacks[type + "Class"] + if (typeof a === "string") + return a + if (typeof a === "function") + return a() + return instance.name + "-" + type +} + + +function callEffectHook(effect, name, cb) { + var hook = effect.callbacks[name] + if (hook) { + hook.call(effect, effect.el, cb) + } +} + +var applyEffect = function (el, dir/*[before, [after, [opts]]]*/) { + var args = aslice.call(arguments, 0) + if (typeof args[2] !== "function") { + args.splice(2, 0, noop) + } + if (typeof args[3] !== "function") { + args.splice(3, 0, noop) + } + var before = args[2] + var after = args[3] + var opts = args[4] + var effect = effectFactory(el, opts) + if (!effect) { + before() + after() + return false + } else { + var method = dir ? 'enter' : 'leave' + effect[method](before, after) + } +} + +avalon.mix(avalon.effect, { + apply: applyEffect, + append: function (el, parent, after, opts) { + return applyEffect(el, 1, function () { + parent.appendChild(el) + }, after, opts) + }, + before: function (el, target, after, opts) { + return applyEffect(el, 1, function () { + target.parentNode.insertBefore(el, target) + }, after, opts) + }, + remove: function (el, parent, after, opts) { + return applyEffect(el, 0, function () { + if (el.parentNode === parent) + parent.removeChild(el) + }, after, opts) + } +}) + + +avalon.directive("html", { + update: function (val) { + var binding = this + var elem = this.element + var isHtmlFilter = elem.nodeType !== 1 + var parent = isHtmlFilter ? elem.parentNode : elem + if (!parent) + return + val = val == null ? "" : val + + if (elem.nodeType === 3) { + var signature = generateID("html") + parent.insertBefore(DOC.createComment(signature), elem) + binding.element = DOC.createComment(signature + ":end") + parent.replaceChild(binding.element, elem) + elem = binding.element + } + if (typeof val !== "object") {//string, number, boolean + var fragment = avalon.parseHTML(String(val)) + } else if (val.nodeType === 11) { //将val转换为文档碎片 + fragment = val + } else if (val.nodeType === 1 || val.item) { + var nodes = val.nodeType === 1 ? val.childNodes : val.item + fragment = avalonFragment.cloneNode(true) + while (nodes[0]) { + fragment.appendChild(nodes[0]) + } + } + + nodes = avalon.slice(fragment.childNodes) + //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空 + if (isHtmlFilter) { + var endValue = elem.nodeValue.slice(0, -4) + while (true) { + var node = elem.previousSibling + if (!node || node.nodeType === 8 && node.nodeValue === endValue) { + break + } else { + parent.removeChild(node) + } + } + parent.insertBefore(fragment, elem) + } else { + avalon.clearHTML(elem).appendChild(fragment) + } + scanNodeArray(nodes, binding.vmodels) + } +}) + +avalon.directive("if", { + priority: 10, + update: function (val) { + var binding = this + var elem = this.element + var stamp = binding.stamp = +new Date() + var par + var after = function () { + if (stamp !== binding.stamp) + return + binding.recoverNode = null + } + if (binding.recoverNode) + binding.recoverNode() // 还原现场,有移动节点的都需要还原现场 + try { + if (!elem.parentNode) + return + par = elem.parentNode + } catch (e) { + return + } + if (val) { //插回DOM树 + function alway() {// jshint ignore:line + if (elem.getAttribute(binding.name)) { + elem.removeAttribute(binding.name) + scanAttr(elem, binding.vmodels) + } + binding.rollback = null + } + if (elem.nodeType === 8) { + var keep = binding.keep + var hasEffect = avalon.effect.apply(keep, 1, function () { + if (stamp !== binding.stamp) + return + elem.parentNode.replaceChild(keep, elem) + elem = binding.element = keep //这时可能为null + if (keep.getAttribute("_required")) {//#1044 + elem.required = true + elem.removeAttribute("_required") + } + if (elem.querySelectorAll) { + avalon.each(elem.querySelectorAll("[_required=true]"), function (el) { + el.required = true + el.removeAttribute("_required") + }) + } + alway() + }, after) + hasEffect = hasEffect === false + } + if (!hasEffect) + alway() + } else { //移出DOM树,并用注释节点占据原位置 + if (elem.nodeType === 1) { + if (elem.required === true) { + elem.required = false + elem.setAttribute("_required", "true") + } + try {// 如果不支持querySelectorAll或:required,可以直接无视 + avalon.each(elem.querySelectorAll(":required"), function (el) { + elem.required = false + el.setAttribute("_required", "true") + }) + } catch (e) { + } + + var node = binding.element = DOC.createComment("ms-if"), + pos = elem.nextSibling + binding.recoverNode = function () { + binding.recoverNode = null + if (node.parentNode !== par) { + par.insertBefore(node, pos) + binding.keep = elem + } + } + + avalon.effect.apply(elem, 0, function () { + binding.recoverNode = null + if (stamp !== binding.stamp) + return + elem.parentNode.replaceChild(node, elem) + binding.keep = elem //元素节点 + ifGroup.appendChild(elem) + binding.rollback = function () { + if (elem.parentNode === ifGroup) { + ifGroup.removeChild(elem) + } + } + }, after) + } + } + } +}) + + + +//ms-important绑定已经在scanTag 方法中实现 +var rnoscripts = /(?:[\s\S]+?)<\/noscript>/img +var rnoscriptText = /([\s\S]+?)<\/noscript>/im + +var getXHR = function () { + return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line +} +//将所有远程加载的模板,以字符串形式存放到这里 +var templatePool = avalon.templateCache = {} + +function getTemplateContainer(binding, id, text) { + var div = binding.templateCache && binding.templateCache[id] + if (div) { + var dom = DOC.createDocumentFragment(), + firstChild + while (firstChild = div.firstChild) { + dom.appendChild(firstChild) + } + return dom + } + return avalon.parseHTML(text) + +} +function nodesToFrag(nodes) { + var frag = DOC.createDocumentFragment() + for (var i = 0, len = nodes.length; i < len; i++) { + frag.appendChild(nodes[i]) + } + return frag +} +avalon.directive("include", { + init: directives.attr.init, + update: function (val) { + var binding = this + var elem = this.element + var vmodels = binding.vmodels + var rendered = binding.includeRendered + var effectClass = binding.effectName && binding.effectClass // 是否开启动画 + var templateCache = binding.templateCache // 是否data-include-cache + var outer = binding.includeReplace // 是否data-include-replace + var loaded = binding.includeLoaded + var target = outer ? elem.parentNode : elem + var _ele = binding._element // data-include-replace binding.element === binding.end + + binding.recoverNodes = binding.recoverNodes || avalon.noop + var scanTemplate = function (text) { + var _stamp = binding._stamp = +(new Date()) // 过滤掉频繁操作 + if (loaded) { + var newText = loaded.apply(target, [text].concat(vmodels)) + if (typeof newText === "string") + text = newText + } + if (rendered) { + checkScan(target, function () { + rendered.call(target) + }, NaN) + } + var lastID = binding.includeLastID || "_default" // 默认 + + binding.includeLastID = val + var leaveEl = templateCache && templateCache[lastID] || DOC.createElement(elem.tagName || binding._element.tagName) // 创建一个离场元素 + if (effectClass) { + leaveEl.className = effectClass + target.insertBefore(leaveEl, binding.start) // 插入到start之前,防止被错误的移动 + } + + // cache or animate,移动节点 + (templateCache || {})[lastID] = leaveEl + var fragOnDom = binding.recoverNodes() // 恢复动画中的节点 + if (fragOnDom) { + target.insertBefore(fragOnDom, binding.end) + } + while (true) { + var node = binding.start.nextSibling + if (node && node !== leaveEl && node !== binding.end) { + leaveEl.appendChild(node) + } else { + break + } + } + // 元素退场 + avalon.effect.remove(leaveEl, target, function () { + if (templateCache) { // write cache + if (_stamp === binding._stamp) + ifGroup.appendChild(leaveEl) + } + }, binding) + + + var enterEl = target, + before = avalon.noop, + after = avalon.noop + + var fragment = getTemplateContainer(binding, val, text) + var nodes = avalon.slice(fragment.childNodes) + + if (outer && effectClass) { + enterEl = _ele + enterEl.innerHTML = "" // 清空 + enterEl.setAttribute("ms-skip", "true") + target.insertBefore(enterEl, binding.end.nextSibling) // 插入到bingding.end之后避免被错误的移动 + before = function () { + enterEl.insertBefore(fragment, null) // 插入节点 + } + after = function () { + binding.recoverNodes = avalon.noop + if (_stamp === binding._stamp) { + fragment = nodesToFrag(nodes) + target.insertBefore(fragment, binding.end) // 插入真实element + scanNodeArray(nodes, vmodels) + } + if (enterEl.parentNode === target) + target.removeChild(enterEl) // 移除入场动画元素 + } + binding.recoverNodes = function () { + binding.recoverNodes = avalon.noop + return nodesToFrag(nodes) + } + } else { + before = function () {// 新添加元素的动画  + target.insertBefore(fragment, binding.end) + scanNodeArray(nodes, vmodels) + } + } + + avalon.effect.apply(enterEl, "enter", before, after) + } + + if (binding.param === "src") { + if (typeof templatePool[val] === "string") { + avalon.nextTick(function () { + scanTemplate(templatePool[val]) + }) + } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求 + templatePool[val].push(scanTemplate) + } else { + var xhr = getXHR() + xhr.onreadystatechange = function () { + if (xhr.readyState === 4) { + var s = xhr.status + if (s >= 200 && s < 300 || s === 304 || s === 1223) { + var text = xhr.responseText + for (var f = 0, fn; fn = templatePool[val][f++]; ) { + fn(text) + } + templatePool[val] = text + }else{ + log("ms-include load ["+ val +"] error") + } + } + } + templatePool[val] = [scanTemplate] + xhr.open("GET", val, true) + if ("withCredentials" in xhr) { + xhr.withCredentials = true + } + xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest") + xhr.send(null) + } + } else { + //IE系列与够新的标准浏览器支持通过ID取得元素(firefox14+) + //http://tjvantoll.com/2012/07/19/dom-element-references-as-global-variables/ + var el = val && val.nodeType === 1 ? val : DOC.getElementById(val) + if (el) { + if (el.tagName === "NOSCRIPT" && !(el.innerHTML || el.fixIE78)) { //IE7-8 innerText,innerHTML都无法取得其内容,IE6能取得其innerHTML + xhr = getXHR() //IE9-11与chrome的innerHTML会得到转义的内容,它们的innerText可以 + xhr.open("GET", location, false) + xhr.send(null) + //http://bbs.csdn.net/topics/390349046?page=1#post-393492653 + var noscripts = DOC.getElementsByTagName("noscript") + var array = (xhr.responseText || "").match(rnoscripts) || [] + var n = array.length + for (var i = 0; i < n; i++) { + var tag = noscripts[i] + if (tag) { //IE6-8中noscript标签的innerHTML,innerText是只读的 + tag.style.display = "none" //http://haslayout.net/css/noscript-Ghost-Bug + tag.fixIE78 = (array[i].match(rnoscriptText) || ["", " "])[1] + } + } + } + avalon.nextTick(function () { + scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML) + }) + } + } + } +}) + +var rdash = /\(([^)]*)\)/ +var onDir = avalon.directive("on", { + priority: 3000, + init: function (binding) { + var value = binding.expr + binding.type = "on" + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (typeof onDir[eventType + "Hook"] === "function") { + onDir[eventType + "Hook"](binding) + } + if (value.indexOf("(") > 0 && value.indexOf(")") > -1) { + var matched = (value.match(rdash) || ["", ""])[1].trim() + if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理 + value = value.replace(rdash, "") + } + } + binding.expr = value + }, + update: function (callback) { + var binding = this + var elem = this.element + callback = function (e) { + var fn = binding.getter || noop + return fn.apply(this, binding.args.concat(e)) + } + + var eventType = binding.param.replace(/-\d+$/, "") // ms-on-mousemove-10 + if (eventType === "scan") { + callback.call(elem, { + type: eventType + }) + } else if (typeof binding.specialBind === "function") { + binding.specialBind(elem, callback) + } else { + var removeFn = avalon.bind(elem, eventType, callback) + } + binding.rollback = function () { + if (typeof binding.specialUnbind === "function") { + binding.specialUnbind() + } else { + avalon.unbind(elem, eventType, removeFn) + } + } + } +}) +avalon.directive("repeat", { + priority: 90, + init: function (binding) { + var type = binding.type + binding.cache = {} //用于存放代理VM + binding.enterCount = 0 + + var elem = binding.element + if (elem.nodeType === 1) { + elem.removeAttribute(binding.name) + effectBinding(elem, binding) + binding.param = binding.param || "el" + binding.sortedCallback = getBindingCallback(elem, "data-with-sorted", binding.vmodels) + var rendered = getBindingCallback(elem, "data-" + type + "-rendered", binding.vmodels) + + var signature = generateID(type) + var start = DOC.createComment(signature + ":start") + var end = binding.element = DOC.createComment(signature + ":end") + binding.signature = signature + binding.start = start + binding.template = avalonFragment.cloneNode(false) + if (type === "repeat") { + var parent = elem.parentNode + parent.replaceChild(end, elem) + parent.insertBefore(start, end) + binding.template.appendChild(elem) + } else { + while (elem.firstChild) { + binding.template.appendChild(elem.firstChild) + } + elem.appendChild(start) + elem.appendChild(end) + parent = elem + } + binding.element = end + + if (rendered) { + var removeFn = avalon.bind(parent, "datasetchanged", function () { + rendered.apply(parent, parent.args) + avalon.unbind(parent, "datasetchanged", removeFn) + parent.msRendered = rendered + }) + } + } + }, + update: function (value, oldValue) { + var binding = this + var xtype = this.xtype + + this.enterCount += 1 + var init = !oldValue + if (init) { + binding.$outer = {} + var check0 = "$key" + var check1 = "$val" + if (xtype === "array") { + check0 = "$first" + check1 = "$last" + } + for (var i = 0, v; v = binding.vmodels[i++]; ) { + if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) { + binding.$outer = v + break + } + } + } + var track = this.track + if (binding.sortedCallback) { //如果有回调,则让它们排序 + var keys2 = binding.sortedCallback.call(parent, track) + if (keys2 && Array.isArray(keys2)) { + track = keys2 + } + } + + var action = "move" + binding.$repeat = value + var fragments = [] + var transation = init && avalonFragment.cloneNode(false) + var proxies = [] + var param = this.param + var retain = avalon.mix({}, this.cache) + var elem = this.element + var length = track.length + + var parent = elem.parentNode + for (i = 0; i < length; i++) { + + var keyOrId = track[i] //array为随机数, object 为keyName + var proxy = retain[keyOrId] + if (!proxy) { + + proxy = getProxyVM(this) + proxy.$up = null + if (xtype === "array") { + action = "add" + proxy.$id = keyOrId + var valueItem = value[i] + proxy[param] = valueItem //index + if(Object(valueItem) === valueItem){ + valueItem.$ups = valueItem.$ups || {} + valueItem.$ups[param] = proxy + } + + } else { + action = "append" + proxy.$key = keyOrId + proxy.$val = value[keyOrId] //key + } + this.cache[keyOrId] = proxy + var node = proxy.$anchor || (proxy.$anchor = elem.cloneNode(false)) + node.nodeValue = this.signature + shimController(binding, transation, proxy, fragments, init && !binding.effectDriver) + decorateProxy(proxy, binding, xtype) + } else { +// if (xtype === "array") { +// proxy[param] = value[i] +// } + fragments.push({}) + retain[keyOrId] = true + } + + //重写proxy + if (this.enterCount === 1) {// 防止多次进入,导致位置不对 + proxy.$active = false + proxy.$oldIndex = proxy.$index + proxy.$active = true + proxy.$index = i + + } + + if (xtype === "array") { + proxy.$first = i === 0 + proxy.$last = i === length - 1 + // proxy[param] = value[i] + } else { + proxy.$val = toJson(value[keyOrId]) // 这里是处理vm.object = newObject的情况 + } + proxies.push(proxy) + } + this.proxies = proxies + if (init && !binding.effectDriver) { + parent.insertBefore(transation, elem) + fragments.forEach(function (fragment) { + scanNodeArray(fragment.nodes || [], fragment.vmodels) + //if(fragment.vmodels.length > 2) + fragment.nodes = fragment.vmodels = null + })// jshint ignore:line + } else { + + var staggerIndex = binding.staggerIndex = 0 + for (keyOrId in retain) { + if (retain[keyOrId] !== true) { + + action = "del" + removeItem(retain[keyOrId].$anchor, binding) + // avalon.log("删除", keyOrId) + // 相当于delete binding.cache[key] + proxyRecycler(this.cache, keyOrId, param) + retain[keyOrId] = null + } + } + + // console.log(effectEnterStagger) + for (i = 0; i < length; i++) { + proxy = proxies[i] + keyOrId = xtype === "array" ? proxy.$id : proxy.$key + var pre = proxies[i - 1] + var preEl = pre ? pre.$anchor : binding.start + if (!retain[keyOrId]) {//如果还没有插入到DOM树 + (function (fragment, preElement) { + var nodes = fragment.nodes + var vmodels = fragment.vmodels + if (nodes) { + staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { + parent.insertBefore(fragment.content, preElement.nextSibling) + scanNodeArray(nodes, vmodels) + animateRepeat(nodes, 1, binding) + }, staggerIndex) + } + fragment.nodes = fragment.vmodels = null + })(fragments[i], preEl)// jshint ignore:line + // avalon.log("插入") + + } else if (proxy.$index !== proxy.$oldIndex) { + (function (proxy2, preElement) { + staggerIndex = mayStaggerAnimate(binding.effectEnterStagger, function () { + var curNode = removeItem(proxy2.$anchor)// 如果位置被挪动了 + var inserted = avalon.slice(curNode.childNodes) + parent.insertBefore(curNode, preElement.nextSibling) + animateRepeat(inserted, 1, binding) + }, staggerIndex) + })(proxy, preEl)// jshint ignore:line + + // avalon.log("移动", proxy.$oldIndex, "-->", proxy.$index) + } + } + + } + if (!value.$track) {//如果是非监控对象,那么就将其$events清空,阻止其持续监听 + for (keyOrId in this.cache) { + proxyRecycler(this.cache, keyOrId, param) + } + + } + + //repeat --> duplex + (function (args) { + parent.args = args + if (parent.msRendered) {//第一次事件触发,以后直接调用 + parent.msRendered.apply(parent, args) + } + })(kernel.newWatch ? arguments : [action]); + var id = setTimeout(function () { + clearTimeout(id) + //触发上层的select回调及自己的rendered回调 + avalon.fireDom(parent, "datasetchanged", { + bubble: parent.msHasEvent + }) + }) + this.enterCount -= 1 + + } + +}) + +"with,each".replace(rword, function (name) { + directives[name] = avalon.mix({}, directives.repeat, { + priority: 1400 + }) +}) + + +function animateRepeat(nodes, isEnter, binding) { + for (var i = 0, node; node = nodes[i++]; ) { + if (node.className === binding.effectClass) { + avalon.effect.apply(node, isEnter, noop, noop, binding) + } + } +} + +function mayStaggerAnimate(staggerTime, callback, index) { + if (staggerTime) { + setTimeout(callback, (++index) * staggerTime) + } else { + callback() + } + return index +} + + +function removeItem(node, binding) { + var fragment = avalonFragment.cloneNode(false) + var last = node + var breakText = last.nodeValue + var staggerIndex = binding && Math.max(+binding.staggerIndex, 0) + var nodes = avalon.slice(last.parentNode.childNodes) + var index = nodes.indexOf(last) + while (true) { + var pre = nodes[--index] //node.previousSibling + if (!pre || String(pre.nodeValue).indexOf(breakText) === 0) { + break + } + + if (binding && (pre.className === binding.effectClass)) { + node = pre; + (function (cur) { + binding.staggerIndex = mayStaggerAnimate(binding.effectLeaveStagger, function () { + avalon.effect.apply(cur, 0, noop, function () { + fragment.appendChild(cur) + }, binding) + }, staggerIndex) + })(pre);// jshint ignore:line + } else { + fragment.insertBefore(pre, fragment.firstChild) + } + } + fragment.appendChild(last) + return fragment +} + + +function shimController(data, transation, proxy, fragments, init) { + var content = data.template.cloneNode(true) + var nodes = avalon.slice(content.childNodes) + content.appendChild(proxy.$anchor) + init && transation.appendChild(content) + var nv = [proxy].concat(data.vmodels) + var fragment = { + nodes: nodes, + vmodels: nv, + content: content + } + fragments.push(fragment) +} +// {} --> {xx: 0, yy: 1, zz: 2} add +// {xx: 0, yy: 1, zz: 2} --> {xx: 0, yy: 1, zz: 2, uu: 3} +// [xx: 0, yy: 1, zz: 2} --> {xx: 0, zz: 1, yy: 2} + +function getProxyVM(binding) { + var agent = binding.xtype === "object" ? withProxyAgent : eachProxyAgent + var proxy = agent(binding) + var node = proxy.$anchor || (proxy.$anchor = binding.element.cloneNode(false)) + node.nodeValue = binding.signature + proxy.$outer = binding.$outer + return proxy +} + +var eachProxyPool = [] + +function eachProxyAgent(data, proxy) { + var itemName = data.param || "el" + for (var i = 0, n = eachProxyPool.length; i < n; i++) { + var candidate = eachProxyPool[i] + if (candidate && candidate.hasOwnProperty(itemName)) { + eachProxyPool.splice(i, 1) + proxy = candidate + break + } + } + if (!proxy) { + proxy = eachProxyFactory(itemName) + } + return proxy +} + +function eachProxyFactory(itemName) { + var source = { + $outer: {}, + $index: 0, + $oldIndex: 0, + $anchor: null, + //----- + $first: false, + $last: false, + $remove: avalon.noop + } + source[itemName] = NaN + + var force = { + $last: 1, + $first: 1, + $index: 1 + } + force[itemName] = 1 + var proxy = modelFactory(source, { + force: force + }) + proxy.$id = generateID("$proxy$each") + return proxy +} + +function decorateProxy(proxy, binding, type) { + if (type === "array") { + proxy.$remove = function () { + + binding.$repeat.removeAt(proxy.$index) + } + var param = binding.param + + + proxy.$watch(param, function (a) { + var index = proxy.$index + binding.$repeat[index] = a + }) + } else { + proxy.$watch("$val", function fn(a) { + binding.$repeat[proxy.$key] = a + }) + } +} + +var withProxyPool = [] + +function withProxyAgent() { + return withProxyPool.pop() || withProxyFactory() +} + +function withProxyFactory() { + var proxy = modelFactory({ + $key: "", + $val: NaN, + $index: 0, + $oldIndex: 0, + $outer: {}, + $anchor: null + }, { + force: { + $key: 1, + $val: 1, + $index: 1 + } + }) + proxy.$id = generateID("$proxy$with") + return proxy +} + + +function proxyRecycler(cache, key, param) { + var proxy = cache[key] + if (proxy) { + var proxyPool = proxy.$id.indexOf("$proxy$each") === 0 ? eachProxyPool : withProxyPool + proxy.$outer = {} + + for (var i in proxy.$events) { + var a = proxy.$events[i] + if (Array.isArray(a)) { + a.length = 0 + if (i === param) { + proxy[param] = NaN + + } else if (i === "$val") { + proxy.$val = NaN + } + } + } + + if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) { + proxyPool.pop() + } + delete cache[key] + } +} +/********************************************************************* + * 各种指令 * + **********************************************************************/ +//ms-skip绑定已经在scanTag 方法中实现 +avalon.directive("text", { + update: function (value) { + var elem = this.element + value = value == null ? "" : value //不在页面上显示undefined null + if (elem.nodeType === 3) { //绑定在文本节点上 + try { //IE对游离于DOM树外的节点赋值会报错 + elem.data = value + } catch (e) { + } + } else { //绑定在特性节点上 + if ("textContent" in elem) { + elem.textContent = value + } else { + elem.innerText = value + } + } + } +}) +function parseDisplay(nodeName, val) { + //用于取得此类标签的默认display值 + var key = "_" + nodeName + if (!parseDisplay[key]) { + var node = DOC.createElement(nodeName) + root.appendChild(node) + if (W3C) { + val = getComputedStyle(node, null).display + } else { + val = node.currentStyle.display + } + root.removeChild(node) + parseDisplay[key] = val + } + return parseDisplay[key] +} + +avalon.parseDisplay = parseDisplay + +avalon.directive("visible", { + init: function (binding) { + effectBinding(binding.element, binding) + }, + update: function (val) { + var binding = this, elem = this.element, stamp + var noEffect = !this.effectName + if (!this.stamp) { + stamp = this.stamp = +new Date + if (val) { + elem.style.display = binding.display || "" + if (avalon(elem).css("display") === "none") { + elem.style.display = binding.display = parseDisplay(elem.nodeName) + } + } else { + elem.style.display = "none" + } + return + } + stamp = this.stamp = +new Date + if (val) { + avalon.effect.apply(elem, 1, function () { + if (stamp !== binding.stamp) + return + var driver = elem.getAttribute("data-effect-driver") || "a" + + if (noEffect) {//不用动画时走这里 + elem.style.display = binding.display || "" + } + // "a", "t" + if (driver === "a" || driver === "t") { + if (avalon(elem).css("display") === "none") { + elem.style.display = binding.display || parseDisplay(elem.nodeName) + } + } + }) + } else { + avalon.effect.apply(elem, 0, function () { + if (stamp !== binding.stamp) + return + elem.style.display = "none" + }) + } + } +}) + +/********************************************************************* + * 自带过滤器 * + **********************************************************************/ +var rscripts = /]*>([\S\s]*?)<\/script\s*>/gim +var ron = /\s+(on[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g +var ropen = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/ig +var rsanitize = { + a: /\b(href)\=("javascript[^"]*"|'javascript[^']*')/ig, + img: /\b(src)\=("javascript[^"]*"|'javascript[^']*')/ig, + form: /\b(action)\=("javascript[^"]*"|'javascript[^']*')/ig +} +var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g +var rnoalphanumeric = /([^\#-~| |!])/g; + +function numberFormat(number, decimals, point, thousands) { + //form http://phpjs.org/functions/number_format/ + //number 必需,要格式化的数字 + //decimals 可选,规定多少个小数位。 + //point 可选,规定用作小数点的字符串(默认为 . )。 + //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。 + number = (number + '') + .replace(/[^0-9+\-Ee.]/g, '') + var n = !isFinite(+number) ? 0 : +number, + prec = !isFinite(+decimals) ? 3 : Math.abs(decimals), + sep = thousands || ",", + dec = point || ".", + s = '', + toFixedFix = function(n, prec) { + var k = Math.pow(10, prec) + return '' + (Math.round(n * k) / k) + .toFixed(prec) + } + // Fix for IE parseFloat(0.55).toFixed(0) = 0; + s = (prec ? toFixedFix(n, prec) : '' + Math.round(n)) + .split('.') + if (s[0].length > 3) { + s[0] = s[0].replace(/\B(?=(?:\d{3})+(?!\d))/g, sep) + } + if ((s[1] || '') + .length < prec) { + s[1] = s[1] || '' + s[1] += new Array(prec - s[1].length + 1) + .join('0') + } + return s.join(dec) +} + + +var filters = avalon.filters = { + uppercase: function(str) { + return str.toUpperCase() + }, + lowercase: function(str) { + return str.toLowerCase() + }, + truncate: function(str, length, truncation) { + //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串 + length = length || 30 + truncation = typeof truncation === "string" ? truncation : "..." + return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str) + }, + $filter: function(val) { + for (var i = 1, n = arguments.length; i < n; i++) { + var array = arguments[i] + var fn = avalon.filters[array[0]] + if (typeof fn === "function") { + var arr = [val].concat(array.slice(1)) + val = fn.apply(null, arr) + } + } + return val + }, + camelize: camelize, + //https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet + // chrome + // chrome + // IE67chrome + // IE67chrome + // IE67chrome + sanitize: function(str) { + return str.replace(rscripts, "").replace(ropen, function(a, b) { + var match = a.toLowerCase().match(/<(\w+)\s/) + if (match) { //处理a标签的href属性,img标签的src属性,form标签的action属性 + var reg = rsanitize[match[1]] + if (reg) { + a = a.replace(reg, function(s, name, value) { + var quote = value.charAt(0) + return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line + }) + } + } + return a.replace(ron, " ").replace(/\s+/g, " ") //移除onXXX事件 + }) + }, + escape: function(str) { + //将字符串经过 str 转义得到适合在页面中显示的内容, 例如替换 < 为 < + return String(str). + replace(/&/g, '&'). + replace(rsurrogate, function(value) { + var hi = value.charCodeAt(0) + var low = value.charCodeAt(1) + return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';' + }). + replace(rnoalphanumeric, function(value) { + return '&#' + value.charCodeAt(0) + ';' + }). + replace(//g, '>') + }, + currency: function(amount, symbol, fractionSize) { + return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2) + }, + number: numberFormat +} +/* + 'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010) + 'yy': 2 digit representation of year, padded (00-99). (e.g. AD 2001 => 01, AD 2010 => 10) + 'y': 1 digit representation of year, e.g. (AD 1 => 1, AD 199 => 199) + 'MMMM': Month in year (January-December) + 'MMM': Month in year (Jan-Dec) + 'MM': Month in year, padded (01-12) + 'M': Month in year (1-12) + 'dd': Day in month, padded (01-31) + 'd': Day in month (1-31) + 'EEEE': Day in Week,(Sunday-Saturday) + 'EEE': Day in Week, (Sun-Sat) + 'HH': Hour in day, padded (00-23) + 'H': Hour in day (0-23) + 'hh': Hour in am/pm, padded (01-12) + 'h': Hour in am/pm, (1-12) + 'mm': Minute in hour, padded (00-59) + 'm': Minute in hour (0-59) + 'ss': Second in minute, padded (00-59) + 's': Second in minute (0-59) + 'a': am/pm marker + 'Z': 4 digit (+sign) representation of the timezone offset (-1200-+1200) + format string can also be one of the following predefined localizable formats: + + 'medium': equivalent to 'MMM d, y h:mm:ss a' for en_US locale (e.g. Sep 3, 2010 12:05:08 pm) + 'short': equivalent to 'M/d/yy h:mm a' for en_US locale (e.g. 9/3/10 12:05 pm) + 'fullDate': equivalent to 'EEEE, MMMM d,y' for en_US locale (e.g. Friday, September 3, 2010) + 'longDate': equivalent to 'MMMM d, y' for en_US locale (e.g. September 3, 2010 + 'mediumDate': equivalent to 'MMM d, y' for en_US locale (e.g. Sep 3, 2010) + 'shortDate': equivalent to 'M/d/yy' for en_US locale (e.g. 9/3/10) + 'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm) + 'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm) + */ +new function() {// jshint ignore:line + function toInt(str) { + return parseInt(str, 10) || 0 + } + + function padNumber(num, digits, trim) { + var neg = "" + if (num < 0) { + neg = '-' + num = -num + } + num = "" + num + while (num.length < digits) + num = "0" + num + if (trim) + num = num.substr(num.length - digits) + return neg + num + } + + function dateGetter(name, size, offset, trim) { + return function(date) { + var value = date["get" + name]() + if (offset > 0 || value > -offset) + value += offset + if (value === 0 && offset === -12) { + value = 12 + } + return padNumber(value, size, trim) + } + } + + function dateStrGetter(name, shortForm) { + return function(date, formats) { + var value = date["get" + name]() + var get = (shortForm ? ("SHORT" + name) : name).toUpperCase() + return formats[get][value] + } + } + + function timeZoneGetter(date) { + var zone = -1 * date.getTimezoneOffset() + var paddedZone = (zone >= 0) ? "+" : "" + paddedZone += padNumber(Math[zone > 0 ? "floor" : "ceil"](zone / 60), 2) + padNumber(Math.abs(zone % 60), 2) + return paddedZone + } + //取得上午下午 + + function ampmGetter(date, formats) { + return date.getHours() < 12 ? formats.AMPMS[0] : formats.AMPMS[1] + } + var DATE_FORMATS = { + yyyy: dateGetter("FullYear", 4), + yy: dateGetter("FullYear", 2, 0, true), + y: dateGetter("FullYear", 1), + MMMM: dateStrGetter("Month"), + MMM: dateStrGetter("Month", true), + MM: dateGetter("Month", 2, 1), + M: dateGetter("Month", 1, 1), + dd: dateGetter("Date", 2), + d: dateGetter("Date", 1), + HH: dateGetter("Hours", 2), + H: dateGetter("Hours", 1), + hh: dateGetter("Hours", 2, -12), + h: dateGetter("Hours", 1, -12), + mm: dateGetter("Minutes", 2), + m: dateGetter("Minutes", 1), + ss: dateGetter("Seconds", 2), + s: dateGetter("Seconds", 1), + sss: dateGetter("Milliseconds", 3), + EEEE: dateStrGetter("Day"), + EEE: dateStrGetter("Day", true), + a: ampmGetter, + Z: timeZoneGetter + } + var rdateFormat = /((?:[^yMdHhmsaZE']+)|(?:'(?:[^']|'')*')|(?:E+|y+|M+|d+|H+|h+|m+|s+|a|Z))(.*)/ + var raspnetjson = /^\/Date\((\d+)\)\/$/ + filters.date = function(date, format) { + var locate = filters.date.locate, + text = "", + parts = [], + fn, match + format = format || "mediumDate" + format = locate[format] || format + if (typeof date === "string") { + if (/^\d+$/.test(date)) { + date = toInt(date) + } else if (raspnetjson.test(date)) { + date = +RegExp.$1 + } else { + var trimDate = date.trim() + var dateArray = [0, 0, 0, 0, 0, 0, 0] + var oDate = new Date(0) + //取得年月日 + trimDate = trimDate.replace(/^(\d+)\D(\d+)\D(\d+)/, function(_, a, b, c) { + var array = c.length === 4 ? [c, a, b] : [a, b, c] + dateArray[0] = toInt(array[0]) //年 + dateArray[1] = toInt(array[1]) - 1 //月 + dateArray[2] = toInt(array[2]) //日 + return "" + }) + var dateSetter = oDate.setFullYear + var timeSetter = oDate.setHours + trimDate = trimDate.replace(/[T\s](\d+):(\d+):?(\d+)?\.?(\d)?/, function(_, a, b, c, d) { + dateArray[3] = toInt(a) //小时 + dateArray[4] = toInt(b) //分钟 + dateArray[5] = toInt(c) //秒 + if (d) { //毫秒 + dateArray[6] = Math.round(parseFloat("0." + d) * 1000) + } + return "" + }) + var tzHour = 0 + var tzMin = 0 + trimDate = trimDate.replace(/Z|([+-])(\d\d):?(\d\d)/, function(z, symbol, c, d) { + dateSetter = oDate.setUTCFullYear + timeSetter = oDate.setUTCHours + if (symbol) { + tzHour = toInt(symbol + c) + tzMin = toInt(symbol + d) + } + return "" + }) + + dateArray[3] -= tzHour + dateArray[4] -= tzMin + dateSetter.apply(oDate, dateArray.slice(0, 3)) + timeSetter.apply(oDate, dateArray.slice(3)) + date = oDate + } + } + if (typeof date === "number") { + date = new Date(date) + } + if (avalon.type(date) !== "date") { + return + } + while (format) { + match = rdateFormat.exec(format) + if (match) { + parts = parts.concat(match.slice(1)) + format = parts.pop() + } else { + parts.push(format) + format = null + } + } + parts.forEach(function(value) { + fn = DATE_FORMATS[value] + text += fn ? fn(date, locate) : value.replace(/(^'|'$)/g, "").replace(/''/g, "'") + }) + return text + } + var locate = { + AMPMS: { + 0: "上午", + 1: "下午" + }, + DAY: { + 0: "星期日", + 1: "星期一", + 2: "星期二", + 3: "星期三", + 4: "星期四", + 5: "星期五", + 6: "星期六" + }, + MONTH: { + 0: "1月", + 1: "2月", + 2: "3月", + 3: "4月", + 4: "5月", + 5: "6月", + 6: "7月", + 7: "8月", + 8: "9月", + 9: "10月", + 10: "11月", + 11: "12月" + }, + SHORTDAY: { + "0": "周日", + "1": "周一", + "2": "周二", + "3": "周三", + "4": "周四", + "5": "周五", + "6": "周六" + }, + fullDate: "y年M月d日EEEE", + longDate: "y年M月d日", + medium: "yyyy-M-d H:mm:ss", + mediumDate: "yyyy-M-d", + mediumTime: "H:mm:ss", + "short": "yy-M-d ah:mm", + shortDate: "yy-M-d", + shortTime: "ah:mm" + } + locate.SHORTMONTH = locate.MONTH + filters.date.locate = locate +}// jshint ignore:line +/********************************************************************* + * DOMReady * + **********************************************************************/ + +var readyList = [], + isReady +var fireReady = function (fn) { + isReady = true + var require = avalon.require + if (require && require.checkDeps) { + modules["domReady!"].state = 4 + require.checkDeps() + } + while (fn = readyList.shift()) { + fn(avalon) + } +} + +function doScrollCheck() { + try { //IE下通过doScrollCheck检测DOM树是否建完 + root.doScroll("left") + fireReady() + } catch (e) { + setTimeout(doScrollCheck) + } +} + +if (DOC.readyState === "complete") { + setTimeout(fireReady) //如果在domReady之外加载 +} else if (W3C) { + DOC.addEventListener("DOMContentLoaded", fireReady) +} else { + DOC.attachEvent("onreadystatechange", function () { + if (DOC.readyState === "complete") { + fireReady() + } + }) + try { + var isTop = window.frameElement === null + } catch (e) {} + if (root.doScroll && isTop && window.external) { //fix IE iframe BUG + doScrollCheck() + } +} +avalon.bind(window, "load", fireReady) + +avalon.ready = function (fn) { + if (!isReady) { + readyList.push(fn) + } else { + fn(avalon) + } +} + +avalon.config({ + loader: true +}) + +avalon.ready(function () { + avalon.scan(DOC.body) +}) + + +// Register as a named AMD module, since avalon can be concatenated with other +// files that may use define, but not via a proper concatenation script that +// understands anonymous AMD modules. A named AMD is safest and most robust +// way to register. Lowercase avalon is used because AMD module names are +// derived from file names, and Avalon is normally delivered in a lowercase +// file name. Do this after creating the global so that if an AMD module wants +// to call noConflict to hide this version of avalon, it will work. + +// Note that for maximum portability, libraries that are not avalon should +// declare themselves as anonymous modules, and avoid setting a global if an +// AMD loader is present. avalon is a special case. For more information, see +// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon + if (typeof define === "function" && define.amd) { + define("avalon", [], function() { + return avalon + }) + } +// Map over avalon in case of overwrite + var _avalon = window.avalon + avalon.noConflict = function(deep) { + if (deep && window.avalon === avalon) { + window.avalon = _avalon + } + return avalon + } +// Expose avalon identifiers, even in AMD +// and CommonJS for browser emulators + if (noGlobal === void 0) { + window.avalon = avalon + } + return avalon + +})); \ No newline at end of file From 4c988ce9325d19b567b5cfe5742352250deadee5 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 29 Oct 2015 14:21:16 +0800 Subject: [PATCH 4/7] fix typo --- static/src/js/app/admin/problem/addProblem.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/src/js/app/admin/problem/addProblem.js b/static/src/js/app/admin/problem/addProblem.js index d8f89289..553e1f48 100644 --- a/static/src/js/app/admin/problem/addProblem.js +++ b/static/src/js/app/admin/problem/addProblem.js @@ -145,7 +145,7 @@ require(["jquery", "avalon", "editor", "uploader", "bsAlert", "csrfToken", "tagE } }, function (file, percentage) { - vm.uploadProgress = praseInt(percentage * 100); + vm.uploadProgress = parseInt(percentage * 100); }); var tagAutoCompleteList = []; From f63685e3f77f2007a30a7224cf15b8403b3e5e3c Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 29 Oct 2015 14:21:56 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2=E7=9A=84=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E6=AF=94=E8=B5=9B=E5=92=8C=E6=98=BE=E7=A4=BA=E6=AF=94=E8=B5=9B?= =?UTF-8?q?=E9=A2=98=E7=9B=AE=E7=9A=84=E5=87=BD=E6=95=B0=E5=90=8D=E5=8F=8A?= =?UTF-8?q?=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- template/src/admin/contest/contest_list.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/template/src/admin/contest/contest_list.html b/template/src/admin/contest/contest_list.html index 0a1eeb42..829f6b23 100644 --- a/template/src/admin/contest/contest_list.html +++ b/template/src/admin/contest/contest_list.html @@ -29,8 +29,8 @@ {{ el.created_by.username }} - 编辑 - 题目 + 编辑 + 题目 From 1cecb542d953fbce33738d0a347222dac8879fe3 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 29 Oct 2015 14:22:34 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E4=B8=8D=E5=86=8D=E4=BD=BF=E7=94=A8=20fire?= =?UTF-8?q?=20=E5=92=8C=20watch=20=E7=9A=84=E6=96=B9=E5=BC=8F=E9=80=9A?= =?UTF-8?q?=E4=BF=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/admin.js | 27 ++------------------------- 1 file changed, 2 insertions(+), 25 deletions(-) diff --git a/static/src/js/app/admin/admin.js b/static/src/js/app/admin/admin.js index 6ef30f62..0e8e83d5 100644 --- a/static/src/js/app/admin/admin.js +++ b/static/src/js/app/admin/admin.js @@ -52,10 +52,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, { name: "首页", children: [{name: "主页", hash: "#index/index"}] }, - { - name: "通用", - children: [{name: "公告管理", hash: "#announcement/announcement"}] - }, { name: "比赛管理", children: [{name: "比赛列表", hash: "#contest/contest_list"}, @@ -75,9 +71,8 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, groupId: -1, problemId: -1, adminNavList: [], - $contestMode: -1, - $problemId: -1, - $contestId: -1, + + contestId: -1, hide_loading: function () { $("#loading-gif").hide(); }, @@ -113,24 +108,6 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "bootstrap"], function ($, vm.template_url = "template/group/group.html"; }); - vm.$watch("showContestProblemPage", function (problemId, contestId, contestMode) { - vm.$problemId = problemId; - vm.$contestId = contestId; - vm.$contestMode = contestMode - vm.template_url = "template/contest/edit_problem.html"; - }); - - vm.$watch("showContestListPage", function () { - vm.template_url = "template/contest/contest_list.html"; - }); - - vm.$watch("showContestSubmissionPage", function (problemId, contestId, contestMode) { - vm.$problemId = problemId; - vm.$contestId = contestId; - vm.$contestMode = contestMode - vm.template_url = "template/contest/submission_list.html"; - }); - avalon.scan(); window.onhashchange = function () { From ff0697ad7f0eaea4185b93d8ca76140b900be8f9 Mon Sep 17 00:00:00 2001 From: virusdefender <1670873886@qq.com> Date: Thu, 29 Oct 2015 14:23:53 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E7=A1=AE=E5=AE=9A=20contest=20list=20vm=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=80=9A=E7=94=A8=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- static/src/js/app/admin/contest/contestList.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/static/src/js/app/admin/contest/contestList.js b/static/src/js/app/admin/contest/contestList.js index 7eb47fad..f3629273 100644 --- a/static/src/js/app/admin/contest/contestList.js +++ b/static/src/js/app/admin/contest/contestList.js @@ -20,6 +20,14 @@ require(["jquery", "avalon", "csrfToken", "bsAlert", "editor", "datetimePicker", search: function () { getPage(1); avalon.vmodels.contestListPager.currentPage = 1; + }, + + editContest: function(contestId){ + avalon.vmodels.admin.contestId = contestId; + // todo 修改template_url + }, + showContestProblems: function(contestId){ + // todo } }) }