avalon-rails 1.4.1.1.20150404164109 → 1.4.6.0.20150915133100

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.mobile.shim.js 1.41 built in 2015.4.4
8
+ avalon.mobile.shim.js 1.46 built in 2015.9.11
9
9
  ==================================================*/
10
10
  (function(global, factory) {
11
11
 
@@ -75,10 +75,10 @@ var aslice = ap.slice
75
75
  var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖
76
76
  var W3C = window.dispatchEvent
77
77
  var root = DOC.documentElement
78
- var hyperspace = DOC.createDocumentFragment()
78
+ var avalonFragment = DOC.createDocumentFragment()
79
79
  var cinerator = DOC.createElement("div")
80
80
  var class2type = {}
81
- "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function(name) {
81
+ "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) {
82
82
  class2type["[object " + name + "]"] = name.toLowerCase()
83
83
  })
84
84
 
@@ -100,30 +100,35 @@ function oneObject(array, val) {
100
100
  }
101
101
 
102
102
  //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
103
- var generateID = function(prefix) {
103
+ var generateID = function (prefix) {
104
104
  prefix = prefix || "avalon"
105
- return (prefix + Math.random() + Math.random()).replace(/0\./g, "")
105
+ return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix)
106
106
  }
107
107
  function IE() {
108
108
  if (window.VBArray) {
109
109
  var mode = document.documentMode
110
110
  return mode ? mode : window.XMLHttpRequest ? 7 : 6
111
111
  } else {
112
- return 0
112
+ return NaN
113
113
  }
114
114
  }
115
115
  var IEVersion = IE()
116
116
 
