avalon-rails 1.3.9.1.20150212184918 → 1.4.1.1.20150404164109

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ }));