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,7 +5,7 @@
5
5
  http://weibo.com/jslouvre/
6
6
 
7
7
  Released under the MIT license
8
- avalon.shim.js(无加载器版本) 1.391 built in 2015.2.12
8
+ avalon.shim.js(无加载器版本) 1.41 built in 2015.4.4
9
9
  support IE6+ and other browsers
10
10
  ==================================================*/
11
11
  (function(global, factory) {
@@ -53,6 +53,7 @@ function log() {
53
53
  var subscribers = "$" + expose
54
54
  var otherRequire = window.require
55
55
  var otherDefine = window.define
56
+ var innerRequire
56
57
  var stopRepeatAssign = false
57
58
  var rword = /[^, ]+/g //切割字符串为一个个小块,以空格或豆号分开它们,结合replace实现字符串的forEach
58
59
  var rcomplexType = /^(?:object|array)$/
@@ -90,16 +91,6 @@ function oneObject(array, val) {
90
91
  return result
91
92
  }
92
93
 
93
- function createCache(maxLength) {
94
- var keys = []
95
- function cache(key, value) {
96
- if (keys.push(key) > maxLength) {
97
- delete cache[keys.shift()]
98
- }
99
- return cache[key] = value;
100
- }
101
- return cache;
102
- }
103
94
  //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
104
95
  var generateID = function(prefix) {
105
96
  prefix = prefix || "avalon"
@@ -114,13 +105,60 @@ function IE() {
114
105
  }
115
106
  }
116
107
  var IEVersion = IE()
117
- /*********************************************************************
118
- * avalon的静态方法定义区 *
119
- **********************************************************************/
108
+
120
109
  avalon = function(el) { //创建jQuery式的无new 实例化结构
121
110
  return new avalon.init(el)
122
111
  }
123
112
 
113
+ /*视浏览器情况采用最快的异步回调*/
114
+ avalon.nextTick = new function() {// jshint ignore:line
115
+ var tickImmediate = window.setImmediate
116
+ var tickObserver = window.MutationObserver
117
+ var tickPost = W3C && window.postMessage
118
+ if (tickImmediate) {
119
+ return tickImmediate.bind(window)
120
+ }
121
+
122
+ var queue = []
123
+ function callback() {
124
+ var n = queue.length
125
+ for (var i = 0; i < n; i++) {
126
+ queue[i]()
127
+ }
128
+ queue = queue.slice(n)
129
+ }
130
+
131
+ if (tickObserver) {
132
+ var node = document.createTextNode("avalon")
133
+ new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line
134
+ return function(fn) {
135
+ queue.push(fn)
136
+ node.data = Math.random()
137
+ }
138
+ }
139
+
140
+ if (tickPost) {
141
+ window.addEventListener("message", function(e) {
142
+ var source = e.source
143
+ if ((source === window || source === null) && e.data === "process-tick") {
144
+ e.stopPropagation()
145
+ callback()
146
+ }
147
+ })
148
+
149
+ return function(fn) {
150
+ queue.push(fn)
151
+ window.postMessage('process-tick', '*')
152
+ }
153
+ }
154
+
155
+ return function(fn) {
156
+ setTimeout(fn, 0)
157
+ }
158
+ }// jshint ignore:line
159
+ /*********************************************************************
160
+ * avalon的静态方法定义区 *
161
+ **********************************************************************/
124
162
  avalon.init = function(el) {
125
163
  this[0] = this.element = el
126
164
  }
@@ -152,7 +190,7 @@ avalon.isWindow = function(obj) {
152
190
  return false
153
191
  // 利用IE678 window == document为true,document == window竟然为false的神奇特性
154
192
  // 标准浏览器及IE9,IE10等使用 正则检测
155
- return obj == obj.document && obj.document != obj
193
+ return obj == obj.document && obj.document != obj //jshint ignore:line
156
194
  }
157
195
 
158
196
  function isWindow(obj) {
@@ -185,7 +223,7 @@ avalon.isPlainObject = function(obj, key) {
185
223
  }
186
224
  for (key in obj) {
187
225
  }
188
- return key === void 0 || ohasOwn.call(obj, key);
226
+ return key === void 0 || ohasOwn.call(obj, key)
189
227
  }
190
228
  if (rnative.test(Object.getPrototypeOf)) {
191
229
  avalon.isPlainObject = function(obj) {
@@ -261,7 +299,7 @@ function _number(a, len) { //用于模拟slice, splice的效果
261
299
  avalon.mix({
262
300
  rword: rword,
263
301
  subscribers: subscribers,
264
- version: 1.391,
302
+ version: 1.41,
265
303
  ui: {},
266
304
  log: log,
267
305
  slice: W3C ? function(nodes, start, end) {
@@ -283,7 +321,7 @@ avalon.mix({
283
321
  noop: noop,
284
322
  /*如果不用Error对象封装一下,str在控制台下可能会乱码*/
285
323
  error: function(str, e) {
286
- throw new (e || Error)(str)
324
+ throw (e || Error)(str)
287
325
  },
288
326
  /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/
289
327
  oneObject: oneObject,
@@ -320,7 +358,7 @@ avalon.mix({
320
358
  if (typeof hook === "object") {
321
359
  type = hook.type
322
360
  if (hook.deel) {
323
- fn = hook.deel(el, fn)
361
+ fn = hook.deel(el, type, fn, phase)
324
362
  }
325
363
  }
326
364
  var callback = W3C ? fn : function(e) {
@@ -340,6 +378,9 @@ avalon.mix({
340
378
  var callback = fn || noop
341
379
  if (typeof hook === "object") {
342
380
  type = hook.type
381
+ if (hook.deel) {
382
+ fn = hook.deel(el, type, fn, false)
383
+ }
343
384
  }
344
385
  if (W3C) {
345
386
  el.removeEventListener(type, callback, !!phase)
@@ -451,24 +492,94 @@ function isArrayLike(obj) {
451
492
  }
452
493
  return false
453
494
  }
454
- /*视浏览器情况采用最快的异步回调(在avalon.ready里,还有一个分支,用于处理IE6-9)*/
455
- avalon.nextTick = window.setImmediate ? setImmediate.bind(window) : function(callback) {
456
- setTimeout(callback, 0) //IE10-11 or W3C
457
- }
495
+
496
+
497
+ // https://github.com/rsms/js-lru
498
+ var Cache = new function() {// jshint ignore:line
499
+ function LRU(maxLength) {
500
+ this.size = 0
501
+ this.limit = maxLength
502
+ this.head = this.tail = void 0
503
+ this._keymap = {}
504
+ }
505
+
506
+ var p = LRU.prototype
507
+
508
+ p.put = function(key, value) {
509
+ var entry = {
510
+ key: key,
511
+ value: value
512
+ }
513
+ this._keymap[key] = entry
514
+ if (this.tail) {
515
+ this.tail.newer = entry
516
+ entry.older = this.tail
517
+ } else {
518
+ this.head = entry
519
+ }
520
+ this.tail = entry
521
+ if (this.size === this.limit) {
522
+ this.shift()
523
+ } else {
524
+ this.size++
525
+ }
526
+ return value
527
+ }
528
+
529
+ p.shift = function() {
530
+ var entry = this.head
531
+ if (entry) {
532
+ this.head = this.head.newer
533
+ this.head.older =
534
+ entry.newer =
535
+ entry.older =
536
+ this._keymap[entry.key] = void 0
537
+ }
538
+ }
539
+ p.get = function(key) {
540
+ var entry = this._keymap[key]
541
+ if (entry === void 0)
542
+ return
543
+ if (entry === this.tail) {
544
+ return entry.value
545
+ }
546
+ // HEAD--------------TAIL
547
+ // <.older .newer>
548
+ // <--- add direction --
549
+ // A B C <D> E
550
+ if (entry.newer) {
551
+ if (entry === this.head) {
552
+ this.head = entry.newer
553
+ }
554
+ entry.newer.older = entry.older // C <-- E.
555
+ }
556
+ if (entry.older) {
557
+ entry.older.newer = entry.newer // C. --> E
558
+ }
559
+ entry.newer = void 0 // D --x
560
+ entry.older = this.tail // D. --> E
561
+ if (this.tail) {
562
+ this.tail.newer = entry // E. <-- D
563
+ }
564
+ this.tail = entry
565
+ return entry.value
566
+ }
567
+ return LRU
568
+ }// jshint ignore:line
458
569
 
459
570
  /*********************************************************************
460
571
  * javascript 底层补丁 *
461
572
  **********************************************************************/
462
573
  if (!"司徒正美".trim) {
463
574
  var rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g
464
- String.prototype.trim = function() {
575
+ String.prototype.trim = function () {
465
576
  return this.replace(rtrim, "")
466
577
  }
467
578
  }
468
579
  var hasDontEnumBug = !({
469
580
  'toString': null
470
581
  }).propertyIsEnumerable('toString'),
471
- hasProtoEnumBug = (function() {
582
+ hasProtoEnumBug = (function () {
472
583
  }).propertyIsEnumerable('prototype'),
473
584
  dontEnums = [
474
585
  "toString",
@@ -481,7 +592,7 @@ var hasDontEnumBug = !({
481
592
  ],
482
593
  dontEnumsLength = dontEnums.length;
483
594
  if (!Object.keys) {
484
- Object.keys = function(object) { //ecma262v5 15.2.3.14
595
+ Object.keys = function (object) { //ecma262v5 15.2.3.14
485
596
  var theKeys = [];
486
597
  var skipProto = hasProtoEnumBug && typeof object === "function"
487
598
  if (typeof object === "string" || (object && object.callee)) {
@@ -510,18 +621,18 @@ if (!Object.keys) {
510
621
  }
511
622
  }
512
623
  if (!Array.isArray) {
513
- Array.isArray = function(a) {
624
+ Array.isArray = function (a) {
514
625
  return serialize.call(a) === "[object Array]"
515
626
  }
516
627
  }
517
628
 
518
629
  if (!noop.bind) {
519
- Function.prototype.bind = function(scope) {
630
+ Function.prototype.bind = function (scope) {
520
631
  if (arguments.length < 2 && scope === void 0)
521
632
  return this
522
633
  var fn = this,
523
634
  argv = arguments
524
- return function() {
635
+ return function () {
525
636
  var args = [],
526
637
  i
527
638
  for (i = 1; i < argv.length; i++)
@@ -535,12 +646,14 @@ if (!noop.bind) {
535
646
 
536
647
  function iterator(vars, body, ret) {
537
648
  var fun = 'for(var ' + vars + 'i=0,n = this.length; i < n; i++){' + body.replace('_', '((i in this) && fn.call(scope,this[i],i,this))') + '}' + ret
649
+ /* jshint ignore:start */
538
650
  return Function("fn,scope", fun)
651
+ /* jshint ignore:end */
539
652
  }
540
653
  if (!rnative.test([].map)) {
541
654
  avalon.mix(ap, {
542
655
  //定位操作,返回数组中第一个等于给定参数的元素的索引值。
543
- indexOf: function(item, index) {
656
+ indexOf: function (item, index) {
544
657
  var n = this.length,
545
658
  i = ~~index
546
659
  if (i < 0)
@@ -551,7 +664,7 @@ if (!rnative.test([].map)) {
551
664
  return -1
552
665
  },
553
666
  //定位操作,同上,不过是从后遍历。
554
- lastIndexOf: function(item, index) {
667
+ lastIndexOf: function (item, index) {
555
668
  var n = this.length,
556
669
  i = index == null ? n - 1 : index
557
670
  if (i < 0)
@@ -581,22 +694,16 @@ function fixContains(root, el) {
581
694
  try { //IE6-8,游离于DOM树外的文本节点,访问parentNode有时会抛错
582
695
  while ((el = el.parentNode))
583
696
  if (el === root)
584
- return true;
697
+ return true
585
698
  return false
586
699
  } catch (e) {
587
700
  return false
588
701
  }
589
702
  }
590
703
  avalon.contains = fixContains
591
- //safari5+是把contains方法放在Element.prototype上而不是Node.prototype
592
- if (!root.contains) {
593
- Node.prototype.contains = function(arg) {
594
- return !!(this.compareDocumentPosition(arg) & 16)
595
- }
596
- }
597
704
  //IE6-11的文档对象没有contains
598
705
  if (!DOC.contains) {
599
- DOC.contains = function(b) {
706
+ DOC.contains = function (b) {
600
707
  return fixContains(DOC, b)
601
708
  }
602
709
  }
@@ -605,22 +712,27 @@ function outerHTML() {
605
712
  return new XMLSerializer().serializeToString(this)
606
713
  }
607
714
 
608
-
609
715
  if (window.SVGElement) {
716
+ //safari5+是把contains方法放在Element.prototype上而不是Node.prototype
717
+ if (!DOC.createTextNode("x").contains) {
718
+ Node.prototype.contains = function (arg) {//IE6-8没有Node对象
719
+ return !!(this.compareDocumentPosition(arg) & 16)
720
+ }
721
+ }
610
722
  var svgns = "http://www.w3.org/2000/svg"
611
723
  var svg = DOC.createElementNS(svgns, "svg")
612
724
  svg.innerHTML = '<circle cx="50" cy="50" r="40" fill="red" />'
613
725
  if (!rsvg.test(svg.firstChild)) { // #409
614
- function enumerateNode(node, targetNode) {
726
+ function enumerateNode(node, targetNode) {// jshint ignore:line
615
727
  if (node && node.childNodes) {
616
728
  var nodes = node.childNodes
617
729
  for (var i = 0, el; el = nodes[i++]; ) {
618
730
  if (el.tagName) {
619
731
  var svg = DOC.createElementNS(svgns,
620
732
  el.tagName.toLowerCase())
621
- ap.forEach.call(el.attributes, function(attr) {
733
+ ap.forEach.call(el.attributes, function (attr) {
622
734
  svg.setAttribute(attr.name, attr.value) //复制属性
623
- })
735
+ })// jshint ignore:line
624
736
  // 递归处理子节点
625
737
  enumerateNode(el, svg)
626
738
  targetNode.appendChild(svg)
@@ -633,7 +745,7 @@ if (window.SVGElement) {
633
745
  enumerable: true,
634
746
  configurable: true,
635
747
  get: outerHTML,
636
- set: function(html) {
748
+ set: function (html) {
637
749
  var tagName = this.tagName.toLowerCase(),
638
750
  par = this.parentNode,
639
751
  frag = avalon.parseHTML(html)
@@ -652,13 +764,13 @@ if (window.SVGElement) {
652
764
  "innerHTML": {
653
765
  enumerable: true,
654
766
  configurable: true,
655
- get: function() {
767
+ get: function () {
656
768
  var s = this.outerHTML
657
769
  var ropen = new RegExp("<" + this.nodeName + '\\b(?:(["\'])[^"]*?(\\1)|[^>])*>', "i")
658
770
  var rclose = new RegExp("<\/" + this.nodeName + ">$", "i")
659
771
  return s.replace(ropen, "").replace(rclose, "")
660
772
  },
661
- set: function(html) {
773
+ set: function (html) {
662
774
  if (avalon.clearHTML) {
663
775
  avalon.clearHTML(this)
664
776
  var frag = avalon.parseHTML(html)
@@ -693,26 +805,26 @@ function fixEvent(event) {
693
805
  }
694
806
  ret.timeStamp = new Date() - 0
695
807
  ret.originalEvent = event
696
- ret.preventDefault = function() { //阻止默认行为
808
+ ret.preventDefault = function () { //阻止默认行为
697
809
  event.returnValue = false
698
810
  }
699
- ret.stopPropagation = function() { //阻止事件在DOM树中的传播
811
+ ret.stopPropagation = function () { //阻止事件在DOM树中的传播
700
812
  event.cancelBubble = true
701
813
  }
702
814
  return ret
703
815
  }
704
816
 
705
- var eventHooks = avalon.eventHooks
817
+ var eventHooks = avalon.eventHooks
706
818
  //针对firefox, chrome修正mouseenter, mouseleave
707
819
  if (!("onmouseenter" in root)) {
708
820
  avalon.each({
709
821
  mouseenter: "mouseover",
710
822
  mouseleave: "mouseout"
711
- }, function(origType, fixType) {
823
+ }, function (origType, fixType) {
712
824
  eventHooks[origType] = {
713
825
  type: fixType,
714
- deel: function(elem, fn) {
715
- return function(e) {
826
+ deel: function (elem, _, fn) {
827
+ return function (e) {
716
828
  var t = e.relatedTarget
717
829
  if (!t || (t !== elem && !(elem.compareDocumentPosition(t) & 16))) {
718
830
  delete e.type
@@ -728,7 +840,7 @@ if (!("onmouseenter" in root)) {
728
840
  avalon.each({
729
841
  AnimationEvent: "animationend",
730
842
  WebKitAnimationEvent: "webkitAnimationEnd"
731
- }, function(construct, fixType) {
843
+ }, function (construct, fixType) {
732
844
  if (window[construct] && !eventHooks.animationend) {
733
845
  eventHooks.animationend = {
734
846
  type: fixType
@@ -739,8 +851,8 @@ avalon.each({
739
851
  if (!("oninput" in DOC.createElement("input"))) {
740
852
  eventHooks.input = {
741
853
  type: "propertychange",
742
- deel: function(elem, fn) {
743
- return function(e) {
854
+ deel: function (elem, _, fn) {
855
+ return function (e) {
744
856
  if (e.propertyName === "value") {
745
857
  e.type = "input"
746
858
  return fn.call(elem, e)
@@ -759,8 +871,8 @@ if (DOC.onmousewheel === void 0) {
759
871
  var fixWheelDelta = fixWheelType === "wheel" ? "deltaY" : "detail"
760
872
  eventHooks.mousewheel = {
761
873
  type: fixWheelType,
762
- deel: function(elem, fn) {
763
- return function(e) {
874
+ deel: function (elem, _, fn) {
875
+ return function (e) {
764
876
  e.wheelDeltaY = e.wheelDelta = e[fixWheelDelta] > 0 ? -120 : 120
765
877
  e.wheelDeltaX = 0
766
878
  if (Object.defineProperty) {
@@ -801,13 +913,14 @@ function escapeRegExp(target) {
801
913
  //将字符串安全格式化为正则表达式的源码
802
914
  return (target + "").replace(rregexp, "\\$&")
803
915
  }
804
- var innerRequire = noop
916
+
805
917
  var plugins = {
806
- loader: function(builtin) {
807
- window.define = builtin ? innerRequire.define : otherDefine
808
- window.require = builtin ? innerRequire : otherRequire
918
+ loader: function (builtin) {
919
+ var flag = innerRequire && builtin
920
+ window.require = flag ? innerRequire : otherRequire
921
+ window.define = flag ? innerRequire.define : otherDefine
809
922
  },
810
- interpolate: function(array) {
923
+ interpolate: function (array) {
811
924
  openTag = array[0]
812
925
  closeTag = array[1]
813
926
  if (openTag === closeTag) {
@@ -837,11 +950,25 @@ kernel.paths = {}
837
950
  kernel.shim = {}
838
951
  kernel.maxRepeatSize = 100
839
952
  avalon.config = kernel
953
+ var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
954
+ var findNodes = DOC.querySelectorAll ? function(str) {
955
+ return DOC.querySelectorAll(str)
956
+ } : function(str) {
957
+ var match = str.match(ravalon)
958
+ var all = DOC.getElementsByTagName(match[1])
959
+ var nodes = []
960
+ for (var i = 0, el; el = all[i++]; ) {
961
+ if (el.getAttribute(match[2]) === match[3]) {
962
+ nodes.push(el)
963
+ }
964
+ }
965
+ return nodes
966
+ }
840
967
  /*********************************************************************
841
968
  * 事件总线 *
842
969
  **********************************************************************/
843
970
  var EventBus = {
844
- $watch: function(type, callback) {
971
+ $watch: function (type, callback) {
845
972
  if (typeof callback === "function") {
846
973
  var callbacks = this.$events[type]
847
974
  if (callbacks) {
@@ -854,7 +981,7 @@ var EventBus = {
854
981
  }
855
982
  return this
856
983
  },
857
- $unwatch: function(type, callback) {
984
+ $unwatch: function (type, callback) {
858
985
  var n = arguments.length
859
986
  if (n === 0) { //让此VM的所有$watch回调无效化
860
987
  this.$watch.backup = this.$events
@@ -872,13 +999,15 @@ var EventBus = {
872
999
  }
873
1000
  return this
874
1001
  },
875
- $fire: function(type) {
1002
+ $fire: function (type) {
876
1003
  var special, i, v, callback
877
1004
  if (/^(\w+)!(\S+)$/.test(type)) {
878
1005
  special = RegExp.$1
879
1006
  type = RegExp.$2
880
1007
  }
881
1008
  var events = this.$events
1009
+ if (!events)
1010
+ return
882
1011
  var args = aslice.call(arguments, 1)
883
1012
  var detail = [type].concat(args)
884
1013
  if (special === "all") {
@@ -901,22 +1030,23 @@ var EventBus = {
901
1030
  continue
902
1031
  }
903
1032
  //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识
904
- Array.prototype.forEach.call(eventNodes, function(node) {
905
- Array.prototype.forEach.call(elements, function(element) {
1033
+ /* jshint ignore:start */
1034
+ Array.prototype.forEach.call(eventNodes, function (node) {
1035
+ Array.prototype.forEach.call(elements, function (element) {
906
1036
  var ok = special === "down" ? element.contains(node) : //向下捕获
907
1037
  node.contains(element) //向上冒泡
908
-
909
1038
  if (ok) {
910
1039
  node._avalon = v //符合条件的加一个标识
911
1040
  }
912
1041
  });
913
1042
  })
1043
+ /* jshint ignore:end */
914
1044
  }
915
1045
  }
916
1046
  }
917
1047
  var nodes = DOC.getElementsByTagName("*") //实现节点排序
918
1048
  var alls = []
919
- Array.prototype.forEach.call(nodes, function(el) {
1049
+ Array.prototype.forEach.call(nodes, function (el) {
920
1050
  if (el._avalon) {
921
1051
  alls.push(el._avalon)
922
1052
  el._avalon = ""
@@ -946,26 +1076,12 @@ var EventBus = {
946
1076
  }
947
1077
  }
948
1078
 
949
- var ravalon = /(\w+)\[(avalonctrl)="(\S+)"\]/
950
- var findNodes = DOC.querySelectorAll ? function(str) {
951
- return DOC.querySelectorAll(str)
952
- } : function(str) {
953
- var match = str.match(ravalon)
954
- var all = DOC.getElementsByTagName(match[1])
955
- var nodes = []
956
- for (var i = 0, el; el = all[i++]; ) {
957
- if (el.getAttribute(match[2]) === match[3]) {
958
- nodes.push(el)
959
- }
960
- }
961
- return nodes
962
- }
963
1079
  /*********************************************************************
964
1080
  * modelFactory *
965
1081
  **********************************************************************/
966
1082
  //avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM)
967
1083
  var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里
968
- avalon.define = function(id, factory) {
1084
+ avalon.define = function (id, factory) {
969
1085
  var $id = id.$id || id
970
1086
  if (!$id) {
971
1087
  log("warning: vm必须指定$id")
@@ -1052,14 +1168,12 @@ function modelFactory(source, $special, $model) {
1052
1168
  if (Array.isArray(source)) {
1053
1169
  var arr = source.concat()
1054
1170
  source.length = 0
1055
- var collection = Collection(source)
1171
+ var collection = Collection(source)// jshint ignore:line
1056
1172
  collection.pushArray(arr)
1057
1173
  return collection
1058
1174
  }
1059
- if (typeof source.nodeType === "number") {
1060
- return source
1061
- }
1062
- if (source.$id && source.$events) { //fix IE6-8 createWithProxy $val: val引发的BUG
1175
+ //0 null undefined || Node || VModel(fix IE6-8 createWithProxy $val: val引发的BUG)
1176
+ if (!source || source.nodeType > 0 || (source.$id && source.$events)) {
1063
1177
  return source
1064
1178
  }
1065
1179
  if (!Array.isArray(source.$skipArray)) {
@@ -1072,7 +1186,7 @@ function modelFactory(source, $special, $model) {
1072
1186
  var watchedProperties = {} //监控属性
1073
1187
  var initCallbacks = [] //初始化才执行的函数
1074
1188
  for (var i in source) {
1075
- (function(name, val) {
1189
+ (function (name, val) {
1076
1190
  $model[name] = val
1077
1191
  if (!isObservable(name, val, source.$skipArray)) {
1078
1192
  return //过滤所有非监控属性
@@ -1080,7 +1194,7 @@ function modelFactory(source, $special, $model) {
1080
1194
  //总共产生三种accessor
1081
1195
  $events[name] = []
1082
1196
  var valueType = avalon.type(val)
1083
- var accessor = function(newValue) {
1197
+ var accessor = function (newValue) {
1084
1198
  var name = accessor._name
1085
1199
  var $vmodel = this
1086
1200
  var $model = $vmodel.$model
@@ -1099,19 +1213,8 @@ function modelFactory(source, $special, $model) {
1099
1213
  }
1100
1214
  if (!isEqual(oldValue, newValue)) {
1101
1215
  $model[name] = newValue
1102
- if ($events.$digest) {
1103
- if (!accessor.pedding) {
1104
- accessor.pedding = true
1105
- setTimeout(function() {
1106
- notifySubscribers($events[name]) //同步视图
1107
- safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
1108
- accessor.pedding = false
1109
- })
1110
- }
1111
- } else {
1112
- notifySubscribers($events[name]) //同步视图
1113
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1114
- }
1216
+ notifySubscribers($events[name]) //同步视图
1217
+ safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1115
1218
  }
1116
1219
  } else {
1117
1220
  if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性
@@ -1120,17 +1223,7 @@ function modelFactory(source, $special, $model) {
1120
1223
  if (oldValue !== newValue) {
1121
1224
  $model[name] = newValue
1122
1225
  //这里不用同步视图
1123
- if ($events.$digest) {
1124
- if (!accessor.pedding) {
1125
- accessor.pedding = true
1126
- setTimeout(function() {
1127
- safeFire($vmodel, name, $model[name], oldValue) //触发$watch回调
1128
- accessor.pedding = false
1129
- })
1130
- }
1131
- } else {
1132
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1133
- }
1226
+ safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1134
1227
  }
1135
1228
  return newValue
1136
1229
  } else {
@@ -1145,11 +1238,11 @@ function modelFactory(source, $special, $model) {
1145
1238
  accessor.set = val.set
1146
1239
  accessor.get = val.get
1147
1240
  accessor.type = 0
1148
- initCallbacks.push(function() {
1241
+ initCallbacks.push(function () {
1149
1242
  var data = {
1150
- evaluator: function() {
1243
+ evaluator: function () {
1151
1244
  data.type = Math.random(),
1152
- data.element = null
1245
+ data.element = null
1153
1246
  $model[name] = accessor.get.call($vmodel)
1154
1247
  },
1155
1248
  element: head,
@@ -1165,7 +1258,7 @@ function modelFactory(source, $special, $model) {
1165
1258
  //第2种为对象属性,产生子VM与监控数组
1166
1259
  accessor.type = 2
1167
1260
  accessor.valueType = valueType
1168
- initCallbacks.push(function() {
1261
+ initCallbacks.push(function () {
1169
1262
  var svmodel = modelFactory(val, 0, $model[name])
1170
1263
  accessor.svmodel = svmodel
1171
1264
  svmodel.$events[subscribers] = $events[name]
@@ -1176,10 +1269,10 @@ function modelFactory(source, $special, $model) {
1176
1269
  }
1177
1270
  accessor._name = name
1178
1271
  watchedProperties[name] = accessor
1179
- })(i, source[i])
1272
+ })(i, source[i])// jshint ignore:line
1180
1273
  }
1181
1274
 
1182
- $$skipArray.forEach(function(name) {
1275
+ $$skipArray.forEach(function (name) {
1183
1276
  delete source[name]
1184
1277
  delete $model[name] //这些特殊属性不应该在$model中出现
1185
1278
  })
@@ -1194,7 +1287,7 @@ function modelFactory(source, $special, $model) {
1194
1287
  $vmodel.$id = generateID()
1195
1288
  $vmodel.$model = $model
1196
1289
  $vmodel.$events = $events
1197
- for ( i in EventBus) {
1290
+ for (i in EventBus) {
1198
1291
  var fn = EventBus[i]
1199
1292
  if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下
1200
1293
  fn = fn.bind($vmodel)
@@ -1204,7 +1297,7 @@ function modelFactory(source, $special, $model) {
1204
1297
 
1205
1298
  if (canHideOwn) {
1206
1299
  Object.defineProperty($vmodel, "hasOwnProperty", {
1207
- value: function(name) {
1300
+ value: function (name) {
1208
1301
  return name in this.$model
1209
1302
  },
1210
1303
  writable: false,
@@ -1213,18 +1306,20 @@ function modelFactory(source, $special, $model) {
1213
1306
  })
1214
1307
 
1215
1308
  } else {
1216
- $vmodel.hasOwnProperty = function(name) {
1309
+ /* jshint ignore:start */
1310
+ $vmodel.hasOwnProperty = function (name) {
1217
1311
  return name in $vmodel.$model
1218
1312
  }
1313
+ /* jshint ignore:end */
1219
1314
  }
1220
- initCallbacks.forEach(function(cb) { //收集依赖
1315
+ initCallbacks.forEach(function (cb) { //收集依赖
1221
1316
  cb()
1222
1317
  })
1223
1318
  return $vmodel
1224
1319
  }
1225
1320
 
1226
1321
  //比较两个值是否相等
1227
- var isEqual = Object.is || function(v1, v2) {
1322
+ var isEqual = Object.is || function (v1, v2) {
1228
1323
  if (v1 === 0 && v2 === 0) {
1229
1324
  return 1 / v1 === 1 / v2
1230
1325
  } else if (v1 !== v1) {
@@ -1240,7 +1335,7 @@ function safeFire(a, b, c, d) {
1240
1335
  }
1241
1336
  }
1242
1337
 
1243
- var descriptorFactory = W3C ? function(obj) {
1338
+ var descriptorFactory = W3C ? function (obj) {
1244
1339
  var descriptors = {}
1245
1340
  for (var i in obj) {
1246
1341
  descriptors[i] = {
@@ -1251,7 +1346,7 @@ var descriptorFactory = W3C ? function(obj) {
1251
1346
  }
1252
1347
  }
1253
1348
  return descriptors
1254
- } : function(a) {
1349
+ } : function (a) {
1255
1350
  return a
1256
1351
  }
1257
1352
 
@@ -1279,16 +1374,17 @@ function objectFactory(parent, name, value, valueType) {
1279
1374
  }
1280
1375
  var ret = modelFactory(value)
1281
1376
  ret.$events[subscribers] = iterators
1282
- midway[ret.$id] = function(data) {
1377
+ midway[ret.$id] = function (data) {
1283
1378
  while (data = iterators.shift()) {
1284
- (function(el) {
1285
- avalon.nextTick(function() {
1286
- if (el.type) { //重新绑定
1379
+ (function (el) {
1380
+ avalon.nextTick(function () {
1381
+ var type = el.type
1382
+ if (type && bindingHandlers[type]) { //#753
1287
1383
  el.rollback && el.rollback() //还原 ms-with ms-on
1288
- bindingHandlers[el.type](el, el.vmodels)
1384
+ bindingHandlers[type](el, el.vmodels)
1289
1385
  }
1290
1386
  })
1291
- })(data)
1387
+ })(data) // jshint ignore:line
1292
1388
  }
1293
1389
  delete midway[ret.$id]
1294
1390
  }
@@ -1298,7 +1394,7 @@ function objectFactory(parent, name, value, valueType) {
1298
1394
  //===================修复浏览器对Object.defineProperties的支持=================
1299
1395
  if (!canHideOwn) {
1300
1396
  if ("__defineGetter__" in avalon) {
1301
- defineProperty = function(obj, prop, desc) {
1397
+ defineProperty = function (obj, prop, desc) {
1302
1398
  if ('value' in desc) {
1303
1399
  obj[prop] = desc.value
1304
1400
  }
@@ -1310,7 +1406,7 @@ if (!canHideOwn) {
1310
1406
  }
1311
1407
  return obj
1312
1408
  }
1313
- defineProperties = function(obj, descs) {
1409
+ defineProperties = function (obj, descs) {
1314
1410
  for (var prop in descs) {
1315
1411
  if (descs.hasOwnProperty(prop)) {
1316
1412
  defineProperty(obj, prop, descs[prop])
@@ -1320,7 +1416,7 @@ if (!canHideOwn) {
1320
1416
  }
1321
1417
  }
1322
1418
  if (IEVersion) {
1323
- window.execScript([
1419
+ window.execScript([ // jshint ignore:line
1324
1420
  "Function parseVB(code)",
1325
1421
  "\tExecuteGlobal(code)",
1326
1422
  "End Function",
@@ -1343,8 +1439,7 @@ if (!canHideOwn) {
1343
1439
  "\tfindOrDefineVBClass=found",
1344
1440
  "End Function"
1345
1441
  ].join("\n"), "VBScript")
1346
-
1347
- function VBMediator(instance, accessors, name, value) {
1442
+ function VBMediator(instance, accessors, name, value) {// jshint ignore:line
1348
1443
  var accessor = accessors[name]
1349
1444
  if (arguments.length === 4) {
1350
1445
  accessor.call(instance, value)
@@ -1352,8 +1447,8 @@ if (!canHideOwn) {
1352
1447
  return accessor.call(instance)
1353
1448
  }
1354
1449
  }
1355
- defineProperties = function(name, accessors, properties) {
1356
- var className = "VBClass" + setTimeout("1"),
1450
+ defineProperties = function (name, accessors, properties) {
1451
+ var className = "VBClass" + setTimeout("1"),// jshint ignore:line
1357
1452
  buffer = []
1358
1453
  buffer.push(
1359
1454
  "\r\n\tPrivate [__data__], [__proxy__]",
@@ -1367,7 +1462,7 @@ if (!canHideOwn) {
1367
1462
  buffer.push("\tPublic [" + name + "]")
1368
1463
  }
1369
1464
  }
1370
- $$skipArray.forEach(function(name) {
1465
+ $$skipArray.forEach(function (name) {
1371
1466
  if (!accessors.hasOwnProperty(name)) {
1372
1467
  buffer.push("\tPublic [" + name + "]")
1373
1468
  }
@@ -1406,7 +1501,6 @@ if (!canHideOwn) {
1406
1501
  "End Function"
1407
1502
  ].join("\r\n"))
1408
1503
  }
1409
- // console.log(code)
1410
1504
  var ret = window[realClassName + "Factory"](accessors, VBMediator) //得到其产品
1411
1505
  return ret //得到其产品
1412
1506
  }
@@ -1426,7 +1520,7 @@ function Collection(model) {
1426
1520
  array._ = modelFactory({
1427
1521
  length: model.length
1428
1522
  })
1429
- array._.$watch("length", function(a, b) {
1523
+ array._.$watch("length", function (a, b) {
1430
1524
  array.$fire("length", a, b)
1431
1525
  })
1432
1526
  for (var i in EventBus) {
@@ -1441,13 +1535,15 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1441
1535
  while (--loop) {
1442
1536
  switch (method) {
1443
1537
  case "add":
1444
- var array = this.$model.slice(pos, pos + n).map(function(el) {
1538
+ /* jshint ignore:start */
1539
+ var array = this.$model.slice(pos, pos + n).map(function (el) {
1445
1540
  if (rcomplexType.test(avalon.type(el))) {
1446
1541
  return el.$id ? el : modelFactory(el, 0, el)
1447
1542
  } else {
1448
1543
  return el
1449
1544
  }
1450
1545
  })
1546
+ /* jshint ignore:end */
1451
1547
  _splice.apply(this, [pos, 0].concat(array))
1452
1548
  this._fire("add", pos, n)
1453
1549
  break
@@ -1474,30 +1570,30 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1474
1570
  var _splice = ap.splice
1475
1571
  var CollectionPrototype = {
1476
1572
  _splice: _splice,
1477
- _fire: function(method, a, b) {
1573
+ _fire: function (method, a, b) {
1478
1574
  notifySubscribers(this.$events[subscribers], method, a, b)
1479
1575
  },
1480
- size: function() { //取得数组长度,这个函数可以同步视图,length不能
1576
+ size: function () { //取得数组长度,这个函数可以同步视图,length不能
1481
1577
  return this._.length
1482
1578
  },
1483
- pushArray: function(array) {
1579
+ pushArray: function (array) {
1484
1580
  var m = array.length, n = this.length
1485
1581
  if (m) {
1486
1582
  ap.push.apply(this.$model, array)
1487
- mutateArray.call(this, "add", n, m, n)
1583
+ mutateArray.call(this, "add", n, m, Math.max(0, n - 1))
1488
1584
  }
1489
1585
  return m + n
1490
1586
  },
1491
- push: function() {
1587
+ push: function () {
1492
1588
  //http://jsperf.com/closure-with-arguments
1493
1589
  var array = []
1494
1590
  var i, n = arguments.length
1495
1591
  for (i = 0; i < n; i++) {
1496
1592
  array[i] = arguments[i]
1497
1593
  }
1498
- return this.pushArray(arguments)
1594
+ return this.pushArray(array)
1499
1595
  },
1500
- unshift: function() {
1596
+ unshift: function () {
1501
1597
  var m = arguments.length, n = this.length
1502
1598
  if (m) {
1503
1599
  ap.unshift.apply(this.$model, arguments)
@@ -1505,22 +1601,22 @@ var CollectionPrototype = {
1505
1601
  }
1506
1602
  return m + n //IE67的unshift不会返回长度
1507
1603
  },
1508
- shift: function() {
1604
+ shift: function () {
1509
1605
  if (this.length) {
1510
1606
  var el = this.$model.shift()
1511
1607
  mutateArray.call(this, "del", 0, 1, 0)
1512
1608
  return el //返回被移除的元素
1513
1609
  }
1514
1610
  },
1515
- pop: function() {
1516
- var m = this.length
1517
- if (m) {
1611
+ pop: function () {
1612
+ var n = this.length
1613
+ if (n) {
1518
1614
  var el = this.$model.pop()
1519
- mutateArray.call(this, "del", m - 1, 1, Math.max(0, m - 2))
1615
+ mutateArray.call(this, "del", n - 1, 1, Math.max(0, n - 2))
1520
1616
  return el //返回被移除的元素
1521
1617
  }
1522
1618
  },
1523
- splice: function(start) {
1619
+ splice: function (start) {
1524
1620
  var m = arguments.length, args = [], change
1525
1621
  var removed = _splice.apply(this.$model, arguments)
1526
1622
  if (removed.length) { //如果用户删掉了元素
@@ -1541,27 +1637,27 @@ var CollectionPrototype = {
1541
1637
  return []
1542
1638
  }
1543
1639
  },
1544
- contains: function(el) { //判定是否包含
1640
+ contains: function (el) { //判定是否包含
1545
1641
  return this.indexOf(el) !== -1
1546
1642
  },
1547
- remove: function(el) { //移除第一个等于给定值的元素
1643
+ remove: function (el) { //移除第一个等于给定值的元素
1548
1644
  return this.removeAt(this.indexOf(el))
1549
1645
  },
1550
- removeAt: function(index) { //移除指定索引上的元素
1646
+ removeAt: function (index) { //移除指定索引上的元素
1551
1647
  if (index >= 0) {
1552
1648
  this.$model.splice(index, 1)
1553
1649
  return mutateArray.call(this, "del", index, 1, 0)
1554
1650
  }
1555
1651
  return []
1556
1652
  },
1557
- clear: function() {
1653
+ clear: function () {
1558
1654
  this.$model.length = this.length = this._.length = 0 //清空数组
1559
1655
  this._fire("clear", 0)
1560
1656
  return this
1561
1657
  },
1562
- removeAll: function(all) { //移除N个元素
1658
+ removeAll: function (all) { //移除N个元素
1563
1659
  if (Array.isArray(all)) {
1564
- all.forEach(function(el) {
1660
+ all.forEach(function (el) {
1565
1661
  this.remove(el)
1566
1662
  }, this)
1567
1663
  } else if (typeof all === "function") {
@@ -1575,13 +1671,13 @@ var CollectionPrototype = {
1575
1671
  this.clear()
1576
1672
  }
1577
1673
  },
1578
- ensure: function(el) {
1674
+ ensure: function (el) {
1579
1675
  if (!this.contains(el)) { //只有不存在才push
1580
1676
  this.push(el)
1581
1677
  }
1582
1678
  return this
1583
1679
  },
1584
- set: function(index, val) {
1680
+ set: function (index, val) {
1585
1681
  if (index >= 0) {
1586
1682
  var valueType = avalon.type(val)
1587
1683
  if (val && val.$model) {
@@ -1620,8 +1716,8 @@ function sortByIndex(array, indexes) {
1620
1716
  }
1621
1717
  }
1622
1718
 
1623
- "sort,reverse".replace(rword, function(method) {
1624
- CollectionPrototype[method] = function() {
1719
+ "sort,reverse".replace(rword, function (method) {
1720
+ CollectionPrototype[method] = function () {
1625
1721
  var newArray = this.$model//这是要排序的新数组
1626
1722
  var oldArray = newArray.concat() //保持原来状态的旧数组
1627
1723
  var mask = Math.random()
@@ -1796,27 +1892,24 @@ function notifySubscribers(list) { //通知依赖于这个访问器的订阅者
1796
1892
  /************************************************************************
1797
1893
  * HTML处理(parseHTML, innerHTML, clearHTML) *
1798
1894
  ************************************************************************/
1799
- //parseHTML的辅助变量
1895
+ // We have to close these tags to support XHTML
1800
1896
  var tagHooks = {
1801
- area: [1, "<map>"],
1802
- param: [1, "<object>"],
1803
- col: [2, "<table><tbody></tbody><colgroup>", "</table>"],
1804
- legend: [1, "<fieldset>"],
1805
- option: [1, "<select multiple='multiple'>"],
1897
+ area: [1, "<map>", "</map>"],
1898
+ param: [1, "<object>", "</object>"],
1899
+ col: [2, "<table><colgroup>", "</colgroup></table>"],
1900
+ legend: [1, "<fieldset>", "</fieldset>"],
1901
+ option: [1, "<select multiple='multiple'>", "</select>"],
1806
1902
  thead: [1, "<table>", "</table>"],
1807
- //如果这里不写</tbody></table>,在IE6-9会在多出一个奇怪的caption标签
1808
- tr: [2, "<table><tbody>", "</tbody></table>"],
1809
- //如果这里不写</tr></tbody></table>,在IE6-9会在多出一个奇怪的caption标签
1810
- th: [3, "<table><tbody><tr>", "</tr></tbody></table>"],
1811
- td: [3, "<table><tbody><tr>"],
1903
+ tr: [2, "<table>", "</table>"],
1904
+ td: [3, "<table><tr>", "</tr></table>"],
1812
1905
  g: [1, '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">', '</svg>'],
1813
1906
  //IE6-8在用innerHTML生成节点时,不能直接创建no-scope元素与HTML5的新标签
1814
- _default: W3C ? [0, ""] : [1, "X<div>"] //div可以不用闭合
1907
+ _default: W3C ? [0, "", ""] : [1, "X<div>", "</div>"] //div可以不用闭合
1815
1908
  }
1816
-
1909
+ tagHooks.th = tagHooks.td
1817
1910
  tagHooks.optgroup = tagHooks.option
1818
1911
  tagHooks.tbody = tagHooks.tfoot = tagHooks.colgroup = tagHooks.caption = tagHooks.thead
1819
- String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function(tag) {
1912
+ String("circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use").replace(rword, function (tag) {
1820
1913
  tagHooks[tag] = tagHooks.g //处理SVG
1821
1914
  })
1822
1915
  var rtagName = /<([\w:]+)/ //取得其tagName
@@ -1825,43 +1918,58 @@ var rcreate = W3C ? /[^\d\D]/ : /(<(?:script|link|style|meta|noscript))/ig
1825
1918
  var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "application/ecmascript", "application/javascript"])
1826
1919
  var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关系的标签
1827
1920
  var script = DOC.createElement("script")
1828
- avalon.parseHTML = function(html) {
1829
- if (typeof html !== "string" ) {
1830
- return DOC.createDocumentFragment()
1921
+ var rhtml = /<|&#?\w+;/
1922
+ avalon.parseHTML = function (html) {
1923
+ var fragment = hyperspace.cloneNode(false)
1924
+ if (typeof html !== "string") {
1925
+ return fragment
1926
+ }
1927
+ if (!rhtml.test(html)) {
1928
+ fragment.appendChild(DOC.createTextNode(html))
1929
+ return fragment
1831
1930
  }
1832
1931
  html = html.replace(rxhtml, "<$1></$2>").trim()
1833
1932
  var tag = (rtagName.exec(html) || ["", ""])[1].toLowerCase(),
1834
1933
  //取得其标签名
1835
1934
  wrap = tagHooks[tag] || tagHooks._default,
1836
- fragment = hyperspace.cloneNode(false),
1837
1935
  wrapper = cinerator,
1838
1936
  firstChild, neo
1839
1937
  if (!W3C) { //fix IE
1840
1938
  html = html.replace(rcreate, "<br class=msNoScope>$1") //在link style script等标签之前添加一个补丁
1841
1939
  }
1842
- wrapper.innerHTML = wrap[1] + html + (wrap[2] || "")
1940
+ wrapper.innerHTML = wrap[1] + html + wrap[2]
1843
1941
  var els = wrapper.getElementsByTagName("script")
1844
1942
  if (els.length) { //使用innerHTML生成的script节点不会发出请求与执行text属性
1845
1943
  for (var i = 0, el; el = els[i++]; ) {
1846
1944
  if (scriptTypes[el.type]) {
1847
1945
  //以偷龙转凤方式恢复执行脚本功能
1848
1946
  neo = script.cloneNode(false) //FF不能省略参数
1849
- ap.forEach.call(el.attributes, function(attr) {
1947
+ ap.forEach.call(el.attributes, function (attr) {
1850
1948
  if (attr && attr.specified) {
1851
1949
  neo[attr.name] = attr.value //复制其属性
1852
1950
  neo.setAttribute(attr.name, attr.value)
1853
1951
  }
1854
- })
1952
+ }) // jshint ignore:line
1855
1953
  neo.text = el.text
1856
1954
  el.parentNode.replaceChild(neo, el) //替换节点
1857
1955
  }
1858
1956
  }
1859
1957
  }
1860
- //移除我们为了符合套嵌关系而添加的标签
1861
- for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {
1862
- }
1863
1958
  if (!W3C) { //fix IE
1864
- els = wrapper.getElementsByTagName("br"), n = els.length
1959
+ var target = wrap[1] === "X<div>" ? wrapper.lastChild.firstChild : wrapper.lastChild
1960
+ if (target && target.tagName === "TABLE" && tag !== "tbody") {
1961
+ //IE6-7处理 <thead> --> <thead>,<tbody>
1962
+ //<tfoot> --> <tfoot>,<tbody>
1963
+ //<table> --> <table><tbody></table>
1964
+ for (els = target.childNodes, i = 0; el = els[i++]; ) {
1965
+ if (el.tagName === "TBODY" && !el.innerHTML) {
1966
+ target.removeChild(el)
1967
+ break
1968
+ }
1969
+ }
1970
+ }
1971
+ els = wrapper.getElementsByTagName("br")
1972
+ var n = els.length
1865
1973
  while (el = els[--n]) {
1866
1974
  if (el.className === "msNoScope") {
1867
1975
  el.parentNode.removeChild(el)
@@ -1873,7 +1981,9 @@ avalon.parseHTML = function(html) {
1873
1981
  }
1874
1982
  }
1875
1983
  }
1876
-
1984
+ //移除我们为了符合套嵌关系而添加的标签
1985
+ for (i = wrap[0]; i--; wrapper = wrapper.lastChild) {
1986
+ }
1877
1987
  while (firstChild = wrapper.firstChild) { // 将wrapper上的节点转移到文档碎片上!
1878
1988
  fragment.appendChild(firstChild)
1879
1989
  }
@@ -1892,7 +2002,7 @@ function fixVML(node) {
1892
2002
  node.style.zoom = 1 //hasLayout
1893
2003
  }
1894
2004
  }
1895
- avalon.innerHTML = function(node, html) {
2005
+ avalon.innerHTML = function (node, html) {
1896
2006
  if (!W3C && (!rcreate.test(html) && !rnest.test(html))) {
1897
2007
  try {
1898
2008
  node.innerHTML = html
@@ -1903,7 +2013,7 @@ avalon.innerHTML = function(node, html) {
1903
2013
  var a = this.parseHTML(html)
1904
2014
  this.clearHTML(node).appendChild(a)
1905
2015
  }
1906
- avalon.clearHTML = function(node) {
2016
+ avalon.clearHTML = function (node) {
1907
2017
  node.textContent = ""
1908
2018
  while (node.firstChild) {
1909
2019
  node.removeChild(node.firstChild)
@@ -2022,8 +2132,9 @@ function scanAttr(elem, vmodels) {
2022
2132
  param = type
2023
2133
  type = "on"
2024
2134
  } else if (obsoleteAttrs[type]) {
2025
- log("ms-" + type + "已经被废弃,请使用ms-attr-*代替")
2026
- if (type === "enabled") { //吃掉ms-enabled绑定,用ms-disabled代替
2135
+ log("warning!请改用ms-attr-" + type + "代替ms-" + type + "!")
2136
+ if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
2137
+ log("warning!ms-enabled或ms-attr-enabled已经被废弃")
2027
2138
  type = "disabled"
2028
2139
  value = "!(" + value + ")"
2029
2140
  }
@@ -2047,11 +2158,11 @@ function scanAttr(elem, vmodels) {
2047
2158
  if (type === "html" || type === "text") {
2048
2159
  var token = getToken(value)
2049
2160
  avalon.mix(binding, token)
2050
- binding.filters = binding.filters.replace(rhasHtml, function() {
2161
+ binding.filters = binding.filters.replace(rhasHtml, function () {
2051
2162
  binding.type = "html"
2052
2163
  binding.group = 1
2053
2164
  return ""
2054
- })
2165
+ })// jshint ignore:line
2055
2166
  }
2056
2167
  if (name === "ms-if-loop") {
2057
2168
  binding.priority += 100
@@ -2067,12 +2178,18 @@ function scanAttr(elem, vmodels) {
2067
2178
  }
2068
2179
  }
2069
2180
  bindings.sort(bindingSorter)
2070
- if (msData["ms-attr-checked"] && msData["ms-duplex"]) {
2071
- log("warning!一个元素上不能同时定义ms-attr-checked与ms-duplex")
2181
+ var control = elem.type
2182
+ if (control && msData["ms-duplex"]) {
2183
+ if (msData["ms-attr-checked"] && /radio|checkbox/.test(control)) {
2184
+ log("warning!" + control + "控件不能同时定义ms-attr-checked与ms-duplex")
2185
+ }
2186
+ if (msData["ms-attr-value"] && /text|password/.test(control)) {
2187
+ log("warning!" + control + "控件不能同时定义ms-attr-value与ms-duplex")
2188
+ }
2072
2189
  }
2073
2190
  var scanNode = true
2074
2191
  for (i = 0; binding = bindings[i]; i++) {
2075
- type = binding.type
2192
+ type = binding.type
2076
2193
  if (rnoscanAttrBinding.test(type)) {
2077
2194
  return executeBindings(bindings.slice(0, i + 1), vmodels)
2078
2195
  } else if (scanNode) {
@@ -2091,7 +2208,7 @@ var rnoscanNodeBinding = /^each|with|html|include$/
2091
2208
  //IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支,
2092
2209
  //但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面
2093
2210
  if (!"1" [0]) {
2094
- var cacheAttrs = createCache(512)
2211
+ var cacheAttrs = new Cache(512)
2095
2212
  var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g,
2096
2213
  rquote = /^['"]/,
2097
2214
  rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i,
@@ -2105,7 +2222,7 @@ if (!"1" [0]) {
2105
2222
  // }
2106
2223
  // }
2107
2224
  //依次输出<SECTION>, </SECTION>
2108
- var getAttributes = function(elem) {
2225
+ var getAttributes = function (elem) {
2109
2226
  var html = elem.outerHTML
2110
2227
  //处理IE6-8解析HTML5新标签的情况,及<br>等半闭合标签outerHTML为空的情况
2111
2228
  if (html.slice(0, 2) === "</" || !html.trim()) {
@@ -2114,9 +2231,10 @@ if (!"1" [0]) {
2114
2231
  var str = html.match(rtag)[0]
2115
2232
  var attributes = [],
2116
2233
  match,
2117
- k, v;
2118
- if (cacheAttrs[str]) {
2119
- return cacheAttrs[str]
2234
+ k, v
2235
+ var ret = cacheAttrs.get(str)
2236
+ if (ret) {
2237
+ return ret
2120
2238
  }
2121
2239
  while (k = rattrs.exec(str)) {
2122
2240
  v = k[2]
@@ -2132,7 +2250,7 @@ if (!"1" [0]) {
2132
2250
  }
2133
2251
  attributes.push(binding)
2134
2252
  }
2135
- return cacheAttrs(str, attributes)
2253
+ return cacheAttrs.put(str, attributes)
2136
2254
  }
2137
2255
  }
2138
2256
 
@@ -2193,7 +2311,7 @@ var rhasHtml = /\|\s*html\s*/,
2193
2311
  function getToken(value) {
2194
2312
  if (value.indexOf("|") > 0) {
2195
2313
  var scapegoat = value.replace( rstringLiteral, function(_){
2196
- return Math.pow(10,_.length)
2314
+ return Array(_.length+1).join("1")// jshint ignore:line
2197
2315
  })
2198
2316
  var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
2199
2317
  if (index > -1) {
@@ -2268,7 +2386,7 @@ function scanText(textNode, vmodels) {
2268
2386
  token.type = "html"
2269
2387
  token.group = 1
2270
2388
  return ""
2271
- })
2389
+ })// jshint ignore:line
2272
2390
  bindings.push(token) //收集带有插值表达式的文本
2273
2391
  }
2274
2392
  hyperspace.appendChild(node)
@@ -2445,7 +2563,7 @@ avalon.fn.mix({
2445
2563
  while (offsetParent && avalon.css(offsetParent, "position") === "static") {
2446
2564
  offsetParent = offsetParent.offsetParent;
2447
2565
  }
2448
- return avalon(offsetParent)
2566
+ return avalon(offsetParent || root)
2449
2567
  },
2450
2568
  bind: function(type, fn, phase) {
2451
2569
  if (this[0]) { //此方法不会链
@@ -2499,10 +2617,10 @@ avalon.parseJSON = window.JSON ? JSON.parse : function(data) {
2499
2617
  if (rvalidchars.test(data.replace(rvalidescape, "@")
2500
2618
  .replace(rvalidtokens, "]")
2501
2619
  .replace(rvalidbraces, ""))) {
2502
- return (new Function("return " + data))();
2620
+ return (new Function("return " + data))()// jshint ignore:line
2503
2621
  }
2504
2622
  }
2505
- avalon.error("Invalid JSON: " + data);
2623
+ avalon.error("Invalid JSON: " + data)
2506
2624
  }
2507
2625
  return data
2508
2626
  }
@@ -2844,28 +2962,26 @@ var quote = window.JSON && JSON.stringify || function(str) {
2844
2962
  }) + '"'
2845
2963
  }
2846
2964
 
2847
- var keywords =
2848
- // 关键字
2849
- "break,case,catch,continue,debugger,default,delete,do,else,false" +
2850
- ",finally,for,function,if,in,instanceof,new,null,return,switch,this" +
2851
- ",throw,true,try,typeof,var,void,while,with" +
2852
- // 保留字
2853
- ",abstract,boolean,byte,char,class,const,double,enum,export,extends" +
2854
- ",final,float,goto,implements,import,int,interface,long,native" +
2855
- ",package,private,protected,public,short,static,super,synchronized" +
2856
- ",throws,transient,volatile" +
2857
- // ECMA 5 - use strict
2858
- ",arguments,let,yield" + ",undefined"
2965
+ var keywords = [
2966
+ "break,case,catch,continue,debugger,default,delete,do,else,false",
2967
+ "finally,for,function,if,in,instanceof,new,null,return,switch,this",
2968
+ "throw,true,try,typeof,var,void,while,with", /* 关键字*/
2969
+ "abstract,boolean,byte,char,class,const,double,enum,export,extends",
2970
+ "final,float,goto,implements,import,int,interface,long,native",
2971
+ "package,private,protected,public,short,static,super,synchronized",
2972
+ "throws,transient,volatile", /*保留字*/
2973
+ "arguments,let,yield,undefined" /* ECMA 5 - use strict*/].join(",")
2859
2974
  var rrexpstr = /\/\*[\w\W]*?\*\/|\/\/[^\n]*\n|\/\/[^\n]*$|"(?:[^"\\]|\\[\w\W])*"|'(?:[^'\\]|\\[\w\W])*'|[\s\t\n]*\.[\s\t\n]*[$\w\.]+/g
2860
2975
  var rsplit = /[^\w$]+/g
2861
2976
  var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g')
2862
2977
  var rnumber = /\b\d[^,]*/g
2863
2978
  var rcomma = /^,+|,+$/g
2864
- var cacheVars = createCache(512)
2865
- var getVariables = function(code) {
2979
+ var cacheVars = new Cache(512)
2980
+ var getVariables = function (code) {
2866
2981
  var key = "," + code.trim()
2867
- if (cacheVars[key]) {
2868
- return cacheVars[key]
2982
+ var ret = cacheVars.get(key)
2983
+ if (ret) {
2984
+ return ret
2869
2985
  }
2870
2986
  var match = code
2871
2987
  .replace(rrexpstr, "")
@@ -2874,7 +2990,7 @@ var getVariables = function(code) {
2874
2990
  .replace(rnumber, "")
2875
2991
  .replace(rcomma, "")
2876
2992
  .split(/^$|,+/)
2877
- return cacheVars(key, uniqSet(match))
2993
+ return cacheVars.put(key, uniqSet(match))
2878
2994
  }
2879
2995
  /*添加赋值语句*/
2880
2996
 
@@ -2907,7 +3023,7 @@ function uniqSet(array) {
2907
3023
  return ret
2908
3024
  }
2909
3025
  //缓存求值函数,以便多次利用
2910
- var cacheExprs = createCache(128)
3026
+ var cacheExprs = new Cache(128)
2911
3027
  //取得求值函数及其传参
2912
3028
  var rduplex = /\w\[.*\]|\w\.\w/
2913
3029
  var rproxy = /(\$proxy\$[a-z]+)\d+$/
@@ -2919,16 +3035,16 @@ var rthimLeftParentheses = /"\s*\(/g
2919
3035
  function parseFilter(val, filters) {
2920
3036
  filters = filters
2921
3037
  .replace(rthimRightParentheses, "")//处理最后的小括号
2922
- .replace(rthimOtherParentheses, function() {//处理其他小括号
3038
+ .replace(rthimOtherParentheses, function () {//处理其他小括号
2923
3039
  return "],|"
2924
3040
  })
2925
- .replace(rquoteFilterName, function(a, b) { //处理|及它后面的过滤器的名字
3041
+ .replace(rquoteFilterName, function (a, b) { //处理|及它后面的过滤器的名字
2926
3042
  return "[" + quote(b)
2927
3043
  })
2928
- .replace(rpatchBracket, function() {
3044
+ .replace(rpatchBracket, function () {
2929
3045
  return '"],["'
2930
3046
  })
2931
- .replace(rthimLeftParentheses, function() {
3047
+ .replace(rthimLeftParentheses, function () {
2932
3048
  return '",'
2933
3049
  }) + "]"
2934
3050
  return "return avalon.filters.$filter(" + val + ", " + filters + ")"
@@ -2937,7 +3053,7 @@ function parseFilter(val, filters) {
2937
3053
  function parseExpr(code, scopes, data) {
2938
3054
  var dataType = data.type
2939
3055
  var filters = data.filters || ""
2940
- var exprId = scopes.map(function(el) {
3056
+ var exprId = scopes.map(function (el) {
2941
3057
  return String(el.$id).replace(rproxy, "$1")
2942
3058
  }) + code + dataType + filters
2943
3059
  var vars = getVariables(code).concat(),
@@ -2961,9 +3077,9 @@ function parseExpr(code, scopes, data) {
2961
3077
  }
2962
3078
  if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) {
2963
3079
  //https://github.com/RubyLouvre/avalon/issues/583
2964
- data.vars.forEach(function(v) {
3080
+ data.vars.forEach(function (v) {
2965
3081
  var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
2966
- code = code.replace(reg, function(_) {
3082
+ code = code.replace(reg, function (_) {
2967
3083
  var c = _.charAt(v.length)
2968
3084
  var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2969
3085
  var method = /^\s*\(/.test(r)
@@ -2990,7 +3106,7 @@ function parseExpr(code, scopes, data) {
2990
3106
  //---------------args----------------
2991
3107
  data.args = args
2992
3108
  //---------------cache----------------
2993
- var fn = cacheExprs[exprId] //直接从缓存,免得重复生成
3109
+ var fn = cacheExprs.get(exprId) //直接从缓存,免得重复生成
2994
3110
  if (fn) {
2995
3111
  data.evaluator = fn
2996
3112
  return
@@ -3014,7 +3130,7 @@ function parseExpr(code, scopes, data) {
3014
3130
  "= vvv;\n} "
3015
3131
  try {
3016
3132
  fn = Function.apply(noop, names.concat(_body))
3017
- data.evaluator = cacheExprs(exprId, fn)
3133
+ data.evaluator = cacheExprs.put(exprId, fn)
3018
3134
  } catch (e) {
3019
3135
  log("debug: parse error," + e.message)
3020
3136
  }
@@ -3036,11 +3152,11 @@ function parseExpr(code, scopes, data) {
3036
3152
  }
3037
3153
  try {
3038
3154
  fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
3039
- data.evaluator = cacheExprs(exprId, fn)
3155
+ data.evaluator = cacheExprs.put(exprId, fn)
3040
3156
  } catch (e) {
3041
3157
  log("debug: parse error," + e.message)
3042
3158
  } finally {
3043
- vars = textBuffer = names = null //释放内存
3159
+ vars = assigns = names = null //释放内存
3044
3160
  }
3045
3161
  }
3046
3162
 
@@ -3049,7 +3165,7 @@ function parseExpr(code, scopes, data) {
3049
3165
 
3050
3166
  function parseExprProxy(code, scopes, data, tokens, noregister) {
3051
3167
  if (Array.isArray(tokens)) {
3052
- code = tokens.map(function(el) {
3168
+ code = tokens.map(function (el) {
3053
3169
  return el.expr ? "(" + el.value + ")" : quote(el.value)
3054
3170
  }).join(" + ")
3055
3171
  }
@@ -3063,10 +3179,12 @@ function parseExprProxy(code, scopes, data, tokens, noregister) {
3063
3179
  }
3064
3180
  }
3065
3181
  avalon.parseExprProxy = parseExprProxy
3066
- var bools = "autofocus,autoplay,async,allowTransparency,checked,controls,declare,disabled,defer,defaultChecked,defaultSelected" +
3067
- "contentEditable,isMap,loop,multiple,noHref,noResize,noShade,open,readOnly,selected"
3182
+ var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls",
3183
+ "declare,disabled,defer,defaultChecked,defaultSelected",
3184
+ "contentEditable,isMap,loop,multiple,noHref,noResize,noShade",
3185
+ "open,readOnly,selected"].join(",")
3068
3186
  var boolMap = {}
3069
- bools.replace(rword, function(name) {
3187
+ bools.replace(rword, function (name) {
3070
3188
  boolMap[name.toLowerCase()] = name
3071
3189
  })
3072
3190
 
@@ -3079,21 +3197,23 @@ var propMap = {//属性名映射
3079
3197
  "http-equiv": "httpEquiv"
3080
3198
  }
3081
3199
 
3082
- var anomaly = "accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan," + "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight," + "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
3083
- anomaly.replace(rword, function(name) {
3200
+ var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan",
3201
+ "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight",
3202
+ "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"].join(",")
3203
+ anomaly.replace(rword, function (name) {
3084
3204
  propMap[name.toLowerCase()] = name
3085
3205
  })
3086
3206
 
3087
3207
  var rnoscripts = /<noscript.*?>(?:[\s\S]+?)<\/noscript>/img
3088
3208
  var rnoscriptText = /<noscript.*?>([\s\S]+?)<\/noscript>/im
3089
3209
 
3090
- var getXHR = function() {
3091
- return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")
3210
+ var getXHR = function () {
3211
+ return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")// jshint ignore:line
3092
3212
  }
3093
3213
 
3094
3214
  var cacheTmpls = avalon.templateCache = {}
3095
3215
 
3096
- bindingHandlers.attr = function(data, vmodels) {
3216
+ bindingHandlers.attr = function (data, vmodels) {
3097
3217
  var text = data.value.trim(),
3098
3218
  simple = true
3099
3219
  if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) {
@@ -3107,7 +3227,10 @@ bindingHandlers.attr = function(data, vmodels) {
3107
3227
  var elem = data.element
3108
3228
  data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels)
3109
3229
  data.includeLoaded = getBindingCallback(elem, "data-include-loaded", vmodels)
3110
- var outer = data.includeReplaced = !!avalon(elem).data("includeReplace")
3230
+ var outer = data.includeReplace = !!avalon(elem).data("includeReplace")
3231
+ if (avalon(elem).data("includeCache")) {
3232
+ data.templateCache = {}
3233
+ }
3111
3234
  data.startInclude = DOC.createComment("ms-include")
3112
3235
  data.endInclude = DOC.createComment("ms-include-end")
3113
3236
  if (outer) {
@@ -3123,7 +3246,7 @@ bindingHandlers.attr = function(data, vmodels) {
3123
3246
  parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value)))
3124
3247
  }
3125
3248
 
3126
- bindingExecutors.attr = function(val, elem, data) {
3249
+ bindingExecutors.attr = function (val, elem, data) {
3127
3250
  var method = data.type,
3128
3251
  attrName = data.param
3129
3252
  if (method === "css") {
@@ -3158,38 +3281,50 @@ bindingExecutors.attr = function(val, elem, data) {
3158
3281
  var vmodels = data.vmodels
3159
3282
  var rendered = data.includeRendered
3160
3283
  var loaded = data.includeLoaded
3161
- var replace = data.includeReplaced
3284
+ var replace = data.includeReplace
3162
3285
  var target = replace ? elem.parentNode : elem
3163
- var scanTemplate = function(text) {
3286
+ var scanTemplate = function (text) {
3164
3287
  if (loaded) {
3165
3288
  text = loaded.apply(target, [text].concat(vmodels))
3166
3289
  }
3167
3290
  if (rendered) {
3168
- checkScan(target, function() {
3291
+ checkScan(target, function () {
3169
3292
  rendered.call(target)
3170
3293
  }, NaN)
3171
3294
  }
3295
+ var lastID = data.includeLastID
3296
+ if (data.templateCache && lastID && lastID !== val) {
3297
+ var lastTemplate = data.templateCache[lastID]
3298
+ if (!lastTemplate) {
3299
+ lastTemplate = data.templateCache[lastID] = DOC.createElement("div")
3300
+ ifGroup.appendChild(lastTemplate)
3301
+ }
3302
+ }
3303
+ data.includeLastID = val
3172
3304
  while (true) {
3173
3305
  var node = data.startInclude.nextSibling
3174
3306
  if (node && node !== data.endInclude) {
3175
3307
  target.removeChild(node)
3308
+ if (lastTemplate)
3309
+ lastTemplate.appendChild(node)
3176
3310
  } else {
3177
3311
  break
3178
3312
  }
3179
3313
  }
3180
- var dom = avalon.parseHTML(text)
3314
+ var dom = getTemplateNodes(data, val, text)
3181
3315
  var nodes = avalon.slice(dom.childNodes)
3182
3316
  target.insertBefore(dom, data.endInclude)
3183
3317
  scanNodeArray(nodes, vmodels)
3184
3318
  }
3319
+
3185
3320
  if (data.param === "src") {
3186
3321
  if (cacheTmpls[val]) {
3187
- avalon.nextTick(function() {
3322
+ avalon.nextTick(function () {
3188
3323
  scanTemplate(cacheTmpls[val])
3189
3324
  })
3190
3325
  } else {
3191
3326
  var xhr = getXHR()
3192
- xhr.onreadystatechange = function() {
3327
+ xhr.onreadystatechange = function () {
3193
3328
  if (xhr.readyState === 4) {
3194
3329
  var s = xhr.status
3195
3330
  if (s >= 200 && s < 300 || s === 304 || s === 1223) {
@@ -3225,7 +3360,7 @@ bindingExecutors.attr = function(val, elem, data) {
3225
3360
  }
3226
3361
  }
3227
3362
  }
3228
- avalon.nextTick(function() {
3363
+ avalon.nextTick(function () {
3229
3364
  scanTemplate(el.fixIE78 || el.value || el.innerText || el.innerHTML)
3230
3365
  })
3231
3366
  }
@@ -3244,8 +3379,20 @@ bindingExecutors.attr = function(val, elem, data) {
3244
3379
  }
3245
3380
  }
3246
3381
 
3382
+ function getTemplateNodes(data, id, text) {
3383
+ var div = data.templateCache && data.templateCache[id]
3384
+ if (div) {
3385
+ var dom = DOC.createDocumentFragment(), firstChild
3386
+ while (firstChild = div.firstChild) {
3387
+ dom.appendChild(firstChild)
3388
+ }
3389
+ return dom
3390
+ }
3391
+ return avalon.parseHTML(text)
3392
+ }
3393
+
3247
3394
  //这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html"
3248
- "title,alt,src,value,css,include,href".replace(rword, function(name) {
3395
+ "title,alt,src,value,css,include,href".replace(rword, function (name) {
3249
3396
  bindingHandlers[name] = bindingHandlers.attr
3250
3397
  })
3251
3398
  //根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag"
@@ -3353,7 +3500,7 @@ bindingExecutors.data = function(val, elem, data) {
3353
3500
  }
3354
3501
 
3355
3502
  //双工绑定
3356
- var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
3503
+ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3357
3504
  var elem = data.element,
3358
3505
  hasCast
3359
3506
  parseExprProxy(data.value, vmodels, data, 0, 1)
@@ -3368,7 +3515,7 @@ var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
3368
3515
  if (elem.msData) {
3369
3516
  elem.msData["ms-duplex"] = data.value
3370
3517
  }
3371
- data.param.replace(/\w+/g, function(name) {
3518
+ data.param.replace(/\w+/g, function (name) {
3372
3519
  if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) {
3373
3520
  if (name === "radio")
3374
3521
  log("ms-duplex-radio已经更名为ms-duplex-checked")
@@ -3391,14 +3538,14 @@ var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
3391
3538
  params.push("string")
3392
3539
  }
3393
3540
  data.param = params.join("-")
3394
- data.bound = function(type, callback) {
3541
+ data.bound = function (type, callback) {
3395
3542
  if (elem.addEventListener) {
3396
3543
  elem.addEventListener(type, callback, false)
3397
3544
  } else {
3398
3545
  elem.attachEvent("on" + type, callback)
3399
3546
  }
3400
3547
  var old = data.rollback
3401
- data.rollback = function() {
3548
+ data.rollback = function () {
3402
3549
  elem.avalonSetter = null
3403
3550
  avalon.unbind(elem, type, callback)
3404
3551
  old && old()
@@ -3420,32 +3567,44 @@ function fixNull(val) {
3420
3567
  }
3421
3568
  avalon.duplexHooks = {
3422
3569
  checked: {
3423
- get: function(val, data) {
3570
+ get: function (val, data) {
3424
3571
  return !data.element.oldValue
3425
3572
  }
3426
3573
  },
3427
3574
  string: {
3428
- get: function(val) { //同步到VM
3575
+ get: function (val) { //同步到VM
3429
3576
  return val
3430
3577
  },
3431
3578
  set: fixNull
3432
3579
  },
3433
3580
  "boolean": {
3434
- get: function(val) {
3581
+ get: function (val) {
3435
3582
  return val === "true"
3436
3583
  },
3437
3584
  set: fixNull
3438
3585
  },
3439
3586
  number: {
3440
- get: function(val) {
3441
- return isFinite(val) ? parseFloat(val) || 0 : val
3587
+ get: function (val, data) {
3588
+ var number = parseFloat(val)
3589
+ if (-val === -number) {
3590
+ return number
3591
+ }
3592
+ var arr = /strong|medium|weak/.exec(data.element.getAttribute("data-duplex-number")) || ["medium"]
3593
+ switch (arr[0]) {
3594
+ case "strong":
3595
+ return 0
3596
+ case "medium":
3597
+ return val === "" ? "" : 0
3598
+ case "weak":
3599
+ return val
3600
+ }
3442
3601
  },
3443
3602
  set: fixNull
3444
3603
  }
3445
3604
  }
3446
3605
 
3447
3606
  function pipe(val, data, action, e) {
3448
- data.param.replace(/\w+/g, function(name) {
3607
+ data.param.replace(/\w+/g, function (name) {
3449
3608
  var hook = avalon.duplexHooks[name]
3450
3609
  if (hook && typeof hook[action] === "function") {
3451
3610
  val = hook[action](val, data)
@@ -3455,18 +3614,8 @@ function pipe(val, data, action, e) {
3455
3614
  }
3456
3615
 
3457
3616
  var TimerID, ribbon = []
3458
- function W3CFire(el, name, detail) {
3459
- var event = DOC.createEvent("Events")
3460
- event.initEvent(name, true, true)
3461
- event.fireByAvalon = true//签名,标记事件是由avalon触发
3462
- //event.isTrusted = false 设置这个opera会报错
3463
- if (detail)
3464
- event.detail = detail
3465
- el.dispatchEvent(event)
3466
- }
3467
3617
 
3468
-
3469
- avalon.tick = function(fn) {
3618
+ avalon.tick = function (fn) {
3470
3619
  if (ribbon.push(fn) === 1) {
3471
3620
  TimerID = setInterval(ticker, 60)
3472
3621
  }
@@ -3485,15 +3634,18 @@ function ticker() {
3485
3634
  }
3486
3635
 
3487
3636
  var watchValueInTimer = noop
3488
- new function() {
3637
+ var rmsinput = /text|password|hidden/
3638
+ new function () {// jshint ignore:line
3489
3639
  try {//#272 IE9-IE11, firefox
3490
3640
  var setters = {}
3491
3641
  var aproto = HTMLInputElement.prototype
3492
3642
  var bproto = HTMLTextAreaElement.prototype
3493
- function newSetter(value) {
3643
+ function newSetter(value) {// jshint ignore:line
3494
3644
  if (avalon.contains(root, this)) {
3495
3645
  setters[this.tagName].call(this, value)
3496
- if (this.avalonSetter) {
3646
+ if (!rmsinput.test(this.type))
3647
+ return
3648
+ if (!this.msFocus && this.avalonSetter) {
3497
3649
  this.avalonSetter()
3498
3650
  }
3499
3651
  }
@@ -3511,10 +3663,10 @@ new function() {
3511
3663
  } catch (e) {
3512
3664
  watchValueInTimer = avalon.tick
3513
3665
  }
3514
- }
3666
+ }// jshint ignore:line
3515
3667
 
3516
3668
  if (IEVersion) {
3517
- avalon.bind(DOC, "selectionchange", function(e) {
3669
+ avalon.bind(DOC, "selectionchange", function (e) {
3518
3670
  var el = DOC.activeElement
3519
3671
  if (el && typeof el.avalonSetter === "function") {
3520
3672
  el.avalonSetter()
@@ -3523,8 +3675,8 @@ if (IEVersion) {
3523
3675
  }
3524
3676
 
3525
3677
  //处理radio, checkbox, text, textarea, password
3526
- duplexBinding.INPUT = function(element, evaluator, data) {
3527
- var type = element.type,
3678
+ duplexBinding.INPUT = function (element, evaluator, data) {
3679
+ var $type = element.type,
3528
3680
  bound = data.bound,
3529
3681
  $elem = avalon(element),
3530
3682
  composing = false
@@ -3541,8 +3693,7 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3541
3693
  composing = false
3542
3694
  }
3543
3695
  //当value变化时改变model的值
3544
-
3545
- function updateVModel() {
3696
+ var updateVModel = function () {
3546
3697
  if (composing) //处理中文输入法在minlengh下引发的BUG
3547
3698
  return
3548
3699
  var val = element.oldValue = element.value //防止递归调用形成死循环
@@ -3551,34 +3702,34 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3551
3702
  evaluator(lastValue)
3552
3703
  callback.call(element, lastValue)
3553
3704
  if ($elem.data("duplex-focus")) {
3554
- avalon.nextTick(function() {
3705
+ avalon.nextTick(function () {
3555
3706
  element.focus()
3556
3707
  })
3557
3708
  }
3558
3709
  }
3559
3710
  }
3560
3711
  //当model变化时,它就会改变value的值
3561
- data.handler = function() {
3712
+ data.handler = function () {
3562
3713
  var val = data.pipe(evaluator(), data, "set") + ""//fix #673
3563
3714
  if (val !== element.oldValue) {
3564
3715
  element.value = val
3565
3716
  }
3566
3717
  }
3567
- if (data.isChecked || element.type === "radio") {
3718
+ if (data.isChecked || $type === "radio") {
3568
3719
  var IE6 = IEVersion === 6
3569
- updateVModel = function() {
3720
+ updateVModel = function () {
3570
3721
  if ($elem.data("duplex-observe") !== false) {
3571
3722
  var lastValue = data.pipe(element.value, data, "get")
3572
3723
  evaluator(lastValue)
3573
3724
  callback.call(element, lastValue)
3574
3725
  }
3575
3726
  }
3576
- data.handler = function() {
3727
+ data.handler = function () {
3577
3728
  var val = evaluator()
3578
3729
  var checked = data.isChecked ? !!val : val + "" === element.value
3579
3730
  element.oldValue = checked
3580
3731
  if (IE6) {
3581
- setTimeout(function() {
3732
+ setTimeout(function () {
3582
3733
  //IE8 checkbox, radio是使用defaultChecked控制选中状态,
3583
3734
  //并且要先设置defaultChecked后设置checked
3584
3735
  //并且必须设置延迟
@@ -3590,8 +3741,8 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3590
3741
  }
3591
3742
  }
3592
3743
  bound("click", updateVModel)
3593
- } else if (type === "checkbox") {
3594
- updateVModel = function() {
3744
+ } else if ($type === "checkbox") {
3745
+ updateVModel = function () {
3595
3746
  if ($elem.data("duplex-observe") !== false) {
3596
3747
  var method = element.checked ? "ensure" : "remove"
3597
3748
  var array = evaluator()
@@ -3604,22 +3755,22 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3604
3755
  }
3605
3756
  }
3606
3757
 
3607
- data.handler = function() {
3758
+ data.handler = function () {
3608
3759
  var array = [].concat(evaluator()) //强制转换为数组
3609
3760
  element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1
3610
3761
  }
3611
3762
  bound(W3C ? "change" : "click", updateVModel)
3612
3763
  } else {
3613
- var events = element.getAttribute("data-duplex-event") || element.getAttribute("data-event") || "input"
3764
+ var events = element.getAttribute("data-duplex-event") || "input"
3614
3765
  if (element.attributes["data-event"]) {
3615
3766
  log("data-event指令已经废弃,请改用data-duplex-event")
3616
3767
  }
3617
- function delay(e) {
3618
- setTimeout(function() {
3768
+ function delay(e) {// jshint ignore:line
3769
+ setTimeout(function () {
3619
3770
  updateVModel(e)
3620
3771
  })
3621
3772
  }
3622
- events.replace(rword, function(name) {
3773
+ events.replace(rword, function (name) {
3623
3774
  switch (name) {
3624
3775
  case "input":
3625
3776
  if (!IEVersion) { // W3C
@@ -3633,7 +3784,7 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3633
3784
  if (IEVersion > 8) {
3634
3785
  bound("input", updateVModel)//IE9使用propertychange无法监听中文输入改动
3635
3786
  } else {
3636
- bound("propertychange", function(e) {//IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正
3787
+ bound("propertychange", function (e) {//IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正
3637
3788
  if (e.propertyName === "value") {
3638
3789
  updateVModel()
3639
3790
  }
@@ -3649,19 +3800,28 @@ duplexBinding.INPUT = function(element, evaluator, data) {
3649
3800
  break
3650
3801
  }
3651
3802
  })
3652
- }
3653
- if (/text|password/.test(element.type)) {
3654
- watchValueInTimer(function() {
3655
- if (root.contains(element)) {
3656
- if (element.value !== element.oldValue) {
3657
- updateVModel()
3658
- }
3659
- } else if (!element.msRetain) {
3660
- return false
3661
- }
3803
+ bound("focus", function () {
3804
+ element.msFocus = true
3805
+ })
3806
+ bound("blur", function () {
3807
+ element.msFocus = false
3662
3808
  })
3809
+
3810
+ if (rmsinput.test($type)) {
3811
+ watchValueInTimer(function () {
3812
+ if (root.contains(element)) {
3813
+ if (!element.msFocus && element.oldValue !== element.value) {
3814
+ updateVModel()
3815
+ }
3816
+ } else if (!element.msRetain) {
3817
+ return false
3818
+ }
3819
+ })
3820
+ }
3821
+
3822
+ element.avalonSetter = updateVModel//#765
3663
3823
  }
3664
- element.avalonSetter = updateVModel
3824
+
3665
3825
  element.oldValue = element.value
3666
3826
  registerSubscriber(data)
3667
3827
  callback.call(element, element.value)
@@ -3853,11 +4013,10 @@ bindingHandlers.repeat = function(data, vmodels) {
3853
4013
  var xtype = avalon.type($repeat)
3854
4014
  if (xtype !== "object" && xtype !== "array") {
3855
4015
  freturn = true
3856
- avalon.log("warning:" + data.value + "对应类型不正确")
4016
+ avalon.log("warning:" + data.value + "只能是对象或数组")
3857
4017
  }
3858
4018
  } catch (e) {
3859
4019
  freturn = true
3860
- avalon.log("warning:" + data.value + "编译出错")
3861
4020
  }
3862
4021
 
3863
4022
  var arr = data.value.split(".") || []
@@ -4199,7 +4358,7 @@ function recycleProxies(proxies, type) {
4199
4358
  proxy.$events[i].forEach(function(data) {
4200
4359
  if (typeof data === "object")
4201
4360
  disposeData(data)
4202
- })
4361
+ })// jshint ignore:line
4203
4362
  proxy.$events[i].length = 0
4204
4363
  }
4205
4364
  }
@@ -4363,18 +4522,18 @@ var rsanitize = {
4363
4522
  var rsurrogate = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g
4364
4523
  var rnoalphanumeric = /([^\#-~| |!])/g;
4365
4524
 
4366
- function numberFormat(number, decimals, dec_point, thousands_sep) {
4525
+ function numberFormat(number, decimals, point, thousands) {
4367
4526
  //form http://phpjs.org/functions/number_format/
4368
4527
  //number 必需,要格式化的数字
4369
4528
  //decimals 可选,规定多少个小数位。
4370
- //dec_point 可选,规定用作小数点的字符串(默认为 . )。
4371
- //thousands_sep 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
4529
+ //point 可选,规定用作小数点的字符串(默认为 . )。
4530
+ //thousands 可选,规定用作千位分隔符的字符串(默认为 , ),如果设置了该参数,那么所有其他参数都是必需的。
4372
4531
  number = (number + '')
4373
4532
  .replace(/[^0-9+\-Ee.]/g, '')
4374
4533
  var n = !isFinite(+number) ? 0 : +number,
4375
- prec = !isFinite(+decimals) ? 0 : Math.abs(decimals),
4376
- sep = (typeof thousands_sep === 'undefined') ? ',' : thousands_sep,
4377
- dec = (typeof dec_point === 'undefined') ? '.' : dec_point,
4534
+ prec = !isFinite(+decimals) ? 3 : Math.abs(decimals),
4535
+ sep = thousands || ",",
4536
+ dec = point || ".",
4378
4537
  s = '',
4379
4538
  toFixedFix = function(n, prec) {
4380
4539
  var k = Math.pow(10, prec)
@@ -4436,7 +4595,7 @@ var filters = avalon.filters = {
4436
4595
  if (reg) {
4437
4596
  a = a.replace(reg, function(s, name, value) {
4438
4597
  var quote = value.charAt(0)
4439
- return name + "=" + quote + "javascript:void(0)" + quote
4598
+ return name + "=" + quote + "javascript:void(0)" + quote// jshint ignore:line
4440
4599
  })
4441
4600
  }
4442
4601
  }
@@ -4461,9 +4620,7 @@ var filters = avalon.filters = {
4461
4620
  currency: function(amount, symbol, fractionSize) {
4462
4621
  return (symbol || "\uFFE5") + numberFormat(amount, isFinite(fractionSize) ? fractionSize : 2)
4463
4622
  },
4464
- number: function(number, fractionSize) {
4465
- return numberFormat(number, isFinite(fractionSize) ? fractionSize : 3)
4466
- }
4623
+ number: numberFormat
4467
4624
  }
4468
4625
  /*
4469
4626
  'yyyy': 4 digit representation of year (e.g. AD 1 => 0001, AD 2010 => 2010)
@@ -4498,7 +4655,7 @@ var filters = avalon.filters = {
4498
4655
  'mediumTime': equivalent to 'h:mm:ss a' for en_US locale (e.g. 12:05:08 pm)
4499
4656
  'shortTime': equivalent to 'h:mm a' for en_US locale (e.g. 12:05 pm)
4500
4657
  */
4501
- new function() {
4658
+ new function() {// jshint ignore:line
4502
4659
  function toInt(str) {
4503
4660
  return parseInt(str, 10) || 0
4504
4661
  }
@@ -4698,7 +4855,7 @@ new function() {
4698
4855
  }
4699
4856
  locate.SHORTMONTH = locate.MONTH
4700
4857
  filters.date.locate = locate
4701
- }
4858
+ }// jshint ignore:line
4702
4859
  /*********************************************************************
4703
4860
  * END *
4704
4861
  **********************************************************************/
@@ -4755,7 +4912,7 @@ new function() {
4755
4912
  var _avalon = window.avalon
4756
4913
  avalon.noConflict = function(deep) {
4757
4914
  if (deep && window.avalon === avalon) {
4758
- window.avalon = avalon
4915
+ window.avalon = _avalon
4759
4916
  }
4760
4917
  return avalon
4761
4918
  }