117
- avalon = function(el) { //创建jQuery式的无new 实例化结构
117
+ avalon = function (el) { //创建jQuery式的无new 实例化结构
118
118
  return new avalon.init(el)
119
119
  }
120
120
 
121
+ avalon.profile = function () {
122
+ if (window.console && avalon.config.profile) {
123
+ Function.apply.call(console.log, console, arguments)
124
+ }
125
+ }
126
+
121
127
  /*视浏览器情况采用最快的异步回调*/
122
- avalon.nextTick = new function() {// jshint ignore:line
128
+ avalon.nextTick = new function () {// jshint ignore:line
123
129
  var tickImmediate = window.setImmediate
124
130
  var tickObserver = window.MutationObserver
125
- var tickPost = W3C && window.postMessage
126
- if (tickImmediate) {
131
+ if (tickImmediate) {//IE10 \11 edage
127
132
  return tickImmediate.bind(window)
128
133
  }
129
134
 
@@ -136,32 +141,32 @@ avalon.nextTick = new function() {// jshint ignore:line
136
141
  queue = queue.slice(n)
137
142
  }
138
143
 
139
- if (tickObserver) {
144
+ if (tickObserver) {// 支持MutationObserver
140
145
  var node = document.createTextNode("avalon")
141
146
  new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line
142
- return function(fn) {
147
+ return function (fn) {
143
148
  queue.push(fn)
144
149
  node.data = Math.random()
145
150
  }
146
151
  }
147
152
 
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) {
153
+ if (window.VBArray) {
154
+ return function (fn) {
158
155
  queue.push(fn)
159
- window.postMessage('process-tick', '*')
156
+ var node = DOC.createElement("script")
157
+ node.onreadystatechange = function () {
158
+ callback() //在interactive阶段就触发
159
+ node.onreadystatechange = null
160
+ head.removeChild(node)
161
+ node = null
162
+ }
163
+ head.appendChild(node)
160
164
  }
161
165
  }
162
166
 
163
- return function(fn) {
164
- setTimeout(fn, 0)
167
+
168
+ return function (fn) {
169
+ setTimeout(fn, 4)
165
170
  }
166
171
  }// jshint ignore:line
167
172
  /*********************************************************************
@@ -262,7 +267,7 @@ function _number(a, len) { //用于模拟slice, splice的效果
262
267
  avalon.mix({
263
268
  rword: rword,
264
269
  subscribers: subscribers,
265
- version: 1.41,
270
+ version: 1.46,
266
271
  ui: {},
267
272
  log: log,
268
273
  slice: function(nodes, start, end) {
@@ -465,6 +470,7 @@ var Cache = new function() {// jshint ignore:line
465
470
  entry.newer =
466
471
  entry.older =
467
472
  this._keymap[entry.key] = void 0
473
+ delete this._keymap[entry.key] //#1029
468
474
  }
469
475
  }
470
476
  p.get = function(key) {
@@ -641,7 +647,6 @@ if (DOC.onmousewheel === void 0) {
641
647
  }
642
648
  }
643
649
  }
644
-
645
650
  /*********************************************************************
646
651
  * 配置系统 *
647
652
  **********************************************************************/
@@ -679,15 +684,15 @@ var plugins = {
679
684
  openTag = array[0]
680
685
  closeTag = array[1]
681
686
  if (openTag === closeTag) {
682
- throw new SyntaxError("openTag!==closeTag")
683
- } else if (array + "" === "<!--,-->") {
684
- kernel.commentInterpolate = true
687
+ throw new SyntaxError("openTag===closeTag")
685
688
  } else {
686
689
  var test = openTag + "test" + closeTag
687
690
  cinerator.innerHTML = test
688
691
  if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("&lt;") > -1) {
689
692
  throw new SyntaxError("此定界符不合法")
690
693
  }
694
+ kernel.openTag = openTag
695
+ kernel.closeTag = closeTag
691
696
  cinerator.innerHTML = ""
692
697
  }
693
698
  var o = escapeRegExp(openTag),
@@ -776,8 +781,8 @@ var EventBus = {
776
781
  }
777
782
  //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识
778
783
  /* jshint ignore:start */
779
- Array.prototype.forEach.call(eventNodes, function (node) {
780
- Array.prototype.forEach.call(elements, function (element) {
784
+ ap.forEach.call(eventNodes, function (node) {
785
+ ap.forEach.call(elements, function (element) {
781
786
  var ok = special === "down" ? element.contains(node) : //向下捕获
782
787
  node.contains(element) //向上冒泡
783
788
  if (ok) {
@@ -791,7 +796,7 @@ var EventBus = {
791
796
  }
792
797
  var nodes = DOC.getElementsByTagName("*") //实现节点排序
793
798
  var alls = []
794
- Array.prototype.forEach.call(nodes, function (el) {
799
+ ap.forEach.call(nodes, function (el) {
795
800
  if (el._avalon) {
796
801
  alls.push(el._avalon)
797
802
  el._avalon = ""
@@ -820,12 +825,11 @@ var EventBus = {
820
825
  }
821
826
  }
822
827
  }
823
-
824
828
  /*********************************************************************
825
829
  * modelFactory *
826
830
  **********************************************************************/
827
831
  //avalon最核心的方法的两个方法之一(另一个是avalon.scan),返回一个ViewModel(VM)
828
- var VMODELS = avalon.vmodels = createMap() //所有vmodel都储存在这里
832
+ var VMODELS = avalon.vmodels = {} //所有vmodel都储存在这里
829
833
  avalon.define = function (id, factory) {
830
834
  var $id = id.$id || id
831
835
  if (!$id) {
@@ -851,194 +855,271 @@ avalon.define = function (id, factory) {
851
855
  }
852
856
 
853
857
  //一些不需要被监听的属性
854
- var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray").match(rword)
855
-
856
- function isObservable(name, value, $skipArray) {
857
- if (isFunction(value) || value && value.nodeType) {
858
- return false
859
- }
860
- if ($skipArray.indexOf(name) !== -1) {
861
- return false
862
- }
863
- if ($$skipArray.indexOf(name) !== -1) {
864
- return false
865
- }
866
- var $special = $skipArray.$special
867
- if (name && name.charAt(0) === "$" && !$special[name]) {
868
- return false
869
- }
870
- return true
871
- }
872
- //ms-with,ms-each, ms-repeat绑定生成的代理对象储存池
873
- var midway = createMap()
874
- function getNewValue(accessor, name, value, $vmodel) {
875
- switch (accessor.type) {
876
- case 0://计算属性
877
- var getter = accessor.get
878
- var setter = accessor.set
879
- if (isFunction(setter)) {
880
- var $events = $vmodel.$events
881
- var lock = $events[name]
882
- $events[name] = [] //清空回调,防止内部冒泡而触发多次$fire
883
- setter.call($vmodel, value)
884
- $events[name] = lock
885
- }
886
- return getter.call($vmodel) //同步$model
887
- case 1://监控属性
888
- return value
889
- case 2://对象属性(包括数组与哈希)
890
- if (value !== $vmodel.$model[name]) {
891
- var svmodel = accessor.svmodel = objectFactory($vmodel, name, value, accessor.valueType)
892
- value = svmodel.$model //同步$model
893
- var fn = midway[svmodel.$id]
894
- fn && fn() //同步视图
895
- }
896
- return value
897
- }
898
- }
858
+ var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray,$reinitialize").match(rword)
899
859
 
900
860
  function modelFactory(source, $special, $model) {
901
861
  if (Array.isArray(source)) {
902
862
  var arr = source.concat()
903
863
  source.length = 0
904
- var collection = Collection(source)// jshint ignore:line
864
+ var collection = arrayFactory(source)// jshint ignore:line
905
865
  collection.pushArray(arr)
906
866
  return collection
907
867
  }
908
- //0 null undefined || Node || VModel
868
+ //0 null undefined || Node || VModel(fix IE6-8 createWithProxy $val: val引发的BUG)
909
869
  if (!source || source.nodeType > 0 || (source.$id && source.$events)) {
910
870
  return source
911
871
  }
912
- if (!Array.isArray(source.$skipArray)) {
913
- source.$skipArray = []
914
- }
915
- source.$skipArray.$special = $special || createMap() //强制要监听的属性
872
+ var $skipArray = Array.isArray(source.$skipArray) ? source.$skipArray : []
873
+ $skipArray.$special = $special || createMap() //强制要监听的属性
916
874
  var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤
917
875
  $model = $model || {} //vmodels.$model属性
918
876
  var $events = createMap() //vmodel.$events属性
919
- var watchedProperties = createMap() //监控属性
920
- var initCallbacks = [] //初始化才执行的函数
921
- for (var i in source) {
922
- (function (name, val) {
923
- $model[name] = val
924
- if (!isObservable(name, val, source.$skipArray)) {
925
- return //过滤所有非监控属性
926
- }
877
+ var accessors = createMap() //监控属性
878
+ var computed = []
879
+ $$skipArray.forEach(function (name) {
880
+ delete source[name]
881
+ })
882
+
883
+ var names = Object.keys(source)
884
+ /* jshint ignore:start */
885
+ names.forEach(function (name, accessor) {
886
+ var val = source[name]
887
+ $model[name] = val
888
+ if (isObservable(name, val, $skipArray)) {
927
889
  //总共产生三种accessor
928
890
  $events[name] = []
929
891
  var valueType = avalon.type(val)
930
- var accessor = function (newValue) {
931
- var name = accessor._name
932
- var $vmodel = this
933
- var $model = $vmodel.$model
934
- var oldValue = $model[name]
935
- var $events = $vmodel.$events
936
-
937
- if (arguments.length) {
938
- if (stopRepeatAssign) {
939
- return
940
- }
941
- //计算属性与对象属性需要重新计算newValue
942
- if (accessor.type !== 1) {
943
- newValue = getNewValue(accessor, name, newValue, $vmodel)
944
- if (!accessor.type)
945
- return
946
- }
947
- if (!isEqual(oldValue, newValue)) {
948
- $model[name] = newValue
949
- notifySubscribers($events[name]) //同步视图
950
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
951
- }
952
- } else {
953
- if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性
954
- //计算属性不需要收集视图刷新函数,都是由其他监控属性代劳
955
- newValue = accessor.get.call($vmodel)
956
- if (oldValue !== newValue) {
957
- $model[name] = newValue
958
- //这里不用同步视图
959
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
960
- }
961
- return newValue
962
- } else {
963
- collectSubscribers($events[name]) //收集视图函数
964
- return accessor.svmodel || oldValue
965
- }
966
- }
967
- }
968
892
  //总共产生三种accessor
969
893
  if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) {
970
- //第1种为计算属性, 因变量,通过其他监控属性触发其改变
971
- accessor.set = val.set
972
- accessor.get = val.get
973
- accessor.type = 0
974
- initCallbacks.push(function () {
975
- var data = {
976
- evaluator: function () {
977
- data.type = Math.random(),
978
- data.element = null
979
- $model[name] = accessor.get.call($vmodel)
980
- },
981
- element: head,
982
- type: Math.random(),
983
- handler: noop,
984
- args: []
985
- }
986
- Registry[expose] = data
987
- accessor.call($vmodel)
988
- delete Registry[expose]
989
- })
894
+ accessor = makeComputedAccessor(name, val)
895
+ computed.push(accessor)
990
896
  } else if (rcomplexType.test(valueType)) {
991
- //第2种为对象属性,产生子VM与监控数组
992
- accessor.type = 2
993
- accessor.valueType = valueType
994
- initCallbacks.push(function () {
995
- var svmodel = modelFactory(val, 0, $model[name])
996
- accessor.svmodel = svmodel
997
- svmodel.$events[subscribers] = $events[name]
998
- })
897
+ // issue #940 解决$model层次依赖丢失 https://github.com/RubyLouvre/avalon/issues/940
898
+ accessor = makeComplexAccessor(name, val, valueType, $events[name], $model)
999
899
  } else {
1000
- accessor.type = 1
1001
- //第3种为监控属性,对应简单的数据类型,自变量
900
+ accessor = makeSimpleAccessor(name, val)
1002
901
  }
1003
- accessor._name = name
1004
- watchedProperties[name] = accessor
1005
- })(i, source[i])// jshint ignore:line
1006
- }
1007
-
1008
- $$skipArray.forEach(function (name) {
1009
- delete source[name]
1010
- delete $model[name] //这些特殊属性不应该在$model中出现
902
+ accessors[name] = accessor
903
+ }
1011
904
  })
1012
-
1013
- $vmodel = Object.defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel
1014
- for (var name in source) {
1015
- if (!watchedProperties[name]) {
905
+ /* jshint ignore:end */
906
+ $vmodel = Object.defineProperties($vmodel, descriptorFactory(accessors)) //生成一个空的ViewModel
907
+ for (var i = 0; i < names.length; i++) {
908
+ var name = names[i]
909
+ if (!accessors[name]) {
1016
910
  $vmodel[name] = source[name]
1017
911
  }
1018
912
  }
1019
913
  //添加$id, $model, $events, $watch, $unwatch, $fire
1020
- $vmodel.$id = generateID()
1021
- $vmodel.$model = $model
1022
- $vmodel.$events = $events
1023
- for (i in EventBus) {
1024
- $vmodel[i] = EventBus[i]
914
+ hideProperty($vmodel, "$id", generateID())
915
+ hideProperty($vmodel, "$model", $model)
916
+ hideProperty($vmodel, "$events", $events)
917
+ /* jshint ignore:start */
918
+ hideProperty($vmodel, "hasOwnProperty", function (name) {
919
+ return name in this.$model
920
+ })
921
+ /* jshint ignore:end */
922
+ for (var i in EventBus) {
923
+ hideProperty($vmodel, i, EventBus[i])
1025
924
  }
1026
925
 
1027
- Object.defineProperty($vmodel, "hasOwnProperty", {
1028
- value: function (name) {
1029
- return name in this.$model
1030
- },
1031
- writable: false,
926
+ $vmodel.$reinitialize = function () {
927
+ computed.forEach(function (accessor) {
928
+ delete accessor._value
929
+ delete accessor.oldArgs
930
+ accessor.digest = function () {
931
+ accessor.call($vmodel)
932
+ }
933
+ dependencyDetection.begin({
934
+ callback: function (vm, dependency) {//dependency为一个accessor
935
+ var name = dependency._name
936
+ if (dependency !== accessor) {
937
+ var list = vm.$events[name]
938
+ accessor.vm = $vmodel
939
+ injectDependency(list, accessor.digest)
940
+ }
941
+ }
942
+ })
943
+ try {
944
+ accessor.get.call($vmodel)
945
+ } finally {
946
+ dependencyDetection.end()
947
+ }
948
+ })
949
+ }
950
+ $vmodel.$reinitialize()
951
+ return $vmodel
952
+ }
953
+
954
+ function hideProperty(host, name, value) {
955
+ Object.defineProperty(host, name, {
956
+ value: value,
957
+ writable: true,
1032
958
  enumerable: false,
1033
959
  configurable: true
1034
960
  })
961
+ }
1035
962
 
1036
- initCallbacks.forEach(function (cb) { //收集依赖
1037
- cb()
1038
- })
1039
- return $vmodel
963
+ function keysVM(obj) {
964
+ var arr = Object.keys(obj)
965
+ for (var i = 0; i < $$skipArray.length; i++) {
966
+ var index = arr.indexOf($$skipArray[i])
967
+ if (index !== -1) {
968
+ arr.splice(index, 1)
969
+ }
970
+ }
971
+ return arr
972
+ }
973
+ //创建一个简单访问器
974
+ function makeSimpleAccessor(name, value) {
975
+ function accessor(value) {
976
+ var oldValue = accessor._value
977
+ if (arguments.length > 0) {
978
+ if (!stopRepeatAssign && !isEqual(value, oldValue)) {
979
+ accessor.updateValue(this, value)
980
+ accessor.notify(this, value, oldValue)
981
+ }
982
+ return this
983
+ } else {
984
+ dependencyDetection.collectDependency(this, accessor)
985
+ return oldValue
986
+ }
987
+ }
988
+ accessorFactory(accessor, name)
989
+ accessor._value = value
990
+ return accessor;
991
+ }
992
+
993
+ ///创建一个计算访问器
994
+ function makeComputedAccessor(name, options) {
995
+ function accessor(value) {//计算属性
996
+ var oldValue = accessor._value
997
+ var init = ("_value" in accessor)
998
+ if (arguments.length > 0) {
999
+ if (stopRepeatAssign) {
1000
+ return this
1001
+ }
1002
+ if (typeof accessor.set === "function") {
1003
+ if (accessor.oldArgs !== value) {
1004
+ accessor.oldArgs = value
1005
+ var $events = this.$events
1006
+ var lock = $events[name]
1007
+ $events[name] = [] //清空回调,防止内部冒泡而触发多次$fire
1008
+ accessor.set.call(this, value)
1009
+ $events[name] = lock
1010
+ value = accessor.get.call(this)
1011
+ if (value !== oldValue) {
1012
+ accessor.updateValue(this, value)
1013
+ accessor.notify(this, value, oldValue) //触发$watch回调
1014
+ }
1015
+ }
1016
+ }
1017
+ return this
1018
+ } else {
1019
+ //将依赖于自己的高层访问器或视图刷新函数(以绑定对象形式)放到自己的订阅数组中
1020
+ //将自己注入到低层访问器的订阅数组中
1021
+ value = accessor.get.call(this)
1022
+ accessor.updateValue(this, value)
1023
+ if (init && oldValue !== value) {
1024
+ accessor.notify(this, value, oldValue) //触发$watch回调
1025
+ }
1026
+ return value
1027
+ }
1028
+ }
1029
+ accessor.set = options.set
1030
+ accessor.get = options.get
1031
+ accessorFactory(accessor, name)
1032
+ return accessor
1033
+ }
1034
+
1035
+
1036
+ //创建一个复杂访问器
1037
+ function makeComplexAccessor(name, initValue, valueType, list, parentModel) {
1038
+ function accessor(value) {
1039
+ var oldValue = accessor._value
1040
+ var son = accessor._vmodel
1041
+ if (arguments.length > 0) {
1042
+ if (stopRepeatAssign) {
1043
+ return this
1044
+ }
1045
+ if (valueType === "array") {
1046
+ var a = son, b = value,
1047
+ an = a.length,
1048
+ bn = b.length
1049
+ a.$lock = true
1050
+ if (an > bn) {
1051
+ a.splice(bn, an - bn)
1052
+ } else if (bn > an) {
1053
+ a.push.apply(a, b.slice(an))
1054
+ }
1055
+ var n = Math.min(an, bn)
1056
+ for (var i = 0; i < n; i++) {
1057
+ a.set(i, b[i])
1058
+ }
1059
+ delete a.$lock
1060
+ a._fire("set")
1061
+ } else if (valueType === "object") {
1062
+ var observes = this.$events[name] || []
1063
+ var newObject = avalon.mix(true, {}, value)
1064
+ for (i in son) {
1065
+ if (son.hasOwnProperty(i) && ohasOwn.call(newObject, i)) {
1066
+ son[i] = newObject[i]
1067
+ }
1068
+ }
1069
+ son = accessor._vmodel = modelFactory(value)
1070
+ son.$events[subscribers] = observes
1071
+ if (observes.length) {
1072
+ observes.forEach(function (data) {
1073
+ if(!data.type) {
1074
+ return //防止模板先加载报错
1075
+ }
1076
+ if (data.rollback) {
1077
+ data.rollback() //还原 ms-with ms-on
1078
+ }
1079
+ bindingHandlers[data.type](data, data.vmodels)
1080
+ })
1081
+ }
1082
+ }
1083
+ accessor.updateValue(this, son.$model)
1084
+ accessor.notify(this, this._value, oldValue)
1085
+ return this
1086
+ } else {
1087
+ dependencyDetection.collectDependency(this, accessor)
1088
+ return son
1089
+ }
1090
+ }
1091
+ accessorFactory(accessor, name)
1092
+ if (Array.isArray(initValue)) {
1093
+ parentModel[name] = initValue
1094
+ } else {
1095
+ parentModel[name] = parentModel[name] || {}
1096
+ }
1097
+ var son = accessor._vmodel = modelFactory(initValue, 0, parentModel[name])
1098
+ son.$events[subscribers] = list
1099
+ return accessor
1100
+ }
1101
+
1102
+ function globalUpdateValue(vmodel, value) {
1103
+ vmodel.$model[this._name] = this._value = value
1104
+ }
1105
+ function globalUpdateModelValue(vmodel, value) {
1106
+ vmodel.$model[this._name] = value
1107
+ }
1108
+ function globalNotify(vmodel, value, oldValue) {
1109
+ var name = this._name
1110
+ var array = vmodel.$events[name] //刷新值
1111
+ if (array) {
1112
+ fireDependencies(array) //同步视图
1113
+ EventBus.$fire.call(vmodel, name, value, oldValue) //触发$watch回调
1114
+ }
1040
1115
  }
1041
1116
 
1117
+ function accessorFactory(accessor, name) {
1118
+ accessor._name = name
1119
+ //同时更新_value与model
1120
+ accessor.updateValue = globalUpdateValue
1121
+ accessor.notify = globalNotify
1122
+ }
1042
1123
  //比较两个值是否相等
1043
1124
  var isEqual = Object.is || function (v1, v2) {
1044
1125
  if (v1 === 0 && v2 === 0) {
@@ -1050,14 +1131,22 @@ var isEqual = Object.is || function (v1, v2) {
1050
1131
  }
1051
1132
  }
1052
1133
 
1053
- function safeFire(a, b, c, d) {
1054
- if (a.$events) {
1055
- EventBus.$fire.call(a, b, c, d)
1134
+ function isObservable(name, value, $skipArray) {
1135
+ if (isFunction(value) || value && value.nodeType) {
1136
+ return false
1137
+ }
1138
+ if ($skipArray.indexOf(name) !== -1) {
1139
+ return false
1140
+ }
1141
+ var $special = $skipArray.$special
1142
+ if (name && name.charAt(0) === "$" && !$special[name]) {
1143
+ return false
1056
1144
  }
1145
+ return true
1057
1146
  }
1058
1147
 
1059
1148
  var descriptorFactory = function (obj) {
1060
- var descriptors = createMap()
1149
+ var descriptors = {}
1061
1150
  for (var i in obj) {
1062
1151
  descriptors[i] = {
1063
1152
  get: obj[i],
@@ -1069,50 +1158,12 @@ var descriptorFactory = function (obj) {
1069
1158
  return descriptors
1070
1159
  }
1071
1160
 
1072
- //应用于第2种accessor
1073
- function objectFactory(parent, name, value, valueType) {
1074
- //a为原来的VM, b为新数组或新对象
1075
- var son = parent[name]
1076
- if (valueType === "array") {
1077
- if (!Array.isArray(value) || son === value) {
1078
- return son //fix https://github.com/RubyLouvre/avalon/issues/261
1079
- }
1080
- son._.$unwatch()
1081
- son.clear()
1082
- son._.$watch()
1083
- son.pushArray(value.concat())
1084
- return son
1085
- } else {
1086
- var iterators = parent.$events[name]
1087
- var pool = son.$events.$withProxyPool
1088
- if (pool) {
1089
- recycleProxies(pool, "with")
1090
- son.$events.$withProxyPool = null
1091
- }
1092
- var ret = modelFactory(value)
1093
- ret.$events[subscribers] = iterators
1094
- midway[ret.$id] = function (data) {
1095
- while (data = iterators.shift()) {
1096
- (function (el) {
1097
- avalon.nextTick(function () {
1098
- var type = el.type
1099
- if (type && bindingHandlers[type]) { //#753
1100
- el.rollback && el.rollback() //还原 ms-with ms-on
1101
- bindingHandlers[type](el, el.vmodels)
1102
- }
1103
- })
1104
- })(data)// jshint ignore:line
1105
- }
1106
- delete midway[ret.$id]
1107
- }
1108
- return ret
1109
- }
1110
- }
1161
+
1111
1162
  /*********************************************************************
1112
1163
  * 监控数组(与ms-each, ms-repeat配合使用) *
1113
1164
  **********************************************************************/
1114
1165
 
1115
- function Collection(model) {
1166
+ function arrayFactory(model) {
1116
1167
  var array = []
1117
1168
  array.$id = generateID()
1118
1169
  array.$model = model //数据模型
@@ -1127,7 +1178,7 @@ function Collection(model) {
1127
1178
  for (var i in EventBus) {
1128
1179
  array[i] = EventBus[i]
1129
1180
  }
1130
- avalon.mix(array, CollectionPrototype)
1181
+ avalon.mix(array, arrayPrototype)
1131
1182
  return array
1132
1183
  }
1133
1184
 
@@ -1135,7 +1186,7 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1135
1186
  var oldLen = this.length, loop = 2
1136
1187
  while (--loop) {
1137
1188
  switch (method) {
1138
- case "add":
1189
+ case "add":
1139
1190
  /* jshint ignore:start */
1140
1191
  var array = this.$model.slice(pos, pos + n).map(function (el) {
1141
1192
  if (rcomplexType.test(avalon.type(el))) {
@@ -1169,10 +1220,10 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1169
1220
  }
1170
1221
 
1171
1222
  var _splice = ap.splice
1172
- var CollectionPrototype = {
1223
+ var arrayPrototype = {
1173
1224
  _splice: _splice,
1174
1225
  _fire: function (method, a, b) {
1175
- notifySubscribers(this.$events[subscribers], method, a, b)
1226
+ fireDependencies(this.$events[subscribers], method, a, b)
1176
1227
  },
1177
1228
  size: function () { //取得数组长度,这个函数可以同步视图,length不能
1178
1229
  return this._.length
@@ -1258,11 +1309,13 @@ var CollectionPrototype = {
1258
1309
  },
1259
1310
  removeAll: function (all) { //移除N个元素
1260
1311
  if (Array.isArray(all)) {
1261
- all.forEach(function (el) {
1262
- this.remove(el)
1263
- }, this)
1264
- } else if (typeof all === "function") {
1265
1312
  for (var i = this.length - 1; i >= 0; i--) {
1313
+ if (all.indexOf(this[i]) !== -1) {
1314
+ this.removeAt(i)
1315
+ }
1316
+ }
1317
+ } else if (typeof all === "function") {
1318
+ for ( i = this.length - 1; i >= 0; i--) {
1266
1319
  var el = this[i]
1267
1320
  if (all(el, i)) {
1268
1321
  this.removeAt(i)
@@ -1279,7 +1332,7 @@ var CollectionPrototype = {
1279
1332
  return this
1280
1333
  },
1281
1334
  set: function (index, val) {
1282
- if (index >= 0) {
1335
+ if (index < this.length && index > -1) {
1283
1336
  var valueType = avalon.type(val)
1284
1337
  if (val && val.$model) {
1285
1338
  val = val.$model
@@ -1302,6 +1355,15 @@ var CollectionPrototype = {
1302
1355
  return this
1303
1356
  }
1304
1357
  }
1358
+ //相当于原来bindingExecutors.repeat 的index分支
1359
+ function resetIndex(array, pos) {
1360
+ var last = array.length - 1
1361
+ for (var el; el = array[pos]; pos++) {
1362
+ el.$index = pos
1363
+ el.$first = pos === 0
1364
+ el.$last = pos === last
1365
+ }
1366
+ }
1305
1367
 
1306
1368
  function sortByIndex(array, indexes) {
1307
1369
  var map = {};
@@ -1318,7 +1380,7 @@ function sortByIndex(array, indexes) {
1318
1380
  }
1319
1381
 
1320
1382
  "sort,reverse".replace(rword, function (method) {
1321
- CollectionPrototype[method] = function () {
1383
+ arrayPrototype[method] = function () {
1322
1384
  var newArray = this.$model//这是要排序的新数组
1323
1385
  var oldArray = newArray.concat() //保持原来状态的旧数组
1324
1386
  var mask = Math.random()
@@ -1339,28 +1401,57 @@ function sortByIndex(array, indexes) {
1339
1401
  }
1340
1402
  if (hasSort) {
1341
1403
  sortByIndex(this, indexes)
1404
+ // sortByIndex(this.$proxy, indexes)
1342
1405
  this._fire("move", indexes)
1343
- this._fire("index", 0)
1406
+ this._fire("index", 0)
1344
1407
  }
1345
1408
  return this
1346
1409
  }
1347
1410
  })
1348
1411
 
1412
+
1349
1413
  /*********************************************************************
1350
1414
  * 依赖调度系统 *
1351
1415
  **********************************************************************/
1416
+ //检测两个对象间的依赖关系
1417
+ var dependencyDetection = (function () {
1418
+ var outerFrames = []
1419
+ var currentFrame
1420
+ return {
1421
+ begin: function (accessorObject) {
1422
+ //accessorObject为一个拥有callback的对象
1423
+ outerFrames.push(currentFrame)
1424
+ currentFrame = accessorObject
1425
+ },
1426
+ end: function () {
1427
+ currentFrame = outerFrames.pop()
1428
+ },
1429
+ collectDependency: function (vmodel, accessor) {
1430
+ if (currentFrame) {
1431
+ //被dependencyDetection.begin调用
1432
+ currentFrame.callback(vmodel, accessor);
1433
+ }
1434
+ }
1435
+ };
1436
+ })()
1437
+ //将绑定对象注入到其依赖项的订阅数组中
1352
1438
  var ronduplex = /^(duplex|on)$/
1353
-
1354
- function registerSubscriber(data) {
1355
- Registry[expose] = data //暴光此函数,方便collectSubscribers收集
1356
- avalon.openComputedCollect = true
1357
- var fn = data.evaluator
1358
- if (fn) { //如果是求值函数
1439
+ avalon.injectBinding = function (data) {
1440
+ var valueFn = data.evaluator
1441
+ if (valueFn) { //如果是求值函数
1442
+ dependencyDetection.begin({
1443
+ callback: function (vmodel, dependency) {
1444
+ injectDependency(vmodel.$events[dependency._name], data)
1445
+ }
1446
+ })
1359
1447
  try {
1360
- var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args)
1361
- data.handler(c, data.element, data)
1448
+ var value = ronduplex.test(data.type) ? data : valueFn.apply(0, data.args)
1449
+ if(value === void 0){
1450
+ delete data.evaluator
1451
+ }
1452
+ data.handler(value, data.element, data)
1362
1453
  } catch (e) {
1363
- //log("warning:exception throwed in [registerSubscriber] " + e)
1454
+ log("warning:exception throwed in [avalon.injectBinding] " , e)
1364
1455
  delete data.evaluator
1365
1456
  var node = data.element
1366
1457
  if (node.nodeType === 3) {
@@ -1368,126 +1459,160 @@ function registerSubscriber(data) {
1368
1459
  if (kernel.commentInterpolate) {
1369
1460
  parent.replaceChild(DOC.createComment(data.value), node)
1370
1461
  } else {
1371
- node.data = openTag + data.value + closeTag
1462
+ node.data = openTag + (data.oneTime ? "::" : "") + data.value + closeTag
1372
1463
  }
1373
1464
  }
1465
+ } finally {
1466
+ dependencyDetection.end()
1374
1467
  }
1375
1468
  }
1376
- avalon.openComputedCollect = false
1377
- delete Registry[expose]
1378
1469
  }
1379
1470
 
1380
- function collectSubscribers(list) { //收集依赖于这个访问器的订阅者
1381
- var data = Registry[expose]
1382
- if (list && data && avalon.Array.ensure(list, data) && data.element) { //只有数组不存在此元素才push进去
1383
- addSubscribers(data, list)
1471
+ //将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组
1472
+ function injectDependency(list, data) {
1473
+ if (data.oneTime)
1474
+ return
1475
+ if (list && avalon.Array.ensure(list, data) && data.element) {
1476
+ injectDisposeQueue(data, list)
1477
+ if (new Date() - beginTime > 444 ) {
1478
+ rejectDisposeQueue()
1479
+ }
1384
1480
  }
1385
1481
  }
1386
1482
 
1387
-
1388
- function addSubscribers(data, list) {
1389
- data.$uuid = data.$uuid || generateID()
1390
- list.$uuid = list.$uuid || generateID()
1391
- var obj = {
1392
- data: data,
1393
- list: list,
1394
- $$uuid: data.$uuid + list.$uuid
1395
- }
1396
- if (!$$subscribers[obj.$$uuid]) {
1397
- $$subscribers[obj.$$uuid] = 1
1398
- $$subscribers.push(obj)
1483
+ //通知依赖于这个访问器的订阅者更新自身
1484
+ function fireDependencies(list) {
1485
+ if (list && list.length) {
1486
+ if (new Date() - beginTime > 444 && typeof list[0] === "object") {
1487
+ rejectDisposeQueue()
1488
+ }
1489
+ var args = aslice.call(arguments, 1)
1490
+ for (var i = list.length, fn; fn = list[--i]; ) {
1491
+ var el = fn.element
1492
+ if (el && el.parentNode) {
1493
+ try {
1494
+ var valueFn = fn.evaluator
1495
+ if (fn.$repeat) {
1496
+ fn.handler.apply(fn, args) //处理监控数组的方法
1497
+ }else if("$repeat" in fn || !valueFn ){//如果没有eval,先eval
1498
+ bindingHandlers[fn.type](fn, fn.vmodels)
1499
+ } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发
1500
+ var value = valueFn.apply(0, fn.args || [])
1501
+ fn.handler(value, el, fn)
1502
+ }
1503
+ } catch (e) {
1504
+ console.log(e)
1505
+ }
1506
+ }
1507
+ }
1399
1508
  }
1400
1509
  }
1401
-
1402
- function disposeData(data) {
1403
- data.element = null
1404
- data.rollback && data.rollback()
1405
- for (var key in data) {
1406
- data[key] = null
1510
+ /*********************************************************************
1511
+ * 定时GC回收机制 *
1512
+ **********************************************************************/
1513
+ var disposeCount = 0
1514
+ var disposeQueue = avalon.$$subscribers = []
1515
+ var beginTime = new Date()
1516
+ var oldInfo = {}
1517
+ //var uuid2Node = {}
1518
+ function getUid(elem, makeID) { //IE9+,标准浏览器
1519
+ if (!elem.uuid && !makeID) {
1520
+ elem.uuid = ++disposeCount
1407
1521
  }
1522
+ return elem.uuid
1408
1523
  }
1409
1524
 
1410
- function isRemove(el) {
1411
- try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错
1412
- if (!el.parentNode) {
1413
- return true
1525
+ //添加到回收列队中
1526
+ function injectDisposeQueue(data, list) {
1527
+ var elem = data.element
1528
+ if (!data.uuid) {
1529
+ if (elem.nodeType !== 1) {
1530
+ data.uuid = data.type + (data.pos || 0) + "-" + getUid(elem.parentNode)
1531
+ } else {
1532
+ data.uuid = data.name + "-" + getUid(elem)
1414
1533
  }
1415
- } catch (e) {
1416
- return true
1417
1534
  }
1418
- return el.msRetain ? 0 : (el.nodeType === 1 ? typeof el.sourceIndex === "number" ?
1419
- el.sourceIndex === 0 : !root.contains(el) : !avalon.contains(root, el))
1535
+ var lists = data.lists || (data.lists = [])
1536
+ avalon.Array.ensure(lists, list)
1537
+ list.$uuid = list.$uuid || generateID()
1538
+ if (!disposeQueue[data.uuid]) {
1539
+ disposeQueue[data.uuid] = 1
1540
+ disposeQueue.push(data)
1541
+ }
1420
1542
  }
1421
- var $$subscribers = avalon.$$subscribers = []
1422
- var beginTime = new Date()
1423
- var oldInfo = {}
1424
- function removeSubscribers() {
1425
- var i = $$subscribers.length
1543
+
1544
+ function rejectDisposeQueue(data) {
1545
+ if (avalon.optimize)
1546
+ return
1547
+ var i = disposeQueue.length
1426
1548
  var n = i
1427
- var k = 0
1428
- var obj
1429
- var types = []
1549
+ var allTypes = []
1550
+ var iffishTypes = {}
1430
1551
  var newInfo = {}
1431
- var needTest = {}
1432
- while (obj = $$subscribers[--i]) {
1433
- var data = obj.data
1552
+ //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型
1553
+ while (data = disposeQueue[--i]) {
1434
1554
  var type = data.type
1435
1555
  if (newInfo[type]) {
1436
1556
  newInfo[type]++
1437
1557
  } else {
1438
1558
  newInfo[type] = 1
1439
- types.push(type)
1559
+ allTypes.push(type)
1440
1560
  }
1441
1561
  }
1442
1562
  var diff = false
1443
- types.forEach(function(type) {
1563
+ allTypes.forEach(function (type) {
1444
1564
  if (oldInfo[type] !== newInfo[type]) {
1445
- needTest[type] = 1
1565
+ iffishTypes[type] = 1
1446
1566
  diff = true
1447
1567
  }
1448
1568
  })
1449
1569
  i = n
1450
- //avalon.log("需要检测的个数 " + i)
1451
1570
  if (diff) {
1452
- //avalon.log("有需要移除的元素")
1453
- while (obj = $$subscribers[--i]) {
1454
- data = obj.data
1455
- if (data.element === void 0)
1571
+ while (data = disposeQueue[--i]) {
1572
+ if (data.element === null) {
1573
+ disposeQueue.splice(i, 1)
1456
1574
  continue
1457
- if (needTest[data.type] && isRemove(data.element)) { //如果它没有在DOM树
1458
- k++
1459
- $$subscribers.splice(i, 1)
1460
- delete $$subscribers[obj.$$uuid]
1461
- avalon.Array.remove(obj.list, data)
1462
- //log("debug: remove " + data.type)
1575
+ }
1576
+ if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树
1577
+ disposeQueue.splice(i, 1)
1578
+ delete disposeQueue[data.uuid]
1579
+ //delete uuid2Node[data.element.uuid]
1580
+ var lists = data.lists
1581
+ for (var k = 0, list; list = lists[k++]; ) {
1582
+ avalon.Array.remove(lists, list)
1583
+ avalon.Array.remove(list, data)
1584
+ }
1463
1585
  disposeData(data)
1464
- obj.data = obj.list = null
1465
1586
  }
1466
1587
  }
1467
1588
  }
1468
1589
  oldInfo = newInfo
1469
- // avalon.log("已经移除的个数 " + k)
1470
1590
  beginTime = new Date()
1471
1591
  }
1472
1592
 
1473
- function notifySubscribers(list) { //通知依赖于这个访问器的订阅者更新自身
1474
- if (list && list.length) {
1475
- if (new Date() - beginTime > 444 && typeof list[0] === "object") {
1476
- removeSubscribers()
1477
- }
1478
- var args = aslice.call(arguments, 1)
1479
- for (var i = list.length, fn; fn = list[--i]; ) {
1480
- var el = fn.element
1481
- if (el && el.parentNode) {
1482
- if (fn.$repeat) {
1483
- fn.handler.apply(fn, args) //处理监控数组的方法
1484
- } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发
1485
- var fun = fn.evaluator || noop
1486
- fn.handler(fun.apply(0, fn.args || []), el, fn)
1487
- }
1488
- }
1593
+ function disposeData(data) {
1594
+ delete disposeQueue[data.uuid] // 先清除,不然无法回收了
1595
+ data.element = null
1596
+ data.rollback && data.rollback()
1597
+ for (var key in data) {
1598
+ data[key] = null
1599
+ }
1600
+ }
1601
+
1602
+ function shouldDispose(el) {
1603
+ try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错
1604
+ var fireError = el.parentNode.nodeType
1605
+ } catch (e) {
1606
+ return true
1607
+ }
1608
+ if (el.ifRemove) {
1609
+ // 如果节点被放到ifGroup,才移除
1610
+ if (!root.contains(el.ifRemove) && (ifGroup === el.parentNode)) {
1611
+ el.parentNode && el.parentNode.removeChild(el)
1612
+ return true
1489
1613
  }
1490
1614
  }
1615
+ return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el))
1491
1616
  }
1492
1617
 
1493
1618
  /************************************************************************
@@ -1520,7 +1645,7 @@ var scriptTypes = oneObject(["", "text/javascript", "text/ecmascript", "applicat
1520
1645
  var script = DOC.createElement("script")
1521
1646
  var rhtml = /<|&#?\w+;/
1522
1647
  avalon.parseHTML = function(html) {
1523
- var fragment = hyperspace.cloneNode(false)
1648
+ var fragment = avalonFragment.cloneNode(false)
1524
1649
  if (typeof html !== "string" ) {
1525
1650
  return fragment
1526
1651
  }
@@ -1568,351 +1693,30 @@ avalon.clearHTML = function(node) {
1568
1693
  }
1569
1694
 
1570
1695
  /*********************************************************************
1571
- * 扫描系统 *
1696
+ * avalon的原型方法定义区 *
1572
1697
  **********************************************************************/
1573
1698
 
1574
- avalon.scan = function(elem, vmodel, group) {
1575
- elem = elem || root
1576
- var vmodels = vmodel ? [].concat(vmodel) : []
1577
- scanTag(elem, vmodels)
1699
+ function hyphen(target) {
1700
+ //转换为连字符线风格
1701
+ return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase()
1578
1702
  }
1579
1703
 
1580
- //http://www.w3.org/TR/html5/syntax.html#void-elements
1581
- var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase())
1582
-
1583
- function checkScan(elem, callback, innerHTML) {
1584
- var id = setTimeout(function() {
1585
- var currHTML = elem.innerHTML
1586
- clearTimeout(id)
1587
- if (currHTML === innerHTML) {
1588
- callback()
1589
- } else {
1590
- checkScan(elem, callback, currHTML)
1591
- }
1704
+ function camelize(target) {
1705
+ //转换为驼峰风格
1706
+ if (target.indexOf("-") < 0 && target.indexOf("_") < 0) {
1707
+ return target //提前判断,提高getStyle等的效率
1708
+ }
1709
+ return target.replace(/[-_][^-_]/g, function(match) {
1710
+ return match.charAt(1).toUpperCase()
1592
1711
  })
1593
1712
  }
1594
1713
 
1595
-
1596
- function createSignalTower(elem, vmodel) {
1597
- var id = elem.getAttribute("avalonctrl") || vmodel.$id
1598
- elem.setAttribute("avalonctrl", id)
1599
- vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]'
1600
- }
1601
-
1602
- var getBindingCallback = function(elem, name, vmodels) {
1603
- var callback = elem.getAttribute(name)
1604
- if (callback) {
1605
- for (var i = 0, vm; vm = vmodels[i++]; ) {
1606
- if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
1607
- return vm[callback]
1608
- }
1609
- }
1610
- }
1611
- }
1612
-
1613
- function executeBindings(bindings, vmodels) {
1614
- for (var i = 0, data; data = bindings[i++]; ) {
1615
- data.vmodels = vmodels
1616
- bindingHandlers[data.type](data, vmodels)
1617
- if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
1618
- //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
1619
- data.element.removeAttribute(data.name)
1620
- }
1621
- }
1622
- bindings.length = 0
1623
- }
1624
-
1625
- //https://github.com/RubyLouvre/avalon/issues/636
1626
- var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) {
1627
- var node = elem.firstChild, text
1628
- while (node) {
1629
- var aaa = node.nextSibling
1630
- if (node.nodeType === 3) {
1631
- if (text) {
1632
- text.nodeValue += node.nodeValue
1633
- elem.removeChild(node)
1634
- } else {
1635
- text = node
1636
- }
1637
- } else {
1638
- text = null
1639
- }
1640
- node = aaa
1641
- }
1642
- } : 0
1643
-
1644
- var rmsAttr = /ms-(\w+)-?(.*)/
1645
- var priorityMap = {
1646
- "if": 10,
1647
- "repeat": 90,
1648
- "data": 100,
1649
- "widget": 110,
1650
- "each": 1400,
1651
- "with": 1500,
1652
- "duplex": 2000,
1653
- "on": 3000
1654
- }
1655
-
1656
- var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
1657
- var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled")
1658
- function bindingSorter(a, b) {
1659
- return a.priority - b.priority
1660
- }
1661
-
1662
- function scanAttr(elem, vmodels) {
1663
- //防止setAttribute, removeAttribute时 attributes自动被同步,导致for循环出错
1664
- var attributes = elem.hasAttributes() ? avalon.slice(elem.attributes) : []
1665
- var bindings = [],
1666
- msData = createMap(),
1667
- match
1668
- for (var i = 0, attr; attr = attributes[i++]; ) {
1669
- if (attr.specified) {
1670
- if (match = attr.name.match(rmsAttr)) {
1671
- //如果是以指定前缀命名的
1672
- var type = match[1]
1673
- var param = match[2] || ""
1674
- var value = attr.value
1675
- var name = attr.name
1676
- msData[name] = value
1677
- if (events[type]) {
1678
- param = type
1679
- type = "on"
1680
- } else if (obsoleteAttrs[type]) {
1681
- log("warning!请改用ms-attr-" + type + "代替ms-" + type + "!")
1682
- if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
1683
- log("warning!ms-enabled或ms-attr-enabled已经被废弃")
1684
- type = "disabled"
1685
- value = "!(" + value + ")"
1686
- }
1687
- param = type
1688
- type = "attr"
1689
- elem.removeAttribute(name)
1690
- name = "ms-attr-" + param
1691
- elem.setAttribute(name, value)
1692
- match = [name]
1693
- msData[name] = value
1694
- }
1695
- if (typeof bindingHandlers[type] === "function") {
1696
- var binding = {
1697
- type: type,
1698
- param: param,
1699
- element: elem,
1700
- name: match[0],
1701
- value: value,
1702
- priority: type in priorityMap ? priorityMap[type] : type.charCodeAt(0) * 10 + (Number(param) || 0)
1703
- }
1704
- if (type === "html" || type === "text") {
1705
- var token = getToken(value)
1706
- avalon.mix(binding, token)
1707
- binding.filters = binding.filters.replace(rhasHtml, function () {
1708
- binding.type = "html"
1709
- binding.group = 1
1710
- return ""
1711
- })// jshint ignore:line
1712
- }
1713
- if (name === "ms-if-loop") {
1714
- binding.priority += 100
1715
- }
1716
- if (vmodels.length) {
1717
- bindings.push(binding)
1718
- if (type === "widget") {
1719
- elem.msData = elem.msData || msData
1720
- }
1721
- }
1722
- }
1723
- }
1724
- }
1725
- }
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
- }
1734
- }
1735
- bindings.sort(bindingSorter)
1736
- var scanNode = true
1737
- for (i = 0; binding = bindings[i]; i++) {
1738
- type = binding.type
1739
- if (rnoscanAttrBinding.test(type)) {
1740
- return executeBindings(bindings.slice(0, i + 1), vmodels)
1741
- } else if (scanNode) {
1742
- scanNode = !rnoscanNodeBinding.test(type)
1743
- }
1744
- }
1745
- executeBindings(bindings, vmodels)
1746
- if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML + elem.textContent)) {
1747
- mergeTextNodes && mergeTextNodes(elem)
1748
- scanNodeList(elem, vmodels) //扫描子孙元素
1749
- }
1750
- }
1751
-
1752
- var rnoscanAttrBinding = /^if|widget|repeat$/
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
- }
1798
- var rhasHtml = /\|\s*html\s*/,
1799
- r11a = /\|\|/g,
1800
- rlt = /&lt;/g,
1801
- rgt = /&gt;/g
1802
- rstringLiteral = /(['"])(\\\1|.)+?\1/g
1803
- function getToken(value) {
1804
- if (value.indexOf("|") > 0) {
1805
- var scapegoat = value.replace( rstringLiteral, function(_){
1806
- return Array(_.length+1).join("1")// jshint ignore:line
1807
- })
1808
- var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
1809
- if (index > -1) {
1810
- return {
1811
- filters: value.slice(index),
1812
- value: value.slice(0, index),
1813
- expr: true
1814
- }
1815
- }
1816
- }
1817
- return {
1818
- value: value,
1819
- filters: "",
1820
- expr: true
1821
- }
1822
- }
1823
-
1824
- function scanExpr(str) {
1825
- var tokens = [],
1826
- value, start = 0,
1827
- stop
1828
- do {
1829
- stop = str.indexOf(openTag, start)
1830
- if (stop === -1) {
1831
- break
1832
- }
1833
- value = str.slice(start, stop)
1834
- if (value) { // {{ 左边的文本
1835
- tokens.push({
1836
- value: value,
1837
- filters: "",
1838
- expr: false
1839
- })
1840
- }
1841
- start = stop + openTag.length
1842
- stop = str.indexOf(closeTag, start)
1843
- if (stop === -1) {
1844
- break
1845
- }
1846
- value = str.slice(start, stop)
1847
- if (value) { //处理{{ }}插值表达式
1848
- tokens.push(getToken(value))
1849
- }
1850
- start = stop + closeTag.length
1851
- } while (1)
1852
- value = str.slice(start)
1853
- if (value) { //}} 右边的文本
1854
- tokens.push({
1855
- value: value,
1856
- expr: false,
1857
- filters: ""
1858
- })
1859
- }
1860
- return tokens
1861
- }
1862
-
1863
- function scanText(textNode, vmodels) {
1864
- var bindings = []
1865
- if (textNode.nodeType === 8) {
1866
- var token = getToken(textNode.nodeValue)
1867
- var tokens = [token]
1868
- } else {
1869
- tokens = scanExpr(textNode.data)
1870
- }
1871
- if (tokens.length) {
1872
- for (var i = 0; token = tokens[i++]; ) {
1873
- var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点
1874
- if (token.expr) {
1875
- token.type = "text"
1876
- token.element = node
1877
- token.filters = token.filters.replace(rhasHtml, function() {
1878
- token.type = "html"
1879
- token.group = 1
1880
- return ""
1881
- })// jshint ignore:line
1882
- bindings.push(token) //收集带有插值表达式的文本
1883
- }
1884
- hyperspace.appendChild(node)
1885
- }
1886
- textNode.parentNode.replaceChild(hyperspace, textNode)
1887
- if (bindings.length)
1888
- executeBindings(bindings, vmodels)
1889
- }
1890
- }
1891
-
1892
-
1893
- /*********************************************************************
1894
- * avalon的原型方法定义区 *
1895
- **********************************************************************/
1896
- function hyphen(target) {
1897
- //转换为连字符线风格
1898
- return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase()
1899
- }
1900
- function camelize(target) {
1901
- //转换为驼峰风格
1902
- if (target.indexOf("-") < 0 && target.indexOf("_") < 0) {
1903
- return target //提前判断,提高getStyle等的效率
1904
- }
1905
- return target.replace(/[-_][^-_]/g, function (match) {
1906
- return match.charAt(1).toUpperCase()
1907
- })
1908
- }
1909
-
1910
- "add,remove".replace(rword, function (method) {
1911
- avalon.fn[method + "Class"] = function (cls) {
1714
+ "add,remove".replace(rword, function(method) {
1715
+ avalon.fn[method + "Class"] = function(cls) {
1912
1716
  var el = this[0]
1913
1717
  //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26
1914
1718
  if (cls && typeof cls === "string" && el && el.nodeType === 1) {
1915
- cls.replace(/\S+/g, function (c) {
1719
+ cls.replace(/\S+/g, function(c) {
1916
1720
  el.classList[method](c)
1917
1721
  })
1918
1722
  }
@@ -1921,13 +1725,13 @@ function camelize(target) {
1921
1725
  })
1922
1726
 
1923
1727
  avalon.fn.mix({
1924
- hasClass: function (cls) {
1728
+ hasClass: function(cls) {
1925
1729
  var el = this[0] || {} //IE10+, chrome8+, firefox3.6+, safari5.1+,opera11.5+支持classList,chrome24+,firefox26+支持classList2.0
1926
1730
  return el.nodeType === 1 && el.classList.contains(cls)
1927
1731
  },
1928
- toggleClass: function (value, stateVal) {
1732
+ toggleClass: function(value, stateVal) {
1929
1733
  var className, i = 0
1930
- var classNames = value.split(/\s+/)
1734
+ var classNames = String(value).split(/\s+/)
1931
1735
  var isBool = typeof stateVal === "boolean"
1932
1736
  while ((className = classNames[i++])) {
1933
1737
  var state = isBool ? stateVal : !this.hasClass(className)
@@ -1935,7 +1739,7 @@ avalon.fn.mix({
1935
1739
  }
1936
1740
  return this
1937
1741
  },
1938
- attr: function (name, value) {
1742
+ attr: function(name, value) {
1939
1743
  if (arguments.length === 2) {
1940
1744
  this[0].setAttribute(name, value)
1941
1745
  return this
@@ -1943,7 +1747,7 @@ avalon.fn.mix({
1943
1747
  return this[0].getAttribute(name)
1944
1748
  }
1945
1749
  },
1946
- data: function (name, value) {
1750
+ data: function(name, value) {
1947
1751
  name = "data-" + hyphen(name || "")
1948
1752
  switch (arguments.length) {
1949
1753
  case 2:
@@ -1954,7 +1758,7 @@ avalon.fn.mix({
1954
1758
  return parseData(val)
1955
1759
  case 0:
1956
1760
  var ret = {}
1957
- ap.forEach.call(this[0].attributes, function (attr) {
1761
+ ap.forEach.call(this[0].attributes, function(attr) {
1958
1762
  if (attr) {
1959
1763
  name = attr.name
1960
1764
  if (!name.indexOf("data-")) {
@@ -1966,12 +1770,12 @@ avalon.fn.mix({
1966
1770
  return ret
1967
1771
  }
1968
1772
  },
1969
- removeData: function (name) {
1773
+ removeData: function(name) {
1970
1774
  name = "data-" + hyphen(name)
1971
1775
  this[0].removeAttribute(name)
1972
1776
  return this
1973
1777
  },
1974
- css: function (name, value) {
1778
+ css: function(name, value) {
1975
1779
  if (avalon.isPlainObject(name)) {
1976
1780
  for (var i in name) {
1977
1781
  avalon.css(this, i, name[i])
@@ -1981,13 +1785,13 @@ avalon.fn.mix({
1981
1785
  }
1982
1786
  return ret !== void 0 ? ret : this
1983
1787
  },
1984
- position: function () {
1788
+ position: function() {
1985
1789
  var offsetParent, offset,
1986
- elem = this[0],
1987
- parentOffset = {
1988
- top: 0,
1989
- left: 0
1990
- };
1790
+ elem = this[0],
1791
+ parentOffset = {
1792
+ top: 0,
1793
+ left: 0
1794
+ };
1991
1795
  if (!elem) {
1992
1796
  return
1993
1797
  }
@@ -2001,31 +1805,34 @@ avalon.fn.mix({
2001
1805
  }
2002
1806
  parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true)
2003
1807
  parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true)
1808
+ // Subtract offsetParent scroll positions
1809
+ parentOffset.top -= offsetParent.scrollTop()
1810
+ parentOffset.left -= offsetParent.scrollLeft()
2004
1811
  }
2005
1812
  return {
2006
1813
  top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true),
2007
1814
  left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true)
2008
1815
  }
2009
1816
  },
2010
- offsetParent: function () {
1817
+ offsetParent: function() {
2011
1818
  var offsetParent = this[0].offsetParent
2012
1819
  while (offsetParent && avalon.css(offsetParent, "position") === "static") {
2013
1820
  offsetParent = offsetParent.offsetParent;
2014
1821
  }
2015
1822
  return avalon(offsetParent || root)
2016
1823
  },
2017
- bind: function (type, fn, phase) {
1824
+ bind: function(type, fn, phase) {
2018
1825
  if (this[0]) { //此方法不会链
2019
1826
  return avalon.bind(this[0], type, fn, phase)
2020
1827
  }
2021
1828
  },
2022
- unbind: function (type, fn, phase) {
1829
+ unbind: function(type, fn, phase) {
2023
1830
  if (this[0]) {
2024
1831
  avalon.unbind(this[0], type, fn, phase)
2025
1832
  }
2026
1833
  return this
2027
1834
  },
2028
- val: function (value) {
1835
+ val: function(value) {
2029
1836
  var node = this[0]
2030
1837
  if (node && node.nodeType === 1) {
2031
1838
  var get = arguments.length === 0
@@ -2044,7 +1851,8 @@ avalon.fn.mix({
2044
1851
  })
2045
1852
 
2046
1853
  if (root.dataset) {
2047
- avalon.fn.data = function (name, val) {
1854
+ avalon.fn.data = function(name, val) {
1855
+ name = name && camelize(name)
2048
1856
  var dataset = this[0].dataset
2049
1857
  switch (arguments.length) {
2050
1858
  case 2:
@@ -2070,19 +1878,18 @@ function parseData(data) {
2070
1878
  if (typeof data === "object")
2071
1879
  return data
2072
1880
  data = data === "true" ? true :
2073
- data === "false" ? false :
2074
- data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? JSON.parse(data) : data
2075
- } catch (e) {
2076
- }
1881
+ data === "false" ? false :
1882
+ data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? JSON.parse(data) : data
1883
+ } catch (e) {}
2077
1884
  return data
2078
1885
  }
2079
1886
  avalon.each({
2080
1887
  scrollLeft: "pageXOffset",
2081
1888
  scrollTop: "pageYOffset"
2082
- }, function (method, prop) {
2083
- avalon.fn[method] = function (val) {
1889
+ }, function(method, prop) {
1890
+ avalon.fn[method] = function(val) {
2084
1891
  var node = this[0] || {}, win = getWindow(node),
2085
- top = method === "scrollTop"
1892
+ top = method === "scrollTop"
2086
1893
  if (!arguments.length) {
2087
1894
  return win ? win[prop] : node[method]
2088
1895
  } else {
@@ -2101,13 +1908,13 @@ function getWindow(node) {
2101
1908
 
2102
1909
  //=============================css相关==================================
2103
1910
  var cssHooks = avalon.cssHooks = createMap()
2104
- var prefixes = ["", "-webkit-", "-moz-", "-ms-"]//去掉opera-15的支持
1911
+ var prefixes = ["", "-webkit-", "-moz-", "-ms-"] //去掉opera-15的支持
2105
1912
  var cssMap = {
2106
1913
  "float": "cssFloat"
2107
1914
  }
2108
- avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
1915
+ avalon.cssNumber = oneObject("animationIterationCount,columnCount,order,flex,flexGrow,flexShrink,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
2109
1916
 
2110
- avalon.cssName = function (name, host, camelCase) {
1917
+ avalon.cssName = function(name, host, camelCase) {
2111
1918
  if (cssMap[name]) {
2112
1919
  return cssMap[name]
2113
1920
  }
@@ -2120,33 +1927,33 @@ avalon.cssName = function (name, host, camelCase) {
2120
1927
  }
2121
1928
  return null
2122
1929
  }
2123
- cssHooks["@:set"] = function (node, name, value) {
1930
+ cssHooks["@:set"] = function(node, name, value) {
2124
1931
  node.style[name] = value
2125
1932
  }
2126
1933
 
2127
- cssHooks["@:get"] = function (node, name) {
1934
+ cssHooks["@:get"] = function(node, name) {
2128
1935
  if (!node || !node.style) {
2129
1936
  throw new Error("getComputedStyle要求传入一个节点 " + node)
2130
1937
  }
2131
1938
  var ret, computed = getComputedStyle(node)
2132
- if (computed) {
2133
- ret = name === "filter" ? computed.getPropertyValue(name) : computed[name]
2134
- if (ret === "") {
2135
- ret = node.style[name] //其他浏览器需要我们手动取内联样式
1939
+ if (computed) {
1940
+ ret = name === "filter" ? computed.getPropertyValue(name) : computed[name]
1941
+ if (ret === "") {
1942
+ ret = node.style[name] //其他浏览器需要我们手动取内联样式
1943
+ }
2136
1944
  }
2137
- }
2138
1945
  return ret
2139
1946
  }
2140
- cssHooks["opacity:get"] = function (node) {
1947
+ cssHooks["opacity:get"] = function(node) {
2141
1948
  var ret = cssHooks["@:get"](node, "opacity")
2142
1949
  return ret === "" ? "1" : ret
2143
1950
  }
2144
1951
 
2145
- "top,left".replace(rword, function (name) {
2146
- cssHooks[name + ":get"] = function (node) {
1952
+ "top,left".replace(rword, function(name) {
1953
+ cssHooks[name + ":get"] = function(node) {
2147
1954
  var computed = cssHooks["@:get"](node, name)
2148
1955
  return /px$/.test(computed) ? computed :
2149
- avalon(node).position()[name] + "px"
1956
+ avalon(node).position()[name] + "px"
2150
1957
  }
2151
1958
  })
2152
1959
  var cssShow = {
@@ -2156,125 +1963,125 @@ var cssShow = {
2156
1963
  }
2157
1964
  var rdisplayswap = /^(none|table(?!-c[ea]).+)/
2158
1965
 
2159
- function showHidden(node, array) {
2160
- //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
2161
- if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0
2162
- var styles = getComputedStyle(node, null)
2163
- if (rdisplayswap.test(styles["display"])) {
2164
- var obj = {
2165
- node: node
1966
+ function showHidden(node, array) {
1967
+ //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
1968
+ if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0
1969
+ var styles = getComputedStyle(node, null)
1970
+ if (rdisplayswap.test(styles["display"])) {
1971
+ var obj = {
1972
+ node: node
1973
+ }
1974
+ for (var name in cssShow) {
1975
+ obj[name] = styles[name]
1976
+ node.style[name] = cssShow[name]
1977
+ }
1978
+ array.push(obj)
2166
1979
  }
2167
- for (var name in cssShow) {
2168
- obj[name] = styles[name]
2169
- node.style[name] = cssShow[name]
1980
+ var parent = node.parentNode
1981
+ if (parent && parent.nodeType === 1) {
1982
+ showHidden(parent, array)
2170
1983
  }
2171
- array.push(obj)
2172
- }
2173
- var parent = node.parentNode
2174
- if (parent && parent.nodeType === 1) {
2175
- showHidden(parent, array)
2176
1984
  }
2177
1985
  }
2178
- }
2179
1986
 
2180
- "Width,Height".replace(rword, function (name) {//fix 481
2181
- var method = name.toLowerCase(),
1987
+ "Width,Height".replace(rword, function(name) { //fix 481
1988
+ var method = name.toLowerCase(),
2182
1989
  clientProp = "client" + name,
2183
1990
  scrollProp = "scroll" + name,
2184
1991
  offsetProp = "offset" + name
2185
- cssHooks[method + ":get"] = function (node, which, override) {
2186
- var boxSizing = -4
2187
- if (typeof override === "number") {
2188
- boxSizing = override
2189
- }
2190
- which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
2191
- var ret = node[offsetProp] // border-box 0
2192
- if (boxSizing === 2) { // margin-box 2
2193
- return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
2194
- }
2195
- if (boxSizing < 0) { // padding-box -2
2196
- ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
2197
- }
2198
- if (boxSizing === -4) { // content-box -4
2199
- ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
1992
+ cssHooks[method + ":get"] = function(node, which, override) {
1993
+ var boxSizing = -4
1994
+ if (typeof override === "number") {
1995
+ boxSizing = override
1996
+ }
1997
+ which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
1998
+ var ret = node[offsetProp] // border-box 0
1999
+ if (boxSizing === 2) { // margin-box 2
2000
+ return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
2001
+ }
2002
+ if (boxSizing < 0) { // padding-box -2
2003
+ ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
2004
+ }
2005
+ if (boxSizing === -4) { // content-box -4
2006
+ ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
2007
+ }
2008
+ return ret
2009
+ }
2010
+ cssHooks[method + "&get"] = function(node) {
2011
+ var hidden = [];
2012
+ showHidden(node, hidden);
2013
+ var val = cssHooks[method + ":get"](node)
2014
+ for (var i = 0, obj; obj = hidden[i++];) {
2015
+ node = obj.node
2016
+ for (var n in obj) {
2017
+ if (typeof obj[n] === "string") {
2018
+ node.style[n] = obj[n]
2019
+ }
2020
+ }
2021
+ }
2022
+ return val;
2200
2023
  }
2201
- return ret
2202
- }
2203
- cssHooks[method + "&get"] = function (node) {
2204
- var hidden = [];
2205
- showHidden(node, hidden);
2206
- var val = cssHooks[method + ":get"](node)
2207
- for (var i = 0, obj; obj = hidden[i++]; ) {
2208
- node = obj.node
2209
- for (var n in obj) {
2210
- if (typeof obj[n] === "string") {
2211
- node.style[n] = obj[n]
2024
+ avalon.fn[method] = function(value) { //会忽视其display
2025
+ var node = this[0]
2026
+ if (arguments.length === 0) {
2027
+ if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
2028
+ return node["inner" + name]
2029
+ }
2030
+ if (node.nodeType === 9) { //取得页面尺寸
2031
+ var doc = node.documentElement
2032
+ //FF chrome html.scrollHeight< body.scrollHeight
2033
+ //IE 标准模式 : html.scrollHeight> body.scrollHeight
2034
+ //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
2035
+ return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp])
2212
2036
  }
2037
+ return cssHooks[method + "&get"](node)
2038
+ } else {
2039
+ return this.css(method, value)
2213
2040
  }
2214
2041
  }
2215
- return val;
2216
- }
2217
- avalon.fn[method] = function (value) { //会忽视其display
2042
+ avalon.fn["inner" + name] = function() {
2043
+ return cssHooks[method + ":get"](this[0], void 0, -2)
2044
+ }
2045
+ avalon.fn["outer" + name] = function(includeMargin) {
2046
+ return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
2047
+ }
2048
+ })
2049
+ avalon.fn.offset = function() { //取得距离页面左右角的坐标
2218
2050
  var node = this[0]
2219
- if (arguments.length === 0) {
2220
- if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
2221
- return node["inner" + name]
2222
- }
2223
- if (node.nodeType === 9) { //取得页面尺寸
2224
- var doc = node.documentElement
2225
- //FF chrome html.scrollHeight< body.scrollHeight
2226
- //IE 标准模式 : html.scrollHeight> body.scrollHeight
2227
- //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
2228
- return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp])
2051
+ try {
2052
+ var rect = node.getBoundingClientRect()
2053
+ // Make sure element is not hidden (display: none) or disconnected
2054
+ // https://github.com/jquery/jquery/pull/2043/files#r23981494
2055
+ if (rect.width || rect.height || node.getClientRects().length) {
2056
+ var doc = node.ownerDocument
2057
+ var root = doc.documentElement
2058
+ var win = doc.defaultView
2059
+ return {
2060
+ top: rect.top + win.pageYOffset - root.clientTop,
2061
+ left: rect.left + win.pageXOffset - root.clientLeft
2062
+ }
2229
2063
  }
2230
- return cssHooks[method + "&get"](node)
2231
- } else {
2232
- return this.css(method, value)
2233
- }
2234
- }
2235
- avalon.fn["inner" + name] = function () {
2236
- return cssHooks[method + ":get"](this[0], void 0, -2)
2237
- }
2238
- avalon.fn["outer" + name] = function (includeMargin) {
2239
- return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
2240
- }
2241
- })
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
2064
+ } catch (e) {
2252
2065
  return {
2253
- top: rect.top + win.pageYOffset - root.clientTop,
2254
- left: rect.left + win.pageXOffset - root.clientLeft
2066
+ left: 0,
2067
+ top: 0
2255
2068
  }
2256
2069
  }
2257
- } catch (e) {
2258
- return {
2259
- left: 0,
2260
- top: 0
2261
- }
2262
2070
  }
2263
- }
2264
- //=============================val相关=======================
2071
+ //=============================val相关=======================
2265
2072
 
2266
- function getValType(el) {
2267
- var ret = el.tagName.toLowerCase()
2268
- return ret === "input" && /checkbox|radio/.test(el.type) ? "checked" : ret
2269
- }
2073
+ function getValType(elem) {
2074
+ var ret = elem.tagName.toLowerCase()
2075
+ return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret
2076
+ }
2270
2077
  var valHooks = {
2271
- "select:get": function (node, value) {
2078
+ "select:get": function(node, value) {
2272
2079
  var option, options = node.options,
2273
- index = node.selectedIndex,
2274
- one = node.type === "select-one" || index < 0,
2275
- values = one ? null : [],
2276
- max = one ? index + 1 : options.length,
2277
- i = index < 0 ? max : one ? index : 0
2080
+ index = node.selectedIndex,
2081
+ one = node.type === "select-one" || index < 0,
2082
+ values = one ? null : [],
2083
+ max = one ? index + 1 : options.length,
2084
+ i = index < 0 ? max : one ? index : 0
2278
2085
  for (; i < max; i++) {
2279
2086
  option = options[i]
2280
2087
  //旧式IE在reset后不会改变selected,需要改用i === index判定
@@ -2291,9 +2098,9 @@ var valHooks = {
2291
2098
  }
2292
2099
  return values
2293
2100
  },
2294
- "select:set": function (node, values, optionSet) {
2101
+ "select:set": function(node, values, optionSet) {
2295
2102
  values = [].concat(values) //强制转换为数组
2296
- for (var i = 0, el; el = node.options[i++]; ) {
2103
+ for (var i = 0, el; el = node.options[i++];) {
2297
2104
  if ((el.selected = values.indexOf(el.value) > -1)) {
2298
2105
  optionSet = true
2299
2106
  }
@@ -2303,7 +2110,6 @@ var valHooks = {
2303
2110
  }
2304
2111
  }
2305
2112
  }
2306
-
2307
2113
  /*********************************************************************
2308
2114
  * 编译系统 *
2309
2115
  **********************************************************************/
@@ -2323,10 +2129,10 @@ var rsplit = /[^\w$]+/g
2323
2129
  var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g')
2324
2130
  var rnumber = /\b\d[^,]*/g
2325
2131
  var rcomma = /^,+|,+$/g
2326
- var cacheVars = new Cache(512)
2132
+ var variablePool = new Cache(512)
2327
2133
  var getVariables = function (code) {
2328
2134
  var key = "," + code.trim()
2329
- var ret = cacheVars.get(key)
2135
+ var ret = variablePool.get(key)
2330
2136
  if (ret) {
2331
2137
  return ret
2332
2138
  }
@@ -2337,7 +2143,7 @@ var getVariables = function (code) {
2337
2143
  .replace(rnumber, "")
2338
2144
  .replace(rcomma, "")
2339
2145
  .split(/^$|,+/)
2340
- return cacheVars.put(key, uniqSet(match))
2146
+ return variablePool.put(key, uniqSet(match))
2341
2147
  }
2342
2148
  /*添加赋值语句*/
2343
2149
 
@@ -2370,7 +2176,7 @@ function uniqSet(array) {
2370
2176
  return ret
2371
2177
  }
2372
2178
  //缓存求值函数,以便多次利用
2373
- var cacheExprs = new Cache(128)
2179
+ var evaluatorPool = new Cache(128)
2374
2180
  //取得求值函数及其传参
2375
2181
  var rduplex = /\w\[.*\]|\w\.\w/
2376
2182
  var rproxy = /(\$proxy\$[a-z]+)\d+$/
@@ -2394,142 +2200,496 @@ function parseFilter(val, filters) {
2394
2200
  .replace(rthimLeftParentheses, function () {
2395
2201
  return '",'
2396
2202
  }) + "]"
2397
- return "return avalon.filters.$filter(" + val + ", " + filters + ")"
2203
+ return "return this.filters.$filter(" + val + ", " + filters + ")"
2204
+ }
2205
+
2206
+ function parseExpr(code, scopes, data) {
2207
+ var dataType = data.type
2208
+ var filters = data.filters || ""
2209
+ var exprId = scopes.map(function (el) {
2210
+ return String(el.$id).replace(rproxy, "$1")
2211
+ }) + code + dataType + filters
2212
+ var vars = getVariables(code).concat(),
2213
+ assigns = [],
2214
+ names = [],
2215
+ args = [],
2216
+ prefix = ""
2217
+ //args 是一个对象数组, names 是将要生成的求值函数的参数
2218
+ scopes = uniqSet(scopes)
2219
+ data.vars = []
2220
+ for (var i = 0, sn = scopes.length; i < sn; i++) {
2221
+ if (vars.length) {
2222
+ var name = "vm" + expose + "_" + i
2223
+ names.push(name)
2224
+ args.push(scopes[i])
2225
+ assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data))
2226
+ }
2227
+ }
2228
+ if (!assigns.length && dataType === "duplex") {
2229
+ return
2230
+ }
2231
+ if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) {
2232
+ //https://github.com/RubyLouvre/avalon/issues/583
2233
+ data.vars.forEach(function (v) {
2234
+ var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
2235
+ code = code.replace(reg, function (_, cap) {
2236
+ var c = _.charAt(v.length)
2237
+ //var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2238
+ //https://github.com/RubyLouvre/avalon/issues/966
2239
+ var r = code.slice(cap + _.length)
2240
+ var method = /^\s*\(/.test(r)
2241
+ if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx
2242
+ var name = "var" + String(Math.random()).replace(/^0\./, "")
2243
+ if (method) {//array.size()
2244
+ var array = _.split(".")
2245
+ if (array.length > 2) {
2246
+ var last = array.pop()
2247
+ assigns.push(name + " = " + array.join("."))
2248
+ return name + "." + last
2249
+ } else {
2250
+ return _
2251
+ }
2252
+ }
2253
+ assigns.push(name + " = " + _)
2254
+ return name
2255
+ } else {
2256
+ return _
2257
+ }
2258
+ })
2259
+ })
2260
+ }
2261
+ //---------------args----------------
2262
+ data.args = args
2263
+ //---------------cache----------------
2264
+ delete data.vars
2265
+ var fn = evaluatorPool.get(exprId) //直接从缓存,免得重复生成
2266
+ if (fn) {
2267
+ data.evaluator = fn
2268
+ return
2269
+ }
2270
+ prefix = assigns.join(", ")
2271
+ if (prefix) {
2272
+ prefix = "var " + prefix
2273
+ }
2274
+ if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器
2275
+ if (!/text|html/.test(data.type)) {
2276
+ throw Error("ms-" + data.type + "不支持过滤器")
2277
+ }
2278
+ code = "\nvar ret" + expose + " = " + code + ";\r\n"
2279
+ code += parseFilter("ret" + expose, filters)
2280
+ try {
2281
+ fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
2282
+ data.evaluator = evaluatorPool.put(exprId, function() {
2283
+ return fn.apply(avalon, arguments)//确保可以在编译代码中使用this获取avalon对象
2284
+ })
2285
+ } catch (e) {
2286
+ log("debug: parse error," + e.message)
2287
+ }
2288
+ vars = assigns = names = null //释放内存
2289
+ return
2290
+ } else if (dataType === "duplex") { //双工绑定
2291
+ var _body = "'use strict';\nreturn function(vvv){\n\t" +
2292
+ prefix +
2293
+ ";\n\tif(!arguments.length){\n\t\treturn " +
2294
+ code +
2295
+ "\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) +
2296
+ "= vvv;\n} "
2297
+ try {
2298
+ fn = Function.apply(noop, names.concat(_body))
2299
+ data.evaluator = evaluatorPool.put(exprId, fn)
2300
+ } catch (e) {
2301
+ log("debug: parse error," + e.message)
2302
+ }
2303
+ vars = assigns = names = null //释放内存
2304
+ return
2305
+ } else if (dataType === "on") { //事件绑定
2306
+ if (code.indexOf("(") === -1) {
2307
+ code += ".call(this, $event)"
2308
+ } else {
2309
+ code = code.replace("(", ".call(this,")
2310
+ }
2311
+ names.push("$event")
2312
+ code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
2313
+ var lastIndex = code.lastIndexOf("\nreturn")
2314
+ var header = code.slice(0, lastIndex)
2315
+ var footer = code.slice(lastIndex)
2316
+ code = header + "\n" + footer
2317
+ } else { //其他绑定
2318
+ code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
2319
+ }
2320
+ try {
2321
+ fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
2322
+ data.evaluator = evaluatorPool.put(exprId, fn)
2323
+ } catch (e) {
2324
+ log("debug: parse error," + e.message)
2325
+ }
2326
+ vars = assigns = names = null //释放内存
2327
+ }
2328
+ function stringifyExpr(code) {
2329
+ var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况
2330
+ if (hasExpr) {
2331
+ var array = scanExpr(code)
2332
+ if (array.length === 1) {
2333
+ return array[0].value
2334
+ }
2335
+ return array.map(function (el) {
2336
+ return el.expr ? "(" + el.value + ")" : quote(el.value)
2337
+ }).join(" + ")
2338
+ } else {
2339
+ return code
2340
+ }
2341
+ }
2342
+ //parseExpr的智能引用代理
2343
+
2344
+ function parseExprProxy(code, scopes, data, noRegister) {
2345
+ code = code || "" //code 可能未定义
2346
+ parseExpr(code, scopes, data)
2347
+ if (data.evaluator && !noRegister) {
2348
+ data.handler = bindingExecutors[data.handlerName || data.type]
2349
+ //方便调试
2350
+ //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定
2351
+ //将它移出订阅者列表
2352
+ avalon.injectBinding(data)
2353
+ }
2354
+ }
2355
+ avalon.parseExprProxy = parseExprProxy
2356
+ /*********************************************************************
2357
+ * 扫描系统 *
2358
+ **********************************************************************/
2359
+
2360
+ avalon.scan = function(elem, vmodel) {
2361
+ elem = elem || root
2362
+ var vmodels = vmodel ? [].concat(vmodel) : []
2363
+ scanTag(elem, vmodels)
2364
+ }
2365
+
2366
+ //http://www.w3.org/TR/html5/syntax.html#void-elements
2367
+ var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase())
2368
+
2369
+ function checkScan(elem, callback, innerHTML) {
2370
+ var id = setTimeout(function() {
2371
+ var currHTML = elem.innerHTML
2372
+ clearTimeout(id)
2373
+ if (currHTML === innerHTML) {
2374
+ callback()
2375
+ } else {
2376
+ checkScan(elem, callback, currHTML)
2377
+ }
2378
+ })
2379
+ }
2380
+
2381
+
2382
+ function createSignalTower(elem, vmodel) {
2383
+ var id = elem.getAttribute("avalonctrl") || vmodel.$id
2384
+ elem.setAttribute("avalonctrl", id)
2385
+ vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]'
2386
+ }
2387
+
2388
+ var getBindingCallback = function(elem, name, vmodels) {
2389
+ var callback = elem.getAttribute(name)
2390
+ if (callback) {
2391
+ for (var i = 0, vm; vm = vmodels[i++]; ) {
2392
+ if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
2393
+ return vm[callback]
2394
+ }
2395
+ }
2396
+ }
2397
+ }
2398
+
2399
+ function executeBindings(bindings, vmodels) {
2400
+ for (var i = 0, data; data = bindings[i++]; ) {
2401
+ data.vmodels = vmodels
2402
+ bindingHandlers[data.type](data, vmodels)
2403
+ if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
2404
+ //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
2405
+ data.element.removeAttribute(data.name)
2406
+ }
2407
+ }
2408
+ bindings.length = 0
2409
+ }
2410
+
2411
+ //https://github.com/RubyLouvre/avalon/issues/636
2412
+ var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) {
2413
+ var node = elem.firstChild, text
2414
+ while (node) {
2415
+ var aaa = node.nextSibling
2416
+ if (node.nodeType === 3) {
2417
+ if (text) {
2418
+ text.nodeValue += node.nodeValue
2419
+ elem.removeChild(node)
2420
+ } else {
2421
+ text = node
2422
+ }
2423
+ } else {
2424
+ text = null
2425
+ }
2426
+ node = aaa
2427
+ }
2428
+ } : 0
2429
+ var roneTime = /^\s*::/
2430
+ var rmsAttr = /ms-(\w+)-?(.*)/
2431
+ var priorityMap = {
2432
+ "if": 10,
2433
+ "repeat": 90,
2434
+ "data": 100,
2435
+ "widget": 110,
2436
+ "each": 1400,
2437
+ "with": 1500,
2438
+ "duplex": 2000,
2439
+ "on": 3000
2440
+ }
2441
+
2442
+ var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
2443
+ var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled")
2444
+ function bindingSorter(a, b) {
2445
+ return a.priority - b.priority
2446
+ }
2447
+
2448
+ function scanAttr(elem, vmodels, match) {
2449
+ var scanNode = true
2450
+ if (vmodels.length) {
2451
+ var attributes = elem.attributes
2452
+ var bindings = []
2453
+ var fixAttrs = []
2454
+ var uniq = {}
2455
+ var msData = createMap()
2456
+ for (var i = 0, attr; attr = attributes[i++]; ) {
2457
+ if (attr.specified) {
2458
+ if (match = attr.name.match(rmsAttr)) {
2459
+ //如果是以指定前缀命名的
2460
+ var type = match[1]
2461
+ var param = match[2] || ""
2462
+ var value = attr.value
2463
+ var name = attr.name
2464
+ if (uniq[name]) {//IE8下ms-repeat,ms-with BUG
2465
+ continue
2466
+ }
2467
+ uniq[name] = 1
2468
+ if (events[type]) {
2469
+ param = type
2470
+ type = "on"
2471
+ } else if (obsoleteAttrs[type]) {
2472
+ if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
2473
+ log("warning!ms-enabled或ms-attr-enabled已经被废弃")
2474
+ type = "disabled"
2475
+ value = "!(" + value + ")"
2476
+ }
2477
+ param = type
2478
+ type = "attr"
2479
+ name = "ms-" + type + "-" + param
2480
+ fixAttrs.push([attr.name, name, value])
2481
+ }
2482
+ msData[name] = value
2483
+ if (typeof bindingHandlers[type] === "function") {
2484
+ var newValue = value.replace(roneTime, "")
2485
+ var oneTime = value !== newValue
2486
+ var binding = {
2487
+ type: type,
2488
+ param: param,
2489
+ element: elem,
2490
+ name: name,
2491
+ value: newValue,
2492
+ oneTime: oneTime,
2493
+ priority: (priorityMap[type] || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0)
2494
+ }
2495
+ if (type === "html" || type === "text") {
2496
+ var token = getToken(value)
2497
+ avalon.mix(binding, token)
2498
+ binding.filters = binding.filters.replace(rhasHtml, function () {
2499
+ binding.type = "html"
2500
+ binding.group = 1
2501
+ return ""
2502
+ })// jshint ignore:line
2503
+ } else if (type === "duplex") {
2504
+ var hasDuplex = name
2505
+ } else if (name === "ms-if-loop") {
2506
+ binding.priority += 100
2507
+ }
2508
+ bindings.push(binding)
2509
+ if (type === "widget") {
2510
+ elem.msData = elem.msData || msData
2511
+ }
2512
+ }
2513
+ }
2514
+ }
2515
+ }
2516
+ if (bindings.length) {
2517
+ bindings.sort(bindingSorter)
2518
+ fixAttrs.forEach(function (arr) {
2519
+ log("warning!请改用" + arr[1] + "代替" + arr[0] + "!")
2520
+ elem.removeAttribute(arr[0])
2521
+ elem.setAttribute(arr[1], arr[2])
2522
+ })
2523
+ var control = elem.type
2524
+ if (control && hasDuplex) {
2525
+ if (msData["ms-attr-value"] && elem.type === "text") {
2526
+ log("warning!" + control + "控件不能同时定义ms-attr-value与" + hasDuplex)
2527
+ }
2528
+ }
2529
+
2530
+ for (i = 0; binding = bindings[i]; i++) {
2531
+ type = binding.type
2532
+ if (rnoscanAttrBinding.test(type)) {
2533
+ return executeBindings(bindings.slice(0, i + 1), vmodels)
2534
+ } else if (scanNode) {
2535
+ scanNode = !rnoscanNodeBinding.test(type)
2536
+ }
2537
+ }
2538
+ executeBindings(bindings, vmodels)
2539
+ }
2540
+ }
2541
+ if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML + elem.textContent)) {
2542
+ mergeTextNodes && mergeTextNodes(elem)
2543
+ scanNodeList(elem, vmodels) //扫描子孙元素
2544
+ }
2545
+ }
2546
+
2547
+ var rnoscanAttrBinding = /^if|widget|repeat$/
2548
+ var rnoscanNodeBinding = /^each|with|html|include$/
2549
+ function scanNodeList(parent, vmodels) {
2550
+ var nodes = avalon.slice(parent.childNodes)
2551
+ scanNodeArray(nodes, vmodels)
2398
2552
  }
2399
2553
 
2400
- function parseExpr(code, scopes, data) {
2401
- var dataType = data.type
2402
- var filters = data.filters || ""
2403
- var exprId = scopes.map(function (el) {
2404
- return String(el.$id).replace(rproxy, "$1")
2405
- }) + code + dataType + filters
2406
- var vars = getVariables(code).concat(),
2407
- assigns = [],
2408
- names = [],
2409
- args = [],
2410
- prefix = ""
2411
- //args 是一个对象数组, names 是将要生成的求值函数的参数
2412
- scopes = uniqSet(scopes)
2413
- data.vars = []
2414
- for (var i = 0, sn = scopes.length; i < sn; i++) {
2415
- if (vars.length) {
2416
- var name = "vm" + expose + "_" + i
2417
- names.push(name)
2418
- args.push(scopes[i])
2419
- assigns.push.apply(assigns, addAssign(vars, scopes[i], name, data))
2554
+ function scanNodeArray(nodes, vmodels) {
2555
+ for (var i = 0, node; node = nodes[i++];) {
2556
+ switch (node.nodeType) {
2557
+ case 1:
2558
+ scanTag(node, vmodels) //扫描元素节点
2559
+ if (node.msCallback) {
2560
+ node.msCallback()
2561
+ node.msCallback = void 0
2562
+ }
2563
+ break
2564
+ case 3:
2565
+ if(rexpr.test(node.nodeValue)){
2566
+ scanText(node, vmodels, i) //扫描文本节点
2567
+ }
2568
+ break
2420
2569
  }
2421
2570
  }
2422
- if (!assigns.length && dataType === "duplex") {
2571
+ }
2572
+
2573
+
2574
+ function scanTag(elem, vmodels, node) {
2575
+ //扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
2576
+ //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
2577
+ var a = elem.getAttribute("ms-skip")
2578
+ var b = elem.getAttributeNode("ms-important")
2579
+ var c = elem.getAttributeNode("ms-controller")
2580
+ if (typeof a === "string") {
2423
2581
  return
2582
+ } else if (node = b || c) {
2583
+ var newVmodel = avalon.vmodels[node.value]
2584
+ if (!newVmodel) {
2585
+ return
2586
+ }
2587
+ //ms-important不包含父VM,ms-controller相反
2588
+ vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
2589
+ elem.removeAttribute(node.name) //removeAttributeNode不会刷新[ms-controller]样式规则
2590
+ elem.classList.remove(node.name)
2591
+ createSignalTower(elem, newVmodel)
2424
2592
  }
2425
- if (dataType !== "duplex" && (code.indexOf("||") > -1 || code.indexOf("&&") > -1)) {
2426
- //https://github.com/RubyLouvre/avalon/issues/583
2427
- data.vars.forEach(function (v) {
2428
- var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
2429
- code = code.replace(reg, function (_) {
2430
- var c = _.charAt(v.length)
2431
- var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2432
- var method = /^\s*\(/.test(r)
2433
- if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx
2434
- var name = "var" + String(Math.random()).replace(/^0\./, "")
2435
- if (method) {//array.size()
2436
- var array = _.split(".")
2437
- if (array.length > 2) {
2438
- var last = array.pop()
2439
- assigns.push(name + " = " + array.join("."))
2440
- return name + "." + last
2441
- } else {
2442
- return _
2443
- }
2444
- }
2445
- assigns.push(name + " = " + _)
2446
- return name
2447
- } else {
2448
- return _
2449
- }
2450
- })
2593
+ scanAttr(elem, vmodels) //扫描特性节点
2594
+ }
2595
+ var rhasHtml = /\|\s*html(?:\b|$)/,
2596
+ r11a = /\|\|/g,
2597
+ rlt = /&lt;/g,
2598
+ rgt = /&gt;/g,
2599
+ rstringLiteral = /(['"])(\\\1|.)+?\1/g
2600
+ function getToken(value) {
2601
+ if (value.indexOf("|") > 0) {
2602
+ var scapegoat = value.replace(rstringLiteral, function (_) {
2603
+ return Array(_.length + 1).join("1")// jshint ignore:line
2451
2604
  })
2605
+ var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
2606
+ if (index > -1) {
2607
+ return {
2608
+ filters: value.slice(index),
2609
+ value: value.slice(0, index),
2610
+ expr: true
2611
+ }
2612
+ }
2452
2613
  }
2453
- //---------------args----------------
2454
- data.args = args
2455
- //---------------cache----------------
2456
- var fn = cacheExprs.get(exprId) //直接从缓存,免得重复生成
2457
- if (fn) {
2458
- data.evaluator = fn
2459
- return
2460
- }
2461
- prefix = assigns.join(", ")
2462
- if (prefix) {
2463
- prefix = "var " + prefix
2614
+ return {
2615
+ value: value,
2616
+ filters: "",
2617
+ expr: true
2464
2618
  }
2465
- if (/\S/.test(filters)) { //文本绑定,双工绑定才有过滤器
2466
- if (!/text|html/.test(data.type)) {
2467
- throw Error("ms-" + data.type + "不支持过滤器")
2619
+ }
2620
+
2621
+ function scanExpr(str) {
2622
+ var tokens = [],
2623
+ value, start = 0,
2624
+ stop
2625
+ do {
2626
+ stop = str.indexOf(openTag, start)
2627
+ if (stop === -1) {
2628
+ break
2468
2629
  }
2469
- code = "\nvar ret" + expose + " = " + code + ";\r\n"
2470
- code += parseFilter("ret" + expose, filters)
2471
- } else if (dataType === "duplex") { //双工绑定
2472
- var _body = "'use strict';\nreturn function(vvv){\n\t" +
2473
- prefix +
2474
- ";\n\tif(!arguments.length){\n\t\treturn " +
2475
- code +
2476
- "\n\t}\n\t" + (!rduplex.test(code) ? vars.get : code) +
2477
- "= vvv;\n} "
2478
- try {
2479
- fn = Function.apply(noop, names.concat(_body))
2480
- data.evaluator = cacheExprs.put(exprId, fn)
2481
- } catch (e) {
2482
- log("debug: parse error," + e.message)
2630
+ value = str.slice(start, stop)
2631
+ if (value) { // {{ 左边的文本
2632
+ tokens.push({
2633
+ value: value,
2634
+ filters: "",
2635
+ expr: false
2636
+ })
2483
2637
  }
2484
- return
2485
- } else if (dataType === "on") { //事件绑定
2486
- if (code.indexOf("(") === -1) {
2487
- code += ".call(this, $event)"
2488
- } else {
2489
- code = code.replace("(", ".call(this,")
2638
+ start = stop + openTag.length
2639
+ stop = str.indexOf(closeTag, start)
2640
+ if (stop === -1) {
2641
+ break
2490
2642
  }
2491
- names.push("$event")
2492
- code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
2493
- var lastIndex = code.lastIndexOf("\nreturn")
2494
- var header = code.slice(0, lastIndex)
2495
- var footer = code.slice(lastIndex)
2496
- code = header + "\n" + footer
2497
- } else { //其他绑定
2498
- code = "\nreturn " + code + ";" //IE全家 Function("return ")出错,需要Function("return ;")
2499
- }
2500
- try {
2501
- fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
2502
- data.evaluator = cacheExprs.put(exprId, fn)
2503
- } catch (e) {
2504
- log("debug: parse error," + e.message)
2505
- } finally {
2506
- vars = assigns = names = null //释放内存
2643
+ value = str.slice(start, stop)
2644
+ if (value) { //处理{{ }}插值表达式
2645
+ tokens.push(getToken(value, start))
2646
+ }
2647
+ start = stop + closeTag.length
2648
+ } while (1)
2649
+ value = str.slice(start)
2650
+ if (value) { //}} 右边的文本
2651
+ tokens.push({
2652
+ value: value,
2653
+ expr: false,
2654
+ filters: ""
2655
+ })
2507
2656
  }
2657
+ return tokens
2508
2658
  }
2509
2659
 
2510
-
2511
- //parseExpr的智能引用代理
2512
-
2513
- function parseExprProxy(code, scopes, data, tokens, noregister) {
2514
- if (Array.isArray(tokens)) {
2515
- code = tokens.map(function (el) {
2516
- return el.expr ? "(" + el.value + ")" : quote(el.value)
2517
- }).join(" + ")
2518
- }
2519
- parseExpr(code, scopes, data)
2520
- if (data.evaluator && !noregister) {
2521
- data.handler = bindingExecutors[data.handlerName || data.type]
2522
- //方便调试
2523
- //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定
2524
- //将它移出订阅者列表
2525
- registerSubscriber(data)
2660
+ function scanText(textNode, vmodels, index) {
2661
+ var bindings = []
2662
+ tokens = scanExpr(textNode.data)
2663
+ if (tokens.length) {
2664
+ for (var i = 0; token = tokens[i++]; ) {
2665
+ var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点
2666
+ if (token.expr) {
2667
+ token.value = token.value.replace(roneTime, function () {
2668
+ token.oneTime = true
2669
+ return ""
2670
+ })// jshint ignore:line
2671
+ token.type = "text"
2672
+ token.element = node
2673
+ token.filters = token.filters.replace(rhasHtml, function (a, b,c) {
2674
+ token.type = "html"
2675
+ return ""
2676
+ })// jshint ignore:line
2677
+ token.pos = index * 1000 + i
2678
+ bindings.push(token) //收集带有插值表达式的文本
2679
+ }
2680
+ avalonFragment.appendChild(node)
2681
+ }
2682
+ textNode.parentNode.replaceChild(avalonFragment, textNode)
2683
+ if (bindings.length)
2684
+ executeBindings(bindings, vmodels)
2526
2685
  }
2527
2686
  }
2528
- avalon.parseExprProxy = parseExprProxy
2687
+
2529
2688
  var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls",
2530
2689
  "declare,disabled,defer,defaultChecked,defaultSelected",
2531
2690
  "contentEditable,isMap,loop,multiple,noHref,noResize,noShade",
2532
- "open,readOnly,selected"].join(",")
2691
+ "open,readOnly,selected"
2692
+ ].join(",")
2533
2693
  var boolMap = {}
2534
2694
  bools.replace(rword, function (name) {
2535
2695
  boolMap[name.toLowerCase()] = name
@@ -2546,7 +2706,8 @@ var propMap = {//属性名映射
2546
2706
 
2547
2707
  var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan",
2548
2708
  "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight",
2549
- "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"].join(",")
2709
+ "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
2710
+ ].join(",")
2550
2711
  anomaly.replace(rword, function (name) {
2551
2712
  propMap[name.toLowerCase()] = name
2552
2713
  })
@@ -2555,21 +2716,13 @@ var rnoscripts = /<noscript.*?>(?:[\s\S]+?)<\/noscript>/img
2555
2716
  var rnoscriptText = /<noscript.*?>([\s\S]+?)<\/noscript>/im
2556
2717
 
2557
2718
  var getXHR = function () {
2558
- return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")// jshint ignore:line
2719
+ return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line
2559
2720
  }
2560
2721
 
2561
- var cacheTmpls = avalon.templateCache = {}
2722
+ var templatePool = avalon.templateCache = {}
2562
2723
 
2563
2724
  bindingHandlers.attr = function (data, vmodels) {
2564
- var text = data.value.trim(),
2565
- simple = true
2566
- if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) {
2567
- simple = false
2568
- if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") {
2569
- simple = true
2570
- text = RegExp.$1
2571
- }
2572
- }
2725
+ var value = stringifyExpr(data.value.trim())
2573
2726
  if (data.type === "include") {
2574
2727
  var elem = data.element
2575
2728
  data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels)
@@ -2590,7 +2743,7 @@ bindingHandlers.attr = function (data, vmodels) {
2590
2743
  }
2591
2744
  }
2592
2745
  data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况
2593
- parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value)))
2746
+ parseExprProxy(value, vmodels, data)
2594
2747
  }
2595
2748
 
2596
2749
  bindingExecutors.attr = function (val, elem, data) {
@@ -2599,28 +2752,29 @@ bindingExecutors.attr = function (val, elem, data) {
2599
2752
  if (method === "css") {
2600
2753
  avalon(elem).css(attrName, val)
2601
2754
  } else if (method === "attr") {
2755
+
2602
2756
  // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc
2603
2757
  // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名
2604
2758
  // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性
2605
- if (boolMap[attrName]) {
2606
- var bool = boolMap[attrName]
2607
- if (typeof elem[bool] === "boolean") {
2608
- // IE6-11不支持动态设置fieldset的disabled属性,IE11下样式是生效了,但无法阻止用户对其底下的input元素进行设值……
2609
- return elem[bool] = !!val
2610
- }
2611
- }
2612
2759
  var toRemove = (val === false) || (val === null) || (val === void 0)
2613
2760
 
2614
2761
  if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射
2615
2762
  attrName = propMap[attrName]
2616
2763
  }
2764
+ var bool = boolMap[attrName]
2765
+ if (typeof elem[bool] === "boolean") {
2766
+ elem[bool] = !!val //布尔属性必须使用el.xxx = true|false方式设值
2767
+ if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理
2768
+ toRemove = true
2769
+ }
2770
+ }
2617
2771
  if (toRemove) {
2618
2772
  return elem.removeAttribute(attrName)
2619
2773
  }
2620
2774
  //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy
2621
2775
  var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false)
2622
2776
  if (isInnate) {
2623
- elem[attrName] = val
2777
+ elem[attrName] = val + ""
2624
2778
  } else {
2625
2779
  elem.setAttribute(attrName, val)
2626
2780
  }
@@ -2632,7 +2786,9 @@ bindingExecutors.attr = function (val, elem, data) {
2632
2786
  var target = replace ? elem.parentNode : elem
2633
2787
  var scanTemplate = function (text) {
2634
2788
  if (loaded) {
2635
- text = loaded.apply(target, [text].concat(vmodels))
2789
+ var newText = loaded.apply(target, [text].concat(vmodels))
2790
+ if (typeof newText === "string")
2791
+ text = newText
2636
2792
  }
2637
2793
  if (rendered) {
2638
2794
  checkScan(target, function () {
@@ -2665,20 +2821,27 @@ bindingExecutors.attr = function (val, elem, data) {
2665
2821
  }
2666
2822
 
2667
2823
  if (data.param === "src") {
2668
- if (cacheTmpls[val]) {
2824
+ if (typeof templatePool[val] === "string") {
2669
2825
  avalon.nextTick(function () {
2670
- scanTemplate(cacheTmpls[val])
2826
+ scanTemplate(templatePool[val])
2671
2827
  })
2828
+ } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求
2829
+ templatePool[val].push(scanTemplate)
2672
2830
  } else {
2673
2831
  var xhr = getXHR()
2674
2832
  xhr.onreadystatechange = function () {
2675
2833
  if (xhr.readyState === 4) {
2676
2834
  var s = xhr.status
2677
2835
  if (s >= 200 && s < 300 || s === 304 || s === 1223) {
2678
- scanTemplate(cacheTmpls[val] = xhr.responseText)
2836
+ var text = xhr.responseText
2837
+ for (var f = 0, fn; fn = templatePool[val][f++]; ) {
2838
+ fn(text)
2839
+ }
2840
+ templatePool[val] = text
2679
2841
  }
2680
2842
  }
2681
2843
  }
2844
+ templatePool[val] = [scanTemplate]
2682
2845
  xhr.open("GET", val, true)
2683
2846
  if ("withCredentials" in xhr) {
2684
2847
  xhr.withCredentials = true
@@ -2729,13 +2892,14 @@ bindingExecutors.attr = function (val, elem, data) {
2729
2892
  function getTemplateNodes(data, id, text) {
2730
2893
  var div = data.templateCache && data.templateCache[id]
2731
2894
  if (div) {
2732
- var dom = DOC.createDocumentFragment(), firstChild
2895
+ var dom = DOC.createDocumentFragment(),
2896
+ firstChild
2733
2897
  while (firstChild = div.firstChild) {
2734
2898
  dom.appendChild(firstChild)
2735
2899
  }
2736
2900
  return dom
2737
2901
  }
2738
- return avalon.parseHTML(text)
2902
+ return avalon.parseHTML(text)
2739
2903
  }
2740
2904
 
2741
2905
  //这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html"
@@ -2744,92 +2908,85 @@ function getTemplateNodes(data, id, text) {
2744
2908
  })
2745
2909
  //根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag"
2746
2910
  //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html
2747
- bindingHandlers["class"] = function(data, vmodels) {
2748
- var oldStyle = data.param,
2749
- text = data.value,
2911
+ bindingHandlers["class"] = function (binding, vmodels) {
2912
+ var oldStyle = binding.param,
2913
+ text = binding.value,
2750
2914
  rightExpr
2751
- data.handlerName = "class"
2915
+ binding.handlerName = "class"
2752
2916
  if (!oldStyle || isFinite(oldStyle)) {
2753
- data.param = "" //去掉数字
2754
- var noExpr = text.replace(rexprg, function(a) {
2917
+ binding.param = "" //去掉数字
2918
+ var colonIndex = text.replace(rexprg, function (a) {
2755
2919
  return a.replace(/./g, "0")
2756
- //return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位
2757
- })
2758
- var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置
2920
+ }).indexOf(":") //取得第一个冒号的位置
2759
2921
  if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况
2760
2922
  var className = text
2761
- } else { // 比如 ms-class-1="ui-state-active:checked" 的情况
2923
+ rightExpr = true
2924
+ } else { // 比如 ms-class-1="ui-state-active:checked" 的情况
2762
2925
  className = text.slice(0, colonIndex)
2763
2926
  rightExpr = text.slice(colonIndex + 1)
2764
- parseExpr(rightExpr, vmodels, data) //决定是添加还是删除
2765
- if (!data.evaluator) {
2766
- log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中")
2767
- return false
2768
- } else {
2769
- data._evaluator = data.evaluator
2770
- data._args = data.args
2771
- }
2772
2927
  }
2773
- var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况
2774
- if (!hasExpr) {
2775
- data.immobileClass = className
2928
+ if (!rexpr.test(text)) {
2929
+ className = quote(className)
2930
+ } else {
2931
+ className = stringifyExpr(className)
2776
2932
  }
2777
- parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0))
2933
+ binding.expr = "[" + className + "," + rightExpr + "]"
2778
2934
  } else {
2779
- data.immobileClass = data.oldStyle = data.param
2780
- parseExprProxy(text, vmodels, data)
2935
+ binding.expr = '[' + quote(oldStyle) + "," + text + "]"
2936
+ binding.oldStyle = oldStyle
2937
+ }
2938
+ var method = binding.type
2939
+ if (method === "hover" || method === "active") { //确保只绑定一次
2940
+ if (!binding.hasBindEvent) {
2941
+ var elem = binding.element
2942
+ var $elem = avalon(elem)
2943
+ var activate = "mouseenter" //在移出移入时切换类名
2944
+ var abandon = "mouseleave"
2945
+ if (method === "active") { //在聚焦失焦中切换类名
2946
+ elem.tabIndex = elem.tabIndex || -1
2947
+ activate = "mousedown"
2948
+ abandon = "mouseup"
2949
+ var fn0 = $elem.bind("mouseleave", function () {
2950
+ binding.toggleClass && $elem.removeClass(binding.newClass)
2951
+ })
2952
+ }
2953
+ }
2954
+
2955
+ var fn1 = $elem.bind(activate, function () {
2956
+ binding.toggleClass && $elem.addClass(binding.newClass)
2957
+ })
2958
+ var fn2 = $elem.bind(abandon, function () {
2959
+ binding.toggleClass && $elem.removeClass(binding.newClass)
2960
+ })
2961
+ binding.rollback = function () {
2962
+ $elem.unbind("mouseleave", fn0)
2963
+ $elem.unbind(activate, fn1)
2964
+ $elem.unbind(abandon, fn2)
2965
+ }
2966
+ binding.hasBindEvent = true
2781
2967
  }
2968
+ parseExprProxy(binding.expr, vmodels, binding)
2782
2969
  }
2783
2970
 
2784
- bindingExecutors ["class"] = function(val, elem, data) {
2785
- var $elem = avalon(elem),
2786
- method = data.type
2787
- if (method === "class" && data.oldStyle) { //如果是旧风格
2788
- $elem.toggleClass(data.oldStyle, !!val)
2789
- } else {
2790
- //如果存在冒号就有求值函数
2791
- data.toggleClass = data._evaluator ? !!data._evaluator.apply(elem, data._args) : true
2792
- data.newClass = data.immobileClass || val
2793
- if (data.oldClass && data.newClass !== data.oldClass) {
2794
- $elem.removeClass(data.oldClass)
2795
- }
2796
- data.oldClass = data.newClass
2797
- switch (method) {
2798
- case "class":
2799
- $elem.toggleClass(data.newClass, data.toggleClass)
2800
- break
2801
- case "hover":
2802
- case "active":
2803
- if (!data.hasBindEvent) { //确保只绑定一次
2804
- var activate = "mouseenter" //在移出移入时切换类名
2805
- var abandon = "mouseleave"
2806
- if (method === "active") { //在聚焦失焦中切换类名
2807
- elem.tabIndex = elem.tabIndex || -1
2808
- activate = "mousedown"
2809
- abandon = "mouseup"
2810
- var fn0 = $elem.bind("mouseleave", function() {
2811
- data.toggleClass && $elem.removeClass(data.newClass)
2812
- })
2813
- }
2814
- var fn1 = $elem.bind(activate, function() {
2815
- data.toggleClass && $elem.addClass(data.newClass)
2816
- })
2817
- var fn2 = $elem.bind(abandon, function() {
2818
- data.toggleClass && $elem.removeClass(data.newClass)
2819
- })
2820
- data.rollback = function() {
2821
- $elem.unbind("mouseleave", fn0)
2822
- $elem.unbind(activate, fn1)
2823
- $elem.unbind(abandon, fn2)
2824
- }
2825
- data.hasBindEvent = true
2826
- }
2827
- break;
2971
+ bindingExecutors["class"] = function (arr, elem, binding) {
2972
+ var $elem = avalon(elem)
2973
+ binding.newClass = arr[0]
2974
+ binding.toggleClass = !!arr[1]
2975
+ if (binding.oldClass && binding.newClass !== binding.oldClass) {
2976
+ $elem.removeClass(binding.oldClass)
2977
+ }
2978
+ binding.oldClass = binding.newClass
2979
+ if (binding.type === "class") {
2980
+ if (binding.oldStyle) {
2981
+ $elem.toggleClass(binding.oldStyle, !!arr[1])
2982
+ } else {
2983
+ $elem.toggleClass(binding.newClass, binding.toggleClass)
2828
2984
  }
2829
2985
  }
2986
+
2830
2987
  }
2831
2988
 
2832
- "hover,active".replace(rword, function(method) {
2989
+ "hover,active".replace(rword, function (method) {
2833
2990
  bindingHandlers[method] = bindingHandlers["class"]
2834
2991
  })
2835
2992
  //ms-controller绑定已经在scanTag 方法中实现
@@ -2838,21 +2995,20 @@ bindingExecutors ["class"] = function(val, elem, data) {
2838
2995
 
2839
2996
  // bindingHandlers.data 定义在if.js
2840
2997
  bindingExecutors.data = function(val, elem, data) {
2841
- var key = "data-" + data.param
2842
- if (val && typeof val === "object") {
2843
- elem[key] = val
2844
- } else {
2845
- elem.setAttribute(key, String(val))
2846
- }
2998
+ var key = "data-" + data.param
2999
+ if (val && typeof val === "object") {
3000
+ elem[key] = val
3001
+ } else {
3002
+ elem.setAttribute(key, String(val))
3003
+ }
2847
3004
  }
2848
-
2849
3005
  //双工绑定
2850
- var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3006
+ var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
2851
3007
  var elem = data.element,
2852
- hasCast
2853
- parseExprProxy(data.value, vmodels, data, 0, 1)
3008
+ hasCast
3009
+ parseExprProxy(data.value, vmodels, data, 1)
2854
3010
 
2855
- data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
3011
+ data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
2856
3012
  if (data.evaluator && data.args) {
2857
3013
  var params = []
2858
3014
  var casting = oneObject("string,number,boolean,checked")
@@ -2862,7 +3018,7 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
2862
3018
  if (elem.msData) {
2863
3019
  elem.msData["ms-duplex"] = data.value
2864
3020
  }
2865
- data.param.replace(/\w+/g, function (name) {
3021
+ data.param.replace(/\w+/g, function(name) {
2866
3022
  if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) {
2867
3023
  if (name === "radio")
2868
3024
  log("ms-duplex-radio已经更名为ms-duplex-checked")
@@ -2885,14 +3041,14 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
2885
3041
  params.push("string")
2886
3042
  }
2887
3043
  data.param = params.join("-")
2888
- data.bound = function (type, callback) {
3044
+ data.bound = function(type, callback) {
2889
3045
  if (elem.addEventListener) {
2890
3046
  elem.addEventListener(type, callback, false)
2891
3047
  } else {
2892
3048
  elem.attachEvent("on" + type, callback)
2893
3049
  }
2894
3050
  var old = data.rollback
2895
- data.rollback = function () {
3051
+ data.rollback = function() {
2896
3052
  elem.avalonSetter = null
2897
3053
  avalon.unbind(elem, type, callback)
2898
3054
  old && old()
@@ -2909,29 +3065,30 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
2909
3065
  }
2910
3066
  }
2911
3067
  //不存在 bindingExecutors.duplex
2912
- function fixNull(val) {
2913
- return val == null ? "" : val
2914
- }
3068
+
3069
+ function fixNull(val) {
3070
+ return val == null ? "" : val
3071
+ }
2915
3072
  avalon.duplexHooks = {
2916
3073
  checked: {
2917
- get: function (val, data) {
3074
+ get: function(val, data) {
2918
3075
  return !data.element.oldValue
2919
3076
  }
2920
3077
  },
2921
3078
  string: {
2922
- get: function (val) { //同步到VM
3079
+ get: function(val) { //同步到VM
2923
3080
  return val
2924
3081
  },
2925
3082
  set: fixNull
2926
3083
  },
2927
3084
  "boolean": {
2928
- get: function (val) {
3085
+ get: function(val) {
2929
3086
  return val === "true"
2930
3087
  },
2931
3088
  set: fixNull
2932
3089
  },
2933
3090
  number: {
2934
- get: function (val, data) {
3091
+ get: function(val, data) {
2935
3092
  var number = parseFloat(val)
2936
3093
  if (-val === -number) {
2937
3094
  return number
@@ -2951,7 +3108,7 @@ avalon.duplexHooks = {
2951
3108
  }
2952
3109
 
2953
3110
  function pipe(val, data, action, e) {
2954
- data.param.replace(/\w+/g, function (name) {
3111
+ data.param.replace(/\w+/g, function(name) {
2955
3112
  var hook = avalon.duplexHooks[name]
2956
3113
  if (hook && typeof hook[action] === "function") {
2957
3114
  val = hook[action](val, data)
@@ -2962,44 +3119,41 @@ function pipe(val, data, action, e) {
2962
3119
 
2963
3120
  var TimerID, ribbon = []
2964
3121
 
2965
- avalon.tick = function (fn) {
2966
- if (ribbon.push(fn) === 1) {
2967
- TimerID = setInterval(ticker, 60)
3122
+ avalon.tick = function(fn) {
3123
+ if (ribbon.push(fn) === 1) {
3124
+ TimerID = setInterval(ticker, 60)
3125
+ }
2968
3126
  }
2969
- }
2970
3127
 
2971
- function ticker() {
2972
- for (var n = ribbon.length - 1; n >= 0; n--) {
2973
- var el = ribbon[n]
2974
- if (el() === false) {
2975
- ribbon.splice(n, 1)
3128
+ function ticker() {
3129
+ for (var n = ribbon.length - 1; n >= 0; n--) {
3130
+ var el = ribbon[n]
3131
+ if (el() === false) {
3132
+ ribbon.splice(n, 1)
3133
+ }
3134
+ }
3135
+ if (!ribbon.length) {
3136
+ clearInterval(TimerID)
2976
3137
  }
2977
3138
  }
2978
- if (!ribbon.length) {
2979
- clearInterval(TimerID)
2980
- }
2981
- }
2982
3139
 
2983
3140
  var watchValueInTimer = noop
2984
3141
  var rmsinput = /text|password|hidden/
2985
- new function () {// jshint ignore:line
2986
- try {//#272 IE9-IE11, firefox
3142
+ new function() { // jshint ignore:line
3143
+ try { //#272 IE9-IE11, firefox
2987
3144
  var setters = {}
2988
3145
  var aproto = HTMLInputElement.prototype
2989
3146
  var bproto = HTMLTextAreaElement.prototype
2990
- function newSetter(value) {// jshint ignore:line
2991
- if (avalon.contains(root, this)) {
3147
+ function newSetter(value) { // jshint ignore:line
2992
3148
  setters[this.tagName].call(this, value)
2993
- if (!rmsinput.test(this.type))
2994
- return
2995
- if (!this.msFocus && this.avalonSetter) {
3149
+ if (rmsinput.test(this.type) && !this.msFocus && this.avalonSetter) {
2996
3150
  this.avalonSetter()
2997
3151
  }
2998
- }
2999
3152
  }
3000
3153
  var inputProto = HTMLInputElement.prototype
3001
3154
  Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错
3002
3155
  setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set
3156
+
3003
3157
  Object.defineProperty(aproto, "value", {
3004
3158
  set: newSetter
3005
3159
  })
@@ -3008,67 +3162,66 @@ new function () {// jshint ignore:line
3008
3162
  set: newSetter
3009
3163
  })
3010
3164
  } catch (e) {
3165
+ //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了
3166
+ // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype
3167
+ // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1
3011
3168
  watchValueInTimer = avalon.tick
3012
3169
  }
3013
- }// jshint ignore:line
3014
-
3015
-
3170
+ } // jshint ignore:line
3016
3171
  //处理radio, checkbox, text, textarea, password
3017
- duplexBinding.INPUT = function (element, evaluator, data) {
3172
+ duplexBinding.INPUT = function(element, evaluator, data) {
3018
3173
  var $type = element.type,
3019
- bound = data.bound,
3020
- $elem = avalon(element),
3021
- composing = false
3022
- function callback(value) {
3023
- data.changed.call(this, value, data)
3024
- }
3025
- function compositionStart() {
3026
- composing = true
3027
- }
3028
- function compositionEnd() {
3174
+ bound = data.bound,
3175
+ $elem = avalon(element),
3029
3176
  composing = false
3030
- }
3031
- //当value变化时改变model的值
3032
3177
 
3033
- var updateVModel = function () {
3034
- if (composing)//处理中文输入法在minlengh下引发的BUG
3178
+ function callback(value) {
3179
+ data.changed.call(this, value, data)
3180
+ }
3181
+
3182
+ function compositionStart() {
3183
+ composing = true
3184
+ }
3185
+
3186
+ function compositionEnd() {
3187
+ composing = false
3188
+ }
3189
+ //当value变化时改变model的值
3190
+
3191
+ var updateVModel = function() {
3192
+ var val = element.value //防止递归调用形成死循环
3193
+ if (composing || val === element.oldValue) //处理中文输入法在minlengh下引发的BUG
3035
3194
  return
3036
- var val = element.oldValue = element.value //防止递归调用形成死循环
3037
3195
  var lastValue = data.pipe(val, data, "get")
3038
- if ($elem.data("duplex-observe") !== false) {
3196
+ if ($elem.data("duplexObserve") !== false) {
3039
3197
  evaluator(lastValue)
3040
3198
  callback.call(element, lastValue)
3041
- if ($elem.data("duplex-focus")) {
3042
- avalon.nextTick(function () {
3043
- element.focus()
3044
- })
3045
- }
3046
3199
  }
3047
3200
  }
3048
3201
  //当model变化时,它就会改变value的值
3049
- data.handler = function () {
3202
+ data.handler = function() {
3050
3203
  var val = data.pipe(evaluator(), data, "set") + ""
3051
3204
  if (val !== element.oldValue) {
3052
- element.value = val
3205
+ element.value = element.oldValue = val
3053
3206
  }
3054
3207
  }
3055
3208
  if (data.isChecked || $type === "radio") {
3056
- updateVModel = function () {
3057
- if ($elem.data("duplex-observe") !== false) {
3209
+ updateVModel = function() {
3210
+ if ($elem.data("duplexObserve") !== false) {
3058
3211
  var lastValue = data.pipe(element.value, data, "get")
3059
3212
  evaluator(lastValue)
3060
3213
  callback.call(element, lastValue)
3061
3214
  }
3062
3215
  }
3063
- data.handler = function () {
3216
+ data.handler = function() {
3064
3217
  var val = evaluator()
3065
- var checked = data.isChecked ? !!val : val + "" === element.value
3218
+ var checked = data.isChecked ? !! val : val + "" === element.value
3066
3219
  element.checked = element.oldValue = checked
3067
3220
  }
3068
3221
  bound("click", updateVModel)
3069
3222
  } else if ($type === "checkbox") {
3070
- updateVModel = function () {
3071
- if ($elem.data("duplex-observe") !== false) {
3223
+ updateVModel = function() {
3224
+ if ($elem.data("duplexObserve") !== false) {
3072
3225
  var method = element.checked ? "ensure" : "remove"
3073
3226
  var array = evaluator()
3074
3227
  if (!Array.isArray(array)) {
@@ -3079,7 +3232,7 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3079
3232
  callback.call(element, array)
3080
3233
  }
3081
3234
  }
3082
- data.handler = function () {
3235
+ data.handler = function() {
3083
3236
  var array = [].concat(evaluator()) //强制转换为数组
3084
3237
  element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1
3085
3238
  }
@@ -3089,7 +3242,7 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3089
3242
  if (element.attributes["data-event"]) {
3090
3243
  log("data-event指令已经废弃,请改用data-duplex-event")
3091
3244
  }
3092
- events.replace(rword, function (name) {
3245
+ events.replace(rword, function(name) {
3093
3246
  switch (name) {
3094
3247
  case "input":
3095
3248
  bound("input", updateVModel)
@@ -3104,14 +3257,14 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3104
3257
  break
3105
3258
  }
3106
3259
  })
3107
- bound("focus", function () {
3260
+ bound("focus", function() {
3108
3261
  element.msFocus = true
3109
3262
  })
3110
- bound("blur", function () {
3263
+ bound("blur", function() {
3111
3264
  element.msFocus = false
3112
3265
  })
3113
3266
  if (rmsinput.test($type)) {
3114
- watchValueInTimer(function () {
3267
+ watchValueInTimer(function() {
3115
3268
  if (root.contains(element)) {
3116
3269
  if (!element.msFocus && element.oldValue !== element.value) {
3117
3270
  updateVModel()
@@ -3126,28 +3279,29 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3126
3279
  }
3127
3280
 
3128
3281
  element.oldValue = element.value
3129
- registerSubscriber(data)
3282
+ avalon.injectBinding(data)
3130
3283
  callback.call(element, element.value)
3131
3284
  }
3132
3285
  duplexBinding.TEXTAREA = duplexBinding.INPUT
3133
3286
  duplexBinding.SELECT = function(element, evaluator, data) {
3134
3287
  var $elem = avalon(element)
3135
- function updateVModel() {
3136
- if ($elem.data("duplex-observe") !== false) {
3137
- var val = $elem.val() //字符串或字符串数组
3138
- if (Array.isArray(val)) {
3139
- val = val.map(function(v) {
3140
- return data.pipe(v, data, "get")
3141
- })
3142
- } else {
3143
- val = data.pipe(val, data, "get")
3144
- }
3145
- if (val + "" !== element.oldValue) {
3146
- evaluator(val)
3288
+
3289
+ function updateVModel() {
3290
+ if ($elem.data("duplexObserve") !== false) {
3291
+ var val = $elem.val() //字符串或字符串数组
3292
+ if (Array.isArray(val)) {
3293
+ val = val.map(function(v) {
3294
+ return data.pipe(v, data, "get")
3295
+ })
3296
+ } else {
3297
+ val = data.pipe(val, data, "get")
3298
+ }
3299
+ if (val + "" !== element.oldValue) {
3300
+ evaluator(val)
3301
+ }
3302
+ data.changed.call(element, val, data)
3147
3303
  }
3148
- data.changed.call(element, val, data)
3149
3304
  }
3150
- }
3151
3305
  data.handler = function() {
3152
3306
  var val = evaluator()
3153
3307
  val = val && val.$model || val
@@ -3168,74 +3322,77 @@ duplexBinding.SELECT = function(element, evaluator, data) {
3168
3322
  }
3169
3323
  }
3170
3324
  data.bound("change", updateVModel)
3171
- checkScan(element, function() {
3172
- registerSubscriber(data)
3325
+ element.msCallback = function() {
3326
+ avalon.injectBinding(data)
3173
3327
  data.changed.call(element, evaluator(), data)
3174
- }, NaN)
3328
+ }
3175
3329
  }
3176
-
3177
-
3178
3330
  // bindingHandlers.html 定义在if.js
3179
- bindingExecutors.html = function(val, elem, data) {
3180
- val = val == null ? "" : val
3181
- var isHtmlFilter = "group" in data
3331
+ bindingExecutors.html = function (val, elem, data) {
3332
+ var isHtmlFilter = elem.nodeType !== 1
3182
3333
  var parent = isHtmlFilter ? elem.parentNode : elem
3183
3334
  if (!parent)
3184
3335
  return
3185
- if (val.nodeType === 11) { //将val转换为文档碎片
3186
- var fragment = val
3336
+ val = val == null ? "" : val
3337
+ if (data.oldText !== val) {
3338
+ data.oldText = val
3339
+ } else {
3340
+ return
3341
+ }
3342
+ if (elem.nodeType === 3) {
3343
+ var signature = generateID("html")
3344
+ parent.insertBefore(DOC.createComment(signature), elem)
3345
+ data.element = DOC.createComment(signature + ":end")
3346
+ parent.replaceChild(data.element, elem)
3347
+ elem = data.element
3348
+ }
3349
+ if (typeof val !== "object") {//string, number, boolean
3350
+ var fragment = avalon.parseHTML(String(val))
3351
+ } else if (val.nodeType === 11) { //将val转换为文档碎片
3352
+ fragment = val
3187
3353
  } else if (val.nodeType === 1 || val.item) {
3188
- var nodes = val.nodeType === 1 ? val.childNodes : val.item ? val : []
3189
- fragment = hyperspace.cloneNode(true)
3354
+ var nodes = val.nodeType === 1 ? val.childNodes : val.item
3355
+ fragment = avalonFragment.cloneNode(true)
3190
3356
  while (nodes[0]) {
3191
3357
  fragment.appendChild(nodes[0])
3192
3358
  }
3193
- } else {
3194
- fragment = avalon.parseHTML(val)
3195
3359
  }
3360
+
3361
+ nodes = avalon.slice(fragment.childNodes)
3196
3362
  //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空
3197
- var comment = DOC.createComment("ms-html")
3198
3363
  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) {
3364
+ var endValue = elem.nodeValue.slice(0, -4)
3365
+ while (true) {
3366
+ var node = elem.previousSibling
3367
+ if (!node || node.nodeType === 8 && node.nodeValue === endValue) {
3368
+ break
3369
+ } else {
3204
3370
  parent.removeChild(node)
3205
- i++
3206
3371
  }
3207
3372
  }
3208
- parent.removeChild(elem)
3209
- data.element = comment //防止被CG
3373
+ parent.insertBefore(fragment, elem)
3210
3374
  } 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
- }
3375
+ avalon.clearHTML(elem).appendChild(fragment)
3223
3376
  }
3224
3377
  scanNodeArray(nodes, data.vmodels)
3225
3378
  }
3226
-
3227
3379
  bindingHandlers["if"] =
3228
- bindingHandlers.data =
3229
- bindingHandlers.text =
3230
- bindingHandlers.html =
3231
- function(data, vmodels) {
3232
- parseExprProxy(data.value, vmodels, data)
3233
- }
3380
+ bindingHandlers.data =
3381
+ bindingHandlers.text =
3382
+ bindingHandlers.html =
3383
+ function(data, vmodels) {
3384
+ parseExprProxy(data.value, vmodels, data)
3385
+ }
3234
3386
 
3235
3387
  bindingExecutors["if"] = function(val, elem, data) {
3388
+ try {
3389
+ if(!elem.parentNode) return
3390
+ } catch(e) {return}
3236
3391
  if (val) { //插回DOM树
3237
3392
  if (elem.nodeType === 8) {
3238
3393
  elem.parentNode.replaceChild(data.template, elem)
3394
+ elem.ifRemove = null
3395
+ // animate.enter(data.template, elem.parentNode)
3239
3396
  elem = data.element = data.template //这时可能为null
3240
3397
  }
3241
3398
  if (elem.getAttribute(data.name)) {
@@ -3247,6 +3404,8 @@ bindingExecutors["if"] = function(val, elem, data) {
3247
3404
  if (elem.nodeType === 1) {
3248
3405
  var node = data.element = DOC.createComment("ms-if")
3249
3406
  elem.parentNode.replaceChild(node, elem)
3407
+ elem.ifRemove = node
3408
+ // animate.leave(elem, node.parentNode, node)
3250
3409
  data.template = elem //元素节点
3251
3410
  ifGroup.appendChild(elem)
3252
3411
  data.rollback = function() {
@@ -3257,8 +3416,6 @@ bindingExecutors["if"] = function(val, elem, data) {
3257
3416
  }
3258
3417
  }
3259
3418
  }
3260
-
3261
-
3262
3419
  //ms-important绑定已经在scanTag 方法中实现
3263
3420
  //ms-include绑定已由ms-attr绑定实现
3264
3421
 
@@ -3302,11 +3459,9 @@ bindingExecutors.on = function(callback, elem, data) {
3302
3459
  }
3303
3460
  }
3304
3461
  }
3305
-
3306
-
3307
- bindingHandlers.repeat = function(data, vmodels) {
3462
+ bindingHandlers.repeat = function (data, vmodels) {
3308
3463
  var type = data.type
3309
- parseExprProxy(data.value, vmodels, data, 0, 1)
3464
+ parseExprProxy(data.value, vmodels, data, 1)
3310
3465
  data.proxies = []
3311
3466
  var freturn = false
3312
3467
  try {
@@ -3315,11 +3470,12 @@ bindingHandlers.repeat = function(data, vmodels) {
3315
3470
  if (xtype !== "object" && xtype !== "array") {
3316
3471
  freturn = true
3317
3472
  avalon.log("warning:" + data.value + "只能是对象或数组")
3473
+ } else {
3474
+ data.xtype = xtype
3318
3475
  }
3319
3476
  } catch (e) {
3320
3477
  freturn = true
3321
3478
  }
3322
-
3323
3479
  var arr = data.value.split(".") || []
3324
3480
  if (arr.length > 1) {
3325
3481
  arr.pop()
@@ -3333,41 +3489,43 @@ bindingHandlers.repeat = function(data, vmodels) {
3333
3489
  }
3334
3490
  }
3335
3491
  }
3492
+
3336
3493
  var elem = data.element
3337
- elem.removeAttribute(data.name)
3338
-
3339
- data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
3340
- data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
3341
- var signature = generateID(type)
3342
- var comment = data.element = DOC.createComment(signature + ":end")
3343
- data.clone = DOC.createComment(signature)
3344
- hyperspace.appendChild(comment)
3345
-
3346
- if (type === "each" || type === "with") {
3347
- data.template = elem.innerHTML.trim()
3348
- avalon.clearHTML(elem).appendChild(comment)
3349
- } else {
3350
- data.template = elem.outerHTML.trim()
3351
- elem.parentNode.replaceChild(comment, elem)
3352
- }
3353
- data.template = avalon.parseHTML(data.template)
3354
- data.rollback = function() {
3355
- var elem = data.element
3356
- if (!elem)
3357
- return
3358
- bindingExecutors.repeat.call(data, "clear")
3359
- var parentNode = elem.parentNode
3360
- var content = data.template
3361
- var target = content.firstChild
3362
- parentNode.replaceChild(content, elem)
3363
- var start = data.$stamp
3364
- start && start.parentNode && start.parentNode.removeChild(start)
3365
- target = data.element = data.type === "repeat" ? target : parentNode
3494
+ if (elem.nodeType === 1) {
3495
+ elem.removeAttribute(data.name)
3496
+ data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
3497
+ data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
3498
+ var signature = generateID(type)
3499
+ var start = DOC.createComment(signature)
3500
+ var end = DOC.createComment(signature + ":end")
3501
+ data.signature = signature
3502
+ data.template = avalonFragment.cloneNode(false)
3503
+ if (type === "repeat") {
3504
+ var parent = elem.parentNode
3505
+ parent.replaceChild(end, elem)
3506
+ parent.insertBefore(start, end)
3507
+ data.template.appendChild(elem)
3508
+ } else {
3509
+ while (elem.firstChild) {
3510
+ data.template.appendChild(elem.firstChild)
3511
+ }
3512
+ elem.appendChild(start)
3513
+ elem.appendChild(end)
3514
+ }
3515
+ data.element = end
3516
+ data.handler = bindingExecutors.repeat
3517
+ data.rollback = function () {
3518
+ var elem = data.element
3519
+ if (!elem)
3520
+ return
3521
+ data.handler("clear")
3522
+ }
3366
3523
  }
3524
+
3367
3525
  if (freturn) {
3368
3526
  return
3369
3527
  }
3370
- data.handler = bindingExecutors.repeat
3528
+
3371
3529
  data.$outer = {}
3372
3530
  var check0 = "$key"
3373
3531
  var check1 = "$val"
@@ -3375,6 +3533,7 @@ bindingHandlers.repeat = function(data, vmodels) {
3375
3533
  check0 = "$first"
3376
3534
  check1 = "$last"
3377
3535
  }
3536
+
3378
3537
  for (i = 0; v = vmodels[i++]; ) {
3379
3538
  if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) {
3380
3539
  data.$outer = v
@@ -3383,81 +3542,101 @@ bindingHandlers.repeat = function(data, vmodels) {
3383
3542
  }
3384
3543
  var $events = $repeat.$events
3385
3544
  var $list = ($events || {})[subscribers]
3386
- if ($list && avalon.Array.ensure($list, data)) {
3387
- addSubscribers(data, $list)
3388
- }
3545
+ injectDependency($list, data)
3389
3546
  if (xtype === "object") {
3390
- data.$with = true
3391
- var pool = !$events ? {} : $events.$withProxyPool || ($events.$withProxyPool = {})
3392
- data.handler("append", $repeat, pool)
3547
+ data.handler("append")
3393
3548
  } else if ($repeat.length) {
3394
3549
  data.handler("add", 0, $repeat.length)
3395
3550
  }
3396
3551
  }
3397
3552
 
3398
- bindingExecutors.repeat = function(method, pos, el) {
3553
+ bindingExecutors.repeat = function (method, pos, el) {
3554
+ var data = this
3555
+ if (!method && data.xtype) {
3556
+ var old = data.$repeat
3557
+ var neo = data.evaluator.apply(0, data.args || [])
3558
+
3559
+ if (data.xtype === "array") {
3560
+ if (old.length === neo.length) {
3561
+ return
3562
+ }
3563
+ method = "add"
3564
+ pos = 0
3565
+ data.$repeat = neo
3566
+ el = neo.length
3567
+ } else {
3568
+ if (keysVM(old).join(";;") === keysVM(neo).join(";;")) {
3569
+ return
3570
+ }
3571
+ method = "append"
3572
+ data.$repeat = neo
3573
+ }
3574
+ }
3399
3575
  if (method) {
3400
- var data = this
3576
+ var start, fragment
3401
3577
  var end = data.element
3578
+ var comments = getComments(data)
3402
3579
  var parent = end.parentNode
3403
3580
  var proxies = data.proxies
3404
- var transation = hyperspace.cloneNode(false)
3581
+ var transation = avalonFragment.cloneNode(false)
3405
3582
  switch (method) {
3406
- case "add": //在pos位置后添加el数组(pos为数字,el为数组)
3583
+ case "add": //在pos位置后添加el数组(pos为插入位置,el为要插入的个数)
3407
3584
  var n = pos + el
3408
- var array = data.$repeat
3409
- var last = array.length - 1
3410
- var fragments = [], fragment
3411
- var start = locateNode(data, pos)
3585
+ var fragments = []
3412
3586
  for (var i = pos; i < n; i++) {
3413
3587
  var proxy = eachProxyAgent(i, data)
3414
3588
  proxies.splice(i, 0, proxy)
3415
3589
  shimController(data, transation, proxy, fragments)
3416
3590
  }
3417
- parent.insertBefore(transation, start)
3591
+ parent.insertBefore(transation, comments[pos] || end)
3418
3592
  for (i = 0; fragment = fragments[i++]; ) {
3419
3593
  scanNodeArray(fragment.nodes, fragment.vmodels)
3420
3594
  fragment.nodes = fragment.vmodels = null
3421
3595
  }
3596
+
3422
3597
  break
3423
3598
  case "del": //将pos后的el个元素删掉(pos, el都是数字)
3424
- start = proxies[pos].$stamp
3425
- end = locateNode(data, pos + el)
3426
- sweepNodes(start, end)
3599
+ sweepNodes(comments[pos], comments[pos + el] || end)
3427
3600
  var removed = proxies.splice(pos, el)
3428
3601
  recycleProxies(removed, "each")
3429
3602
  break
3430
3603
  case "clear":
3431
- var check = data.$stamp || proxies[0]
3432
- if (check) {
3433
- start = check.$stamp || check
3604
+ start = comments[0]
3605
+ if (start) {
3434
3606
  sweepNodes(start, end)
3607
+ if (data.xtype === "object") {
3608
+ parent.insertBefore(start, end)
3609
+ }else{
3610
+ recycleProxies(proxies, "each")
3611
+ }
3435
3612
  }
3436
- recycleProxies(proxies, "each")
3437
3613
  break
3438
3614
  case "move":
3439
- start = proxies[0].$stamp
3440
- var signature = start.nodeValue
3441
- var rooms = []
3442
- var room = [], node
3443
- sweepNodes(start, end, function() {
3444
- room.unshift(this)
3445
- if (this.nodeValue === signature) {
3446
- rooms.unshift(room)
3447
- room = []
3448
- }
3449
- })
3450
- sortByIndex(proxies, pos)
3451
- sortByIndex(rooms, pos)
3452
- while (room = rooms.shift()) {
3453
- while (node = room.shift()) {
3454
- transation.appendChild(node)
3615
+ start = comments[0]
3616
+ if (start) {
3617
+ var signature = start.nodeValue
3618
+ var rooms = []
3619
+ var room = [],
3620
+ node
3621
+ sweepNodes(start, end, function () {
3622
+ room.unshift(this)
3623
+ if (this.nodeValue === signature) {
3624
+ rooms.unshift(room)
3625
+ room = []
3626
+ }
3627
+ })
3628
+ sortByIndex(rooms, pos)
3629
+ sortByIndex(proxies, pos)
3630
+ while (room = rooms.shift()) {
3631
+ while (node = room.shift()) {
3632
+ transation.appendChild(node)
3633
+ }
3455
3634
  }
3635
+ parent.insertBefore(transation, end)
3456
3636
  }
3457
- parent.insertBefore(transation, end)
3458
3637
  break
3459
3638
  case "index": //将proxies中的第pos个起的所有元素重新索引
3460
- last = proxies.length - 1
3639
+ var last = proxies.length - 1
3461
3640
  for (; el = proxies[pos]; pos++) {
3462
3641
  el.$index = pos
3463
3642
  el.$first = pos === 0
@@ -3467,15 +3646,23 @@ bindingExecutors.repeat = function(method, pos, el) {
3467
3646
  case "set": //将proxies中的第pos个元素的VM设置为el(pos为数字,el任意)
3468
3647
  proxy = proxies[pos]
3469
3648
  if (proxy) {
3470
- notifySubscribers(proxy.$events.$index)
3649
+ fireDependencies(proxy.$events[data.param || "el"])
3471
3650
  }
3472
- return
3473
- case "append": //将pos的键值对从el中取出(pos为一个普通对象,el为预先生成好的代理VM对象池)
3474
- var pool = el
3651
+ break
3652
+ case "append":
3653
+ var object = data.$repeat //原来第2参数, 被循环对象
3654
+ var pool = Array.isArray(proxies) ||!proxies ? {}: proxies //代理对象组成的hash
3655
+ data.proxies = pool
3475
3656
  var keys = []
3476
3657
  fragments = []
3477
- for (var key in pos) { //得到所有键名
3478
- if (pos.hasOwnProperty(key) && key !== "hasOwnProperty") {
3658
+ for (var key in pool) {
3659
+ if (!object.hasOwnProperty(key)) {
3660
+ proxyRecycler(pool[key], withProxyPool) //去掉之前的代理VM
3661
+ delete(pool[key])
3662
+ }
3663
+ }
3664
+ for (key in object) { //得到所有键名
3665
+ if (object.hasOwnProperty(key) && key !== "hasOwnProperty") {
3479
3666
  keys.push(key)
3480
3667
  }
3481
3668
  }
@@ -3487,14 +3674,11 @@ bindingExecutors.repeat = function(method, pos, el) {
3487
3674
  }
3488
3675
  for (i = 0; key = keys[i++]; ) {
3489
3676
  if (key !== "hasOwnProperty") {
3490
- if (!pool[key]) {
3491
- pool[key] = withProxyAgent(key, data)
3492
- }
3677
+ pool[key] = withProxyAgent(pool[key], key, data)
3493
3678
  shimController(data, transation, pool[key], fragments)
3494
3679
  }
3495
3680
  }
3496
- var comment = data.$stamp = data.clone
3497
- parent.insertBefore(comment, end)
3681
+
3498
3682
  parent.insertBefore(transation, end)
3499
3683
  for (i = 0; fragment = fragments[i++]; ) {
3500
3684
  scanNodeArray(fragment.nodes, fragment.vmodels)
@@ -3502,29 +3686,26 @@ bindingExecutors.repeat = function(method, pos, el) {
3502
3686
  }
3503
3687
  break
3504
3688
  }
3689
+ if (!data.$repeat || data.$repeat.hasOwnProperty("$lock")) //IE6-8 VBScript对象会报错, 有时候data.$repeat不存在
3690
+ return
3505
3691
  if (method === "clear")
3506
3692
  method = "del"
3507
3693
  var callback = data.renderedCallback || noop,
3508
3694
  args = arguments
3509
- checkScan(parent, function() {
3510
- callback.apply(parent, args)
3511
- if (parent.oldValue && parent.tagName === "SELECT") { //fix #503
3512
- avalon(parent).val(parent.oldValue.split(","))
3513
- }
3514
- }, NaN)
3695
+ if (parent.oldValue && parent.tagName === "SELECT") { //fix #503
3696
+ avalon(parent).val(parent.oldValue.split(","))
3697
+ }
3698
+ callback.apply(parent, args)
3515
3699
  }
3516
3700
  }
3517
-
3518
- "with,each".replace(rword, function(name) {
3701
+ "with,each".replace(rword, function (name) {
3519
3702
  bindingHandlers[name] = bindingHandlers.repeat
3520
3703
  })
3521
3704
 
3522
3705
  function shimController(data, transation, proxy, fragments) {
3523
3706
  var content = data.template.cloneNode(true)
3524
3707
  var nodes = avalon.slice(content.childNodes)
3525
- if (proxy.$stamp) {
3526
- content.insertBefore(proxy.$stamp, content.firstChild)
3527
- }
3708
+ content.insertBefore(DOC.createComment(data.signature), content.firstChild)
3528
3709
  transation.appendChild(content)
3529
3710
  var nv = [proxy].concat(data.vmodels)
3530
3711
  var fragment = {
@@ -3534,11 +3715,21 @@ function shimController(data, transation, proxy, fragments) {
3534
3715
  fragments.push(fragment)
3535
3716
  }
3536
3717
 
3537
- function locateNode(data, pos) {
3538
- var proxy = data.proxies[pos]
3539
- return proxy ? proxy.$stamp : data.element
3718
+ function getComments(data) {
3719
+ var ret = []
3720
+ var nodes = data.element.parentNode.childNodes
3721
+ for (var i = 0, node; node = nodes[i++]; ) {
3722
+ if (node.nodeValue === data.signature) {
3723
+ ret.push(node)
3724
+ } else if (node.nodeValue === data.signature + ":end") {
3725
+ break
3726
+ }
3727
+ }
3728
+ return ret
3540
3729
  }
3541
3730
 
3731
+
3732
+ //移除掉start与end之间的节点(保留end)
3542
3733
  function sweepNodes(start, end, callback) {
3543
3734
  while (true) {
3544
3735
  var node = end.previousSibling
@@ -3555,24 +3746,89 @@ function sweepNodes(start, end, callback) {
3555
3746
  // 为ms-each,ms-with, ms-repeat会创建一个代理VM,
3556
3747
  // 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法
3557
3748
  // 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现
3558
- var eachProxyPool = []
3559
3749
  var withProxyPool = []
3750
+ function withProxyFactory() {
3751
+ var proxy = modelFactory({
3752
+ $key: "",
3753
+ $outer: {},
3754
+ $host: {},
3755
+ $val: {
3756
+ get: function () {
3757
+ return this.$host[this.$key]
3758
+ },
3759
+ set: function (val) {
3760
+ this.$host[this.$key] = val
3761
+ }
3762
+ }
3763
+ }, {
3764
+ $val: 1
3765
+ })
3766
+ proxy.$id = generateID("$proxy$with")
3767
+ return proxy
3768
+ }
3769
+
3770
+ function withProxyAgent(proxy, key, data) {
3771
+ proxy = proxy || withProxyPool.pop()
3772
+ if (!proxy) {
3773
+ proxy = withProxyFactory()
3774
+ } else {
3775
+ proxy.$reinitialize()
3776
+ }
3777
+ var host = data.$repeat
3778
+ proxy.$key = key
3779
+
3780
+ proxy.$host = host
3781
+ proxy.$outer = data.$outer
3782
+ if (host.$events) {
3783
+ proxy.$events.$val = host.$events[key]
3784
+ } else {
3785
+ proxy.$events = {}
3786
+ }
3787
+ return proxy
3788
+ }
3789
+
3790
+
3791
+ function recycleProxies(proxies) {
3792
+ eachProxyRecycler(proxies)
3793
+ }
3794
+ function eachProxyRecycler(proxies) {
3795
+ proxies.forEach(function (proxy) {
3796
+ proxyRecycler(proxy, eachProxyPool)
3797
+ })
3798
+ proxies.length = 0
3799
+ }
3800
+
3801
+
3802
+ var eachProxyPool = []
3560
3803
  function eachProxyFactory(name) {
3561
3804
  var source = {
3562
3805
  $host: [],
3563
3806
  $outer: {},
3564
- $stamp: 1,
3565
3807
  $index: 0,
3566
3808
  $first: false,
3567
3809
  $last: false,
3568
3810
  $remove: avalon.noop
3569
3811
  }
3570
3812
  source[name] = {
3571
- get: function() {
3572
- return this.$host[this.$index]
3813
+ get: function () {
3814
+ var e = this.$events
3815
+ var array = e.$index
3816
+ e.$index = e[name] //#817 通过$index为el收集依赖
3817
+ try {
3818
+ return this.$host[this.$index]
3819
+ } finally {
3820
+ e.$index = array
3821
+ }
3573
3822
  },
3574
- set: function(val) {
3575
- this.$host.set(this.$index, val)
3823
+ set: function (val) {
3824
+ try {
3825
+ var e = this.$events
3826
+ var array = e.$index
3827
+ e.$index = []
3828
+ this.$host.set(this.$index, val)
3829
+ } finally {
3830
+ e.$index = array
3831
+ }
3576
3832
  }
3577
3833
  }
3578
3834
  var second = {
@@ -3581,14 +3837,13 @@ function eachProxyFactory(name) {
3581
3837
  $index: 1
3582
3838
  }
3583
3839
  var proxy = modelFactory(source, second)
3584
- var e = proxy.$events
3585
- e[name] = e.$first = e.$last = e.$index
3586
3840
  proxy.$id = generateID("$proxy$each")
3587
3841
  return proxy
3588
3842
  }
3589
3843
 
3590
3844
  function eachProxyAgent(index, data) {
3591
- var param = data.param || "el", proxy
3845
+ var param = data.param || "el",
3846
+ proxy
3592
3847
  for (var i = 0, n = eachProxyPool.length; i < n; i++) {
3593
3848
  var candidate = eachProxyPool[i]
3594
3849
  if (candidate && candidate.hasOwnProperty(param)) {
@@ -3606,93 +3861,45 @@ function eachProxyAgent(index, data) {
3606
3861
  proxy.$last = index === last
3607
3862
  proxy.$host = host
3608
3863
  proxy.$outer = data.$outer
3609
- proxy.$stamp = data.clone.cloneNode(false)
3610
- proxy.$remove = function() {
3864
+ proxy.$remove = function () {
3611
3865
  return host.removeAt(proxy.$index)
3612
3866
  }
3613
3867
  return proxy
3614
3868
  }
3615
3869
 
3616
- function withProxyFactory() {
3617
- var proxy = modelFactory({
3618
- $key: "",
3619
- $outer: {},
3620
- $host: {},
3621
- $val: {
3622
- get: function() {
3623
- return this.$host[this.$key]
3624
- },
3625
- set: function(val) {
3626
- this.$host[this.$key] = val
3627
- }
3628
- }
3629
- }, {
3630
- $val: 1
3631
- })
3632
- proxy.$id = generateID("$proxy$with")
3633
- return proxy
3634
- }
3635
3870
 
3636
- function withProxyAgent(key, data) {
3637
- var proxy = withProxyPool.pop()
3638
- if (!proxy) {
3639
- proxy = withProxyFactory()
3871
+ function proxyRecycler(proxy, proxyPool) {
3872
+ for (var i in proxy.$events) {
3873
+ var arr = proxy.$events[i]
3874
+ if (Array.isArray(arr)) {
3875
+ arr.forEach(function (data) {
3876
+ if (typeof data === "object")
3877
+ disposeData(data)
3878
+ })// jshint ignore:line
3879
+ arr.length = 0
3880
+ }
3640
3881
  }
3641
- var host = data.$repeat
3642
- proxy.$key = key
3643
- proxy.$host = host
3644
- proxy.$outer = data.$outer
3645
- if (host.$events) {
3646
- proxy.$events.$val = host.$events[key]
3647
- } else {
3648
- proxy.$events = {}
3882
+ proxy.$host = proxy.$outer = {}
3883
+ if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) {
3884
+ proxyPool.pop()
3649
3885
  }
3650
- return proxy
3651
- }
3652
-
3653
- function recycleProxies(proxies, type) {
3654
- var proxyPool = type === "each" ? eachProxyPool : withProxyPool
3655
- avalon.each(proxies, function(key, proxy) {
3656
- if (proxy.$events) {
3657
- for (var i in proxy.$events) {
3658
- if (Array.isArray(proxy.$events[i])) {
3659
- proxy.$events[i].forEach(function(data) {
3660
- if (typeof data === "object")
3661
- disposeData(data)
3662
- })// jshint ignore:line
3663
- proxy.$events[i].length = 0
3664
- }
3665
- }
3666
- proxy.$host = proxy.$outer = {}
3667
- if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) {
3668
- proxyPool.pop()
3669
- }
3670
- }
3671
- })
3672
- if (type === "each")
3673
- proxies.length = 0
3674
3886
  }
3675
3887
 
3676
-
3677
-
3678
-
3679
3888
  /*********************************************************************
3680
3889
  * 各种指令 *
3681
3890
  **********************************************************************/
3682
3891
  //ms-skip绑定已经在scanTag 方法中实现
3683
3892
  // bindingHandlers.text 定义在if.js
3684
3893
  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
- }
3894
+ val = val == null ? "" : val //不在页面上显示undefined null
3895
+ if (elem.nodeType === 3) { //绑定在文本节点上
3896
+ try { //IE对游离于DOM树外的节点赋值会报错
3897
+ elem.data = val
3898
+ } catch (e) {}
3899
+ } else { //绑定在特性节点上
3900
+ elem.textContent = val
3901
+ }
3694
3902
  }
3695
-
3696
3903
  function parseDisplay(nodeName, val) {
3697
3904
  //用于取得此类标签的默认display值
3698
3905
  var key = "_" + nodeName
@@ -3712,42 +3919,33 @@ function parseDisplay(nodeName, val) {
3712
3919
 
3713
3920
  avalon.parseDisplay = parseDisplay
3714
3921
 
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
3922
+ bindingHandlers.visible = function (data, vmodels) {
3731
3923
  parseExprProxy(data.value, vmodels, data)
3732
3924
  }
3733
3925
 
3734
- bindingExecutors.visible = function(val, elem, data) {
3735
- elem.style.display = val ? data.display : "none"
3926
+ bindingExecutors.visible = function (val, elem, binding) {
3927
+ if (val) {
3928
+ elem.style.display = binding.display || ""
3929
+ if (avalon(elem).css("display") === "none") {
3930
+ elem.style.display = binding.display = parseDisplay(elem.nodeName)
3931
+ }
3932
+ } else {
3933
+ elem.style.display = "none"
3934
+ }
3736
3935
  }
3737
-
3738
3936
  bindingHandlers.widget = function(data, vmodels) {
3739
3937
  var args = data.value.match(rword)
3740
3938
  var elem = data.element
3741
3939
  var widget = args[0]
3742
3940
  var id = args[1]
3743
- if (!id || id === "$") {//没有定义或为$时,取组件名+随机数
3941
+ if (!id || id === "$") { //没有定义或为$时,取组件名+随机数
3744
3942
  id = generateID(widget)
3745
3943
  }
3746
- var optName = args[2] || widget//没有定义,取组件名
3944
+ var optName = args[2] || widget //没有定义,取组件名
3747
3945
  var constructor = avalon.ui[widget]
3748
3946
  if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname"
3749
3947
  vmodels = elem.vmodels || vmodels
3750
- for (var i = 0, v; v = vmodels[i++]; ) {
3948
+ for (var i = 0, v; v = vmodels[i++];) {
3751
3949
  if (v.hasOwnProperty(optName) && typeof v[optName] === "object") {
3752
3950
  var vmOptions = v[optName]
3753
3951
  vmOptions = vmOptions.$model || vmOptions
@@ -3757,6 +3955,7 @@ bindingHandlers.widget = function(data, vmodels) {
3757
3955
  if (vmOptions) {
3758
3956
  var wid = vmOptions[widget + "Id"]
3759
3957
  if (typeof wid === "string") {
3958
+ log("warning!不再支持" + widget + "Id")
3760
3959
  id = wid
3761
3960
  }
3762
3961
  }
@@ -3772,27 +3971,26 @@ bindingHandlers.widget = function(data, vmodels) {
3772
3971
  if (vmodel.$id) {
3773
3972
  avalon.vmodels[id] = vmodel
3774
3973
  createSignalTower(elem, vmodel)
3775
- if (vmodel.hasOwnProperty("$init")) {
3974
+ try {
3776
3975
  vmodel.$init(function() {
3777
3976
  avalon.scan(elem, [vmodel].concat(vmodels))
3778
3977
  if (typeof options.onInit === "function") {
3779
3978
  options.onInit.call(elem, vmodel, options, vmodels)
3780
3979
  }
3781
3980
  })
3782
- }
3981
+ } catch (e) {}
3783
3982
  data.rollback = function() {
3784
3983
  try {
3785
- vmodel.widgetElement = null
3786
3984
  vmodel.$remove()
3787
- } catch (e) {
3788
- }
3985
+ vmodel.widgetElement = null // 放到$remove后边
3986
+ } catch (e) {}
3789
3987
  elem.msData = {}
3790
3988
  delete avalon.vmodels[vmodel.$id]
3791
3989
  }
3792
- addSubscribers(data, widgetList)
3990
+ injectDisposeQueue(data, widgetList)
3793
3991
  if (window.chrome) {
3794
3992
  elem.addEventListener("DOMNodeRemovedFromDocument", function() {
3795
- setTimeout(removeSubscribers)
3993
+ setTimeout(rejectDisposeQueue)
3796
3994
  })
3797
3995
  }
3798
3996
  } else {
@@ -3862,7 +4060,7 @@ var filters = avalon.filters = {
3862
4060
  truncate: function(str, length, truncation) {
3863
4061
  //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串
3864
4062
  length = length || 30
3865
- truncation = truncation === void(0) ? "..." : truncation
4063
+ truncation = typeof truncation === "string" ? truncation : "..."
3866
4064
  return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str)
3867
4065
  },
3868
4066
  $filter: function(val) {
@@ -4155,71 +4353,56 @@ new function() {// jshint ignore:line
4155
4353
  /*********************************************************************
4156
4354
  * END *
4157
4355
  **********************************************************************/
4158
- new function() {
4356
+ new function () {
4159
4357
  avalon.config({
4160
4358
  loader: false
4161
4359
  })
4162
- var fns = [], fn, loaded
4360
+ var fns = [], loaded = DOC.readyState === "complete", fn
4163
4361
  function flush(f) {
4164
4362
  loaded = 1
4165
4363
  while (f = fns.shift())
4166
4364
  f()
4167
4365
  }
4168
- if (W3C) {
4169
- avalon.bind(DOC, "DOMContentLoaded", fn = function() {
4170
- avalon.unbind(DOC, "DOMContentLoaded", fn)
4366
+
4367
+ avalon.bind(DOC, "DOMContentLoaded", fn = function () {
4368
+ avalon.unbind(DOC, "DOMContentLoaded", fn)
4369
+ flush()
4370
+ })
4371
+
4372
+ var id = setInterval(function () {
4373
+ if (document.readyState === "complete" && document.body) {
4374
+ clearInterval(id)
4171
4375
  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) {
4376
+ }
4377
+ }, 50)
4378
+
4379
+ avalon.ready = function (fn) {
4182
4380
  loaded ? fn(avalon) : fns.push(fn)
4183
4381
  }
4184
- avalon.ready(function() {
4382
+ avalon.ready(function () {
4185
4383
  avalon.scan(DOC.body)
4186
4384
  })
4187
4385
  }
4188
4386
 
4189
4387
  new function() {// jshint ignore:line
4190
- // http://www.cnblogs.com/yexiaochai/p/3462657.html
4388
+ var touchProxy = {}
4389
+ var IEtouch = navigator.pointerEnabled
4390
+ var IEMStouch = navigator.msPointerEnabled
4191
4391
  var ua = navigator.userAgent
4192
4392
  var isAndroid = ua.indexOf("Android") > 0
4193
- var isIOS = /iP(ad|hone|od)/.test(ua)
4393
+ var isGoingtoFixTouchEndEvent = isAndroid && ua.match(/Firefox|Opera/gi)
4394
+ //合成做成触屏事件所需要的各种原生事件
4395
+ var touchNames = ["touchstart", "touchmove", "touchend", "touchcancel"]
4396
+ var touchTimeout = null
4397
+ var longTapTimeout = null
4398
+ var dragDistance = 30
4399
+ var clickDuration = 750 //小于750ms是点击,长于它是长按或拖动
4194
4400
  var me = bindingHandlers.on
4195
- var touchProxy = {}
4196
4401
 
4197
- var IE11touch = navigator.pointerEnabled
4198
- var IE9_10touch = navigator.msPointerEnabled
4199
- var w3ctouch = (function() {
4200
- var supported = isIOS || false
4201
- //http://stackoverflow.com/questions/5713393/creating-and-firing-touch-events-on-a-touch-enabled-browser
4202
- try {
4203
- var div = document.createElement("div")
4204
- div.ontouchstart = function() {
4205
- supported = true
4206
- }
4207
- var e = document.createEvent("TouchEvent")
4208
- e.initUIEvent("touchstart", true, true)
4209
- div.dispatchEvent(e)
4210
- } catch (err) {
4211
- }
4212
- div = div.ontouchstart = null
4213
- return supported
4214
- })()
4215
- var touchSupported = !!(w3ctouch || IE11touch || IE9_10touch)
4216
- //合成做成触屏事件所需要的各种原生事件
4217
- var touchNames = ["mousedown", "mousemove", "mouseup", ""]
4218
- if (w3ctouch) {
4219
- touchNames = ["touchstart", "touchmove", "touchend", "touchcancel"]
4220
- } else if (IE11touch) {
4402
+ if (IEtouch) {
4221
4403
  touchNames = ["pointerdown", "pointermove", "pointerup", "pointercancel"]
4222
- } else if (IE9_10touch) {
4404
+ }
4405
+ if (IEMStouch) {
4223
4406
  touchNames = ["MSPointerDown", "MSPointerMove", "MSPointerUp", "MSPointerCancel"]
4224
4407
  }
4225
4408
  function isPrimaryTouch(event){
@@ -4230,140 +4413,132 @@ new function() {// jshint ignore:line
4230
4413
  return (e.type === 'pointer'+type || e.type.toLowerCase() === 'mspointer'+type)
4231
4414
  }
4232
4415
 
4233
- var touchTimeout, longTapTimeout
4234
4416
  //判定滑动方向
4235
4417
  function swipeDirection(x1, x2, y1, y2) {
4236
4418
  return Math.abs(x1 - x2) >=
4237
4419
  Math.abs(y1 - y2) ? (x1 - x2 > 0 ? "left" : "right") : (y1 - y2 > 0 ? "up" : "down")
4238
4420
  }
4239
- function getCoordinates(event) {
4240
- var touches = event.touches && event.touches.length ? event.touches : [event];
4241
- var e = event.changedTouches ? event.changedTouches[0] : touches[0]
4242
- return {
4243
- x: e.clientX,
4244
- y: e.clientY
4245
- }
4246
- }
4421
+
4247
4422
  function fireEvent(el, name, detail) {
4248
4423
  var event = document.createEvent("Events")
4249
4424
  event.initEvent(name, true, true)
4250
- event.fireByAvalon = true//签名,标记事件是由avalon触发
4251
- //event.isTrusted = false 设置这个opera会报错
4252
- if (detail)
4425
+ if (detail) {
4253
4426
  event.detail = detail
4427
+ }
4254
4428
  el.dispatchEvent(event)
4255
4429
  }
4430
+
4256
4431
  function onMouse(event) {
4257
- if (event.fireByAvalon) {
4258
- return true
4259
- }
4260
- if (event.stopImmediatePropagation) {
4261
- event.stopImmediatePropagation()
4262
- } else {
4263
- event.propagationStopped = true
4432
+ var target = event.target,
4433
+ element = touchProxy.element
4434
+
4435
+ if (element && element !== target) {
4436
+ var type = target.type || '',
4437
+ targetTag = target.tagName.toLowerCase(),
4438
+ elementTag = element.tagName.toLowerCase()
4439
+ // 通过手机的“前往”提交表单时不可禁止默认行为;通过label focus input时也不可以阻止默认行为
4440
+ if ((targetTag === 'input' && elementTag === "label") || type === 'submit') {
4441
+ return false
4442
+ }
4443
+ if (event.stopImmediatePropagation) {
4444
+ event.stopImmediatePropagation()
4445
+ } else {
4446
+ event.propagationStopped = true
4447
+ }
4448
+ event.stopPropagation()
4449
+ event.preventDefault()
4264
4450
  }
4265
- event.stopPropagation()
4266
- event.preventDefault()
4267
4451
  }
4268
4452
  function cancelLongTap() {
4269
4453
  if (longTapTimeout) clearTimeout(longTapTimeout)
4270
4454
  longTapTimeout = null
4271
4455
  }
4272
4456
  function touchstart(event) {
4273
- var _isPointerType = isPointerEventType(event, 'down'),
4457
+ var _isPointerType = isPointerEventType(event, "down"),
4274
4458
  firstTouch = _isPointerType ? event : event.touches[0],
4275
- element = 'tagName' in firstTouch.target ? firstTouch.target: firstTouch.target.parentNode,
4459
+ element = "tagName" in firstTouch.target ? firstTouch.target: firstTouch.target.parentNode,
4276
4460
  now = Date.now(),
4277
4461
  delta = now - (touchProxy.last || now)
4278
4462
 
4279
4463
  if (_isPointerType && !isPrimaryTouch(event)) return
4280
-
4281
- avalon.mix(touchProxy, getCoordinates(event))
4282
- touchProxy.mx = 0
4283
- touchProxy.my = 0
4464
+ if (touchProxy.x1 || touchProxy.y1) {
4465
+ touchProxy.x1 = undefined
4466
+ touchProxy.y1 = undefined
4467
+ }
4284
4468
  if (delta > 0 && delta <= 250) {
4285
4469
  touchProxy.isDoubleTap = true
4286
4470
  }
4471
+ touchProxy.x = firstTouch.pageX
4472
+ touchProxy.y = firstTouch.pageY
4473
+ touchProxy.mx = 0
4474
+ touchProxy.my = 0
4287
4475
  touchProxy.last = now
4288
4476
  touchProxy.element = element
4289
- /*
4290
- 当触发hold和longtap事件时会触发touchcancel事件,从而阻止touchend事件的触发,继而保证在同时绑定tap和hold(longtap)事件时只触发其中一个事件
4291
- */
4292
- avalon(element).addClass(fastclick.activeClass)
4477
+
4293
4478
  longTapTimeout = setTimeout(function() {
4294
4479
  longTapTimeout = null
4295
4480
  fireEvent(element, "hold")
4296
4481
  fireEvent(element, "longtap")
4297
4482
  touchProxy = {}
4298
- avalon(element).removeClass(fastclick.activeClass)
4299
- }, fastclick.clickDuration)
4483
+ }, clickDuration)
4300
4484
  return true
4301
4485
  }
4302
4486
  function touchmove(event) {
4487
+
4303
4488
  var _isPointerType = isPointerEventType(event, 'down'),
4304
- e = getCoordinates(event)
4489
+ firstTouch = _isPointerType ? event : event.touches[0],
4490
+ x = firstTouch.pageX,
4491
+ y = firstTouch.pageY
4305
4492
  if (_isPointerType && !isPrimaryTouch(event)) return
4306
-
4493
+ /*
4494
+ android下某些浏览器触发了touchmove事件的话touchend事件不触发,禁用touchmove可以解决此bug
4495
+ http://stackoverflow.com/questions/14486804/understanding-touch-events
4496
+ */
4497
+ if (isGoingtoFixTouchEndEvent && Math.abs(touchProxy.x - x) > 10) {
4498
+ event.preventDefault()
4499
+ }
4307
4500
  cancelLongTap()
4308
- touchProxy.mx += Math.abs(touchProxy.x - e.x)
4309
- touchProxy.my += Math.abs(touchProxy.y - e.y)
4501
+
4502
+ touchProxy.x1 = x // touchend事件没有pageX、pageY始终为0,且没有clientX和clientY事件
4503
+ touchProxy.y1 = y
4504
+ touchProxy.mx += Math.abs(touchProxy.x - x)
4505
+ touchProxy.my += Math.abs(touchProxy.y - y)
4310
4506
  }
4311
4507
  function touchend(event) {
4312
- var _isPointerType = isPointerEventType(event, 'down')
4508
+ var _isPointerType = isPointerEventType(event, 'down'),
4313
4509
  element = touchProxy.element
4314
4510
 
4315
4511
  if (_isPointerType && !isPrimaryTouch(event)) return
4512
+ if (!element) return // longtap|hold触发后touchProxy为{}
4316
4513
 
4317
- if (!element) {
4318
- return
4319
- }
4320
4514
  cancelLongTap()
4321
- var e = getCoordinates(event)
4322
- var totalX = Math.abs(touchProxy.x - e.x)
4323
- var totalY = Math.abs(touchProxy.y - e.y)
4324
- if (totalX > 30 || totalY > 30) {
4515
+ if ((touchProxy.x1 && Math.abs(touchProxy.x1 - touchProxy.x) > dragDistance) || (touchProxy.y1 && Math.abs(touchProxy.y1 - touchProxy.y) > dragDistance)) {
4325
4516
  //如果用户滑动的距离有点大,就认为是swipe事件
4326
- var direction = swipeDirection(touchProxy.x, e.x, touchProxy.y, e.y)
4517
+ var direction = swipeDirection(touchProxy.x, touchProxy.x1, touchProxy.y, touchProxy.y1)
4327
4518
  var details = {
4328
4519
  direction: direction
4329
4520
  }
4330
4521
  fireEvent(element, "swipe", details)
4331
4522
  fireEvent(element, "swipe" + direction, details)
4332
- avalon(element).removeClass(fastclick.activeClass)
4333
4523
  touchProxy = {}
4334
4524
  } else {
4335
- if (fastclick.canClick(element) && touchProxy.mx < fastclick.dragDistance && touchProxy.my < fastclick.dragDistance) {
4336
- // 失去焦点的处理
4337
- if (document.activeElement && document.activeElement !== element) {
4338
- document.activeElement.blur()
4339
- }
4340
- //如果此元素不为表单元素,或者它没有disabled
4341
- var forElement
4342
- if (element.tagName.toLowerCase() === "label") {
4343
- forElement = element.htmlFor ? document.getElementById(element.htmlFor) : null
4344
- }
4345
- if (forElement) {
4346
- fastclick.focus(forElement)
4347
- } else {
4348
- fastclick.focus(element)
4349
- }
4350
- event.preventDefault()
4525
+ if (touchProxy.mx < dragDistance && touchProxy.my < dragDistance) {
4351
4526
  fireEvent(element, 'tap')
4352
- avalon.fastclick.fireEvent(element, "click", event)
4353
- avalon(element).removeClass(fastclick.activeClass)
4354
4527
  if (touchProxy.isDoubleTap) {
4355
4528
  fireEvent(element, "doubletap")
4356
- avalon.fastclick.fireEvent(element, "dblclick", event)
4357
4529
  touchProxy = {}
4358
- avalon(element).removeClass(fastclick.activeClass)
4530
+ touchProxy.element = element
4359
4531
  } else {
4360
4532
  touchTimeout = setTimeout(function() {
4361
4533
  clearTimeout(touchTimeout)
4362
4534
  touchTimeout = null
4363
- touchProxy = {}
4364
- avalon(element).removeClass(fastclick.activeClass)
4535
+ if (touchProxy.element) fireEvent(touchProxy.element, "singletap")
4536
+ touchProxy = {};
4537
+ touchProxy.element = element
4365
4538
  }, 250)
4366
4539
  }
4540
+ } else {
4541
+ touchProxy = {}
4367
4542
  }
4368
4543
  }
4369
4544
  }
@@ -4380,71 +4555,7 @@ new function() {// jshint ignore:line
4380
4555
  touchProxy = {}
4381
4556
  })
4382
4557
  }
4383
- //fastclick只要是处理移动端点击存在300ms延迟的问题
4384
- //这是苹果乱搞异致的,他们想在小屏幕设备上通过快速点击两次,将放大了的网页缩放至原始比例。
4385
- var fastclick = avalon.fastclick = {
4386
- activeClass: "ms-click-active",
4387
- clickDuration: 750, //小于750ms是点击,长于它是长按或拖动
4388
- dragDistance: 30, //最大移动的距离
4389
- fireEvent: function(element, type, event) {
4390
- var clickEvent = document.createEvent("MouseEvents")
4391
- clickEvent.initMouseEvent(type, true, true, window, 1, event.screenX, event.screenY,
4392
- event.clientX, event.clientY, false, false, false, false, 0, null)
4393
- Object.defineProperty(clickEvent, "fireByAvalon", {
4394
- value: true
4395
- })
4396
- element.dispatchEvent(clickEvent)
4397
- },
4398
- focus: function(target) {
4399
- if (this.canFocus(target)) {
4400
- //https://github.com/RubyLouvre/avalon/issues/254
4401
- var value = target.value
4402
- target.value = value
4403
- if (isIOS && target.setSelectionRange && target.type.indexOf("date") !== 0 && target.type !== 'time') {
4404
- // iOS 7, date datetime等控件直接对selectionStart,selectionEnd赋值会抛错
4405
- var n = value.length
4406
- target.setSelectionRange(n, n)
4407
- } else {
4408
- target.focus()
4409
- }
4410
- }
4411
- },
4412
- canClick: function(target) {
4413
- switch (target.nodeName.toLowerCase()) {
4414
- case "textarea":
4415
- case "select":
4416
- case "input":
4417
- return !target.disabled
4418
- default:
4419
- return true
4420
- }
4421
- },
4422
- canFocus: function(target) {
4423
- switch (target.nodeName.toLowerCase()) {
4424
- case "textarea":
4425
- return true;
4426
- case "select":
4427
- return !isAndroid
4428
- case "input":
4429
- switch (target.type) {
4430
- case "button":
4431
- case "checkbox":
4432
- case "file":
4433
- case "image":
4434
- case "radio":
4435
- case "submit":
4436
- return false
4437
- }
4438
- // No point in attempting to focus disabled inputs
4439
- return !target.disabled && !target.readOnly
4440
- default:
4441
- return false
4442
- }
4443
- }
4444
- };
4445
-
4446
-
4447
- ["swipe", "swipeleft", "swiperight", "swipeup", "swipedown", "doubletap", "tap", "dblclick", "longtap", "hold"].forEach(function(method) {
4558
+ ["swipe", "swipeleft", "swiperight", "swipeup", "swipedown", "doubletap", "tap", "singletap", "dblclick", "longtap", "hold"].forEach(function(method) {
4448
4559
  me[method + "Hook"] = me["clickHook"]
4449
4560
  })
4450
4561
 
@@ -4481,6 +4592,7 @@ new function() {// jshint ignore:line
4481
4592
  if (noGlobal === void 0) {
4482
4593
  window.avalon = avalon
4483
4594
  }
4595
+
4484
4596
  return avalon
4485
4597
 
4486
4598
  }));