avalon-rails 1.3.9.1.20150212184918 → 1.4.1.1.20150404164109

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,8 +5,7 @@
5
5
  http://weibo.com/jslouvre/
6
6
 
7
7
  Released under the MIT license
8
- avalon.mobile.shim.js(去掉加载器与domReady) 1.391 build in 2015.2.12
9
- upport IE6+ and other browsers
8
+ avalon.mobile.shim.js 1.41 built in 2015.4.4
10
9
  ==================================================*/
11
10
  (function(global, factory) {
12
11
 
@@ -46,10 +45,23 @@ function log() {
46
45
  console.log.apply(console, arguments)
47
46
  }
48
47
  }
48
+ /**
49
+ * Creates a new object without a prototype. This object is useful for lookup without having to
50
+ * guard against prototypically inherited properties via hasOwnProperty.
51
+ *
52
+ * Related micro-benchmarks:
53
+ * - http://jsperf.com/object-create2
54
+ * - http://jsperf.com/proto-map-lookup/2
55
+ * - http://jsperf.com/for-in-vs-object-keys2
56
+ */
57
+ function createMap() {
58
+ return Object.create(null)
59
+ }
49
60
 
50
61
  var subscribers = "$" + expose
51
62
  var otherRequire = window.require
52
63
  var otherDefine = window.define
64
+ var innerRequire
53
65
  var stopRepeatAssign = false
54
66
  var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach
55
67
  var rcomplexType = /^(?:object|array)$/
@@ -87,16 +99,6 @@ function oneObject(array, val) {
87
99
  return result
88
100
  }
89
101
 
90
- function createCache(maxLength) {
91
- var keys = []
92
- function cache(key, value) {
93
- if (keys.push(key) > maxLength) {
94
- delete cache[keys.shift()]
95
- }
96
- return cache[key] = value;
97
- }
98
- return cache;
99
- }
100
102
  //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
101
103
  var generateID = function(prefix) {
102
104
  prefix = prefix || "avalon"
@@ -111,13 +113,60 @@ function IE() {
111
113
  }
112
114
  }
113
115
  var IEVersion = IE()
114
- /*********************************************************************
115
- * avalon的静态方法定义区 *
116
- **********************************************************************/
116
+
117
117
  avalon = function(el) { //创建jQuery式的无new 实例化结构
118
118
  return new avalon.init(el)
119
119
  }
120
120
 
121
+ /*视浏览器情况采用最快的异步回调*/
122
+ avalon.nextTick = new function() {// jshint ignore:line
123
+ var tickImmediate = window.setImmediate
124
+ var tickObserver = window.MutationObserver
125
+ var tickPost = W3C && window.postMessage
126
+ if (tickImmediate) {
127
+ return tickImmediate.bind(window)
128
+ }
129
+
130
+ var queue = []
131
+ function callback() {
132
+ var n = queue.length
133
+ for (var i = 0; i < n; i++) {
134
+ queue[i]()
135
+ }
136
+ queue = queue.slice(n)
137
+ }
138
+
139
+ if (tickObserver) {
140
+ var node = document.createTextNode("avalon")
141
+ new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line
142
+ return function(fn) {
143
+ queue.push(fn)
144
+ node.data = Math.random()
145
+ }
146
+ }
147
+
148
+ if (tickPost) {
149
+ window.addEventListener("message", function(e) {
150
+ var source = e.source
151
+ if ((source === window || source === null) && e.data === "process-tick") {
152
+ e.stopPropagation()
153
+ callback()
154
+ }
155
+ })
156
+
157
+ return function(fn) {
158
+ queue.push(fn)
159
+ window.postMessage('process-tick', '*')
160
+ }
161
+ }
162
+
163
+ return function(fn) {
164
+ setTimeout(fn, 0)
165
+ }
166
+ }// jshint ignore:line
167
+ /*********************************************************************
168
+ * avalon的静态方法定义区 *
169
+ **********************************************************************/
121
170
  avalon.init = function(el) {
122
171
  this[0] = this.element = el
123
172
  }
@@ -213,7 +262,7 @@ function _number(a, len) { //用于模拟slice, splice的效果
213
262
  avalon.mix({
214
263
  rword: rword,
215
264
  subscribers: subscribers,
216
- version: 1.391,
265
+ version: 1.41,
217
266
  ui: {},
218
267
  log: log,
219
268
  slice: function(nodes, start, end) {
@@ -222,7 +271,7 @@ avalon.mix({
222
271
  noop: noop,
223
272
  /*如果不用Error对象封装一下,str在控制台下可能会乱码*/
224
273
  error: function(str, e) {
225
- throw new (e || Error)(str)
274
+ throw new (e || Error)(str)// jshint ignore:line
226
275
  },
227
276
  /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/
228
277
  oneObject: oneObject,
@@ -259,7 +308,7 @@ avalon.mix({
259
308
  if (typeof hook === "object") {
260
309
  type = hook.type
261
310
  if (hook.deel) {
262
- fn = hook.deel(el, type, fn, true)
311
+ fn = hook.deel(el, type, fn, phase)
263
312
  }
264
313
  }
265
314
  if (!fn.unbind)
@@ -374,35 +423,107 @@ function isArrayLike(obj) {
374
423
  }
375
424
  return false
376
425
  }
377
- /*视浏览器情况采用最快的异步回调(在avalon.ready里,还有一个分支,用于处理IE6-9)*/
378
- avalon.nextTick = window.setImmediate ? setImmediate.bind(window) : function(callback) {
379
- setTimeout(callback, 0) //IE10-11 or W3C
380
- }
426
+
427
+
428
+ // https://github.com/rsms/js-lru
429
+ var Cache = new function() {// jshint ignore:line
430
+ function LRU(maxLength) {
431
+ this.size = 0
432
+ this.limit = maxLength
433
+ this.head = this.tail = void 0
434
+ this._keymap = {}
435
+ }
436
+
437
+ var p = LRU.prototype
438
+
439
+ p.put = function(key, value) {
440
+ var entry = {
441
+ key: key,
442
+ value: value
443
+ }
444
+ this._keymap[key] = entry
445
+ if (this.tail) {
446
+ this.tail.newer = entry
447
+ entry.older = this.tail
448
+ } else {
449
+ this.head = entry
450
+ }
451
+ this.tail = entry
452
+ if (this.size === this.limit) {
453
+ this.shift()
454
+ } else {
455
+ this.size++
456
+ }
457
+ return value
458
+ }
459
+
460
+ p.shift = function() {
461
+ var entry = this.head
462
+ if (entry) {
463
+ this.head = this.head.newer
464
+ this.head.older =
465
+ entry.newer =
466
+ entry.older =
467
+ this._keymap[entry.key] = void 0
468
+ }
469
+ }
470
+ p.get = function(key) {
471
+ var entry = this._keymap[key]
472
+ if (entry === void 0)
473
+ return
474
+ if (entry === this.tail) {
475
+ return entry.value
476
+ }
477
+ // HEAD--------------TAIL
478
+ // <.older .newer>
479
+ // <--- add direction --
480
+ // A B C <D> E
481
+ if (entry.newer) {
482
+ if (entry === this.head) {
483
+ this.head = entry.newer
484
+ }
485
+ entry.newer.older = entry.older // C <-- E.
486
+ }
487
+ if (entry.older) {
488
+ entry.older.newer = entry.newer // C. --> E
489
+ }
490
+ entry.newer = void 0 // D --x
491
+ entry.older = this.tail // D. --> E
492
+ if (this.tail) {
493
+ this.tail.newer = entry // E. <-- D
494
+ }
495
+ this.tail = entry
496
+ return entry.value
497
+ }
498
+ return LRU
499
+ }// jshint ignore:line
381
500
 
382
501
  /*********************************************************************
383
502
  * DOM 底层补丁 *
384
503
  **********************************************************************/
385
- if (!root.contains) { //safari5+是把contains方法放在Element.prototype上而不是Node.prototype
386
- Node.prototype.contains = function(arg) {
504
+ //safari5+是把contains方法放在Element.prototype上而不是Node.prototype
505
+ if (!DOC.contains) {
506
+ Node.prototype.contains = function (arg) {
387
507
  return !!(this.compareDocumentPosition(arg) & 16)
388
508
  }
389
509
  }
390
- avalon.contains = function(root, el) {
510
+ avalon.contains = function (root, el) {
391
511
  try {
392
512
  while ((el = el.parentNode))
393
513
  if (el === root)
394
- return true;
514
+ return true
395
515
  return false
396
516
  } catch (e) {
397
517
  return false
398
518
  }
399
519
  }
520
+
400
521
  if (window.SVGElement) {
401
522
  var svgns = "http://www.w3.org/2000/svg"
402
523
  var svg = DOC.createElementNS(svgns, "svg")
403
524
  svg.innerHTML = '<circle cx="50" cy="50" r="40" fill="red" />'
404
525
  if (!rsvg.test(svg.firstChild)) {// #409
405
-
526
+ /* jshint ignore:start */
406
527
  function enumerateNode(node, targetNode) {
407
528
  if (node && node.childNodes) {
408
529
  var nodes = node.childNodes
@@ -411,7 +532,7 @@ if (window.SVGElement) {
411
532
  var svg = DOC.createElementNS(svgns,
412
533
  el.tagName.toLowerCase())
413
534
  // copy attrs
414
- ap.forEach.call(el.attributes, function(attr) {
535
+ ap.forEach.call(el.attributes, function (attr) {
415
536
  svg.setAttribute(attr.name, attr.value)
416
537
  })
417
538
  // 递归处理子节点
@@ -421,14 +542,15 @@ if (window.SVGElement) {
421
542
  }
422
543
  }
423
544
  }
545
+ /* jshint ignore:end */
424
546
  Object.defineProperties(SVGElement.prototype, {
425
547
  "outerHTML": {//IE9-11,firefox不支持SVG元素的innerHTML,outerHTML属性
426
548
  enumerable: true,
427
549
  configurable: true,
428
- get: function() {
550
+ get: function () {
429
551
  return new XMLSerializer().serializeToString(this)
430
552
  },
431
- set: function(html) {
553
+ set: function (html) {
432
554
  var tagName = this.tagName.toLowerCase(),
433
555
  par = this.parentNode,
434
556
  frag = avalon.parseHTML(html)
@@ -447,13 +569,13 @@ if (window.SVGElement) {
447
569
  "innerHTML": {
448
570
  enumerable: true,
449
571
  configurable: true,
450
- get: function() {
572
+ get: function () {
451
573
  var s = this.outerHTML
452
574
  var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i")
453
575
  var rclose = new RegExp("<\/" + this.nodeName + ">$", "i")
454
576
  return s.replace(ropen, "").replace(rclose, "")
455
577
  },
456
- set: function(html) {
578
+ set: function (html) {
457
579
  if (avalon.clearHTML) {
458
580
  avalon.clearHTML(this)
459
581
  var frag = avalon.parseHTML(html)
@@ -465,17 +587,17 @@ if (window.SVGElement) {
465
587
  }
466
588
  }
467
589
  //========================= event binding ====================
468
- var eventHooks = avalon.eventHooks
590
+ var eventHooks = avalon.eventHooks
469
591
  //针对firefox, chrome修正mouseenter, mouseleave(chrome30+)
470
592
  if (!("onmouseenter" in root)) {
471
593
  avalon.each({
472
594
  mouseenter: "mouseover",
473
595
  mouseleave: "mouseout"
474
- }, function(origType, fixType) {
596
+ }, function (origType, fixType) {
475
597
  eventHooks[origType] = {
476
598
  type: fixType,
477
- deel: function(elem, fn) {
478
- return function(e) {
599
+ deel: function (elem, _, fn) {
600
+ return function (e) {
479
601
  var t = e.relatedTarget
480
602
  if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) {
481
603
  delete e.type
@@ -491,7 +613,7 @@ if (!("onmouseenter" in root)) {
491
613
  avalon.each({
492
614
  AnimationEvent: "animationend",
493
615
  WebKitAnimationEvent: "webkitAnimationEnd"
494
- }, function(construct, fixType) {
616
+ }, function (construct, fixType) {
495
617
  if (window[construct] && !eventHooks.animationend) {
496
618
  eventHooks.animationend = {
497
619
  type: fixType
@@ -507,8 +629,8 @@ if (DOC.onmousewheel === void 0) {
507
629
  chrome wheel deltaY 下100 上-100 */
508
630
  eventHooks.mousewheel = {
509
631
  type: "wheel",
510
- deel: function(elem, fn) {
511
- return function(e) {
632
+ deel: function (elem, _, fn) {
633
+ return function (e) {
512
634
  e.wheelDeltaY = e.wheelDelta = e.deltaY > 0 ? -120 : 120
513
635
  e.wheelDeltaX = 0
514
636
  Object.defineProperty(e, "type", {
@@ -546,13 +668,14 @@ function escapeRegExp(target) {
546
668
  //将字符串安全格式化为正则表达式的源码
547
669
  return (target + "").replace(rregexp, "\\$&")
548
670
  }
549
- var innerRequire = noop
671
+
550
672
  var plugins = {
551
- loader: function(builtin) {
552
- window.define = builtin ? innerRequire.define : otherDefine
553
- window.require = builtin ? innerRequire : otherRequire
673
+ loader: function (builtin) {
674
+ var flag = innerRequire && builtin
675
+ window.require = flag ? innerRequire : otherRequire
676
+ window.define = flag ? innerRequire.define : otherDefine
554
677
  },
555
- interpolate: function(array) {
678
+ interpolate: function (array) {
556
679
  openTag = array[0]
557
680
  closeTag = array[1]
558
681
  if (openTag === closeTag) {
@@ -582,11 +705,15 @@ kernel.paths = {}
582
705
  kernel.shim = {}
583
706
  kernel.maxRepeatSize = 100
584
707
  avalon.config = kernel
708
+ var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
709
+ var findNodes = function(str) {
710
+ return DOC.querySelectorAll(str)
711
+ }
585
712
  /*********************************************************************
586
713
  * 事件总线 *
587
714
  **********************************************************************/
588
715
  var EventBus = {
589
- $watch: function(type, callback) {
716
+ $watch: function (type, callback) {
590
717
  if (typeof callback === "function") {
591
718
  var callbacks = this.$events[type]
592
719
  if (callbacks) {
@@ -599,7 +726,7 @@ var EventBus = {
599
726
  }
600
727
  return this
601
728
  },
602
- $unwatch: function(type, callback) {
729
+ $unwatch: function (type, callback) {
603
730
  var n = arguments.length
604
731
  if (n === 0) { //让此VM的所有$watch回调无效化
605
732
  this.$watch.backup = this.$events
@@ -617,13 +744,15 @@ var EventBus = {
617
744
  }
618
745
  return this
619
746
  },
620
- $fire: function(type) {
747
+ $fire: function (type) {
621
748
  var special, i, v, callback
622
749
  if (/^(\w+)!(\S+)$/.test(type)) {
623
750
  special = RegExp.$1
624
751
  type = RegExp.$2
625
752
  }
626
753
  var events = this.$events
754
+ if (!events)
755
+ return
627
756
  var args = aslice.call(arguments, 1)
628
757
  var detail = [type].concat(args)
629
758
  if (special === "all") {
@@ -646,22 +775,23 @@ var EventBus = {
646
775
  continue
647
776
  }
648
777
  //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识
649
- Array.prototype.forEach.call(eventNodes, function(node) {
650
- Array.prototype.forEach.call(elements, function(element) {
778
+ /* jshint ignore:start */
779
+ Array.prototype.forEach.call(eventNodes, function (node) {
780
+ Array.prototype.forEach.call(elements, function (element) {
651
781
  var ok = special === "down" ? element.contains(node) : //向下捕获
652
782
  node.contains(element) //向上冒泡
653
-
654
783
  if (ok) {
655
784
  node._avalon = v //符合条件的加一个标识
656
785
  }
657
786
  });
658
787
  })
788
+ /* jshint ignore:end */
659
789
  }
660
790
  }
661
791
  }
662
792
  var nodes = DOC.getElementsByTagName("*") //实现节点排序
663
793
  var alls = []
664
- Array.prototype.forEach.call(nodes, function(el) {
794
+ Array.prototype.forEach.call(nodes, function (el) {
665
795
  if (el._avalon) {
666
796
  alls.push(el._avalon)
667
797
  el._avalon = ""
@@ -691,16 +821,12 @@ var EventBus = {
691
821
  }
692
822
  }
693
823
 
694
- var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
695
- var findNodes = function(str) {
696
- return DOC.querySelectorAll(str)
697
- }
698
824
  /*********************************************************************
699
825
  * modelFactory *
700
826
  **********************************************************************/
701
827
  //avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM)
702
- var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里
703
- avalon.define = function(id, factory) {
828
+ var VMODELS = avalon.vmodels = createMap() //所有vmodel都储存在这里
829
+ avalon.define = function (id, factory) {
704
830
  var $id = id.$id || id
705
831
  if (!$id) {
706
832
  log("warning: vm必须指定$id")
@@ -744,7 +870,7 @@ function isObservable(name, value, $skipArray) {
744
870
  return true
745
871
  }
746
872
  //ms-with,ms-each, ms-repeat绑定生成的代理对象储存池
747
- var midway = {}
873
+ var midway = createMap()
748
874
  function getNewValue(accessor, name, value, $vmodel) {
749
875
  switch (accessor.type) {
750
876
  case 0://计算属性
@@ -771,43 +897,29 @@ function getNewValue(accessor, name, value, $vmodel) {
771
897
  }
772
898
  }
773
899
 
774
- var defineProperty = Object.defineProperty
775
- var canHideOwn = true
776
- //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8
777
- //标准浏览器使用__defineGetter__, __defineSetter__实现
778
- try {
779
- defineProperty({}, "_", {
780
- value: "x"
781
- })
782
- var defineProperties = Object.defineProperties
783
- } catch (e) {
784
- canHideOwn = false
785
- }
786
900
  function modelFactory(source, $special, $model) {
787
901
  if (Array.isArray(source)) {
788
902
  var arr = source.concat()
789
903
  source.length = 0
790
- var collection = Collection(source)
904
+ var collection = Collection(source)// jshint ignore:line
791
905
  collection.pushArray(arr)
792
906
  return collection
793
907
  }
794
- if (typeof source.nodeType === "number") {
795
- return source
796
- }
797
- if (source.$id && source.$events) { //fix IE6-8 createWithProxy $val: val引发的BUG
908
+ //0 null undefined || Node || VModel
909
+ if (!source || source.nodeType > 0 || (source.$id && source.$events)) {
798
910
  return source
799
911
  }
800
912
  if (!Array.isArray(source.$skipArray)) {
801
913
  source.$skipArray = []
802
914
  }
803
- source.$skipArray.$special = $special || {} //强制要监听的属性
915
+ source.$skipArray.$special = $special || createMap() //强制要监听的属性
804
916
  var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤
805
917
  $model = $model || {} //vmodels.$model属性
806
- var $events = {} //vmodel.$events属性
807
- var watchedProperties = {} //监控属性
918
+ var $events = createMap() //vmodel.$events属性
919
+ var watchedProperties = createMap() //监控属性
808
920
  var initCallbacks = [] //初始化才执行的函数
809
921
  for (var i in source) {
810
- (function(name, val) {
922
+ (function (name, val) {
811
923
  $model[name] = val
812
924
  if (!isObservable(name, val, source.$skipArray)) {
813
925
  return //过滤所有非监控属性
@@ -815,7 +927,7 @@ function modelFactory(source, $special, $model) {
815
927
  //总共产生三种accessor
816
928
  $events[name] = []
817
929
  var valueType = avalon.type(val)
818
- var accessor = function(newValue) {
930
+ var accessor = function (newValue) {
819
931
  var name = accessor._name
820
932
  var $vmodel = this
821
933
  var $model = $vmodel.$model
@@ -834,19 +946,8 @@ function modelFactory(source, $special, $model) {
834
946
  }
835
947
  if (!isEqual(oldValue, newValue)) {
836
948
  $model[name] = newValue
837
- if ($events.$digest) {
838
- if (!accessor.pedding) {
839
- accessor.pedding = true
840
- setTimeout(function() {
841
- notifySubscribers($events[name]) //同步视图
842
- safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
843
- accessor.pedding = false
844
- })
845
- }
846
- } else {
847
- notifySubscribers($events[name]) //同步视图
848
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
849
- }
949
+ notifySubscribers($events[name]) //同步视图
950
+ safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
850
951
  }
851
952
  } else {
852
953
  if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性
@@ -855,17 +956,7 @@ function modelFactory(source, $special, $model) {
855
956
  if (oldValue !== newValue) {
856
957
  $model[name] = newValue
857
958
  //这里不用同步视图
858
- if ($events.$digest) {
859
- if (!accessor.pedding) {
860
- accessor.pedding = true
861
- setTimeout(function() {
862
- safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
863
- accessor.pedding = false
864
- })
865
- }
866
- } else {
867
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
868
- }
959
+ safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
869
960
  }
870
961
  return newValue
871
962
  } else {
@@ -880,11 +971,11 @@ function modelFactory(source, $special, $model) {
880
971
  accessor.set = val.set
881
972
  accessor.get = val.get
882
973
  accessor.type = 0
883
- initCallbacks.push(function() {
974
+ initCallbacks.push(function () {
884
975
  var data = {
885
- evaluator: function() {
976
+ evaluator: function () {
886
977
  data.type = Math.random(),
887
- data.element = null
978
+ data.element = null
888
979
  $model[name] = accessor.get.call($vmodel)
889
980
  },
890
981
  element: head,
@@ -900,7 +991,7 @@ function modelFactory(source, $special, $model) {
900
991
  //第2种为对象属性,产生子VM与监控数组
901
992
  accessor.type = 2
902
993
  accessor.valueType = valueType
903
- initCallbacks.push(function() {
994
+ initCallbacks.push(function () {
904
995
  var svmodel = modelFactory(val, 0, $model[name])
905
996
  accessor.svmodel = svmodel
906
997
  svmodel.$events[subscribers] = $events[name]
@@ -911,15 +1002,15 @@ function modelFactory(source, $special, $model) {
911
1002
  }
912
1003
  accessor._name = name
913
1004
  watchedProperties[name] = accessor
914
- })(i, source[i])
1005
+ })(i, source[i])// jshint ignore:line
915
1006
  }
916
1007
 
917
- $$skipArray.forEach(function(name) {
1008
+ $$skipArray.forEach(function (name) {
918
1009
  delete source[name]
919
1010
  delete $model[name] //这些特殊属性不应该在$model中出现
920
1011
  })
921
1012
 
922
- $vmodel = defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel
1013
+ $vmodel = Object.defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel
923
1014
  for (var name in source) {
924
1015
  if (!watchedProperties[name]) {
925
1016
  $vmodel[name] = source[name]
@@ -929,37 +1020,27 @@ function modelFactory(source, $special, $model) {
929
1020
  $vmodel.$id = generateID()
930
1021
  $vmodel.$model = $model
931
1022
  $vmodel.$events = $events
932
- for ( i in EventBus) {
933
- var fn = EventBus[i]
934
- if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下
935
- fn = fn.bind($vmodel)
936
- }
937
- $vmodel[i] = fn
1023
+ for (i in EventBus) {
1024
+ $vmodel[i] = EventBus[i]
938
1025
  }
939
1026
 
940
- if (canHideOwn) {
941
- Object.defineProperty($vmodel, "hasOwnProperty", {
942
- value: function(name) {
943
- return name in this.$model
944
- },
945
- writable: false,
946
- enumerable: false,
947
- configurable: true
948
- })
1027
+ Object.defineProperty($vmodel, "hasOwnProperty", {
1028
+ value: function (name) {
1029
+ return name in this.$model
1030
+ },
1031
+ writable: false,
1032
+ enumerable: false,
1033
+ configurable: true
1034
+ })
949
1035
 
950
- } else {
951
- $vmodel.hasOwnProperty = function(name) {
952
- return name in $vmodel.$model
953
- }
954
- }
955
- initCallbacks.forEach(function(cb) { //收集依赖
1036
+ initCallbacks.forEach(function (cb) { //收集依赖
956
1037
  cb()
957
1038
  })
958
1039
  return $vmodel
959
1040
  }
960
1041
 
961
1042
  //比较两个值是否相等
962
- var isEqual = Object.is || function(v1, v2) {
1043
+ var isEqual = Object.is || function (v1, v2) {
963
1044
  if (v1 === 0 && v2 === 0) {
964
1045
  return 1 / v1 === 1 / v2
965
1046
  } else if (v1 !== v1) {
@@ -975,8 +1056,8 @@ function safeFire(a, b, c, d) {
975
1056
  }
976
1057
  }
977
1058
 
978
- var descriptorFactory = W3C ? function(obj) {
979
- var descriptors = {}
1059
+ var descriptorFactory = function (obj) {
1060
+ var descriptors = createMap()
980
1061
  for (var i in obj) {
981
1062
  descriptors[i] = {
982
1063
  get: obj[i],
@@ -986,12 +1067,8 @@ var descriptorFactory = W3C ? function(obj) {
986
1067
  }
987
1068
  }
988
1069
  return descriptors
989
- } : function(a) {
990
- return a
991
1070
  }
992
1071
 
993
-
994
-
995
1072
  //应用于第2种accessor
996
1073
  function objectFactory(parent, name, value, valueType) {
997
1074
  //a为原来的VM, b为新数组或新对象
@@ -1014,16 +1091,17 @@ function objectFactory(parent, name, value, valueType) {
1014
1091
  }
1015
1092
  var ret = modelFactory(value)
1016
1093
  ret.$events[subscribers] = iterators
1017
- midway[ret.$id] = function(data) {
1094
+ midway[ret.$id] = function (data) {
1018
1095
  while (data = iterators.shift()) {
1019
- (function(el) {
1020
- avalon.nextTick(function() {
1021
- if (el.type) { //重新绑定
1096
+ (function (el) {
1097
+ avalon.nextTick(function () {
1098
+ var type = el.type
1099
+ if (type && bindingHandlers[type]) { //#753
1022
1100
  el.rollback && el.rollback() //还原 ms-with ms-on
1023
- bindingHandlers[el.type](el, el.vmodels)
1101
+ bindingHandlers[type](el, el.vmodels)
1024
1102
  }
1025
1103
  })
1026
- })(data)
1104
+ })(data)// jshint ignore:line
1027
1105
  }
1028
1106
  delete midway[ret.$id]
1029
1107
  }
@@ -1043,7 +1121,7 @@ function Collection(model) {
1043
1121
  array._ = modelFactory({
1044
1122
  length: model.length
1045
1123
  })
1046
- array._.$watch("length", function(a, b) {
1124
+ array._.$watch("length", function (a, b) {
1047
1125
  array.$fire("length", a, b)
1048
1126
  })
1049
1127
  for (var i in EventBus) {
@@ -1058,13 +1136,15 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1058
1136
  while (--loop) {
1059
1137
  switch (method) {
1060
1138
  case "add":
1061
- var array = this.$model.slice(pos, pos + n).map(function(el) {
1139
+ /* jshint ignore:start */
1140
+ var array = this.$model.slice(pos, pos + n).map(function (el) {
1062
1141
  if (rcomplexType.test(avalon.type(el))) {
1063
1142
  return el.$id ? el : modelFactory(el, 0, el)
1064
1143
  } else {
1065
1144
  return el
1066
1145
  }
1067
1146
  })
1147
+ /* jshint ignore:end */
1068
1148
  _splice.apply(this, [pos, 0].concat(array))
1069
1149
  this._fire("add", pos, n)
1070
1150
  break
@@ -1091,30 +1171,30 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1091
1171
  var _splice = ap.splice
1092
1172
  var CollectionPrototype = {
1093
1173
  _splice: _splice,
1094
- _fire: function(method, a, b) {
1174
+ _fire: function (method, a, b) {
1095
1175
  notifySubscribers(this.$events[subscribers], method, a, b)
1096
1176
  },
1097
- size: function() { //取得数组长度,这个函数可以同步视图,length不能
1177
+ size: function () { //取得数组长度,这个函数可以同步视图,length不能
1098
1178
  return this._.length
1099
1179
  },
1100
- pushArray: function(array) {
1180
+ pushArray: function (array) {
1101
1181
  var m = array.length, n = this.length
1102
1182
  if (m) {
1103
1183
  ap.push.apply(this.$model, array)
1104
- mutateArray.call(this, "add", n, m, n)
1184
+ mutateArray.call(this, "add", n, m, Math.max(0, n - 1))
1105
1185
  }
1106
1186
  return m + n
1107
1187
  },
1108
- push: function() {
1188
+ push: function () {
1109
1189
  //http://jsperf.com/closure-with-arguments
1110
1190
  var array = []
1111
1191
  var i, n = arguments.length
1112
1192
  for (i = 0; i < n; i++) {
1113
1193
  array[i] = arguments[i]
1114
1194
  }
1115
- return this.pushArray(arguments)
1195
+ return this.pushArray(array)
1116
1196
  },
1117
- unshift: function() {
1197
+ unshift: function () {
1118
1198
  var m = arguments.length, n = this.length
1119
1199
  if (m) {
1120
1200
  ap.unshift.apply(this.$model, arguments)
@@ -1122,22 +1202,22 @@ var CollectionPrototype = {
1122
1202
  }
1123
1203
  return m + n //IE67的unshift不会返回长度
1124
1204
  },
1125
- shift: function() {
1205
+ shift: function () {
1126
1206
  if (this.length) {
1127
1207
  var el = this.$model.shift()
1128
1208
  mutateArray.call(this, "del", 0, 1, 0)
1129
1209
  return el //返回被移除的元素
1130
1210
  }
1131
1211
  },
1132
- pop: function() {
1133
- var m = this.length
1134
- if (m) {
1212
+ pop: function () {
1213
+ var n = this.length
1214
+ if (n) {
1135
1215
  var el = this.$model.pop()
1136
- mutateArray.call(this, "del", m - 1, 1, Math.max(0, m - 2))
1216
+ mutateArray.call(this, "del", n - 1, 1, Math.max(0, n - 2))
1137
1217
  return el //返回被移除的元素
1138
1218
  }
1139
1219
  },
1140
- splice: function(start) {
1220
+ splice: function (start) {
1141
1221
  var m = arguments.length, args = [], change
1142
1222
  var removed = _splice.apply(this.$model, arguments)
1143
1223
  if (removed.length) { //如果用户删掉了元素
@@ -1158,27 +1238,27 @@ var CollectionPrototype = {
1158
1238
  return []
1159
1239
  }
1160
1240
  },
1161
- contains: function(el) { //判定是否包含
1241
+ contains: function (el) { //判定是否包含
1162
1242
  return this.indexOf(el) !== -1
1163
1243
  },
1164
- remove: function(el) { //移除第一个等于给定值的元素
1244
+ remove: function (el) { //移除第一个等于给定值的元素
1165
1245
  return this.removeAt(this.indexOf(el))
1166
1246
  },
1167
- removeAt: function(index) { //移除指定索引上的元素
1247
+ removeAt: function (index) { //移除指定索引上的元素
1168
1248
  if (index >= 0) {
1169
1249
  this.$model.splice(index, 1)
1170
1250
  return mutateArray.call(this, "del", index, 1, 0)
1171
1251
  }
1172
1252
  return []
1173
1253
  },
1174
- clear: function() {
1254
+ clear: function () {
1175
1255
  this.$model.length = this.length = this._.length = 0 //清空数组
1176
1256
  this._fire("clear", 0)
1177
1257
  return this
1178
1258
  },
1179
- removeAll: function(all) { //移除N个元素
1259
+ removeAll: function (all) { //移除N个元素
1180
1260
  if (Array.isArray(all)) {
1181
- all.forEach(function(el) {
1261
+ all.forEach(function (el) {
1182
1262
  this.remove(el)
1183
1263
  }, this)
1184
1264
  } else if (typeof all === "function") {
@@ -1192,13 +1272,13 @@ var CollectionPrototype = {
1192
1272
  this.clear()
1193
1273
  }
1194
1274
  },
1195
- ensure: function(el) {
1275
+ ensure: function (el) {
1196
1276
  if (!this.contains(el)) { //只有不存在才push
1197
1277
  this.push(el)
1198
1278
  }
1199
1279
  return this
1200
1280
  },
1201
- set: function(index, val) {
1281
+ set: function (index, val) {
1202
1282
  if (index >= 0) {
1203
1283
  var valueType = avalon.type(val)
1204
1284
  if (val && val.$model) {
@@ -1237,8 +1317,8 @@ function sortByIndex(array, indexes) {
1237
1317
  }
1238
1318
  }
1239
1319
 
1240
- "sort,reverse".replace(rword, function(method) {
1241
- CollectionPrototype[method] = function() {
1320
+ "sort,reverse".replace(rword, function (method) {
1321
+ CollectionPrototype[method] = function () {
1242
1322
  var newArray = this.$model//这是要排序的新数组
1243
1323
  var oldArray = newArray.concat() //保持原来状态的旧数组
1244
1324
  var mask = Math.random()
@@ -1414,7 +1494,7 @@ function notifySubscribers(list) { //通知依赖于这个访问器的订阅者
1414
1494
  * HTML处理(parseHTML, innerHTML, clearHTML) *
1415
1495
  **************************************************************************/
1416
1496
  //parseHTML的辅助变量
1417
- var tagHooks = new function() {
1497
+ var tagHooks = new function() {// jshint ignore:line
1418
1498
  avalon.mix(this, {
1419
1499
  option: DOC.createElement("select"),
1420
1500
  thead: DOC.createElement("table"),
@@ -1429,7 +1509,7 @@ var tagHooks = new function() {
1429
1509
  this.optgroup = this.option
1430
1510
  this.tbody = this.tfoot = this.colgroup = this.caption = this.thead
1431
1511
  this.th = this.td
1432
- }
1512
+ }// jshint ignore:line
1433
1513
 
1434
1514
  String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function(tag) {
1435
1515
  tagHooks[tag] = tagHooks.g //处理SVG
@@ -1438,16 +1518,20 @@ var rtagName = /<([\w:]+)/
1438
1518
  var rxhtml = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig
1439
1519
  var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"])
1440
1520
  var script = DOC.createElement("script")
1441
-
1521
+ var rhtml = /<|&#?\w+;/
1442
1522
  avalon.parseHTML = function(html) {
1523
+ var fragment = hyperspace.cloneNode(false)
1443
1524
  if (typeof html !== "string" ) {
1444
- return DOC.createDocumentFragment()
1525
+ return fragment
1526
+ }
1527
+ if (!rhtml.test(html)) {
1528
+ fragment.appendChild(DOC.createTextNode(html))
1529
+ return fragment
1445
1530
  }
1446
1531
  html = html.replace(rxhtml, "<$1></$2>").trim()
1447
1532
  var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(),
1448
1533
  //取得其标签名
1449
1534
  wrapper = tagHooks[tag] || tagHooks._default,
1450
- fragment = hyperspace.cloneNode(false),
1451
1535
  firstChild
1452
1536
  wrapper.innerHTML = html
1453
1537
  var els = wrapper.getElementsByTagName("script")
@@ -1457,7 +1541,7 @@ avalon.parseHTML = function(html) {
1457
1541
  var neo = script.cloneNode(false) //FF不能省略参数
1458
1542
  ap.forEach.call(el.attributes, function(attr) {
1459
1543
  neo.setAttribute(attr.name, attr.value)
1460
- })
1544
+ })// jshint ignore:line
1461
1545
  neo.text = el.text
1462
1546
  el.parentNode.replaceChild(neo, el)
1463
1547
  }
@@ -1575,55 +1659,11 @@ function bindingSorter(a, b) {
1575
1659
  return a.priority - b.priority
1576
1660
  }
1577
1661
 
1578
- function scanTag(elem, vmodels, node) {
1579
- //扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
1580
- //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
1581
- var a = elem.getAttribute("ms-skip")
1582
- var b = elem.getAttributeNode("ms-important")
1583
- var c = elem.getAttributeNode("ms-controller")
1584
- if (typeof a === "string") {
1585
- return
1586
- } else if (node = b || c) {
1587
- var newVmodel = avalon.vmodels[node.value]
1588
- if (!newVmodel) {
1589
- return
1590
- }
1591
- //ms-important不包含父VM,ms-controller相反
1592
- vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
1593
- elem.removeAttribute(node.name) //removeAttributeNode不会刷新[ms-controller]样式规则
1594
- elem.classList.remove(node.name)
1595
- createSignalTower(elem, newVmodel)
1596
- }
1597
- scanAttr(elem, vmodels) //扫描特性节点
1598
- }
1599
- function scanNodeList(parent, vmodels) {
1600
- var node = parent.firstChild
1601
- while (node) {
1602
- var nextNode = node.nextSibling
1603
- scanNode(node, node.nodeType, vmodels)
1604
- node = nextNode
1605
- }
1606
- }
1607
-
1608
- function scanNodeArray(nodes, vmodels) {
1609
- for (var i = 0, node; node = nodes[i++]; ) {
1610
- scanNode(node, node.nodeType, vmodels)
1611
- }
1612
- }
1613
- function scanNode(node, nodeType, vmodels) {
1614
- if (nodeType === 1) {
1615
- scanTag(node, vmodels) //扫描元素节点
1616
- } else if (nodeType === 3 && rexpr.test(node.data)){
1617
- scanText(node, vmodels) //扫描文本节点
1618
- } else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
1619
- scanText(node, vmodels) //扫描注释节点
1620
- }
1621
- }
1622
1662
  function scanAttr(elem, vmodels) {
1623
1663
  //防止setAttribute, removeAttribute时 attributes自动被同步,导致for循环出错
1624
1664
  var attributes = elem.hasAttributes() ? avalon.slice(elem.attributes) : []
1625
1665
  var bindings = [],
1626
- msData = {},
1666
+ msData = createMap(),
1627
1667
  match
1628
1668
  for (var i = 0, attr; attr = attributes[i++]; ) {
1629
1669
  if (attr.specified) {
@@ -1638,8 +1678,9 @@ function scanAttr(elem, vmodels) {
1638
1678
  param = type
1639
1679
  type = "on"
1640
1680
  } else if (obsoleteAttrs[type]) {
1641
- log("ms-" + type + "已经被废弃,请使用ms-attr-*代替")
1681
+ log("warning!请改用ms-attr-" + type + "代替ms-" + type + "!")
1642
1682
  if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
1683
+ log("warning!ms-enabled或ms-attr-enabled已经被废弃")
1643
1684
  type = "disabled"
1644
1685
  value = "!(" + value + ")"
1645
1686
  }
@@ -1663,11 +1704,11 @@ function scanAttr(elem, vmodels) {
1663
1704
  if (type === "html" || type === "text") {
1664
1705
  var token = getToken(value)
1665
1706
  avalon.mix(binding, token)
1666
- binding.filters = binding.filters.replace(rhasHtml, function() {
1707
+ binding.filters = binding.filters.replace(rhasHtml, function () {
1667
1708
  binding.type = "html"
1668
1709
  binding.group = 1
1669
1710
  return ""
1670
- })
1711
+ })// jshint ignore:line
1671
1712
  }
1672
1713
  if (name === "ms-if-loop") {
1673
1714
  binding.priority += 100
@@ -1682,8 +1723,14 @@ function scanAttr(elem, vmodels) {
1682
1723
  }
1683
1724
  }
1684
1725
  }
1685
- if (msData["ms-attr-checked"] && msData["ms-duplex"]) {
1686
- log("warning!一个元素上不能同时定义ms-attr-checked与ms-duplex")
1726
+ var control = elem.type
1727
+ if (control && msData["ms-duplex"]) {
1728
+ if (msData["ms-attr-checked"] && /radio|checkbox/.test(control)) {
1729
+ log("warning!" + control + "控件不能同时定义ms-attr-checked与ms-duplex")
1730
+ }
1731
+ if (msData["ms-attr-value"] && /text|password/.test(control)) {
1732
+ log("warning!" + control + "控件不能同时定义ms-attr-value与ms-duplex")
1733
+ }
1687
1734
  }
1688
1735
  bindings.sort(bindingSorter)
1689
1736
  var scanNode = true
@@ -1704,6 +1751,50 @@ function scanAttr(elem, vmodels) {
1704
1751
 
1705
1752
  var rnoscanAttrBinding = /^if|widget|repeat$/
1706
1753
  var rnoscanNodeBinding = /^each|with|html|include$/
1754
+ function scanNodeList(parent, vmodels) {
1755
+ var node = parent.firstChild
1756
+ while (node) {
1757
+ var nextNode = node.nextSibling
1758
+ scanNode(node, node.nodeType, vmodels)
1759
+ node = nextNode
1760
+ }
1761
+ }
1762
+
1763
+ function scanNodeArray(nodes, vmodels) {
1764
+ for (var i = 0, node; node = nodes[i++]; ) {
1765
+ scanNode(node, node.nodeType, vmodels)
1766
+ }
1767
+ }
1768
+ function scanNode(node, nodeType, vmodels) {
1769
+ if (nodeType === 1) {
1770
+ scanTag(node, vmodels) //扫描元素节点
1771
+ } else if (nodeType === 3 && rexpr.test(node.data)){
1772
+ scanText(node, vmodels) //扫描文本节点
1773
+ } else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
1774
+ scanText(node, vmodels) //扫描注释节点
1775
+ }
1776
+ }
1777
+ function scanTag(elem, vmodels, node) {
1778
+ //扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
1779
+ //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
1780
+ var a = elem.getAttribute("ms-skip")
1781
+ var b = elem.getAttributeNode("ms-important")
1782
+ var c = elem.getAttributeNode("ms-controller")
1783
+ if (typeof a === "string") {
1784
+ return
1785
+ } else if (node = b || c) {
1786
+ var newVmodel = avalon.vmodels[node.value]
1787
+ if (!newVmodel) {
1788
+ return
1789
+ }
1790
+ //ms-important不包含父VM,ms-controller相反
1791
+ vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
1792
+ elem.removeAttribute(node.name) //removeAttributeNode不会刷新[ms-controller]样式规则
1793
+ elem.classList.remove(node.name)
1794
+ createSignalTower(elem, newVmodel)
1795
+ }
1796
+ scanAttr(elem, vmodels) //扫描特性节点
1797
+ }
1707
1798
  var rhasHtml = /\|\s*html\s*/,
1708
1799
  r11a = /\|\|/g,
1709
1800
  rlt = /&lt;/g,
@@ -1712,7 +1803,7 @@ var rhasHtml = /\|\s*html\s*/,
1712
1803
  function getToken(value) {
1713
1804
  if (value.indexOf("|") > 0) {
1714
1805
  var scapegoat = value.replace( rstringLiteral, function(_){
1715
- return Math.pow(10,_.length)
1806
+ return Array(_.length+1).join("1")// jshint ignore:line
1716
1807
  })
1717
1808
  var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
1718
1809
  if (index > -1) {
@@ -1787,7 +1878,7 @@ function scanText(textNode, vmodels) {
1787
1878
  token.type = "html"
1788
1879
  token.group = 1
1789
1880
  return ""
1790
- })
1881
+ })// jshint ignore:line
1791
1882
  bindings.push(token) //收集带有插值表达式的文本
1792
1883
  }
1793
1884
  hyperspace.appendChild(node)
@@ -1811,17 +1902,17 @@ function camelize(target) {
1811
1902
  if (target.indexOf("-") < 0 && target.indexOf("_") < 0) {
1812
1903
  return target //提前判断,提高getStyle等的效率
1813
1904
  }
1814
- return target.replace(/[-_][^-_]/g, function(match) {
1905
+ return target.replace(/[-_][^-_]/g, function (match) {
1815
1906
  return match.charAt(1).toUpperCase()
1816
1907
  })
1817
1908
  }
1818
1909
 
1819
- "add,remove".replace(rword, function(method) {
1820
- avalon.fn[method + "Class"] = function(cls) {
1910
+ "add,remove".replace(rword, function (method) {
1911
+ avalon.fn[method + "Class"] = function (cls) {
1821
1912
  var el = this[0]
1822
1913
  //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26
1823
1914
  if (cls && typeof cls === "string" && el && el.nodeType === 1) {
1824
- cls.replace(/\S+/g, function(c) {
1915
+ cls.replace(/\S+/g, function (c) {
1825
1916
  el.classList[method](c)
1826
1917
  })
1827
1918
  }
@@ -1830,11 +1921,11 @@ function camelize(target) {
1830
1921
  })
1831
1922
 
1832
1923
  avalon.fn.mix({
1833
- hasClass: function(cls) {
1924
+ hasClass: function (cls) {
1834
1925
  var el = this[0] || {} //IE10+, chrome8+, firefox3.6+, safari5.1+,opera11.5+支持classList,chrome24+,firefox26+支持classList2.0
1835
1926
  return el.nodeType === 1 && el.classList.contains(cls)
1836
1927
  },
1837
- toggleClass: function(value, stateVal) {
1928
+ toggleClass: function (value, stateVal) {
1838
1929
  var className, i = 0
1839
1930
  var classNames = value.split(/\s+/)
1840
1931
  var isBool = typeof stateVal === "boolean"
@@ -1844,7 +1935,7 @@ avalon.fn.mix({
1844
1935
  }
1845
1936
  return this
1846
1937
  },
1847
- attr: function(name, value) {
1938
+ attr: function (name, value) {
1848
1939
  if (arguments.length === 2) {
1849
1940
  this[0].setAttribute(name, value)
1850
1941
  return this
@@ -1852,7 +1943,7 @@ avalon.fn.mix({
1852
1943
  return this[0].getAttribute(name)
1853
1944
  }
1854
1945
  },
1855
- data: function(name, value) {
1946
+ data: function (name, value) {
1856
1947
  name = "data-" + hyphen(name || "")
1857
1948
  switch (arguments.length) {
1858
1949
  case 2:
@@ -1863,7 +1954,7 @@ avalon.fn.mix({
1863
1954
  return parseData(val)
1864
1955
  case 0:
1865
1956
  var ret = {}
1866
- ap.forEach.call(this[0].attributes, function(attr) {
1957
+ ap.forEach.call(this[0].attributes, function (attr) {
1867
1958
  if (attr) {
1868
1959
  name = attr.name
1869
1960
  if (!name.indexOf("data-")) {
@@ -1875,12 +1966,12 @@ avalon.fn.mix({
1875
1966
  return ret
1876
1967
  }
1877
1968
  },
1878
- removeData: function(name) {
1969
+ removeData: function (name) {
1879
1970
  name = "data-" + hyphen(name)
1880
1971
  this[0].removeAttribute(name)
1881
1972
  return this
1882
1973
  },
1883
- css: function(name, value) {
1974
+ css: function (name, value) {
1884
1975
  if (avalon.isPlainObject(name)) {
1885
1976
  for (var i in name) {
1886
1977
  avalon.css(this, i, name[i])
@@ -1890,7 +1981,7 @@ avalon.fn.mix({
1890
1981
  }
1891
1982
  return ret !== void 0 ? ret : this
1892
1983
  },
1893
- position: function() {
1984
+ position: function () {
1894
1985
  var offsetParent, offset,
1895
1986
  elem = this[0],
1896
1987
  parentOffset = {
@@ -1916,25 +2007,25 @@ avalon.fn.mix({
1916
2007
  left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true)
1917
2008
  }
1918
2009
  },
1919
- offsetParent: function() {
2010
+ offsetParent: function () {
1920
2011
  var offsetParent = this[0].offsetParent
1921
2012
  while (offsetParent && avalon.css(offsetParent, "position") === "static") {
1922
2013
  offsetParent = offsetParent.offsetParent;
1923
2014
  }
1924
- return avalon(offsetParent)
2015
+ return avalon(offsetParent || root)
1925
2016
  },
1926
- bind: function(type, fn, phase) {
2017
+ bind: function (type, fn, phase) {
1927
2018
  if (this[0]) { //此方法不会链
1928
2019
  return avalon.bind(this[0], type, fn, phase)
1929
2020
  }
1930
2021
  },
1931
- unbind: function(type, fn, phase) {
2022
+ unbind: function (type, fn, phase) {
1932
2023
  if (this[0]) {
1933
2024
  avalon.unbind(this[0], type, fn, phase)
1934
2025
  }
1935
2026
  return this
1936
2027
  },
1937
- val: function(value) {
2028
+ val: function (value) {
1938
2029
  var node = this[0]
1939
2030
  if (node && node.nodeType === 1) {
1940
2031
  var get = arguments.length === 0
@@ -1953,7 +2044,7 @@ avalon.fn.mix({
1953
2044
  })
1954
2045
 
1955
2046
  if (root.dataset) {
1956
- avalon.fn.data = function(name, val) {
2047
+ avalon.fn.data = function (name, val) {
1957
2048
  var dataset = this[0].dataset
1958
2049
  switch (arguments.length) {
1959
2050
  case 2:
@@ -1963,8 +2054,8 @@ if (root.dataset) {
1963
2054
  val = dataset[name]
1964
2055
  return parseData(val)
1965
2056
  case 0:
1966
- var ret = {}
1967
- for (var name in dataset) {
2057
+ var ret = createMap()
2058
+ for (name in dataset) {
1968
2059
  ret[name] = parseData(dataset[name])
1969
2060
  }
1970
2061
  return ret
@@ -1988,15 +2079,15 @@ function parseData(data) {
1988
2079
  avalon.each({
1989
2080
  scrollLeft: "pageXOffset",
1990
2081
  scrollTop: "pageYOffset"
1991
- }, function(method, prop) {
1992
- avalon.fn[method] = function(val) {
2082
+ }, function (method, prop) {
2083
+ avalon.fn[method] = function (val) {
1993
2084
  var node = this[0] || {}, win = getWindow(node),
1994
2085
  top = method === "scrollTop"
1995
2086
  if (!arguments.length) {
1996
2087
  return win ? win[prop] : node[method]
1997
2088
  } else {
1998
2089
  if (win) {
1999
- win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop())
2090
+ win.scrollTo(!top ? val : win[prop], top ? val : win[prop])
2000
2091
  } else {
2001
2092
  node[method] = val
2002
2093
  }
@@ -2009,14 +2100,14 @@ function getWindow(node) {
2009
2100
  }
2010
2101
 
2011
2102
  //=============================css相关==================================
2012
- var cssHooks = avalon.cssHooks = {}
2103
+ var cssHooks = avalon.cssHooks = createMap()
2013
2104
  var prefixes = ["", "-webkit-", "-moz-", "-ms-"]//去掉opera-15的支持
2014
2105
  var cssMap = {
2015
2106
  "float": "cssFloat"
2016
2107
  }
2017
2108
  avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
2018
2109
 
2019
- avalon.cssName = function(name, host, camelCase) {
2110
+ avalon.cssName = function (name, host, camelCase) {
2020
2111
  if (cssMap[name]) {
2021
2112
  return cssMap[name]
2022
2113
  }
@@ -2029,15 +2120,15 @@ avalon.cssName = function(name, host, camelCase) {
2029
2120
  }
2030
2121
  return null
2031
2122
  }
2032
- cssHooks["@:set"] = function(node, name, value) {
2123
+ cssHooks["@:set"] = function (node, name, value) {
2033
2124
  node.style[name] = value
2034
2125
  }
2035
2126
 
2036
- cssHooks["@:get"] = function(node, name) {
2127
+ cssHooks["@:get"] = function (node, name) {
2037
2128
  if (!node || !node.style) {
2038
2129
  throw new Error("getComputedStyle要求传入一个节点 " + node)
2039
2130
  }
2040
- var ret, computed = getComputedStyle(node, null)
2131
+ var ret, computed = getComputedStyle(node)
2041
2132
  if (computed) {
2042
2133
  ret = name === "filter" ? computed.getPropertyValue(name) : computed[name]
2043
2134
  if (ret === "") {
@@ -2046,13 +2137,13 @@ cssHooks["@:get"] = function(node, name) {
2046
2137
  }
2047
2138
  return ret
2048
2139
  }
2049
- cssHooks["opacity:get"] = function(node) {
2140
+ cssHooks["opacity:get"] = function (node) {
2050
2141
  var ret = cssHooks["@:get"](node, "opacity")
2051
2142
  return ret === "" ? "1" : ret
2052
2143
  }
2053
2144
 
2054
- "top,left".replace(rword, function(name) {
2055
- cssHooks[name + ":get"] = function(node) {
2145
+ "top,left".replace(rword, function (name) {
2146
+ cssHooks[name + ":get"] = function (node) {
2056
2147
  var computed = cssHooks["@:get"](node, name)
2057
2148
  return /px$/.test(computed) ? computed :
2058
2149
  avalon(node).position()[name] + "px"
@@ -2086,12 +2177,12 @@ function showHidden(node, array) {
2086
2177
  }
2087
2178
  }
2088
2179
 
2089
- "Width,Height".replace(rword, function(name) {//fix 481
2180
+ "Width,Height".replace(rword, function (name) {//fix 481
2090
2181
  var method = name.toLowerCase(),
2091
2182
  clientProp = "client" + name,
2092
2183
  scrollProp = "scroll" + name,
2093
2184
  offsetProp = "offset" + name
2094
- cssHooks[method + ":get"] = function(node, which, override) {
2185
+ cssHooks[method + ":get"] = function (node, which, override) {
2095
2186
  var boxSizing = -4
2096
2187
  if (typeof override === "number") {
2097
2188
  boxSizing = override
@@ -2099,23 +2190,17 @@ function showHidden(node, array) {
2099
2190
  which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
2100
2191
  var ret = node[offsetProp] // border-box 0
2101
2192
  if (boxSizing === 2) { // margin-box 2
2102
- return ret
2103
- + avalon.css(node, "margin" + which[0], true)
2104
- + avalon.css(node, "margin" + which[1], true)
2193
+ return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
2105
2194
  }
2106
2195
  if (boxSizing < 0) { // padding-box -2
2107
- ret = ret
2108
- - avalon.css(node, "border" + which[0] + "Width", true)
2109
- - avalon.css(node, "border" + which[1] + "Width", true)
2196
+ ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
2110
2197
  }
2111
2198
  if (boxSizing === -4) { // content-box -4
2112
- ret = ret
2113
- - avalon.css(node, "padding" + which[0], true)
2114
- - avalon.css(node, "padding" + which[1], true)
2199
+ ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
2115
2200
  }
2116
2201
  return ret
2117
2202
  }
2118
- cssHooks[method + "&get"] = function(node) {
2203
+ cssHooks[method + "&get"] = function (node) {
2119
2204
  var hidden = [];
2120
2205
  showHidden(node, hidden);
2121
2206
  var val = cssHooks[method + ":get"](node)
@@ -2129,11 +2214,11 @@ function showHidden(node, array) {
2129
2214
  }
2130
2215
  return val;
2131
2216
  }
2132
- avalon.fn[method] = function(value) { //会忽视其display
2217
+ avalon.fn[method] = function (value) { //会忽视其display
2133
2218
  var node = this[0]
2134
2219
  if (arguments.length === 0) {
2135
2220
  if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
2136
- return node["inner" + name]
2221
+ return node["inner" + name]
2137
2222
  }
2138
2223
  if (node.nodeType === 9) { //取得页面尺寸
2139
2224
  var doc = node.documentElement
@@ -2147,33 +2232,33 @@ function showHidden(node, array) {
2147
2232
  return this.css(method, value)
2148
2233
  }
2149
2234
  }
2150
- avalon.fn["inner" + name] = function() {
2235
+ avalon.fn["inner" + name] = function () {
2151
2236
  return cssHooks[method + ":get"](this[0], void 0, -2)
2152
2237
  }
2153
- avalon.fn["outer" + name] = function(includeMargin) {
2238
+ avalon.fn["outer" + name] = function (includeMargin) {
2154
2239
  return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
2155
2240
  }
2156
2241
  })
2157
- avalon.fn.offset = function() { //取得距离页面左右角的坐标
2158
- var node = this[0], box = {
2159
- left: 0,
2160
- top: 0
2161
- }
2162
- if (!node || !node.tagName || !node.ownerDocument) {
2163
- return box
2164
- }
2165
- var doc = node.ownerDocument,
2166
- root = doc.documentElement,
2167
- win = doc.defaultView
2168
- if (!root.contains(node)) {
2169
- return box
2170
- }
2171
- if (node.getBoundingClientRect !== void 0) {
2172
- box = node.getBoundingClientRect()
2173
- }
2174
- return {
2175
- top: box.top + win.pageYOffset - root.clientTop,
2176
- left: box.left + win.pageXOffset - root.clientLeft
2242
+ avalon.fn.offset = function () { //取得距离页面左右角的坐标
2243
+ var node = this[0]
2244
+ try {
2245
+ var rect = node.getBoundingClientRect()
2246
+ // Make sure element is not hidden (display: none) or disconnected
2247
+ // https://github.com/jquery/jquery/pull/2043/files#r23981494
2248
+ if (rect.width || rect.height || node.getClientRects().length) {
2249
+ var doc = node.ownerDocument
2250
+ var root = doc.documentElement
2251
+ var win = doc.defaultView
2252
+ return {
2253
+ top: rect.top + win.pageYOffset - root.clientTop,
2254
+ left: rect.left + win.pageXOffset - root.clientLeft
2255
+ }
2256
+ }
2257
+ } catch (e) {
2258
+ return {
2259
+ left: 0,
2260
+ top: 0
2261
+ }
2177
2262
  }
2178
2263
  }
2179
2264
  //=============================val相关=======================
@@ -2183,7 +2268,7 @@ function getValType(el) {
2183
2268
  return ret === "input" && /checkbox|radio/.test(el.type) ? "checked" : ret
2184
2269
  }
2185
2270
  var valHooks = {
2186
- "select:get": function(node, value) {
2271
+ "select:get": function (node, value) {
2187
2272
  var option, options = node.options,
2188
2273
  index = node.selectedIndex,
2189
2274
  one = node.type === "select-one" || index < 0,
@@ -2206,7 +2291,7 @@ var valHooks = {
2206
2291
  }
2207
2292
  return values
2208
2293
  },
2209
- "select:set": function(node, values, optionSet) {
2294
+ "select:set": function (node, values, optionSet) {
2210
2295
  values = [].concat(values) //强制转换为数组
2211
2296
  for (var i = 0, el; el = node.options[i++]; ) {
2212
2297
  if ((el.selected = values.indexOf(el.value) > -1)) {
@@ -2224,28 +2309,26 @@ var valHooks = {
2224
2309
  **********************************************************************/
2225
2310
  var quote = JSON.stringify
2226
2311
 
2227
- var keywords =
2228
- // 关键字
2229
- "break,case,catch,continue,debugger,default,delete,do,else,false" +
2230
- ",finally,for,function,if,in,instanceof,new,null,return,switch,this" +
2231
- ",throw,true,try,typeof,var,void,while,with" +
2232
- // 保留字
2233
- ",abstract,boolean,byte,char,class,const,double,enum,export,extends" +
2234
- ",final,float,goto,implements,import,int,interface,long,native" +
2235
- ",package,private,protected,public,short,static,super,synchronized" +
2236
- ",throws,transient,volatile" +
2237
- // ECMA 5 - use strict
2238
- ",arguments,let,yield" + ",undefined"
2312
+ var keywords = [
2313
+ "break,case,catch,continue,debugger,default,delete,do,else,false",
2314
+ "finally,for,function,if,in,instanceof,new,null,return,switch,this",
2315
+ "throw,true,try,typeof,var,void,while,with", /* 关键字*/
2316
+ "abstract,boolean,byte,char,class,const,double,enum,export,extends",
2317
+ "final,float,goto,implements,import,int,interface,long,native",
2318
+ "package,private,protected,public,short,static,super,synchronized",
2319
+ "throws,transient,volatile", /*保留字*/
2320
+ "arguments,let,yield,undefined" /* ECMA 5 - use strict*/].join(",")
2239
2321
  var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g
2240
2322
  var rsplit = /[^\w$]+/g
2241
2323
  var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g')
2242
2324
  var rnumber = /\b\d[^,]*/g
2243
2325
  var rcomma = /^,+|,+$/g
2244
- var cacheVars = createCache(512)
2245
- var getVariables = function(code) {
2326
+ var cacheVars = new Cache(512)
2327
+ var getVariables = function (code) {
2246
2328
  var key = "," + code.trim()
2247
- if (cacheVars[key]) {
2248
- return cacheVars[key]
2329
+ var ret = cacheVars.get(key)
2330
+ if (ret) {
2331
+ return ret
2249
2332
  }
2250
2333
  var match = code
2251
2334
  .replace(rrexpstr, "")
@@ -2254,7 +2337,7 @@ var getVariables = function(code) {
2254
2337
  .replace(rnumber, "")
2255
2338
  .replace(rcomma, "")
2256
2339
  .split(/^$|,+/)
2257
- return cacheVars(key, uniqSet(match))
2340
+ return cacheVars.put(key, uniqSet(match))
2258
2341
  }
2259
2342
  /*添加赋值语句*/
2260
2343
 
@@ -2287,7 +2370,7 @@ function uniqSet(array) {
2287
2370
  return ret
2288
2371
  }
2289
2372
  //缓存求值函数,以便多次利用
2290
- var cacheExprs = createCache(128)
2373
+ var cacheExprs = new Cache(128)
2291
2374
  //取得求值函数及其传参
2292
2375
  var rduplex = /\w\[.*\]|\w\.\w/
2293
2376
  var rproxy = /(\$proxy\$[a-z]+)\d+$/
@@ -2299,16 +2382,16 @@ var rthimLeftParentheses = /"\s*\(/g
2299
2382
  function parseFilter(val, filters) {
2300
2383
  filters = filters
2301
2384
  .replace(rthimRightParentheses, "")//处理最后的小括号
2302
- .replace(rthimOtherParentheses, function() {//处理其他小括号
2385
+ .replace(rthimOtherParentheses, function () {//处理其他小括号
2303
2386
  return "],|"
2304
2387
  })
2305
- .replace(rquoteFilterName, function(a, b) { //处理|及它后面的过滤器的名字
2388
+ .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字
2306
2389
  return "[" + quote(b)
2307
2390
  })
2308
- .replace(rpatchBracket, function() {
2391
+ .replace(rpatchBracket, function () {
2309
2392
  return '"],["'
2310
2393
  })
2311
- .replace(rthimLeftParentheses, function() {
2394
+ .replace(rthimLeftParentheses, function () {
2312
2395
  return '",'
2313
2396
  }) + "]"
2314
2397
  return "return avalon.filters.$filter(" + val + ", " + filters + ")"
@@ -2317,7 +2400,7 @@ function parseFilter(val, filters) {
2317
2400
  function parseExpr(code, scopes, data) {
2318
2401
  var dataType = data.type
2319
2402
  var filters = data.filters || ""
2320
- var exprId = scopes.map(function(el) {
2403
+ var exprId = scopes.map(function (el) {
2321
2404
  return String(el.$id).replace(rproxy, "$1")
2322
2405
  }) + code + dataType + filters
2323
2406
  var vars = getVariables(code).concat(),
@@ -2341,9 +2424,9 @@ function parseExpr(code, scopes, data) {
2341
2424
  }
2342
2425
  if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) {
2343
2426
  //https://github.com/RubyLouvre/avalon/issues/583
2344
- data.vars.forEach(function(v) {
2427
+ data.vars.forEach(function (v) {
2345
2428
  var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
2346
- code = code.replace(reg, function(_) {
2429
+ code = code.replace(reg, function (_) {
2347
2430
  var c = _.charAt(v.length)
2348
2431
  var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2349
2432
  var method = /^\s*\(/.test(r)
@@ -2370,7 +2453,7 @@ function parseExpr(code, scopes, data) {
2370
2453
  //---------------args----------------
2371
2454
  data.args = args
2372
2455
  //---------------cache----------------
2373
- var fn = cacheExprs[exprId] //直接从缓存,免得重复生成
2456
+ var fn = cacheExprs.get(exprId) //直接从缓存,免得重复生成
2374
2457
  if (fn) {
2375
2458
  data.evaluator = fn
2376
2459
  return
@@ -2394,7 +2477,7 @@ function parseExpr(code, scopes, data) {
2394
2477
  "= vvv;\n} "
2395
2478
  try {
2396
2479
  fn = Function.apply(noop, names.concat(_body))
2397
- data.evaluator = cacheExprs(exprId, fn)
2480
+ data.evaluator = cacheExprs.put(exprId, fn)
2398
2481
  } catch (e) {
2399
2482
  log("debug: parse error," + e.message)
2400
2483
  }
@@ -2416,11 +2499,11 @@ function parseExpr(code, scopes, data) {
2416
2499
  }
2417
2500
  try {
2418
2501
  fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
2419
- data.evaluator = cacheExprs(exprId, fn)
2502
+ data.evaluator = cacheExprs.put(exprId, fn)
2420
2503
  } catch (e) {
2421
2504
  log("debug: parse error," + e.message)
2422
2505
  } finally {
2423
- vars = textBuffer = names = null //释放内存
2506
+ vars = assigns = names = null //释放内存
2424
2507
  }
2425
2508
  }
2426
2509
 
@@ -2429,7 +2512,7 @@ function parseExpr(code, scopes, data) {
2429
2512
 
2430
2513
  function parseExprProxy(code, scopes, data, tokens, noregister) {
2431
2514
  if (Array.isArray(tokens)) {
2432
- code = tokens.map(function(el) {
2515
+ code = tokens.map(function (el) {
2433
2516
  return el.expr ? "(" + el.value + ")" : quote(el.value)
2434
2517
  }).join(" + ")
2435
2518
  }
@@ -2443,16 +2526,12 @@ function parseExprProxy(code, scopes, data, tokens, noregister) {
2443
2526
  }
2444
2527
  }
2445
2528
  avalon.parseExprProxy = parseExprProxy
2446
- /*********************************************************************
2447
- * 各种指令 *
2448
- **********************************************************************/
2449
- //ms-skip绑定已经在scanTag 方法中实现
2450
- //ms-controller绑定已经在scanTag 方法中实现
2451
- //ms-important绑定已经在scanTag 方法中实现
2452
- var bools = "autofocus,autoplay,async,allowTransparency,checked,controls,declare,disabled,defer,defaultChecked,defaultSelected" +
2453
- "contentEditable,isMap,loop,multiple,noHref,noResize,noShade,open,readOnly,selected"
2529
+ var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls",
2530
+ "declare,disabled,defer,defaultChecked,defaultSelected",
2531
+ "contentEditable,isMap,loop,multiple,noHref,noResize,noShade",
2532
+ "open,readOnly,selected"].join(",")
2454
2533
  var boolMap = {}
2455
- bools.replace(rword, function(name) {
2534
+ bools.replace(rword, function (name) {
2456
2535
  boolMap[name.toLowerCase()] = name
2457
2536
  })
2458
2537
 
@@ -2465,21 +2544,23 @@ var propMap = {//属性名映射
2465
2544
  "http-equiv": "httpEquiv"
2466
2545
  }
2467
2546
 
2468
- var anomaly = "accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan," + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight," + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
2469
- anomaly.replace(rword, function(name) {
2547
+ var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan",
2548
+ "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight",
2549
+ "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"].join(",")
2550
+ anomaly.replace(rword, function (name) {
2470
2551
  propMap[name.toLowerCase()] = name
2471
2552
  })
2472
2553
 
2473
2554
  var rnoscripts = /<noscript.*?>(?:[\s\S]+?)<\/noscript>/img
2474
2555
  var rnoscriptText = /<noscript.*?>([\s\S]+?)<\/noscript>/im
2475
2556
 
2476
- var getXHR = function() {
2477
- return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")
2557
+ var getXHR = function () {
2558
+ return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")// jshint ignore:line
2478
2559
  }
2479
2560
 
2480
2561
  var cacheTmpls = avalon.templateCache = {}
2481
2562
 
2482
- bindingHandlers.attr = function(data, vmodels) {
2563
+ bindingHandlers.attr = function (data, vmodels) {
2483
2564
  var text = data.value.trim(),
2484
2565
  simple = true
2485
2566
  if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) {
@@ -2493,7 +2574,10 @@ bindingHandlers.attr = function(data, vmodels) {
2493
2574
  var elem = data.element
2494
2575
  data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels)
2495
2576
  data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels)
2496
- var outer = data.includeReplaced = !!avalon(elem).data("includeReplace")
2577
+ var outer = data.includeReplace = !!avalon(elem).data("includeReplace")
2578
+ if (avalon(elem).data("includeCache")) {
2579
+ data.templateCache = {}
2580
+ }
2497
2581
  data.startInclude = DOC.createComment("ms-include")
2498
2582
  data.endInclude = DOC.createComment("ms-include-end")
2499
2583
  if (outer) {
@@ -2509,7 +2593,7 @@ bindingHandlers.attr = function(data, vmodels) {
2509
2593
  parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value)))
2510
2594
  }
2511
2595
 
2512
- bindingExecutors.attr = function(val, elem, data) {
2596
+ bindingExecutors.attr = function (val, elem, data) {
2513
2597
  var method = data.type,
2514
2598
  attrName = data.param
2515
2599
  if (method === "css") {
@@ -2544,38 +2628,50 @@ bindingExecutors.attr = function(val, elem, data) {
2544
2628
  var vmodels = data.vmodels
2545
2629
  var rendered = data.includeRendered
2546
2630
  var loaded = data.includeLoaded
2547
- var replace = data.includeReplaced
2631
+ var replace = data.includeReplace
2548
2632
  var target = replace ? elem.parentNode : elem
2549
- var scanTemplate = function(text) {
2633
+ var scanTemplate = function (text) {
2550
2634
  if (loaded) {
2551
2635
  text = loaded.apply(target, [text].concat(vmodels))
2552
2636
  }
2553
2637
  if (rendered) {
2554
- checkScan(target, function() {
2638
+ checkScan(target, function () {
2555
2639
  rendered.call(target)
2556
2640
  }, NaN)
2557
2641
  }
2642
+ var lastID = data.includeLastID
2643
+ if (data.templateCache && lastID && lastID !== val) {
2644
+ var lastTemplate = data.templateCache[lastID]
2645
+ if (!lastTemplate) {
2646
+ lastTemplate = data.templateCache[lastID] = DOC.createElement("div")
2647
+ ifGroup.appendChild(lastTemplate)
2648
+ }
2649
+ }
2650
+ data.includeLastID = val
2558
2651
  while (true) {
2559
2652
  var node = data.startInclude.nextSibling
2560
2653
  if (node && node !== data.endInclude) {
2561
2654
  target.removeChild(node)
2655
+ if (lastTemplate)
2656
+ lastTemplate.appendChild(node)
2562
2657
  } else {
2563
2658
  break
2564
2659
  }
2565
2660
  }
2566
- var dom = avalon.parseHTML(text)
2661
+ var dom = getTemplateNodes(data, val, text)
2567
2662
  var nodes = avalon.slice(dom.childNodes)
2568
2663
  target.insertBefore(dom, data.endInclude)
2569
2664
  scanNodeArray(nodes, vmodels)
2570
2665
  }
2666
+
2571
2667
  if (data.param === "src") {
2572
2668
  if (cacheTmpls[val]) {
2573
- avalon.nextTick(function() {
2669
+ avalon.nextTick(function () {
2574
2670
  scanTemplate(cacheTmpls[val])
2575
2671
  })
2576
2672
  } else {
2577
2673
  var xhr = getXHR()
2578
- xhr.onreadystatechange = function() {
2674
+ xhr.onreadystatechange = function () {
2579
2675
  if (xhr.readyState === 4) {
2580
2676
  var s = xhr.status
2581
2677
  if (s >= 200 && s < 300 || s === 304 || s === 1223) {
@@ -2611,7 +2707,7 @@ bindingExecutors.attr = function(val, elem, data) {
2611
2707
  }
2612
2708
  }
2613
2709
  }
2614
- avalon.nextTick(function() {
2710
+ avalon.nextTick(function () {
2615
2711
  scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML)
2616
2712
  })
2617
2713
  }
@@ -2630,12 +2726,22 @@ bindingExecutors.attr = function(val, elem, data) {
2630
2726
  }
2631
2727
  }
2632
2728
 
2729
+ function getTemplateNodes(data, id, text) {
2730
+ var div = data.templateCache && data.templateCache[id]
2731
+ if (div) {
2732
+ var dom = DOC.createDocumentFragment(), firstChild
2733
+ while (firstChild = div.firstChild) {
2734
+ dom.appendChild(firstChild)
2735
+ }
2736
+ return dom
2737
+ }
2738
+ return avalon.parseHTML(text)
2739
+ }
2740
+
2633
2741
  //这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html"
2634
- "title,alt,src,value,css,include,href".replace(rword, function(name) {
2742
+ "title,alt,src,value,css,include,href".replace(rword, function (name) {
2635
2743
  bindingHandlers[name] = bindingHandlers.attr
2636
2744
  })
2637
- //ms-include绑定已由ms-attr绑定实现
2638
-
2639
2745
  //根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag"
2640
2746
  //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html
2641
2747
  bindingHandlers["class"] = function(data, vmodels) {
@@ -2726,6 +2832,10 @@ bindingExecutors ["class"] = function(val, elem, data) {
2726
2832
  "hover,active".replace(rword, function(method) {
2727
2833
  bindingHandlers[method] = bindingHandlers["class"]
2728
2834
  })
2835
+ //ms-controller绑定已经在scanTag 方法中实现
2836
+ //ms-css绑定已由ms-attr绑定实现
2837
+
2838
+
2729
2839
  // bindingHandlers.data 定义在if.js
2730
2840
  bindingExecutors.data = function(val, elem, data) {
2731
2841
  var key = "data-" + data.param
@@ -2736,273 +2846,23 @@ bindingExecutors.data = function(val, elem, data) {
2736
2846
  }
2737
2847
  }
2738
2848
 
2739
- // bindingHandlers.text 定义在if.js
2740
- bindingExecutors.text = function(val, elem) {
2741
- val = val == null ? "" : val //不在页面上显示undefined null
2742
- if (elem.nodeType === 3) { //绑定在文本节点上
2743
- try { //IE对游离于DOM树外的节点赋值会报错
2744
- elem.data = val
2745
- } catch (e) {
2746
- }
2747
- } else { //绑定在特性节点上
2748
- elem.textContent = val
2749
- }
2750
- }
2849
+ //双工绑定
2850
+ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
2851
+ var elem = data.element,
2852
+ hasCast
2853
+ parseExprProxy(data.value, vmodels, data, 0, 1)
2751
2854
 
2752
- // bindingHandlers.html 定义在if.js
2753
- bindingExecutors.html = function(val, elem, data) {
2754
- val = val == null ? "" : val
2755
- var isHtmlFilter = "group" in data
2756
- var parent = isHtmlFilter ? elem.parentNode : elem
2757
- if (!parent)
2758
- return
2759
- if (val.nodeType === 11) { //将val转换为文档碎片
2760
- var fragment = val
2761
- } else if (val.nodeType === 1 || val.item) {
2762
- var nodes = val.nodeType === 1 ? val.childNodes : val.item ? val : []
2763
- fragment = hyperspace.cloneNode(true)
2764
- while (nodes[0]) {
2765
- fragment.appendChild(nodes[0])
2766
- }
2767
- } else {
2768
- fragment = avalon.parseHTML(val)
2769
- }
2770
- //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空
2771
- var comment = DOC.createComment("ms-html")
2772
- if (isHtmlFilter) {
2773
- parent.insertBefore(comment, elem)
2774
- var n = data.group, i = 1
2775
- while (i < n) {
2776
- var node = elem.nextSibling
2777
- if (node) {
2778
- parent.removeChild(node)
2779
- i++
2780
- }
2781
- }
2782
- parent.removeChild(elem)
2783
- data.element = comment //防止被CG
2784
- } else {
2785
- avalon.clearHTML(parent).appendChild(comment)
2786
- }
2787
- if (isHtmlFilter) {
2788
- data.group = fragment.childNodes.length || 1
2789
- }
2790
- nodes = avalon.slice(fragment.childNodes)
2791
- if (nodes[0]) {
2792
- if (comment.parentNode)
2793
- comment.parentNode.replaceChild(fragment, comment)
2794
- if (isHtmlFilter) {
2795
- data.element = nodes[0]
2796
- }
2797
- }
2798
- scanNodeArray(nodes, data.vmodels)
2799
- }
2800
-
2801
- bindingHandlers["if"] =
2802
- bindingHandlers.data =
2803
- bindingHandlers.text =
2804
- bindingHandlers.html =
2805
- function(data, vmodels) {
2806
- parseExprProxy(data.value, vmodels, data)
2807
- }
2808
-
2809
- bindingExecutors["if"] = function(val, elem, data) {
2810
- if (val) { //插回DOM树
2811
- if (elem.nodeType === 8) {
2812
- elem.parentNode.replaceChild(data.template, elem)
2813
- elem = data.element = data.template //这时可能为null
2814
- }
2815
- if (elem.getAttribute(data.name)) {
2816
- elem.removeAttribute(data.name)
2817
- scanAttr(elem, data.vmodels)
2818
- }
2819
- data.rollback = null
2820
- } else { //移出DOM树,并用注释节点占据原位置
2821
- if (elem.nodeType === 1) {
2822
- var node = data.element = DOC.createComment("ms-if")
2823
- elem.parentNode.replaceChild(node, elem)
2824
- data.template = elem //元素节点
2825
- ifGroup.appendChild(elem)
2826
- data.rollback = function() {
2827
- if (elem.parentNode === ifGroup) {
2828
- ifGroup.removeChild(elem)
2829
- }
2830
- }
2831
- }
2832
- }
2833
- }
2834
-
2835
-
2836
- function parseDisplay(nodeName, val) {
2837
- //用于取得此类标签的默认display值
2838
- var key = "_" + nodeName
2839
- if (!parseDisplay[key]) {
2840
- var node = DOC.createElement(nodeName)
2841
- root.appendChild(node)
2842
- if (W3C) {
2843
- val = getComputedStyle(node, null).display
2844
- } else {
2845
- val = node.currentStyle.display
2846
- }
2847
- root.removeChild(node)
2848
- parseDisplay[key] = val
2849
- }
2850
- return parseDisplay[key]
2851
- }
2852
-
2853
- avalon.parseDisplay = parseDisplay
2854
-
2855
- bindingHandlers.visible = function(data, vmodels) {
2856
- var elem = avalon(data.element)
2857
- var display = elem.css("display")
2858
- if (display === "none") {
2859
- var style = elem[0].style
2860
- var has = /visibility/i.test(style.cssText)
2861
- var visible = elem.css("visibility")
2862
- style.display = ""
2863
- style.visibility = "hidden"
2864
- display = elem.css("display")
2865
- if (display === "none") {
2866
- display = parseDisplay(elem[0].nodeName)
2867
- }
2868
- style.visibility = has ? visible : ""
2869
- }
2870
- data.display = display
2871
- parseExprProxy(data.value, vmodels, data)
2872
- }
2873
-
2874
- bindingExecutors.visible = function(val, elem, data) {
2875
- elem.style.display = val ? data.display : "none"
2876
- }
2877
-
2878
- var rdash = /\(([^)]*)\)/
2879
- bindingHandlers.on = function(data, vmodels) {
2880
- var value = data.value
2881
- data.type = "on"
2882
- var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
2883
- if (typeof bindingHandlers.on[eventType + "Hook"] === "function") {
2884
- bindingHandlers.on[eventType + "Hook"](data)
2885
- }
2886
- if (value.indexOf("(") > 0 && value.indexOf(")") > -1) {
2887
- var matched = (value.match(rdash) || ["", ""])[1].trim()
2888
- if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理
2889
- value = value.replace(rdash, "")
2890
- }
2891
- }
2892
- parseExprProxy(value, vmodels, data)
2893
- }
2894
-
2895
- bindingExecutors.on = function(callback, elem, data) {
2896
- callback = function(e) {
2897
- var fn = data.evaluator || noop
2898
- return fn.apply(this, data.args.concat(e))
2899
- }
2900
- var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
2901
- if (eventType === "scan") {
2902
- callback.call(elem, {
2903
- type: eventType
2904
- })
2905
- } else if (typeof data.specialBind === "function") {
2906
- data.specialBind(elem, callback)
2907
- } else {
2908
- var removeFn = avalon.bind(elem, eventType, callback)
2909
- }
2910
- data.rollback = function() {
2911
- if (typeof data.specialUnbind === "function") {
2912
- data.specialUnbind()
2913
- } else {
2914
- avalon.unbind(elem, eventType, removeFn)
2915
- }
2916
- }
2917
- }
2918
-
2919
-
2920
- bindingHandlers.widget = function(data, vmodels) {
2921
- var args = data.value.match(rword)
2922
- var elem = data.element
2923
- var widget = args[0]
2924
- var id = args[1]
2925
- if (!id || id === "$") {//没有定义或为$时,取组件名+随机数
2926
- id = generateID(widget)
2927
- }
2928
- var optName = args[2] || widget//没有定义,取组件名
2929
- var constructor = avalon.ui[widget]
2930
- if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname"
2931
- vmodels = elem.vmodels || vmodels
2932
- for (var i = 0, v; v = vmodels[i++]; ) {
2933
- if (v.hasOwnProperty(optName) && typeof v[optName] === "object") {
2934
- var vmOptions = v[optName]
2935
- vmOptions = vmOptions.$model || vmOptions
2936
- break
2937
- }
2938
- }
2939
- if (vmOptions) {
2940
- var wid = vmOptions[widget + "Id"]
2941
- if (typeof wid === "string") {
2942
- id = wid
2943
- }
2944
- }
2945
- //抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象
2946
- var widgetData = avalon.getWidgetData(elem, widget)
2947
- data.value = [widget, id, optName].join(",")
2948
- data[widget + "Id"] = id
2949
- data.evaluator = noop
2950
- elem.msData["ms-widget-id"] = id
2951
- var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData)
2952
- elem.removeAttribute("ms-widget")
2953
- var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM
2954
- if (vmodel.$id) {
2955
- avalon.vmodels[id] = vmodel
2956
- createSignalTower(elem, vmodel)
2957
- if (vmodel.hasOwnProperty("$init")) {
2958
- vmodel.$init(function() {
2959
- avalon.scan(elem, [vmodel].concat(vmodels))
2960
- if (typeof options.onInit === "function") {
2961
- options.onInit.call(elem, vmodel, options, vmodels)
2962
- }
2963
- })
2964
- }
2965
- data.rollback = function() {
2966
- try {
2967
- vmodel.widgetElement = null
2968
- vmodel.$remove()
2969
- } catch (e) {
2970
- }
2971
- elem.msData = {}
2972
- delete avalon.vmodels[vmodel.$id]
2973
- }
2974
- addSubscribers(data, widgetList)
2975
- if (window.chrome) {
2976
- elem.addEventListener("DOMNodeRemovedFromDocument", function() {
2977
- setTimeout(removeSubscribers)
2978
- })
2979
- }
2980
- } else {
2981
- avalon.scan(elem, vmodels)
2982
- }
2983
- } else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels
2984
- elem.vmodels = vmodels
2985
- }
2986
- }
2987
- var widgetList = []
2988
- //不存在 bindingExecutors.widget
2989
- //双工绑定
2990
- var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
2991
- var elem = data.element,
2992
- hasCast
2993
- parseExprProxy(data.value, vmodels, data, 0, 1)
2994
-
2995
- data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
2996
- if (data.evaluator && data.args) {
2997
- var params = []
2998
- var casting = oneObject("string,number,boolean,checked")
2999
- if (elem.type === "radio" && data.param === "") {
3000
- data.param = "checked"
2855
+ data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
2856
+ if (data.evaluator && data.args) {
2857
+ var params = []
2858
+ var casting = oneObject("string,number,boolean,checked")
2859
+ if (elem.type === "radio" && data.param === "") {
2860
+ data.param = "checked"
3001
2861
  }
3002
2862
  if (elem.msData) {
3003
2863
  elem.msData["ms-duplex"] = data.value
3004
2864
  }
3005
- data.param.replace(/\w+/g, function(name) {
2865
+ data.param.replace(/\w+/g, function (name) {
3006
2866
  if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) {
3007
2867
  if (name === "radio")
3008
2868
  log("ms-duplex-radio已经更名为ms-duplex-checked")
@@ -3025,14 +2885,14 @@ var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
3025
2885
  params.push("string")
3026
2886
  }
3027
2887
  data.param = params.join("-")
3028
- data.bound = function(type, callback) {
2888
+ data.bound = function (type, callback) {
3029
2889
  if (elem.addEventListener) {
3030
2890
  elem.addEventListener(type, callback, false)
3031
2891
  } else {
3032
2892
  elem.attachEvent("on" + type, callback)
3033
2893
  }
3034
2894
  var old = data.rollback
3035
- data.rollback = function() {
2895
+ data.rollback = function () {
3036
2896
  elem.avalonSetter = null
3037
2897
  avalon.unbind(elem, type, callback)
3038
2898
  old && old()
@@ -3054,32 +2914,44 @@ function fixNull(val) {
3054
2914
  }
3055
2915
  avalon.duplexHooks = {
3056
2916
  checked: {
3057
- get: function(val, data) {
2917
+ get: function (val, data) {
3058
2918
  return !data.element.oldValue
3059
2919
  }
3060
2920
  },
3061
2921
  string: {
3062
- get: function(val) { //同步到VM
2922
+ get: function (val) { //同步到VM
3063
2923
  return val
3064
2924
  },
3065
2925
  set: fixNull
3066
2926
  },
3067
2927
  "boolean": {
3068
- get: function(val) {
2928
+ get: function (val) {
3069
2929
  return val === "true"
3070
2930
  },
3071
2931
  set: fixNull
3072
2932
  },
3073
2933
  number: {
3074
- get: function(val) {
3075
- return isFinite(val) ? parseFloat(val) || 0 : val
2934
+ get: function (val, data) {
2935
+ var number = parseFloat(val)
2936
+ if (-val === -number) {
2937
+ return number
2938
+ }
2939
+ var arr = /strong|medium|weak/.exec(data.element.getAttribute("data-duplex-number")) || ["medium"]
2940
+ switch (arr[0]) {
2941
+ case "strong":
2942
+ return 0
2943
+ case "medium":
2944
+ return val === "" ? "" : 0
2945
+ case "weak":
2946
+ return val
2947
+ }
3076
2948
  },
3077
2949
  set: fixNull
3078
2950
  }
3079
2951
  }
3080
2952
 
3081
2953
  function pipe(val, data, action, e) {
3082
- data.param.replace(/\w+/g, function(name) {
2954
+ data.param.replace(/\w+/g, function (name) {
3083
2955
  var hook = avalon.duplexHooks[name]
3084
2956
  if (hook && typeof hook[action] === "function") {
3085
2957
  val = hook[action](val, data)
@@ -3089,18 +2961,8 @@ function pipe(val, data, action, e) {
3089
2961
  }
3090
2962
 
3091
2963
  var TimerID, ribbon = []
3092
- function W3CFire(el, name, detail) {
3093
- var event = DOC.createEvent("Events")
3094
- event.initEvent(name, true, true)
3095
- event.fireByAvalon = true//签名,标记事件是由avalon触发
3096
- //event.isTrusted = false 设置这个opera会报错
3097
- if (detail)
3098
- event.detail = detail
3099
- el.dispatchEvent(event)
3100
- }
3101
2964
 
3102
-
3103
- avalon.tick = function(fn) {
2965
+ avalon.tick = function (fn) {
3104
2966
  if (ribbon.push(fn) === 1) {
3105
2967
  TimerID = setInterval(ticker, 60)
3106
2968
  }
@@ -3119,15 +2981,18 @@ function ticker() {
3119
2981
  }
3120
2982
 
3121
2983
  var watchValueInTimer = noop
3122
- new function() {
2984
+ var rmsinput = /text|password|hidden/
2985
+ new function () {// jshint ignore:line
3123
2986
  try {//#272 IE9-IE11, firefox
3124
2987
  var setters = {}
3125
2988
  var aproto = HTMLInputElement.prototype
3126
2989
  var bproto = HTMLTextAreaElement.prototype
3127
- function newSetter(value) {
2990
+ function newSetter(value) {// jshint ignore:line
3128
2991
  if (avalon.contains(root, this)) {
3129
2992
  setters[this.tagName].call(this, value)
3130
- if (this.avalonSetter) {
2993
+ if (!rmsinput.test(this.type))
2994
+ return
2995
+ if (!this.msFocus && this.avalonSetter) {
3131
2996
  this.avalonSetter()
3132
2997
  }
3133
2998
  }
@@ -3145,12 +3010,12 @@ new function() {
3145
3010
  } catch (e) {
3146
3011
  watchValueInTimer = avalon.tick
3147
3012
  }
3148
- }
3013
+ }// jshint ignore:line
3149
3014
 
3150
3015
 
3151
3016
  //处理radio, checkbox, text, textarea, password
3152
- duplexBinding.INPUT = function(element, evaluator, data) {
3153
- var type = element.type,
3017
+ duplexBinding.INPUT = function (element, evaluator, data) {
3018
+ var $type = element.type,
3154
3019
  bound = data.bound,
3155
3020
  $elem = avalon(element),
3156
3021
  composing = false
@@ -3164,7 +3029,8 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3164
3029
  composing = false
3165
3030
  }
3166
3031
  //当value变化时改变model的值
3167
- function updateVModel() {
3032
+
3033
+ var updateVModel = function () {
3168
3034
  if (composing)//处理中文输入法在minlengh下引发的BUG
3169
3035
  return
3170
3036
  var val = element.oldValue = element.value //防止递归调用形成死循环
@@ -3173,35 +3039,35 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3173
3039
  evaluator(lastValue)
3174
3040
  callback.call(element, lastValue)
3175
3041
  if ($elem.data("duplex-focus")) {
3176
- avalon.nextTick(function() {
3042
+ avalon.nextTick(function () {
3177
3043
  element.focus()
3178
3044
  })
3179
3045
  }
3180
3046
  }
3181
3047
  }
3182
3048
  //当model变化时,它就会改变value的值
3183
- data.handler = function() {
3049
+ data.handler = function () {
3184
3050
  var val = data.pipe(evaluator(), data, "set") + ""
3185
3051
  if (val !== element.oldValue) {
3186
3052
  element.value = val
3187
3053
  }
3188
3054
  }
3189
- if (data.isChecked || element.type === "radio") {
3190
- updateVModel = function() {
3055
+ if (data.isChecked || $type === "radio") {
3056
+ updateVModel = function () {
3191
3057
  if ($elem.data("duplex-observe") !== false) {
3192
3058
  var lastValue = data.pipe(element.value, data, "get")
3193
3059
  evaluator(lastValue)
3194
3060
  callback.call(element, lastValue)
3195
3061
  }
3196
3062
  }
3197
- data.handler = function() {
3063
+ data.handler = function () {
3198
3064
  var val = evaluator()
3199
3065
  var checked = data.isChecked ? !!val : val + "" === element.value
3200
3066
  element.checked = element.oldValue = checked
3201
3067
  }
3202
3068
  bound("click", updateVModel)
3203
- } else if (type === "checkbox") {
3204
- updateVModel = function() {
3069
+ } else if ($type === "checkbox") {
3070
+ updateVModel = function () {
3205
3071
  if ($elem.data("duplex-observe") !== false) {
3206
3072
  var method = element.checked ? "ensure" : "remove"
3207
3073
  var array = evaluator()
@@ -3213,17 +3079,17 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3213
3079
  callback.call(element, array)
3214
3080
  }
3215
3081
  }
3216
- data.handler = function() {
3082
+ data.handler = function () {
3217
3083
  var array = [].concat(evaluator()) //强制转换为数组
3218
3084
  element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1
3219
3085
  }
3220
3086
  bound("change", updateVModel)
3221
3087
  } else {
3222
- var events = element.getAttribute("data-duplex-event") || element.getAttribute("data-event") || "input"
3088
+ var events = element.getAttribute("data-duplex-event") || "input"
3223
3089
  if (element.attributes["data-event"]) {
3224
3090
  log("data-event指令已经废弃,请改用data-duplex-event")
3225
3091
  }
3226
- events.replace(rword, function(name) {
3092
+ events.replace(rword, function (name) {
3227
3093
  switch (name) {
3228
3094
  case "input":
3229
3095
  bound("input", updateVModel)
@@ -3238,21 +3104,27 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3238
3104
  break
3239
3105
  }
3240
3106
  })
3241
- }
3242
-
3243
- if (/text|password/.test(element.type)) {
3244
- watchValueInTimer(function() {
3245
- if (root.contains(element)) {
3246
- if (element.value !== element.oldValue) {
3247
- updateVModel()
3248
- }
3249
- } else if (!element.msRetain) {
3250
- return false
3251
- }
3107
+ bound("focus", function () {
3108
+ element.msFocus = true
3252
3109
  })
3110
+ bound("blur", function () {
3111
+ element.msFocus = false
3112
+ })
3113
+ if (rmsinput.test($type)) {
3114
+ watchValueInTimer(function () {
3115
+ if (root.contains(element)) {
3116
+ if (!element.msFocus && element.oldValue !== element.value) {
3117
+ updateVModel()
3118
+ }
3119
+ } else if (!element.msRetain) {
3120
+ return false
3121
+ }
3122
+ })
3123
+ }
3124
+
3125
+ element.avalonSetter = updateVModel
3253
3126
  }
3254
3127
 
3255
- element.avalonSetter = updateVModel
3256
3128
  element.oldValue = element.value
3257
3129
  registerSubscriber(data)
3258
3130
  callback.call(element, element.value)
@@ -3303,6 +3175,135 @@ duplexBinding.SELECT = function(element, evaluator, data) {
3303
3175
  }
3304
3176
 
3305
3177
 
3178
+ // bindingHandlers.html 定义在if.js
3179
+ bindingExecutors.html = function(val, elem, data) {
3180
+ val = val == null ? "" : val
3181
+ var isHtmlFilter = "group" in data
3182
+ var parent = isHtmlFilter ? elem.parentNode : elem
3183
+ if (!parent)
3184
+ return
3185
+ if (val.nodeType === 11) { //将val转换为文档碎片
3186
+ var fragment = val
3187
+ } else if (val.nodeType === 1 || val.item) {
3188
+ var nodes = val.nodeType === 1 ? val.childNodes : val.item ? val : []
3189
+ fragment = hyperspace.cloneNode(true)
3190
+ while (nodes[0]) {
3191
+ fragment.appendChild(nodes[0])
3192
+ }
3193
+ } else {
3194
+ fragment = avalon.parseHTML(val)
3195
+ }
3196
+ //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空
3197
+ var comment = DOC.createComment("ms-html")
3198
+ if (isHtmlFilter) {
3199
+ parent.insertBefore(comment, elem)
3200
+ var n = data.group, i = 1
3201
+ while (i < n) {
3202
+ var node = elem.nextSibling
3203
+ if (node) {
3204
+ parent.removeChild(node)
3205
+ i++
3206
+ }
3207
+ }
3208
+ parent.removeChild(elem)
3209
+ data.element = comment //防止被CG
3210
+ } else {
3211
+ avalon.clearHTML(parent).appendChild(comment)
3212
+ }
3213
+ if (isHtmlFilter) {
3214
+ data.group = fragment.childNodes.length || 1
3215
+ }
3216
+ nodes = avalon.slice(fragment.childNodes)
3217
+ if (nodes[0]) {
3218
+ if (comment.parentNode)
3219
+ comment.parentNode.replaceChild(fragment, comment)
3220
+ if (isHtmlFilter) {
3221
+ data.element = nodes[0]
3222
+ }
3223
+ }
3224
+ scanNodeArray(nodes, data.vmodels)
3225
+ }
3226
+
3227
+ bindingHandlers["if"] =
3228
+ bindingHandlers.data =
3229
+ bindingHandlers.text =
3230
+ bindingHandlers.html =
3231
+ function(data, vmodels) {
3232
+ parseExprProxy(data.value, vmodels, data)
3233
+ }
3234
+
3235
+ bindingExecutors["if"] = function(val, elem, data) {
3236
+ if (val) { //插回DOM树
3237
+ if (elem.nodeType === 8) {
3238
+ elem.parentNode.replaceChild(data.template, elem)
3239
+ elem = data.element = data.template //这时可能为null
3240
+ }
3241
+ if (elem.getAttribute(data.name)) {
3242
+ elem.removeAttribute(data.name)
3243
+ scanAttr(elem, data.vmodels)
3244
+ }
3245
+ data.rollback = null
3246
+ } else { //移出DOM树,并用注释节点占据原位置
3247
+ if (elem.nodeType === 1) {
3248
+ var node = data.element = DOC.createComment("ms-if")
3249
+ elem.parentNode.replaceChild(node, elem)
3250
+ data.template = elem //元素节点
3251
+ ifGroup.appendChild(elem)
3252
+ data.rollback = function() {
3253
+ if (elem.parentNode === ifGroup) {
3254
+ ifGroup.removeChild(elem)
3255
+ }
3256
+ }
3257
+ }
3258
+ }
3259
+ }
3260
+
3261
+
3262
+ //ms-important绑定已经在scanTag 方法中实现
3263
+ //ms-include绑定已由ms-attr绑定实现
3264
+
3265
+ var rdash = /\(([^)]*)\)/
3266
+ bindingHandlers.on = function(data, vmodels) {
3267
+ var value = data.value
3268
+ data.type = "on"
3269
+ var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
3270
+ if (typeof bindingHandlers.on[eventType + "Hook"] === "function") {
3271
+ bindingHandlers.on[eventType + "Hook"](data)
3272
+ }
3273
+ if (value.indexOf("(") > 0 && value.indexOf(")") > -1) {
3274
+ var matched = (value.match(rdash) || ["", ""])[1].trim()
3275
+ if (matched === "" || matched === "$event") { // aaa() aaa($event)当成aaa处理
3276
+ value = value.replace(rdash, "")
3277
+ }
3278
+ }
3279
+ parseExprProxy(value, vmodels, data)
3280
+ }
3281
+
3282
+ bindingExecutors.on = function(callback, elem, data) {
3283
+ callback = function(e) {
3284
+ var fn = data.evaluator || noop
3285
+ return fn.apply(this, data.args.concat(e))
3286
+ }
3287
+ var eventType = data.param.replace(/-\d+$/, "") // ms-on-mousemove-10
3288
+ if (eventType === "scan") {
3289
+ callback.call(elem, {
3290
+ type: eventType
3291
+ })
3292
+ } else if (typeof data.specialBind === "function") {
3293
+ data.specialBind(elem, callback)
3294
+ } else {
3295
+ var removeFn = avalon.bind(elem, eventType, callback)
3296
+ }
3297
+ data.rollback = function() {
3298
+ if (typeof data.specialUnbind === "function") {
3299
+ data.specialUnbind()
3300
+ } else {
3301
+ avalon.unbind(elem, eventType, removeFn)
3302
+ }
3303
+ }
3304
+ }
3305
+
3306
+
3306
3307
  bindingHandlers.repeat = function(data, vmodels) {
3307
3308
  var type = data.type
3308
3309
  parseExprProxy(data.value, vmodels, data, 0, 1)
@@ -3313,11 +3314,10 @@ bindingHandlers.repeat = function(data, vmodels) {
3313
3314
  var xtype = avalon.type($repeat)
3314
3315
  if (xtype !== "object" && xtype !== "array") {
3315
3316
  freturn = true
3316
- avalon.log("warning:" + data.value + "对应类型不正确")
3317
+ avalon.log("warning:" + data.value + "只能是对象或数组")
3317
3318
  }
3318
3319
  } catch (e) {
3319
3320
  freturn = true
3320
- avalon.log("warning:" + data.value + "编译出错")
3321
3321
  }
3322
3322
 
3323
3323
  var arr = data.value.split(".") || []
@@ -3659,7 +3659,7 @@ function recycleProxies(proxies, type) {
3659
3659
  proxy.$events[i].forEach(function(data) {
3660
3660
  if (typeof data === "object")
3661
3661
  disposeData(data)
3662
- })
3662
+ })// jshint ignore:line
3663
3663
  proxy.$events[i].length = 0
3664
3664
  }
3665
3665
  }
@@ -3676,6 +3676,134 @@ function recycleProxies(proxies, type) {
3676
3676
 
3677
3677
 
3678
3678
 
3679
+ /*********************************************************************
3680
+ * 各种指令 *
3681
+ **********************************************************************/
3682
+ //ms-skip绑定已经在scanTag 方法中实现
3683
+ // bindingHandlers.text 定义在if.js
3684
+ bindingExecutors.text = function(val, elem) {
3685
+ val = val == null ? "" : val //不在页面上显示undefined null
3686
+ if (elem.nodeType === 3) { //绑定在文本节点上
3687
+ try { //IE对游离于DOM树外的节点赋值会报错
3688
+ elem.data = val
3689
+ } catch (e) {
3690
+ }
3691
+ } else { //绑定在特性节点上
3692
+ elem.textContent = val
3693
+ }
3694
+ }
3695
+
3696
+ function parseDisplay(nodeName, val) {
3697
+ //用于取得此类标签的默认display值
3698
+ var key = "_" + nodeName
3699
+ if (!parseDisplay[key]) {
3700
+ var node = DOC.createElement(nodeName)
3701
+ root.appendChild(node)
3702
+ if (W3C) {
3703
+ val = getComputedStyle(node, null).display
3704
+ } else {
3705
+ val = node.currentStyle.display
3706
+ }
3707
+ root.removeChild(node)
3708
+ parseDisplay[key] = val
3709
+ }
3710
+ return parseDisplay[key]
3711
+ }
3712
+
3713
+ avalon.parseDisplay = parseDisplay
3714
+
3715
+ bindingHandlers.visible = function(data, vmodels) {
3716
+ var elem = avalon(data.element)
3717
+ var display = elem.css("display")
3718
+ if (display === "none") {
3719
+ var style = elem[0].style
3720
+ var has = /visibility/i.test(style.cssText)
3721
+ var visible = elem.css("visibility")
3722
+ style.display = ""
3723
+ style.visibility = "hidden"
3724
+ display = elem.css("display")
3725
+ if (display === "none") {
3726
+ display = parseDisplay(elem[0].nodeName)
3727
+ }
3728
+ style.visibility = has ? visible : ""
3729
+ }
3730
+ data.display = display
3731
+ parseExprProxy(data.value, vmodels, data)
3732
+ }
3733
+
3734
+ bindingExecutors.visible = function(val, elem, data) {
3735
+ elem.style.display = val ? data.display : "none"
3736
+ }
3737
+
3738
+ bindingHandlers.widget = function(data, vmodels) {
3739
+ var args = data.value.match(rword)
3740
+ var elem = data.element
3741
+ var widget = args[0]
3742
+ var id = args[1]
3743
+ if (!id || id === "$") {//没有定义或为$时,取组件名+随机数
3744
+ id = generateID(widget)
3745
+ }
3746
+ var optName = args[2] || widget//没有定义,取组件名
3747
+ var constructor = avalon.ui[widget]
3748
+ if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname"
3749
+ vmodels = elem.vmodels || vmodels
3750
+ for (var i = 0, v; v = vmodels[i++]; ) {
3751
+ if (v.hasOwnProperty(optName) && typeof v[optName] === "object") {
3752
+ var vmOptions = v[optName]
3753
+ vmOptions = vmOptions.$model || vmOptions
3754
+ break
3755
+ }
3756
+ }
3757
+ if (vmOptions) {
3758
+ var wid = vmOptions[widget + "Id"]
3759
+ if (typeof wid === "string") {
3760
+ id = wid
3761
+ }
3762
+ }
3763
+ //抽取data-tooltip-text、data-tooltip-attr属性,组成一个配置对象
3764
+ var widgetData = avalon.getWidgetData(elem, widget)
3765
+ data.value = [widget, id, optName].join(",")
3766
+ data[widget + "Id"] = id
3767
+ data.evaluator = noop
3768
+ elem.msData["ms-widget-id"] = id
3769
+ var options = data[widget + "Options"] = avalon.mix({}, constructor.defaults, vmOptions || {}, widgetData)
3770
+ elem.removeAttribute("ms-widget")
3771
+ var vmodel = constructor(elem, data, vmodels) || {} //防止组件不返回VM
3772
+ if (vmodel.$id) {
3773
+ avalon.vmodels[id] = vmodel
3774
+ createSignalTower(elem, vmodel)
3775
+ if (vmodel.hasOwnProperty("$init")) {
3776
+ vmodel.$init(function() {
3777
+ avalon.scan(elem, [vmodel].concat(vmodels))
3778
+ if (typeof options.onInit === "function") {
3779
+ options.onInit.call(elem, vmodel, options, vmodels)
3780
+ }
3781
+ })
3782
+ }
3783
+ data.rollback = function() {
3784
+ try {
3785
+ vmodel.widgetElement = null
3786
+ vmodel.$remove()
3787
+ } catch (e) {
3788
+ }
3789
+ elem.msData = {}
3790
+ delete avalon.vmodels[vmodel.$id]
3791
+ }
3792
+ addSubscribers(data, widgetList)
3793
+ if (window.chrome) {
3794
+ elem.addEventListener("DOMNodeRemovedFromDocument", function() {
3795
+ setTimeout(removeSubscribers)
3796
+ })
3797
+ }
3798
+ } else {
3799
+ avalon.scan(elem, vmodels)
3800
+ }
3801
+ } else if (vmodels.length) { //如果该组件还没有加载,那么保存当前的vmodels
3802
+ elem.vmodels = vmodels
3803
+ }
3804
+ }
3805
+ var widgetList = []
3806
+ //不存在 bindingExecutors.widget
3679
3807
  /*********************************************************************
3680
3808
  * 自带过滤器 *
3681
3809
  **********************************************************************/
@@ -3690,18 +3818,18 @@ var rsanitize = {
3690
3818
  var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
3691
3819
  var rnoalphanumeric = /([^\#-~| |!])/g;
3692
3820
 
3693
- function numberFormat(number, decimals, dec_point, thousands_sep) {
3821
+ function numberFormat(number, decimals, point, thousands) {
3694
3822
  //form http://phpjs.org/functions/number_format/
3695
3823
  //number 必需,要格式化的数字
3696
3824
  //decimals 可选,规定多少个小数位。
3697
- //dec_point 可选,规定用作小数点的字符串(默认为 . )。
3698
- //thousands_sep 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
3825
+ //point 可选,规定用作小数点的字符串(默认为 . )。
3826
+ //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
3699
3827
  number = (number + '')
3700
3828
  .replace(/[^0-9+\-Ee.]/g, '')
3701
3829
  var n = !isFinite(+number) ? 0 : +number,
3702
- prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
3703
- sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
3704
- dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
3830
+ prec = !isFinite(+decimals) ? 3 : Math.abs(decimals),
3831
+ sep = thousands || ",",
3832
+ dec = point || ".",
3705
3833
  s = '',
3706
3834
  toFixedFix = function(n, prec) {
3707
3835
  var k = Math.pow(10, prec)
@@ -3763,7 +3891,7 @@ var filters = avalon.filters = {
3763
3891
  if (reg) {
3764
3892
  a = a.replace(reg, function(s, name, value) {
3765
3893
  var quote = value.charAt(0)
3766
- return name + "=" + quote + "javascript:void(0)" + quote
3894
+ return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line
3767
3895
  })
3768
3896
  }
3769
3897
  }
@@ -3788,9 +3916,7 @@ var filters = avalon.filters = {
3788
3916
  currency: function(amount, symbol, fractionSize) {
3789
3917
  return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2)
3790
3918
  },
3791
- number: function(number, fractionSize) {
3792
- return numberFormat(number, isFinite(fractionSize) ? fractionSize : 3)
3793
- }
3919
+ number: numberFormat
3794
3920
  }
3795
3921
  /*
3796
3922
  'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
@@ -3825,7 +3951,7 @@ var filters = avalon.filters = {
3825
3951
  'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm)
3826
3952
  'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm)
3827
3953
  */
3828
- new function() {
3954
+ new function() {// jshint ignore:line
3829
3955
  function toInt(str) {
3830
3956
  return parseInt(str, 10) || 0
3831
3957
  }
@@ -4025,8 +4151,43 @@ new function() {
4025
4151
  }
4026
4152
  locate.SHORTMONTH = locate.MONTH
4027
4153
  filters.date.locate = locate
4028
- }
4154
+ }// jshint ignore:line
4155
+ /*********************************************************************
4156
+ * END *
4157
+ **********************************************************************/
4029
4158
  new function() {
4159
+ avalon.config({
4160
+ loader: false
4161
+ })
4162
+ var fns = [], fn, loaded
4163
+ function flush(f) {
4164
+ loaded = 1
4165
+ while (f = fns.shift())
4166
+ f()
4167
+ }
4168
+ if (W3C) {
4169
+ avalon.bind(DOC, "DOMContentLoaded", fn = function() {
4170
+ avalon.unbind(DOC, "DOMContentLoaded", fn)
4171
+ flush()
4172
+ })
4173
+ } else {
4174
+ var id = setInterval(function() {
4175
+ if (document.readyState === "complete" && document.body) {
4176
+ clearInterval(id)
4177
+ flush()
4178
+ }
4179
+ }, 50)
4180
+ }
4181
+ avalon.ready = function(fn) {
4182
+ loaded ? fn(avalon) : fns.push(fn)
4183
+ }
4184
+ avalon.ready(function() {
4185
+ avalon.scan(DOC.body)
4186
+ })
4187
+ }
4188
+
4189
+ new function() {// jshint ignore:line
4190
+ // http://www.cnblogs.com/yexiaochai/p/3462657.html
4030
4191
  var ua = navigator.userAgent
4031
4192
  var isAndroid = ua.indexOf("Android") > 0
4032
4193
  var isIOS = /iP(ad|hone|od)/.test(ua)
@@ -4061,83 +4222,15 @@ new function() {
4061
4222
  } else if (IE9_10touch) {
4062
4223
  touchNames = ["MSPointerDown", "MSPointerMove", "MSPointerUp", "MSPointerCancel"]
4063
4224
  }
4064
- var clickbuster = {
4065
- underFrame: null,
4066
- coordinates: [],
4067
- addUnderFrame: function(event) {
4068
- var underFrame = null
4069
- if(!clickbuster.underFrame) {
4070
- underFrame = document.createElement('div');
4071
- underFrame.style.cssText = [
4072
- "opacity: 0",
4073
- "display: none;",
4074
- "border-radius: 60px;",
4075
- "position: absolute;",
4076
- "z-index: 99999;",
4077
- "width: 60px;",
4078
- "height: 60px"
4079
- ].join("");
4080
- document.body.appendChild(underFrame)
4081
- }
4082
- clickbuster.underFrame = underFrame
4083
- underFrame.style.top = (event.changedTouches[0].clientY - 30) + "px"
4084
- underFrame.style.left = (event.changedTouches[0].clientX - 30) + "px"
4085
- underFrame.style.display = "block"
4086
- setTimeout(function(){
4087
- underFrame.style.display = "none"
4088
- }, 360)
4089
- },
4090
- preventGhostClick: function(x, y) {
4091
- clickbuster.coordinates.push(x, y);
4092
- window.setTimeout(clickbuster.pop, 2500)
4093
- },
4094
- pop: function() {
4095
- clickbuster.coordinates.splice(0, 2)
4096
- },
4097
- onClick: function(event) {
4098
- for (var i = 0; i < clickbuster.coordinates.length; i += 2) {
4099
- var x = clickbuster.coordinates[i]
4100
- var y = clickbuster.coordinates[i + 1]
4101
- if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
4102
- event.stopPropagation();
4103
- event.preventDefault();
4104
- }
4105
- }
4106
- }
4225
+ function isPrimaryTouch(event){
4226
+ return (event.pointerType === 'touch' || event.pointerType === event.MSPOINTER_TYPE_TOUCH) && event.isPrimary
4107
4227
  }
4108
4228
 
4109
- if (touchSupported) {
4110
- //我们经由ms-click, ms-on-tap绑定事件, 其实没有直接绑在元素,全部放到一个数组中
4111
- //当通过touchstart, touchend等事件混合计算得当前场景应该需要执行click, tap,那么
4112
- //直接执行el["msdispatch"]方法
4113
- "click,tap".replace(/\w+/g, function(method) {
4114
- avalon.eventHooks[method] = {
4115
- type: method,
4116
- deel: function(el, type, fn, isAdd) {
4117
- var listName = "ms" + method + "list"
4118
- var fns = el[listName] || (el[listName] = [])
4119
- if (!el["msdispatch"]) {
4120
- el["msdispatch"] = function(event) {
4121
- if (event.fireByAvalon) {
4122
- for (var i = 0, fn; fn = fns[i++]; ) {
4123
- fn.call(el, event)
4124
- }
4125
- }
4126
- }
4127
- el.addEventListener(type, el["msdispatch"])
4128
- }
4129
- if (isAdd) {
4130
- fns.push(fn)
4131
- } else {
4132
- avalon.Array.remove(fns, fn)
4133
- }
4134
- fn.unbind = true
4135
- return fn
4136
- }
4137
- }
4138
- })
4229
+ function isPointerEventType(e, type){
4230
+ return (e.type === 'pointer'+type || e.type.toLowerCase() === 'mspointer'+type)
4139
4231
  }
4140
4232
 
4233
+ var touchTimeout, longTapTimeout
4141
4234
  //判定滑动方向
4142
4235
  function swipeDirection(x1, x2, y1, y2) {
4143
4236
  return Math.abs(x1 - x2) >=
@@ -4151,31 +4244,95 @@ new function() {
4151
4244
  y: e.clientY
4152
4245
  }
4153
4246
  }
4154
-
4247
+ function fireEvent(el, name, detail) {
4248
+ var event = document.createEvent("Events")
4249
+ event.initEvent(name, true, true)
4250
+ event.fireByAvalon = true//签名,标记事件是由avalon触发
4251
+ //event.isTrusted = false 设置这个opera会报错
4252
+ if (detail)
4253
+ event.detail = detail
4254
+ el.dispatchEvent(event)
4255
+ }
4256
+ function onMouse(event) {
4257
+ if (event.fireByAvalon) {
4258
+ return true
4259
+ }
4260
+ if (event.stopImmediatePropagation) {
4261
+ event.stopImmediatePropagation()
4262
+ } else {
4263
+ event.propagationStopped = true
4264
+ }
4265
+ event.stopPropagation()
4266
+ event.preventDefault()
4267
+ }
4268
+ function cancelLongTap() {
4269
+ if (longTapTimeout) clearTimeout(longTapTimeout)
4270
+ longTapTimeout = null
4271
+ }
4272
+ function touchstart(event) {
4273
+ var _isPointerType = isPointerEventType(event, 'down'),
4274
+ firstTouch = _isPointerType ? event : event.touches[0],
4275
+ element = 'tagName' in firstTouch.target ? firstTouch.target: firstTouch.target.parentNode,
4276
+ now = Date.now(),
4277
+ delta = now - (touchProxy.last || now)
4278
+
4279
+ if (_isPointerType && !isPrimaryTouch(event)) return
4280
+
4281
+ avalon.mix(touchProxy, getCoordinates(event))
4282
+ touchProxy.mx = 0
4283
+ touchProxy.my = 0
4284
+ if (delta > 0 && delta <= 250) {
4285
+ touchProxy.isDoubleTap = true
4286
+ }
4287
+ touchProxy.last = now
4288
+ touchProxy.element = element
4289
+ /*
4290
+ 当触发hold和longtap事件时会触发touchcancel事件,从而阻止touchend事件的触发,继而保证在同时绑定tap和hold(longtap)事件时只触发其中一个事件
4291
+ */
4292
+ avalon(element).addClass(fastclick.activeClass)
4293
+ longTapTimeout = setTimeout(function() {
4294
+ longTapTimeout = null
4295
+ fireEvent(element, "hold")
4296
+ fireEvent(element, "longtap")
4297
+ touchProxy = {}
4298
+ avalon(element).removeClass(fastclick.activeClass)
4299
+ }, fastclick.clickDuration)
4300
+ return true
4301
+ }
4302
+ function touchmove(event) {
4303
+ var _isPointerType = isPointerEventType(event, 'down'),
4304
+ e = getCoordinates(event)
4305
+ if (_isPointerType && !isPrimaryTouch(event)) return
4306
+
4307
+ cancelLongTap()
4308
+ touchProxy.mx += Math.abs(touchProxy.x - e.x)
4309
+ touchProxy.my += Math.abs(touchProxy.y - e.y)
4310
+ }
4155
4311
  function touchend(event) {
4156
- var element = touchProxy.element
4157
- if (!element)
4312
+ var _isPointerType = isPointerEventType(event, 'down')
4313
+ element = touchProxy.element
4314
+
4315
+ if (_isPointerType && !isPrimaryTouch(event)) return
4316
+
4317
+ if (!element) {
4158
4318
  return
4319
+ }
4320
+ cancelLongTap()
4159
4321
  var e = getCoordinates(event)
4160
- var diff = Date.now() - touchProxy.startTime //经过时间
4161
4322
  var totalX = Math.abs(touchProxy.x - e.x)
4162
4323
  var totalY = Math.abs(touchProxy.y - e.y)
4163
- if (touchProxy.doubleIndex === 2 && avalon.config.stopZoom) {//如果已经点了两次,就可以触发dblclick 回调
4164
- event.preventDefault()
4165
- }
4166
4324
  if (totalX > 30 || totalY > 30) {
4167
4325
  //如果用户滑动的距离有点大,就认为是swipe事件
4168
4326
  var direction = swipeDirection(touchProxy.x, e.x, touchProxy.y, e.y)
4169
4327
  var details = {
4170
4328
  direction: direction
4171
4329
  }
4172
- W3CFire(element, "swipe", details)
4173
- W3CFire(element, "swipe" + direction, details)
4330
+ fireEvent(element, "swipe", details)
4331
+ fireEvent(element, "swipe" + direction, details)
4332
+ avalon(element).removeClass(fastclick.activeClass)
4333
+ touchProxy = {}
4174
4334
  } else {
4175
- //如果移动的距离太少,则认为是tap,click,hold,dblclick
4176
- if (fastclick.canClick(element) &&
4177
- touchProxy.mx < fastclick.dragDistance &&
4178
- touchProxy.my < fastclick.dragDistance) {
4335
+ if (fastclick.canClick(element) && touchProxy.mx < fastclick.dragDistance && touchProxy.my < fastclick.dragDistance) {
4179
4336
  // 失去焦点的处理
4180
4337
  if (document.activeElement && document.activeElement !== element) {
4181
4338
  document.activeElement.blur()
@@ -4190,92 +4347,39 @@ new function() {
4190
4347
  } else {
4191
4348
  fastclick.focus(element)
4192
4349
  }
4193
- avalon.fastclick.fireEvent(element, "click", event)//触发click事件
4194
- W3CFire(element, "tap")//触发tap事件
4195
- if (forElement) {
4196
- avalon.fastclick.fireEvent(forElement, "click", event)
4197
- W3CFire(element, "tap")//触发tap事件
4198
- }
4199
- if (diff < 250) {
4200
- if (touchProxy.doubleIndex == 2) {
4201
- avalon.fastclick.fireEvent(element, "dblclick", event)//触发dblclick事件
4202
- W3CFire(element, "doubletap")//触发doubletap事件
4203
- touchProxy.doubleIndex = 0
4204
- } else {
4205
- setTimeout(function() {
4206
- touchProxy.doubleIndex = 0
4207
- }, 250)
4208
- }
4209
- }
4210
-
4211
- if (diff > fastclick.clickDuration) {
4212
- W3CFire(element, "hold")
4213
- W3CFire(element, "longtap")
4214
- touchProxy.doubleIndex = 0
4350
+ event.preventDefault()
4351
+ fireEvent(element, 'tap')
4352
+ avalon.fastclick.fireEvent(element, "click", event)
4353
+ avalon(element).removeClass(fastclick.activeClass)
4354
+ if (touchProxy.isDoubleTap) {
4355
+ fireEvent(element, "doubletap")
4356
+ avalon.fastclick.fireEvent(element, "dblclick", event)
4357
+ touchProxy = {}
4358
+ avalon(element).removeClass(fastclick.activeClass)
4359
+ } else {
4360
+ touchTimeout = setTimeout(function() {
4361
+ clearTimeout(touchTimeout)
4362
+ touchTimeout = null
4363
+ touchProxy = {}
4364
+ avalon(element).removeClass(fastclick.activeClass)
4365
+ }, 250)
4215
4366
  }
4216
4367
  }
4217
4368
  }
4218
- avalon(element).removeClass(fastclick.activeClass)
4219
- touchProxy.element = null
4220
4369
  }
4221
- // 避免被点击的元素下有input、textarea时会触发其focus从而调出键盘
4222
- // http://hzxiaosheng.github.io/work/2014/09/13/click-event-300ms-delay-and-ghost-click-in-mobile-browser/
4223
- document.addEventListener('click', clickbuster.onClick, true)
4224
- document.addEventListener(touchNames[1], function(event) {
4225
- if (!touchProxy.element)
4226
- return
4227
- var e = getCoordinates(event)
4228
- touchProxy.mx += Math.abs(touchProxy.cx - e.x)
4229
- touchProxy.my += Math.abs(touchProxy.cy - e.y)
4230
- if (touchProxy.tapping && (touchProxy.mx > fastclick.dragDistance || touchProxy.my > fastclick.dragDistance)) {
4231
- touchProxy.element = null
4232
- }
4233
- })
4234
-
4370
+ document.addEventListener('mousedown', onMouse, true)
4371
+ document.addEventListener('click', onMouse, true)
4372
+ document.addEventListener(touchNames[0], touchstart)
4373
+ document.addEventListener(touchNames[1], touchmove)
4235
4374
  document.addEventListener(touchNames[2], touchend)
4236
4375
  if (touchNames[3]) {
4237
- document.addEventListener(touchNames[3], touchend)
4238
- }
4239
- me["clickHook"] = function(data) {
4240
- function touchstart(event) {
4241
- var element = data.element
4242
- avalon.mix(touchProxy, getCoordinates(event))
4243
- touchProxy.cx = touchProxy.x
4244
- touchProxy.cy = touchProxy.y
4245
- touchProxy.mx = 0 //计算总结移动了多少距离,指在移动距离重分
4246
- touchProxy.my = 0
4247
- // touchProxy.startTime = Date.now()
4248
- touchProxy.event = data.param
4249
- touchProxy.tapping = /click|tap|hold$/.test(touchProxy.event)
4250
-
4251
- //--------------处理双击事件--------------
4252
- if (!touchProxy.doubleIndex) {
4253
- touchProxy.doubleIndex = 1
4254
- touchProxy.startTime = Date.now()
4255
- } else {
4256
- touchProxy.doubleIndex = 2
4257
- }
4258
- touchProxy.element = element
4259
- if (touchProxy.tapping && avalon.fastclick.canClick(element)) {
4260
- avalon(element).addClass(fastclick.activeClass)
4261
- }
4262
- }
4263
- function needFixClick(type) {
4264
- return type === "click"
4265
- }
4266
- if (needFixClick(data.param) ? touchSupported : true) {
4267
- data.specialBind = function(element, callback) {
4268
- element.addEventListener(touchNames[0], touchstart)
4269
- data.msCallback = callback
4270
- avalon.bind(element, data.param, callback)
4271
- }
4272
- data.specialUnbind = function() {
4273
- element.removeEventListener(touchNames[0], touchstart)
4274
- avalon.bind(data.element, data.param, data.msCallback)
4275
- }
4276
- }
4376
+ document.addEventListener(touchNames[3], function(event) {
4377
+ if (longTapTimeout) clearTimeout(longTapTimeout)
4378
+ if (touchTimeout) clearTimeout(touchTimeout)
4379
+ longTapTimeout = touchTimeout = null
4380
+ touchProxy = {}
4381
+ })
4277
4382
  }
4278
-
4279
4383
  //fastclick只要是处理移动端点击存在300ms延迟的问题
4280
4384
  //这是苹果乱搞异致的,他们想在小屏幕设备上通过快速点击两次,将放大了的网页缩放至原始比例。
4281
4385
  var fastclick = avalon.fastclick = {
@@ -4343,63 +4447,9 @@ new function() {
4343
4447
  ["swipe", "swipeleft", "swiperight", "swipeup", "swipedown", "doubletap", "tap", "dblclick", "longtap", "hold"].forEach(function(method) {
4344
4448
  me[method + "Hook"] = me["clickHook"]
4345
4449
  })
4346
- if (touchSupported) {
4347
- me[touchNames[2] + "Hook"] = function(data) {
4348
- data.specialBind = function(element, callback) {
4349
- var _callback = callback
4350
- if (element.hasAttribute('avoidFocus')) {
4351
- _callback = function(event) {
4352
- var e = getCoordinates(event)
4353
- clickbuster.preventGhostClick(e.x, e.y)
4354
- clickbuster.addUnderFrame(event)
4355
- callback.apply(this, [].slice.call(arguments))
4356
- }
4357
- }
4358
- data.msCallback = _callback
4359
- avalon.bind(element, data.param, _callback)
4360
- }
4361
- data.specialUnbind = function() {
4362
- avalon.unbind(data.element, data.param, data.msCallback)
4363
- }
4364
- }
4365
- }
4366
- //各种摸屏事件的示意图 http://quojs.tapquo.com/ http://touch.code.baidu.com/
4367
- }
4368
-
4369
- /*********************************************************************
4370
- * END *
4371
- **********************************************************************/
4372
- new function() {
4373
- avalon.config({
4374
- loader: false
4375
- })
4376
- var fns = [], fn, loaded
4377
- function flush(f) {
4378
- loaded = 1
4379
- while (f = fns.shift())
4380
- f()
4381
- }
4382
- if (W3C) {
4383
- avalon.bind(DOC, "DOMContentLoaded", fn = function() {
4384
- avalon.unbind(DOC, "DOMContentLoaded", fn)
4385
- flush()
4386
- })
4387
- } else {
4388
- var id = setInterval(function() {
4389
- if (document.readyState === "complete" && document.body) {
4390
- clearInterval(id)
4391
- flush()
4392
- }
4393
- }, 50)
4394
- }
4395
- avalon.ready = function(fn) {
4396
- loaded ? fn(avalon) : fns.push(fn)
4397
- }
4398
- avalon.ready(function() {
4399
- avalon.scan(DOC.body)
4400
- })
4401
- }
4402
4450
 
4451
+ //各种摸屏事件的示意图 http://quojs.tapquo.com/ http://touch.code.baidu.com/
4452
+ }// jshint ignore:line
4403
4453
 
4404
4454
  // Register as a named AMD module, since avalon can be concatenated with other
4405
4455
  // files that may use define, but not via a proper concatenation script that
@@ -4422,15 +4472,15 @@ new function() {
4422
4472
  var _avalon = window.avalon
4423
4473
  avalon.noConflict = function(deep) {
4424
4474
  if (deep && window.avalon === avalon) {
4425
- window.avalon = avalon
4475
+ window.avalon = _avalon
4426
4476
  }
4427
4477
  return avalon
4428
4478
  }
4429
- // Expose avalon and $ identifiers, even in AMD
4479
+ // Expose avalon identifiers, even in AMD
4430
4480
  // and CommonJS for browser emulators
4431
4481
  if (noGlobal === void 0) {
4432
4482
  window.avalon = avalon
4433
4483
  }
4434
4484
  return avalon
4435
4485
 
4436
- }));
4486
+ }));