PageRenderTime 1197ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/avalon.js

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