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.shim.js(无加载器版本) 1.41 built in 2015.4.4
8
+ avalon.shim.js(无加载器版本) 1.46 built in 2015.9.11
9
9
  support IE6+ and other browsers
10
10
  ==================================================*/
11
11
  (function(global, factory) {
@@ -67,10 +67,10 @@ var aslice = ap.slice
67
67
  var Registry = {} //将函数曝光到此对象上,方便访问器收集依赖
68
68
  var W3C = window.dispatchEvent
69
69
  var root = DOC.documentElement
70
- var hyperspace = DOC.createDocumentFragment()
70
+ var avalonFragment = DOC.createDocumentFragment()
71
71
  var cinerator = DOC.createElement("div")
72
72
  var class2type = {}
73
- "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function(name) {
73
+ "Boolean Number String Function Array Date RegExp Object Error".replace(rword, function (name) {
74
74
  class2type["[object " + name + "]"] = name.toLowerCase()
75
75
  })
76
76
 
@@ -92,30 +92,35 @@ function oneObject(array, val) {
92
92
  }
93
93
 
94
94
  //生成UUID http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
95
- var generateID = function(prefix) {
95
+ var generateID = function (prefix) {
96
96
  prefix = prefix || "avalon"
97
- return (prefix + Math.random() + Math.random()).replace(/0\./g, "")
97
+ return String(Math.random() + Math.random()).replace(/\d\.\d{4}/, prefix)
98
98
  }
99
99
  function IE() {
100
100
  if (window.VBArray) {
101
101
  var mode = document.documentMode
102
102
  return mode ? mode : window.XMLHttpRequest ? 7 : 6
103
103
  } else {
104
- return 0
104
+ return NaN
105
105
  }
106
106
  }
107
107
  var IEVersion = IE()
108
108
 
109
- avalon = function(el) { //创建jQuery式的无new 实例化结构
109
+ avalon = function (el) { //创建jQuery式的无new 实例化结构
110
110
  return new avalon.init(el)
111
111
  }
112
112
 
113
+ avalon.profile = function () {
114
+ if (window.console && avalon.config.profile) {
115
+ Function.apply.call(console.log, console, arguments)
116
+ }
117
+ }
118
+
113
119
  /*视浏览器情况采用最快的异步回调*/
114
- avalon.nextTick = new function() {// jshint ignore:line
120
+ avalon.nextTick = new function () {// jshint ignore:line
115
121
  var tickImmediate = window.setImmediate
116
122
  var tickObserver = window.MutationObserver
117
- var tickPost = W3C && window.postMessage
118
- if (tickImmediate) {
123
+ if (tickImmediate) {//IE10 \11 edage
119
124
  return tickImmediate.bind(window)
120
125
  }
121
126
 
@@ -128,43 +133,43 @@ avalon.nextTick = new function() {// jshint ignore:line
128
133
  queue = queue.slice(n)
129
134
  }
130
135
 
131
- if (tickObserver) {
136
+ if (tickObserver) {// 支持MutationObserver
132
137
  var node = document.createTextNode("avalon")
133
138
  new tickObserver(callback).observe(node, {characterData: true})// jshint ignore:line
134
- return function(fn) {
139
+ return function (fn) {
135
140
  queue.push(fn)
136
141
  node.data = Math.random()
137
142
  }
138
143
  }
139
144
 
140
- if (tickPost) {
141
- window.addEventListener("message", function(e) {
142
- var source = e.source
143
- if ((source === window || source === null) && e.data === "process-tick") {
144
- e.stopPropagation()
145
- callback()
146
- }
147
- })
148
-
149
- return function(fn) {
145
+ if (window.VBArray) {
146
+ return function (fn) {
150
147
  queue.push(fn)
151
- window.postMessage('process-tick', '*')
148
+ var node = DOC.createElement("script")
149
+ node.onreadystatechange = function () {
150
+ callback() //在interactive阶段就触发
151
+ node.onreadystatechange = null
152
+ head.removeChild(node)
153
+ node = null
154
+ }
155
+ head.appendChild(node)
152
156
  }
153
157
  }
154
158
 
155
- return function(fn) {
156
- setTimeout(fn, 0)
159
+
160
+ return function (fn) {
161
+ setTimeout(fn, 4)
157
162
  }
158
163
  }// jshint ignore:line
159
164
  /*********************************************************************
160
165
  * avalon的静态方法定义区 *
161
166
  **********************************************************************/
162
- avalon.init = function(el) {
167
+ avalon.init = function (el) {
163
168
  this[0] = this.element = el
164
169
  }
165
170
  avalon.fn = avalon.prototype = avalon.init.prototype
166
171
 
167
- avalon.type = function(obj) { //取得目标的类型
172
+ avalon.type = function (obj) { //取得目标的类型
168
173
  if (obj == null) {
169
174
  return String(obj)
170
175
  }
@@ -174,18 +179,18 @@ avalon.type = function(obj) { //取得目标的类型
174
179
  typeof obj
175
180
  }
176
181
 
177
- var isFunction = typeof alert === "object" ? function(fn) {
182
+ var isFunction = typeof alert === "object" ? function (fn) {
178
183
  try {
179
184
  return /^\s*\bfunction\b/.test(fn + "")
180
185
  } catch (e) {
181
186
  return false
182
187
  }
183
- } : function(fn) {
188
+ } : function (fn) {
184
189
  return serialize.call(fn) === "[object Function]"
185
190
  }
186
191
  avalon.isFunction = isFunction
187
192
 
188
- avalon.isWindow = function(obj) {
193
+ avalon.isWindow = function (obj) {
189
194
  if (!obj)
190
195
  return false
191
196
  // 利用IE678 window == document为true,document == window竟然为false的神奇特性
@@ -205,7 +210,7 @@ for (enu in avalon({})) {
205
210
  }
206
211
  var enumerateBUG = enu !== "0" //IE6下为true, 其他为false
207
212
  /*判定是否是一个朴素的javascript对象(Object),不是DOM对象,不是BOM对象,不是自定义类的实例*/
208
- avalon.isPlainObject = function(obj, key) {
213
+ avalon.isPlainObject = function (obj, key) {
209
214
  if (!obj || avalon.type(obj) !== "object" || obj.nodeType || avalon.isWindow(obj)) {
210
215
  return false;
211
216
  }
@@ -226,13 +231,13 @@ avalon.isPlainObject = function(obj, key) {
226
231
  return key === void 0 || ohasOwn.call(obj, key)
227
232
  }
228
233
  if (rnative.test(Object.getPrototypeOf)) {
229
- avalon.isPlainObject = function(obj) {
234
+ avalon.isPlainObject = function (obj) {
230
235
  // 简单的 typeof obj === "object"检测,会致使用isPlainObject(window)在opera下通不过
231
236
  return serialize.call(obj) === "[object Object]" && Object.getPrototypeOf(obj) === oproto
232
237
  }
233
238
  }
234
239
  //与jQuery.extend方法,可用于浅拷贝,深拷贝
235
- avalon.mix = avalon.fn.mix = function() {
240
+ avalon.mix = avalon.fn.mix = function () {
236
241
  var options, name, src, copy, copyIsArray, clone,
237
242
  target = arguments[0] || {},
238
243
  i = 1,
@@ -299,12 +304,12 @@ function _number(a, len) { //用于模拟slice, splice的效果
299
304
  avalon.mix({
300
305
  rword: rword,
301
306
  subscribers: subscribers,
302
- version: 1.41,
307
+ version: 1.46,
303
308
  ui: {},
304
309
  log: log,
305
- slice: W3C ? function(nodes, start, end) {
310
+ slice: W3C ? function (nodes, start, end) {
306
311
  return aslice.call(nodes, start, end)
307
- } : function(nodes, start, end) {
312
+ } : function (nodes, start, end) {
308
313
  var ret = []
309
314
  var len = nodes.length
310
315
  if (end === void 0)
@@ -320,7 +325,7 @@ avalon.mix({
320
325
  },
321
326
  noop: noop,
322
327
  /*如果不用Error对象封装一下,str在控制台下可能会乱码*/
323
- error: function(str, e) {
328
+ error: function (str, e) {
324
329
  throw (e || Error)(str)
325
330
  },
326
331
  /*将一个以空格或逗号隔开的字符串或数组,转换成一个键值都为1的对象*/
@@ -335,7 +340,7 @@ avalon.mix({
335
340
  => [0, -1, -2, -3, -4, -5, -6, -7, -8, -9]
336
341
  avalon.range(0)
337
342
  => []*/
338
- range: function(start, end, step) { // 用于生成整数数组
343
+ range: function (start, end, step) { // 用于生成整数数组
339
344
  step || (step = 1)
340
345
  if (end == null) {
341
346
  end = start || 0
@@ -350,7 +355,7 @@ avalon.mix({
350
355
  }
351
356
  return result
352
357
  },
353
- eventHooks: {},
358
+ eventHooks: [],
354
359
  /*绑定事件*/
355
360
  bind: function(el, type, fn, phase) {
356
361
  var hooks = avalon.eventHooks
@@ -389,7 +394,7 @@ avalon.mix({
389
394
  }
390
395
  },
391
396
  /*读写删除元素节点的样式*/
392
- css: function(node, name, value) {
397
+ css: function (node, name, value) {
393
398
  if (node instanceof avalon) {
394
399
  node = node[0]
395
400
  }
@@ -416,7 +421,7 @@ avalon.mix({
416
421
  }
417
422
  },
418
423
  /*遍历数组与对象,回调的第一个参数为索引或键名,第二个或元素或键值*/
419
- each: function(obj, fn) {
424
+ each: function (obj, fn) {
420
425
  if (obj) { //排除null, undefined
421
426
  var i = 0
422
427
  if (isArrayLike(obj)) {
@@ -434,12 +439,12 @@ avalon.mix({
434
439
  }
435
440
  },
436
441
  //收集元素的data-{{prefix}}-*属性,并转换为对象
437
- getWidgetData: function(elem, prefix) {
442
+ getWidgetData: function (elem, prefix) {
438
443
  var raw = avalon(elem).data()
439
444
  var result = {}
440
445
  for (var i in raw) {
441
446
  if (i.indexOf(prefix) === 0) {
442
- result[i.replace(prefix, "").replace(/\w/, function(a) {
447
+ result[i.replace(prefix, "").replace(/\w/, function (a) {
443
448
  return a.toLowerCase()
444
449
  })] = raw[i]
445
450
  }
@@ -448,17 +453,17 @@ avalon.mix({
448
453
  },
449
454
  Array: {
450
455
  /*只有当前数组不存在此元素时只添加它*/
451
- ensure: function(target, item) {
456
+ ensure: function (target, item) {
452
457
  if (target.indexOf(item) === -1) {
453
458
  return target.push(item)
454
459
  }
455
460
  },
456
461
  /*移除数组中指定位置的元素,返回布尔表示成功与否*/
457
- removeAt: function(target, index) {
462
+ removeAt: function (target, index) {
458
463
  return !!target.splice(index, 1).length
459
464
  },
460
465
  /*移除数组中第一个匹配传参的那个元素,返回布尔表示成功与否*/
461
- remove: function(target, item) {
466
+ remove: function (target, item) {
462
467
  var index = target.indexOf(item)
463
468
  if (~index)
464
469
  return avalon.Array.removeAt(target, index)
@@ -534,6 +539,7 @@ var Cache = new function() {// jshint ignore:line
534
539
  entry.newer =
535
540
  entry.older =
536
541
  this._keymap[entry.key] = void 0
542
+ delete this._keymap[entry.key] //#1029
537
543
  }
538
544
  }
539
545
  p.get = function(key) {
@@ -593,7 +599,7 @@ var hasDontEnumBug = !({
593
599
  dontEnumsLength = dontEnums.length;
594
600
  if (!Object.keys) {
595
601
  Object.keys = function (object) { //ecma262v5 15.2.3.14
596
- var theKeys = [];
602
+ var theKeys = []
597
603
  var skipProto = hasProtoEnumBug && typeof object === "function"
598
604
  if (typeof object === "string" || (object && object.callee)) {
599
605
  for (var i = 0; i < object.length; ++i) {
@@ -609,7 +615,7 @@ if (!Object.keys) {
609
615
 
610
616
  if (hasDontEnumBug) {
611
617
  var ctor = object.constructor,
612
- skipConstructor = ctor && ctor.prototype === object;
618
+ skipConstructor = ctor && ctor.prototype === object
613
619
  for (var j = 0; j < dontEnumsLength; j++) {
614
620
  var dontEnum = dontEnums[j]
615
621
  if (!(skipConstructor && dontEnum === "constructor") && ohasOwn.call(object, dontEnum)) {
@@ -785,6 +791,7 @@ if (!root.outerHTML && window.HTMLElement) { //firefox 到11时才有outerHTML
785
791
  HTMLElement.prototype.__defineGetter__("outerHTML", outerHTML);
786
792
  }
787
793
 
794
+
788
795
  //============================= event binding =======================
789
796
  var rmouseEvent = /^(?:mouse|contextmenu|drag)|click/
790
797
  function fixEvent(event) {
@@ -887,6 +894,7 @@ if (DOC.onmousewheel === void 0) {
887
894
  }
888
895
 
889
896
 
897
+
890
898
  /*********************************************************************
891
899
  * 配置系统 *
892
900
  **********************************************************************/
@@ -924,15 +932,15 @@ var plugins = {
924
932
  openTag = array[0]
925
933
  closeTag = array[1]
926
934
  if (openTag === closeTag) {
927
- throw new SyntaxError("openTag!==closeTag")
928
- } else if (array + "" === "<!--,-->") {
929
- kernel.commentInterpolate = true
935
+ throw new SyntaxError("openTag===closeTag")
930
936
  } else {
931
937
  var test = openTag + "test" + closeTag
932
938
  cinerator.innerHTML = test
933
939
  if (cinerator.innerHTML !== test && cinerator.innerHTML.indexOf("&lt;") > -1) {
934
940
  throw new SyntaxError("此定界符不合法")
935
941
  }
942
+ kernel.openTag = openTag
943
+ kernel.closeTag = closeTag
936
944
  cinerator.innerHTML = ""
937
945
  }
938
946
  var o = escapeRegExp(openTag),
@@ -1031,8 +1039,8 @@ var EventBus = {
1031
1039
  }
1032
1040
  //循环两个vmodel中的节点,查找匹配(向上匹配或者向下匹配)的节点并设置标识
1033
1041
  /* jshint ignore:start */
1034
- Array.prototype.forEach.call(eventNodes, function (node) {
1035
- Array.prototype.forEach.call(elements, function (element) {
1042
+ ap.forEach.call(eventNodes, function (node) {
1043
+ ap.forEach.call(elements, function (element) {
1036
1044
  var ok = special === "down" ? element.contains(node) : //向下捕获
1037
1045
  node.contains(element) //向上冒泡
1038
1046
  if (ok) {
@@ -1046,7 +1054,7 @@ var EventBus = {
1046
1054
  }
1047
1055
  var nodes = DOC.getElementsByTagName("*") //实现节点排序
1048
1056
  var alls = []
1049
- Array.prototype.forEach.call(nodes, function (el) {
1057
+ ap.forEach.call(nodes, function (el) {
1050
1058
  if (el._avalon) {
1051
1059
  alls.push(el._avalon)
1052
1060
  el._avalon = ""
@@ -1075,7 +1083,6 @@ var EventBus = {
1075
1083
  }
1076
1084
  }
1077
1085
  }
1078
-
1079
1086
  /*********************************************************************
1080
1087
  * modelFactory *
1081
1088
  **********************************************************************/
@@ -1096,6 +1103,7 @@ avalon.define = function (id, factory) {
1096
1103
  $watch: noop
1097
1104
  }
1098
1105
  factory(scope) //得到所有定义
1106
+
1099
1107
  model = modelFactory(scope) //偷天换日,将scope换为model
1100
1108
  stopRepeatAssign = true
1101
1109
  factory(model)
@@ -1106,52 +1114,7 @@ avalon.define = function (id, factory) {
1106
1114
  }
1107
1115
 
1108
1116
  //一些不需要被监听的属性
1109
- var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray").match(rword)
1110
-
1111
- function isObservable(name, value, $skipArray) {
1112
- if (isFunction(value) || value && value.nodeType) {
1113
- return false
1114
- }
1115
- if ($skipArray.indexOf(name) !== -1) {
1116
- return false
1117
- }
1118
- if ($$skipArray.indexOf(name) !== -1) {
1119
- return false
1120
- }
1121
- var $special = $skipArray.$special
1122
- if (name && name.charAt(0) === "$" && !$special[name]) {
1123
- return false
1124
- }
1125
- return true
1126
- }
1127
- //ms-with,ms-each, ms-repeat绑定生成的代理对象储存池
1128
- var midway = {}
1129
- function getNewValue(accessor, name, value, $vmodel) {
1130
- switch (accessor.type) {
1131
- case 0://计算属性
1132
- var getter = accessor.get
1133
- var setter = accessor.set
1134
- if (isFunction(setter)) {
1135
- var $events = $vmodel.$events
1136
- var lock = $events[name]
1137
- $events[name] = [] //清空回调,防止内部冒泡而触发多次$fire
1138
- setter.call($vmodel, value)
1139
- $events[name] = lock
1140
- }
1141
- return getter.call($vmodel) //同步$model
1142
- case 1://监控属性
1143
- return value
1144
- case 2://对象属性(包括数组与哈希)
1145
- if (value !== $vmodel.$model[name]) {
1146
- var svmodel = accessor.svmodel = objectFactory($vmodel, name, value, accessor.valueType)
1147
- value = svmodel.$model //同步$model
1148
- var fn = midway[svmodel.$id]
1149
- fn && fn() //同步视图
1150
- }
1151
- return value
1152
- }
1153
- }
1154
-
1117
+ var $$skipArray = String("$id,$watch,$unwatch,$fire,$events,$model,$skipArray,$reinitialize").match(rword)
1155
1118
  var defineProperty = Object.defineProperty
1156
1119
  var canHideOwn = true
1157
1120
  //如果浏览器不支持ecma262v5的Object.defineProperties或者存在BUG,比如IE8
@@ -1164,11 +1127,12 @@ try {
1164
1127
  } catch (e) {
1165
1128
  canHideOwn = false
1166
1129
  }
1130
+
1167
1131
  function modelFactory(source, $special, $model) {
1168
1132
  if (Array.isArray(source)) {
1169
1133
  var arr = source.concat()
1170
1134
  source.length = 0
1171
- var collection = Collection(source)// jshint ignore:line
1135
+ var collection = arrayFactory(source)
1172
1136
  collection.pushArray(arr)
1173
1137
  return collection
1174
1138
  }
@@ -1176,146 +1140,254 @@ function modelFactory(source, $special, $model) {
1176
1140
  if (!source || source.nodeType > 0 || (source.$id && source.$events)) {
1177
1141
  return source
1178
1142
  }
1179
- if (!Array.isArray(source.$skipArray)) {
1180
- source.$skipArray = []
1181
- }
1182
- source.$skipArray.$special = $special || {} //强制要监听的属性
1143
+ var $skipArray = Array.isArray(source.$skipArray) ? source.$skipArray : []
1144
+ $skipArray.$special = $special || {} //强制要监听的属性
1183
1145
  var $vmodel = {} //要返回的对象, 它在IE6-8下可能被偷龙转凤
1184
1146
  $model = $model || {} //vmodels.$model属性
1185
1147
  var $events = {} //vmodel.$events属性
1186
- var watchedProperties = {} //监控属性
1187
- var initCallbacks = [] //初始化才执行的函数
1188
- for (var i in source) {
1189
- (function (name, val) {
1190
- $model[name] = val
1191
- if (!isObservable(name, val, source.$skipArray)) {
1192
- return //过滤所有非监控属性
1193
- }
1148
+ var accessors = {} //监控属性
1149
+ var computed = []
1150
+ $$skipArray.forEach(function (name) {
1151
+ delete source[name]
1152
+ })
1153
+ var names = Object.keys(source)
1154
+ /* jshint ignore:start */
1155
+ names.forEach(function (name, accessor) {
1156
+ var val = source[name]
1157
+ $model[name] = val
1158
+ if (isObservable(name, val, $skipArray)) {
1194
1159
  //总共产生三种accessor
1195
1160
  $events[name] = []
1196
1161
  var valueType = avalon.type(val)
1197
- var accessor = function (newValue) {
1198
- var name = accessor._name
1199
- var $vmodel = this
1200
- var $model = $vmodel.$model
1201
- var oldValue = $model[name]
1202
- var $events = $vmodel.$events
1203
-
1204
- if (arguments.length) {
1205
- if (stopRepeatAssign) {
1206
- return
1207
- }
1208
- //计算属性与对象属性需要重新计算newValue
1209
- if (accessor.type !== 1) {
1210
- newValue = getNewValue(accessor, name, newValue, $vmodel)
1211
- if (!accessor.type)
1212
- return
1213
- }
1214
- if (!isEqual(oldValue, newValue)) {
1215
- $model[name] = newValue
1216
- notifySubscribers($events[name]) //同步视图
1217
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1218
- }
1219
- } else {
1220
- if (accessor.type === 0) { //type 0 计算属性 1 监控属性 2 对象属性
1221
- //计算属性不需要收集视图刷新函数,都是由其他监控属性代劳
1222
- newValue = accessor.get.call($vmodel)
1223
- if (oldValue !== newValue) {
1224
- $model[name] = newValue
1225
- //这里不用同步视图
1226
- safeFire($vmodel, name, newValue, oldValue) //触发$watch回调
1227
- }
1228
- return newValue
1229
- } else {
1230
- collectSubscribers($events[name]) //收集视图函数
1231
- return accessor.svmodel || oldValue
1232
- }
1233
- }
1234
- }
1235
1162
  //总共产生三种accessor
1236
1163
  if (valueType === "object" && isFunction(val.get) && Object.keys(val).length <= 2) {
1237
- //第1种为计算属性, 因变量,通过其他监控属性触发其改变
1238
- accessor.set = val.set
1239
- accessor.get = val.get
1240
- accessor.type = 0
1241
- initCallbacks.push(function () {
1242
- var data = {
1243
- evaluator: function () {
1244
- data.type = Math.random(),
1245
- data.element = null
1246
- $model[name] = accessor.get.call($vmodel)
1247
- },
1248
- element: head,
1249
- type: Math.random(),
1250
- handler: noop,
1251
- args: []
1252
- }
1253
- Registry[expose] = data
1254
- accessor.call($vmodel)
1255
- delete Registry[expose]
1256
- })
1164
+ accessor = makeComputedAccessor(name, val)
1165
+ computed.push(accessor)
1257
1166
  } else if (rcomplexType.test(valueType)) {
1258
- //第2种为对象属性,产生子VM与监控数组
1259
- accessor.type = 2
1260
- accessor.valueType = valueType
1261
- initCallbacks.push(function () {
1262
- var svmodel = modelFactory(val, 0, $model[name])
1263
- accessor.svmodel = svmodel
1264
- svmodel.$events[subscribers] = $events[name]
1265
- })
1167
+ // issue #940 解决$model层次依赖丢失 https://github.com/RubyLouvre/avalon/issues/940
1168
+ // $model[name] = {}
1169
+ accessor = makeComplexAccessor(name, val, valueType, $events[name], $model)
1266
1170
  } else {
1267
- accessor.type = 1
1268
- //第3种为监控属性,对应简单的数据类型,自变量
1171
+ accessor = makeSimpleAccessor(name, val)
1269
1172
  }
1270
- accessor._name = name
1271
- watchedProperties[name] = accessor
1272
- })(i, source[i])// jshint ignore:line
1273
- }
1274
-
1275
- $$skipArray.forEach(function (name) {
1276
- delete source[name]
1277
- delete $model[name] //这些特殊属性不应该在$model中出现
1173
+ accessors[name] = accessor
1174
+ }
1278
1175
  })
1279
-
1280
- $vmodel = defineProperties($vmodel, descriptorFactory(watchedProperties), source) //生成一个空的ViewModel
1281
- for (var name in source) {
1282
- if (!watchedProperties[name]) {
1176
+ /* jshint ignore:end */
1177
+ $vmodel = defineProperties($vmodel, descriptorFactory(accessors), source) //生成一个空的ViewModel
1178
+ for (var i = 0; i < names.length; i++) {
1179
+ var name = names[i]
1180
+ if (!accessors[name]) {
1283
1181
  $vmodel[name] = source[name]
1284
1182
  }
1285
1183
  }
1286
1184
  //添加$id, $model, $events, $watch, $unwatch, $fire
1287
- $vmodel.$id = generateID()
1288
- $vmodel.$model = $model
1289
- $vmodel.$events = $events
1290
- for (i in EventBus) {
1291
- var fn = EventBus[i]
1292
- if (!W3C) { //在IE6-8下,VB对象的方法里的this并不指向自身,需要用bind处理一下
1293
- fn = fn.bind($vmodel)
1185
+ hideProperty($vmodel, "$id", generateID())
1186
+ hideProperty($vmodel, "$model", $model)
1187
+ hideProperty($vmodel, "$events", $events)
1188
+ /* jshint ignore:start */
1189
+ if (canHideOwn) {
1190
+ hideProperty($vmodel, "hasOwnProperty", function (name) {
1191
+ return name in $vmodel.$model
1192
+ })
1193
+ } else {
1194
+ $vmodel.hasOwnProperty = function (name) {
1195
+ return (name in $vmodel.$model) && (name !== "hasOwnProperty")
1294
1196
  }
1295
- $vmodel[i] = fn
1296
1197
  }
1198
+ /* jshint ignore:end */
1199
+ for ( i in EventBus) {
1200
+ hideProperty($vmodel, i, EventBus[i].bind($vmodel))
1201
+ }
1202
+
1203
+ $vmodel.$reinitialize = function () {
1204
+ computed.forEach(function (accessor) {
1205
+ delete accessor._value
1206
+ delete accessor.oldArgs
1207
+ accessor.digest = function () {
1208
+ accessor.call($vmodel)
1209
+ }
1210
+ dependencyDetection.begin({
1211
+ callback: function (vm, dependency) {//dependency为一个accessor
1212
+ var name = dependency._name
1213
+ if (dependency !== accessor) {
1214
+ var list = vm.$events[name]
1215
+ injectDependency(list, accessor.digest)
1216
+ }
1217
+ }
1218
+ })
1219
+ try {
1220
+ accessor.get.call($vmodel)
1221
+ } finally {
1222
+ dependencyDetection.end()
1223
+ }
1224
+ })
1225
+ }
1226
+ $vmodel.$reinitialize()
1227
+ return $vmodel
1228
+ }
1297
1229
 
1230
+
1231
+ function hideProperty(host, name, value) {
1298
1232
  if (canHideOwn) {
1299
- Object.defineProperty($vmodel, "hasOwnProperty", {
1300
- value: function (name) {
1301
- return name in this.$model
1302
- },
1303
- writable: false,
1233
+ Object.defineProperty(host, name, {
1234
+ value: value,
1235
+ writable: true,
1304
1236
  enumerable: false,
1305
1237
  configurable: true
1306
1238
  })
1307
-
1308
1239
  } else {
1309
- /* jshint ignore:start */
1310
- $vmodel.hasOwnProperty = function (name) {
1311
- return name in $vmodel.$model
1240
+ host[name] = value
1241
+ }
1242
+ }
1243
+ //创建一个简单访问器
1244
+ function makeSimpleAccessor(name, value) {
1245
+ function accessor(value) {
1246
+ var oldValue = accessor._value
1247
+ if (arguments.length > 0) {
1248
+ if (!stopRepeatAssign && !isEqual(value, oldValue)) {
1249
+ accessor.updateValue(this, value)
1250
+ accessor.notify(this, value, oldValue)
1251
+ }
1252
+ return this
1253
+ } else {
1254
+ dependencyDetection.collectDependency(this, accessor)
1255
+ return oldValue
1312
1256
  }
1313
- /* jshint ignore:end */
1314
1257
  }
1315
- initCallbacks.forEach(function (cb) { //收集依赖
1316
- cb()
1317
- })
1318
- return $vmodel
1258
+ accessorFactory(accessor, name)
1259
+ accessor._value = value
1260
+ return accessor;
1261
+ }
1262
+
1263
+ //创建一个计算访问器
1264
+ function makeComputedAccessor(name, options) {
1265
+ function accessor(value) {//计算属性
1266
+ var oldValue = accessor._value
1267
+ var init = ("_value" in accessor)
1268
+ if (arguments.length > 0) {
1269
+ if (stopRepeatAssign) {
1270
+ return this
1271
+ }
1272
+ if (typeof accessor.set === "function") {
1273
+ if (accessor.oldArgs !== value) {
1274
+ accessor.oldArgs = value
1275
+ var $events = this.$events
1276
+ var lock = $events[name]
1277
+ $events[name] = [] //清空回调,防止内部冒泡而触发多次$fire
1278
+ accessor.set.call(this, value)
1279
+ $events[name] = lock
1280
+ value = accessor.get.call(this)
1281
+ if (value !== oldValue) {
1282
+ accessor.updateValue(this, value)
1283
+ accessor.notify(this, value, oldValue) //触发$watch回调
1284
+ }
1285
+ }
1286
+ }
1287
+ return this
1288
+ } else {
1289
+ //将依赖于自己的高层访问器或视图刷新函数(以绑定对象形式)放到自己的订阅数组中
1290
+ //将自己注入到低层访问器的订阅数组中
1291
+ value = accessor.get.call(this)
1292
+ accessor.updateValue(this, value)
1293
+ if (init && oldValue !== value) {
1294
+ accessor.notify(this, value, oldValue) //触发$watch回调
1295
+ }
1296
+ return value
1297
+ }
1298
+ }
1299
+ accessor.set = options.set
1300
+ accessor.get = options.get
1301
+ accessorFactory(accessor, name)
1302
+ return accessor
1303
+ }
1304
+
1305
+ //创建一个复杂访问器
1306
+ function makeComplexAccessor(name, initValue, valueType, list, parentModel) {
1307
+
1308
+ function accessor(value) {
1309
+ var oldValue = accessor._value
1310
+
1311
+ var son = accessor._vmodel
1312
+ if (arguments.length > 0) {
1313
+ if (stopRepeatAssign) {
1314
+ return this
1315
+ }
1316
+ if (valueType === "array") {
1317
+ var a = son, b = value,
1318
+ an = a.length,
1319
+ bn = b.length
1320
+ a.$lock = true
1321
+ if (an > bn) {
1322
+ a.splice(bn, an - bn)
1323
+ } else if (bn > an) {
1324
+ a.push.apply(a, b.slice(an))
1325
+ }
1326
+ var n = Math.min(an, bn)
1327
+ for (var i = 0; i < n; i++) {
1328
+ a.set(i, b[i])
1329
+ }
1330
+ delete a.$lock
1331
+ a._fire("set")
1332
+ } else if (valueType === "object") {
1333
+ var observes = this.$events[name] || []
1334
+ var newObject = avalon.mix(true, {}, value)
1335
+ for(i in son ){
1336
+ if(son.hasOwnProperty(i) && ohasOwn.call(newObject,i)){
1337
+ son[i] = newObject[i]
1338
+ }
1339
+ }
1340
+ son = accessor._vmodel = modelFactory(value)
1341
+ son.$events[subscribers] = observes
1342
+ if (observes.length) {
1343
+ observes.forEach(function (data) {
1344
+ if (!data.type) {
1345
+ return //数据未准备好时忽略更新
1346
+ }
1347
+ if (data.rollback) {
1348
+ data.rollback() //还原 ms-with ms-on
1349
+ }
1350
+ bindingHandlers[data.type](data, data.vmodels)
1351
+ })
1352
+ }
1353
+ }
1354
+ accessor.updateValue(this, son.$model)
1355
+ accessor.notify(this, this._value, oldValue)
1356
+ return this
1357
+ } else {
1358
+ dependencyDetection.collectDependency(this, accessor)
1359
+ return son
1360
+ }
1361
+ }
1362
+ accessorFactory(accessor, name)
1363
+ if (Array.isArray(initValue)) {
1364
+ parentModel[name] = initValue
1365
+ } else {
1366
+ parentModel[name] = parentModel[name] || {}
1367
+ }
1368
+ var son = accessor._vmodel = modelFactory(initValue, 0, parentModel[name])
1369
+ son.$events[subscribers] = list
1370
+ return accessor
1371
+ }
1372
+
1373
+ function globalUpdateValue(vmodel, value) {
1374
+ vmodel.$model[this._name] = this._value = value
1375
+ }
1376
+
1377
+ function globalNotify(vmodel, value, oldValue) {
1378
+ var name = this._name
1379
+ var array = vmodel.$events[name] //刷新值
1380
+ if (array) {
1381
+ fireDependencies(array) //同步视图
1382
+ EventBus.$fire.call(vmodel, name, value, oldValue) //触发$watch回调
1383
+ }
1384
+ }
1385
+
1386
+ function accessorFactory(accessor, name) {
1387
+ accessor._name = name
1388
+ //同时更新_value与model
1389
+ accessor.updateValue = globalUpdateValue
1390
+ accessor.notify = globalNotify
1319
1391
  }
1320
1392
 
1321
1393
  //比较两个值是否相等
@@ -1329,12 +1401,29 @@ var isEqual = Object.is || function (v1, v2) {
1329
1401
  }
1330
1402
  }
1331
1403
 
1332
- function safeFire(a, b, c, d) {
1333
- if (a.$events) {
1334
- EventBus.$fire.call(a, b, c, d)
1404
+ function isObservable(name, value, $skipArray) {
1405
+ if (isFunction(value) || value && value.nodeType) {
1406
+ return false
1407
+ }
1408
+ if ($skipArray.indexOf(name) !== -1) {
1409
+ return false
1410
+ }
1411
+ var $special = $skipArray.$special
1412
+ if (name && name.charAt(0) === "$" && !$special[name]) {
1413
+ return false
1335
1414
  }
1415
+ return true
1416
+ }
1417
+ function keysVM(obj) {
1418
+ var arr = Object.keys(obj.$model ? obj.$model: obj)
1419
+ for (var i = 0; i < $$skipArray.length; i++) {
1420
+ var index = arr.indexOf($$skipArray[i])
1421
+ if (index !== -1) {
1422
+ arr.splice(index, 1)
1423
+ }
1424
+ }
1425
+ return arr
1336
1426
  }
1337
-
1338
1427
  var descriptorFactory = W3C ? function (obj) {
1339
1428
  var descriptors = {}
1340
1429
  for (var i in obj) {
@@ -1350,47 +1439,6 @@ var descriptorFactory = W3C ? function (obj) {
1350
1439
  return a
1351
1440
  }
1352
1441
 
1353
-
1354
-
1355
- //应用于第2种accessor
1356
- function objectFactory(parent, name, value, valueType) {
1357
- //a为原来的VM, b为新数组或新对象
1358
- var son = parent[name]
1359
- if (valueType === "array") {
1360
- if (!Array.isArray(value) || son === value) {
1361
- return son //fix https://github.com/RubyLouvre/avalon/issues/261
1362
- }
1363
- son._.$unwatch()
1364
- son.clear()
1365
- son._.$watch()
1366
- son.pushArray(value.concat())
1367
- return son
1368
- } else {
1369
- var iterators = parent.$events[name]
1370
- var pool = son.$events.$withProxyPool
1371
- if (pool) {
1372
- recycleProxies(pool, "with")
1373
- son.$events.$withProxyPool = null
1374
- }
1375
- var ret = modelFactory(value)
1376
- ret.$events[subscribers] = iterators
1377
- midway[ret.$id] = function (data) {
1378
- while (data = iterators.shift()) {
1379
- (function (el) {
1380
- avalon.nextTick(function () {
1381
- var type = el.type
1382
- if (type && bindingHandlers[type]) { //#753
1383
- el.rollback && el.rollback() //还原 ms-with ms-on
1384
- bindingHandlers[type](el, el.vmodels)
1385
- }
1386
- })
1387
- })(data) // jshint ignore:line
1388
- }
1389
- delete midway[ret.$id]
1390
- }
1391
- return ret
1392
- }
1393
- }
1394
1442
  //===================修复浏览器对Object.defineProperties的支持=================
1395
1443
  if (!canHideOwn) {
1396
1444
  if ("__defineGetter__" in avalon) {
@@ -1416,28 +1464,11 @@ if (!canHideOwn) {
1416
1464
  }
1417
1465
  }
1418
1466
  if (IEVersion) {
1419
- window.execScript([ // jshint ignore:line
1467
+ var VBClassPool = {}
1468
+ window.execScript([// jshint ignore:line
1420
1469
  "Function parseVB(code)",
1421
1470
  "\tExecuteGlobal(code)",
1422
- "End Function",
1423
- "Dim VBClassBodies",
1424
- "Set VBClassBodies=CreateObject(\"Scripting.Dictionary\")",
1425
- "Function findOrDefineVBClass(name,body)",
1426
- "\tDim found",
1427
- "\tfound=\"\"",
1428
- "\tFor Each key in VBClassBodies",
1429
- "\t\tIf body=VBClassBodies.Item(key) Then",
1430
- "\t\t\tfound=key",
1431
- "\t\t\tExit For",
1432
- "\t\tEnd If",
1433
- "\tnext",
1434
- "\tIf found=\"\" Then",
1435
- "\t\tparseVB(\"Class \" + name + body)",
1436
- "\t\tVBClassBodies.Add name, body",
1437
- "\t\tfound=name",
1438
- "\tEnd If",
1439
- "\tfindOrDefineVBClass=found",
1440
- "End Function"
1471
+ "End Function" //转换一段文本为VB代码
1441
1472
  ].join("\n"), "VBScript")
1442
1473
  function VBMediator(instance, accessors, name, value) {// jshint ignore:line
1443
1474
  var accessor = accessors[name]
@@ -1448,12 +1479,12 @@ if (!canHideOwn) {
1448
1479
  }
1449
1480
  }
1450
1481
  defineProperties = function (name, accessors, properties) {
1451
- var className = "VBClass" + setTimeout("1"),// jshint ignore:line
1452
- buffer = []
1482
+ // jshint ignore:line
1483
+ var buffer = []
1453
1484
  buffer.push(
1454
1485
  "\r\n\tPrivate [__data__], [__proxy__]",
1455
- "\tPublic Default Function [__const__](d, p)",
1456
- "\t\tSet [__data__] = d: set [__proxy__] = p",
1486
+ "\tPublic Default Function [__const__](d"+expose+", p"+expose+")",
1487
+ "\t\tSet [__data__] = d"+expose+": set [__proxy__] = p"+expose,
1457
1488
  "\t\tSet [__const__] = Me", //链式调用
1458
1489
  "\tEnd Function")
1459
1490
  //添加普通属性,因为VBScript对象不能像JS那样随意增删属性,必须在这里预先定义好
@@ -1490,9 +1521,11 @@ if (!canHideOwn) {
1490
1521
  }
1491
1522
 
1492
1523
  buffer.push("End Class")
1493
- var code = buffer.join("\r\n"),
1494
- realClassName = window['findOrDefineVBClass'](className, code) //如果该VB类已定义,返回类名。否则用className创建一个新类。
1495
- if (realClassName === className) {
1524
+ var body = buffer.join("\r\n")
1525
+ var className =VBClassPool[body]
1526
+ if (!className) {
1527
+ className = generateID("VBClass")
1528
+ window.parseVB("Class " + className + body)
1496
1529
  window.parseVB([
1497
1530
  "Function " + className + "Factory(a, b)", //创建实例并传入两个关键的参数
1498
1531
  "\tDim o",
@@ -1500,8 +1533,9 @@ if (!canHideOwn) {
1500
1533
  "\tSet " + className + "Factory = o",
1501
1534
  "End Function"
1502
1535
  ].join("\r\n"))
1536
+ VBClassPool[body] = className
1503
1537
  }
1504
- var ret = window[realClassName + "Factory"](accessors, VBMediator) //得到其产品
1538
+ var ret = window[className + "Factory"](accessors, VBMediator) //得到其产品
1505
1539
  return ret //得到其产品
1506
1540
  }
1507
1541
  }
@@ -1511,7 +1545,7 @@ if (!canHideOwn) {
1511
1545
  * 监控数组(与ms-each, ms-repeat配合使用) *
1512
1546
  **********************************************************************/
1513
1547
 
1514
- function Collection(model) {
1548
+ function arrayFactory(model) {
1515
1549
  var array = []
1516
1550
  array.$id = generateID()
1517
1551
  array.$model = model //数据模型
@@ -1526,7 +1560,7 @@ function Collection(model) {
1526
1560
  for (var i in EventBus) {
1527
1561
  array[i] = EventBus[i]
1528
1562
  }
1529
- avalon.mix(array, CollectionPrototype)
1563
+ avalon.mix(array, arrayPrototype)
1530
1564
  return array
1531
1565
  }
1532
1566
 
@@ -1534,7 +1568,7 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1534
1568
  var oldLen = this.length, loop = 2
1535
1569
  while (--loop) {
1536
1570
  switch (method) {
1537
- case "add":
1571
+ case "add":
1538
1572
  /* jshint ignore:start */
1539
1573
  var array = this.$model.slice(pos, pos + n).map(function (el) {
1540
1574
  if (rcomplexType.test(avalon.type(el))) {
@@ -1568,10 +1602,10 @@ function mutateArray(method, pos, n, index, method2, pos2, n2) {
1568
1602
  }
1569
1603
 
1570
1604
  var _splice = ap.splice
1571
- var CollectionPrototype = {
1605
+ var arrayPrototype = {
1572
1606
  _splice: _splice,
1573
1607
  _fire: function (method, a, b) {
1574
- notifySubscribers(this.$events[subscribers], method, a, b)
1608
+ fireDependencies(this.$events[subscribers], method, a, b)
1575
1609
  },
1576
1610
  size: function () { //取得数组长度,这个函数可以同步视图,length不能
1577
1611
  return this._.length
@@ -1657,11 +1691,13 @@ var CollectionPrototype = {
1657
1691
  },
1658
1692
  removeAll: function (all) { //移除N个元素
1659
1693
  if (Array.isArray(all)) {
1660
- all.forEach(function (el) {
1661
- this.remove(el)
1662
- }, this)
1663
- } else if (typeof all === "function") {
1664
1694
  for (var i = this.length - 1; i >= 0; i--) {
1695
+ if (all.indexOf(this[i]) !== -1) {
1696
+ this.removeAt(i)
1697
+ }
1698
+ }
1699
+ } else if (typeof all === "function") {
1700
+ for ( i = this.length - 1; i >= 0; i--) {
1665
1701
  var el = this[i]
1666
1702
  if (all(el, i)) {
1667
1703
  this.removeAt(i)
@@ -1678,7 +1714,7 @@ var CollectionPrototype = {
1678
1714
  return this
1679
1715
  },
1680
1716
  set: function (index, val) {
1681
- if (index >= 0) {
1717
+ if (index < this.length && index > -1) {
1682
1718
  var valueType = avalon.type(val)
1683
1719
  if (val && val.$model) {
1684
1720
  val = val.$model
@@ -1701,6 +1737,15 @@ var CollectionPrototype = {
1701
1737
  return this
1702
1738
  }
1703
1739
  }
1740
+ //相当于原来bindingExecutors.repeat 的index分支
1741
+ function resetIndex(array, pos) {
1742
+ var last = array.length - 1
1743
+ for (var el; el = array[pos]; pos++) {
1744
+ el.$index = pos
1745
+ el.$first = pos === 0
1746
+ el.$last = pos === last
1747
+ }
1748
+ }
1704
1749
 
1705
1750
  function sortByIndex(array, indexes) {
1706
1751
  var map = {};
@@ -1717,7 +1762,7 @@ function sortByIndex(array, indexes) {
1717
1762
  }
1718
1763
 
1719
1764
  "sort,reverse".replace(rword, function (method) {
1720
- CollectionPrototype[method] = function () {
1765
+ arrayPrototype[method] = function () {
1721
1766
  var newArray = this.$model//这是要排序的新数组
1722
1767
  var oldArray = newArray.concat() //保持原来状态的旧数组
1723
1768
  var mask = Math.random()
@@ -1738,28 +1783,57 @@ function sortByIndex(array, indexes) {
1738
1783
  }
1739
1784
  if (hasSort) {
1740
1785
  sortByIndex(this, indexes)
1786
+ // sortByIndex(this.$proxy, indexes)
1741
1787
  this._fire("move", indexes)
1742
- this._fire("index", 0)
1788
+ this._fire("index", 0)
1743
1789
  }
1744
1790
  return this
1745
1791
  }
1746
1792
  })
1747
1793
 
1794
+
1748
1795
  /*********************************************************************
1749
1796
  * 依赖调度系统 *
1750
1797
  **********************************************************************/
1798
+ //检测两个对象间的依赖关系
1799
+ var dependencyDetection = (function () {
1800
+ var outerFrames = []
1801
+ var currentFrame
1802
+ return {
1803
+ begin: function (accessorObject) {
1804
+ //accessorObject为一个拥有callback的对象
1805
+ outerFrames.push(currentFrame)
1806
+ currentFrame = accessorObject
1807
+ },
1808
+ end: function () {
1809
+ currentFrame = outerFrames.pop()
1810
+ },
1811
+ collectDependency: function (vmodel, accessor) {
1812
+ if (currentFrame) {
1813
+ //被dependencyDetection.begin调用
1814
+ currentFrame.callback(vmodel, accessor);
1815
+ }
1816
+ }
1817
+ };
1818
+ })()
1819
+ //将绑定对象注入到其依赖项的订阅数组中
1751
1820
  var ronduplex = /^(duplex|on)$/
1752
-
1753
- function registerSubscriber(data) {
1754
- Registry[expose] = data //暴光此函数,方便collectSubscribers收集
1755
- avalon.openComputedCollect = true
1756
- var fn = data.evaluator
1757
- if (fn) { //如果是求值函数
1821
+ avalon.injectBinding = function (data) {
1822
+ var valueFn = data.evaluator
1823
+ if (valueFn) { //如果是求值函数
1824
+ dependencyDetection.begin({
1825
+ callback: function (vmodel, dependency) {
1826
+ injectDependency(vmodel.$events[dependency._name], data)
1827
+ }
1828
+ })
1758
1829
  try {
1759
- var c = ronduplex.test(data.type) ? data : fn.apply(0, data.args)
1760
- data.handler(c, data.element, data)
1830
+ var value = ronduplex.test(data.type) ? data : valueFn.apply(0, data.args)
1831
+ if(value === void 0){
1832
+ delete data.evaluator
1833
+ }
1834
+ data.handler(value, data.element, data)
1761
1835
  } catch (e) {
1762
- //log("warning:exception throwed in [registerSubscriber] " + e)
1836
+ log("warning:exception throwed in [avalon.injectBinding] " , e)
1763
1837
  delete data.evaluator
1764
1838
  var node = data.element
1765
1839
  if (node.nodeType === 3) {
@@ -1767,126 +1841,160 @@ function registerSubscriber(data) {
1767
1841
  if (kernel.commentInterpolate) {
1768
1842
  parent.replaceChild(DOC.createComment(data.value), node)
1769
1843
  } else {
1770
- node.data = openTag + data.value + closeTag
1844
+ node.data = openTag + (data.oneTime ? "::" : "") + data.value + closeTag
1771
1845
  }
1772
1846
  }
1847
+ } finally {
1848
+ dependencyDetection.end()
1773
1849
  }
1774
1850
  }
1775
- avalon.openComputedCollect = false
1776
- delete Registry[expose]
1777
1851
  }
1778
1852
 
1779
- function collectSubscribers(list) { //收集依赖于这个访问器的订阅者
1780
- var data = Registry[expose]
1781
- if (list && data && avalon.Array.ensure(list, data) && data.element) { //只有数组不存在此元素才push进去
1782
- addSubscribers(data, list)
1853
+ //将依赖项(比它高层的访问器或构建视图刷新函数的绑定对象)注入到订阅者数组
1854
+ function injectDependency(list, data) {
1855
+ if (data.oneTime)
1856
+ return
1857
+ if (list && avalon.Array.ensure(list, data) && data.element) {
1858
+ injectDisposeQueue(data, list)
1859
+ if (new Date() - beginTime > 444 ) {
1860
+ rejectDisposeQueue()
1861
+ }
1783
1862
  }
1784
1863
  }
1785
1864
 
1786
-
1787
- function addSubscribers(data, list) {
1788
- data.$uuid = data.$uuid || generateID()
1789
- list.$uuid = list.$uuid || generateID()
1790
- var obj = {
1791
- data: data,
1792
- list: list,
1793
- $$uuid: data.$uuid + list.$uuid
1794
- }
1795
- if (!$$subscribers[obj.$$uuid]) {
1796
- $$subscribers[obj.$$uuid] = 1
1797
- $$subscribers.push(obj)
1865
+ //通知依赖于这个访问器的订阅者更新自身
1866
+ function fireDependencies(list) {
1867
+ if (list && list.length) {
1868
+ if (new Date() - beginTime > 444 && typeof list[0] === "object") {
1869
+ rejectDisposeQueue()
1870
+ }
1871
+ var args = aslice.call(arguments, 1)
1872
+ for (var i = list.length, fn; fn = list[--i]; ) {
1873
+ var el = fn.element
1874
+ if (el && el.parentNode) {
1875
+ try {
1876
+ var valueFn = fn.evaluator
1877
+ if (fn.$repeat) {
1878
+ fn.handler.apply(fn, args) //处理监控数组的方法
1879
+ }else if("$repeat" in fn || !valueFn ){//如果没有eval,先eval
1880
+ bindingHandlers[fn.type](fn, fn.vmodels)
1881
+ } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发
1882
+ var value = valueFn.apply(0, fn.args || [])
1883
+ fn.handler(value, el, fn)
1884
+ }
1885
+ } catch (e) {
1886
+ console.log(e)
1887
+ }
1888
+ }
1889
+ }
1798
1890
  }
1799
1891
  }
1800
-
1801
- function disposeData(data) {
1802
- data.element = null
1803
- data.rollback && data.rollback()
1804
- for (var key in data) {
1805
- data[key] = null
1892
+ /*********************************************************************
1893
+ * 定时GC回收机制 *
1894
+ **********************************************************************/
1895
+ var disposeCount = 0
1896
+ var disposeQueue = avalon.$$subscribers = []
1897
+ var beginTime = new Date()
1898
+ var oldInfo = {}
1899
+ //var uuid2Node = {}
1900
+ function getUid(elem, makeID) { //IE9+,标准浏览器
1901
+ if (!elem.uuid && !makeID) {
1902
+ elem.uuid = ++disposeCount
1806
1903
  }
1904
+ return elem.uuid
1807
1905
  }
1808
1906
 
1809
- function isRemove(el) {
1810
- try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错
1811
- if (!el.parentNode) {
1812
- return true
1907
+ //添加到回收列队中
1908
+ function injectDisposeQueue(data, list) {
1909
+ var elem = data.element
1910
+ if (!data.uuid) {
1911
+ if (elem.nodeType !== 1) {
1912
+ data.uuid = data.type + (data.pos || 0) + "-" + getUid(elem.parentNode)
1913
+ } else {
1914
+ data.uuid = data.name + "-" + getUid(elem)
1813
1915
  }
1814
- } catch (e) {
1815
- return true
1816
1916
  }
1817
- return el.msRetain ? 0 : (el.nodeType === 1 ? typeof el.sourceIndex === "number" ?
1818
- el.sourceIndex === 0 : !root.contains(el) : !avalon.contains(root, el))
1917
+ var lists = data.lists || (data.lists = [])
1918
+ avalon.Array.ensure(lists, list)
1919
+ list.$uuid = list.$uuid || generateID()
1920
+ if (!disposeQueue[data.uuid]) {
1921
+ disposeQueue[data.uuid] = 1
1922
+ disposeQueue.push(data)
1923
+ }
1819
1924
  }
1820
- var $$subscribers = avalon.$$subscribers = []
1821
- var beginTime = new Date()
1822
- var oldInfo = {}
1823
- function removeSubscribers() {
1824
- var i = $$subscribers.length
1925
+
1926
+ function rejectDisposeQueue(data) {
1927
+ if (avalon.optimize)
1928
+ return
1929
+ var i = disposeQueue.length
1825
1930
  var n = i
1826
- var k = 0
1827
- var obj
1828
- var types = []
1931
+ var allTypes = []
1932
+ var iffishTypes = {}
1829
1933
  var newInfo = {}
1830
- var needTest = {}
1831
- while (obj = $$subscribers[--i]) {
1832
- var data = obj.data
1934
+ //对页面上所有绑定对象进行分门别类, 只检测个数发生变化的类型
1935
+ while (data = disposeQueue[--i]) {
1833
1936
  var type = data.type
1834
1937
  if (newInfo[type]) {
1835
1938
  newInfo[type]++
1836
1939
  } else {
1837
1940
  newInfo[type] = 1
1838
- types.push(type)
1941
+ allTypes.push(type)
1839
1942
  }
1840
1943
  }
1841
1944
  var diff = false
1842
- types.forEach(function(type) {
1945
+ allTypes.forEach(function (type) {
1843
1946
  if (oldInfo[type] !== newInfo[type]) {
1844
- needTest[type] = 1
1947
+ iffishTypes[type] = 1
1845
1948
  diff = true
1846
1949
  }
1847
1950
  })
1848
1951
  i = n
1849
- //avalon.log("需要检测的个数 " + i)
1850
1952
  if (diff) {
1851
- //avalon.log("有需要移除的元素")
1852
- while (obj = $$subscribers[--i]) {
1853
- data = obj.data
1854
- if (data.element === void 0)
1953
+ while (data = disposeQueue[--i]) {
1954
+ if (data.element === null) {
1955
+ disposeQueue.splice(i, 1)
1855
1956
  continue
1856
- if (needTest[data.type] && isRemove(data.element)) { //如果它没有在DOM树
1857
- k++
1858
- $$subscribers.splice(i, 1)
1859
- delete $$subscribers[obj.$$uuid]
1860
- avalon.Array.remove(obj.list, data)
1861
- //log("debug: remove " + data.type)
1957
+ }
1958
+ if (iffishTypes[data.type] && shouldDispose(data.element)) { //如果它没有在DOM树
1959
+ disposeQueue.splice(i, 1)
1960
+ delete disposeQueue[data.uuid]
1961
+ //delete uuid2Node[data.element.uuid]
1962
+ var lists = data.lists
1963
+ for (var k = 0, list; list = lists[k++]; ) {
1964
+ avalon.Array.remove(lists, list)
1965
+ avalon.Array.remove(list, data)
1966
+ }
1862
1967
  disposeData(data)
1863
- obj.data = obj.list = null
1864
1968
  }
1865
1969
  }
1866
1970
  }
1867
1971
  oldInfo = newInfo
1868
- // avalon.log("已经移除的个数 " + k)
1869
1972
  beginTime = new Date()
1870
1973
  }
1871
1974
 
1872
- function notifySubscribers(list) { //通知依赖于这个访问器的订阅者更新自身
1873
- if (list && list.length) {
1874
- if (new Date() - beginTime > 444 && typeof list[0] === "object") {
1875
- removeSubscribers()
1876
- }
1877
- var args = aslice.call(arguments, 1)
1878
- for (var i = list.length, fn; fn = list[--i]; ) {
1879
- var el = fn.element
1880
- if (el && el.parentNode) {
1881
- if (fn.$repeat) {
1882
- fn.handler.apply(fn, args) //处理监控数组的方法
1883
- } else if (fn.type !== "on") { //事件绑定只能由用户触发,不能由程序触发
1884
- var fun = fn.evaluator || noop
1885
- fn.handler(fun.apply(0, fn.args || []), el, fn)
1886
- }
1887
- }
1975
+ function disposeData(data) {
1976
+ delete disposeQueue[data.uuid] // 先清除,不然无法回收了
1977
+ data.element = null
1978
+ data.rollback && data.rollback()
1979
+ for (var key in data) {
1980
+ data[key] = null
1981
+ }
1982
+ }
1983
+
1984
+ function shouldDispose(el) {
1985
+ try {//IE下,如果文本节点脱离DOM树,访问parentNode会报错
1986
+ var fireError = el.parentNode.nodeType
1987
+ } catch (e) {
1988
+ return true
1989
+ }
1990
+ if (el.ifRemove) {
1991
+ // 如果节点被放到ifGroup,才移除
1992
+ if (!root.contains(el.ifRemove) && (ifGroup === el.parentNode)) {
1993
+ el.parentNode && el.parentNode.removeChild(el)
1994
+ return true
1888
1995
  }
1889
1996
  }
1997
+ return el.msRetain ? 0 : (el.nodeType === 1 ? !root.contains(el) : !avalon.contains(root, el))
1890
1998
  }
1891
1999
 
1892
2000
  /************************************************************************
@@ -1920,7 +2028,7 @@ var rnest = /<(?:tb|td|tf|th|tr|col|opt|leg|cap|area)/ //需要处理套嵌关
1920
2028
  var script = DOC.createElement("script")
1921
2029
  var rhtml = /<|&#?\w+;/
1922
2030
  avalon.parseHTML = function (html) {
1923
- var fragment = hyperspace.cloneNode(false)
2031
+ var fragment = avalonFragment.cloneNode(false)
1924
2032
  if (typeof html !== "string") {
1925
2033
  return fragment
1926
2034
  }
@@ -2022,879 +2130,506 @@ avalon.clearHTML = function (node) {
2022
2130
  }
2023
2131
 
2024
2132
  /*********************************************************************
2025
- * 扫描系统 *
2133
+ * avalon的原型方法定义区 *
2026
2134
  **********************************************************************/
2027
2135
 
2028
- avalon.scan = function(elem, vmodel, group) {
2029
- elem = elem || root
2030
- var vmodels = vmodel ? [].concat(vmodel) : []
2031
- scanTag(elem, vmodels)
2136
+ function hyphen(target) {
2137
+ //转换为连字符线风格
2138
+ return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase()
2032
2139
  }
2033
2140
 
2034
- //http://www.w3.org/TR/html5/syntax.html#void-elements
2035
- var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase())
2036
-
2037
- function checkScan(elem, callback, innerHTML) {
2038
- var id = setTimeout(function() {
2039
- var currHTML = elem.innerHTML
2040
- clearTimeout(id)
2041
- if (currHTML === innerHTML) {
2042
- callback()
2043
- } else {
2044
- checkScan(elem, callback, currHTML)
2045
- }
2141
+ function camelize(target) {
2142
+ //提前判断,提高getStyle等的效率
2143
+ if (!target || target.indexOf("-") < 0 && target.indexOf("_") < 0) {
2144
+ return target
2145
+ }
2146
+ //转换为驼峰风格
2147
+ return target.replace(/[-_][^-_]/g, function(match) {
2148
+ return match.charAt(1).toUpperCase()
2046
2149
  })
2047
2150
  }
2048
2151
 
2049
-
2050
- function createSignalTower(elem, vmodel) {
2051
- var id = elem.getAttribute("avalonctrl") || vmodel.$id
2052
- elem.setAttribute("avalonctrl", id)
2053
- vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]'
2054
- }
2055
-
2056
- var getBindingCallback = function(elem, name, vmodels) {
2057
- var callback = elem.getAttribute(name)
2058
- if (callback) {
2059
- for (var i = 0, vm; vm = vmodels[i++]; ) {
2060
- if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
2061
- return vm[callback]
2062
- }
2152
+ var fakeClassListMethods = {
2153
+ _toString: function() {
2154
+ var node = this.node
2155
+ var cls = node.className
2156
+ var str = typeof cls === "string" ? cls : cls.baseVal
2157
+ return str.split(/\s+/).join(" ")
2158
+ },
2159
+ _contains: function(cls) {
2160
+ return (" " + this + " ").indexOf(" " + cls + " ") > -1
2161
+ },
2162
+ _add: function(cls) {
2163
+ if (!this.contains(cls)) {
2164
+ this._set(this + " " + cls)
2063
2165
  }
2064
- }
2065
- }
2066
-
2067
- function executeBindings(bindings, vmodels) {
2068
- for (var i = 0, data; data = bindings[i++]; ) {
2069
- data.vmodels = vmodels
2070
- bindingHandlers[data.type](data, vmodels)
2071
- if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
2072
- //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
2073
- data.element.removeAttribute(data.name)
2166
+ },
2167
+ _remove: function(cls) {
2168
+ this._set((" " + this + " ").replace(" " + cls + " ", " "))
2169
+ },
2170
+ __set: function(cls) {
2171
+ cls = cls.trim()
2172
+ var node = this.node
2173
+ if (rsvg.test(node)) {
2174
+ //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作
2175
+ node.setAttribute("class", cls)
2176
+ } else {
2177
+ node.className = cls
2074
2178
  }
2075
- }
2076
- bindings.length = 0
2179
+ } //toggle存在版本差异,因此不使用它
2077
2180
  }
2078
2181
 
2079
- //https://github.com/RubyLouvre/avalon/issues/636
2080
- var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) {
2081
- var node = elem.firstChild, text
2082
- while (node) {
2083
- var aaa = node.nextSibling
2084
- if (node.nodeType === 3) {
2085
- if (text) {
2086
- text.nodeValue += node.nodeValue
2087
- elem.removeChild(node)
2088
- } else {
2089
- text = node
2182
+ function fakeClassList(node) {
2183
+ if (!("classList" in node)) {
2184
+ node.classList = {
2185
+ node: node
2186
+ }
2187
+ for (var k in fakeClassListMethods) {
2188
+ node.classList[k.slice(1)] = fakeClassListMethods[k]
2090
2189
  }
2091
- } else {
2092
- text = null
2093
2190
  }
2094
- node = aaa
2191
+ return node.classList
2095
2192
  }
2096
- } : 0
2097
-
2098
- var rmsAttr = /ms-(\w+)-?(.*)/
2099
- var priorityMap = {
2100
- "if": 10,
2101
- "repeat": 90,
2102
- "data": 100,
2103
- "widget": 110,
2104
- "each": 1400,
2105
- "with": 1500,
2106
- "duplex": 2000,
2107
- "on": 3000
2108
- }
2109
2193
 
2110
- var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
2111
- var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled")
2112
- function bindingSorter(a, b) {
2113
- return a.priority - b.priority
2114
- }
2115
2194
 
2116
- function scanAttr(elem, vmodels) {
2117
- //防止setAttribute, removeAttribute时 attributes自动被同步,导致for循环出错
2118
- var attributes = getAttributes ? getAttributes(elem) : avalon.slice(elem.attributes)
2119
- var bindings = [],
2120
- msData = {},
2121
- match
2122
- for (var i = 0, attr; attr = attributes[i++]; ) {
2123
- if (attr.specified) {
2124
- if (match = attr.name.match(rmsAttr)) {
2125
- //如果是以指定前缀命名的
2126
- var type = match[1]
2127
- var param = match[2] || ""
2128
- var value = attr.value
2129
- var name = attr.name
2130
- msData[name] = value
2131
- if (events[type]) {
2132
- param = type
2133
- type = "on"
2134
- } else if (obsoleteAttrs[type]) {
2135
- log("warning!请改用ms-attr-" + type + "代替ms-" + type + "!")
2136
- if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
2137
- log("warning!ms-enabled或ms-attr-enabled已经被废弃")
2138
- type = "disabled"
2139
- value = "!(" + value + ")"
2140
- }
2141
- param = type
2142
- type = "attr"
2143
- elem.removeAttribute(name)
2144
- name = "ms-attr-" + param
2145
- elem.setAttribute(name, value)
2146
- match = [name]
2147
- msData[name] = value
2148
- }
2149
- if (typeof bindingHandlers[type] === "function") {
2150
- var binding = {
2151
- type: type,
2152
- param: param,
2153
- element: elem,
2154
- name: match[0],
2155
- value: value,
2156
- priority: type in priorityMap ? priorityMap[type] : type.charCodeAt(0) * 10 + (Number(param) || 0)
2157
- }
2158
- if (type === "html" || type === "text") {
2159
- var token = getToken(value)
2160
- avalon.mix(binding, token)
2161
- binding.filters = binding.filters.replace(rhasHtml, function () {
2162
- binding.type = "html"
2163
- binding.group = 1
2164
- return ""
2165
- })// jshint ignore:line
2166
- }
2167
- if (name === "ms-if-loop") {
2168
- binding.priority += 100
2169
- }
2170
- if (vmodels.length) {
2171
- bindings.push(binding)
2172
- if (type === "widget") {
2173
- elem.msData = elem.msData || msData
2195
+ "add,remove".replace(rword, function(method) {
2196
+ avalon.fn[method + "Class"] = function(cls) {
2197
+ var el = this[0]
2198
+ //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26
2199
+ if (cls && typeof cls === "string" && el && el.nodeType === 1) {
2200
+ cls.replace(/\S+/g, function(c) {
2201
+ fakeClassList(el)[method](c)
2202
+ })
2203
+ }
2204
+ return this
2205
+ }
2206
+ })
2207
+ avalon.fn.mix({
2208
+ hasClass: function(cls) {
2209
+ var el = this[0] || {}
2210
+ return el.nodeType === 1 && fakeClassList(el).contains(cls)
2211
+ },
2212
+ toggleClass: function(value, stateVal) {
2213
+ var className, i = 0
2214
+ var classNames = String(value).split(/\s+/)
2215
+ var isBool = typeof stateVal === "boolean"
2216
+ while ((className = classNames[i++])) {
2217
+ var state = isBool ? stateVal : !this.hasClass(className)
2218
+ this[state ? "addClass" : "removeClass"](className)
2219
+ }
2220
+ return this
2221
+ },
2222
+ attr: function(name, value) {
2223
+ if (arguments.length === 2) {
2224
+ this[0].setAttribute(name, value)
2225
+ return this
2226
+ } else {
2227
+ return this[0].getAttribute(name)
2228
+ }
2229
+ },
2230
+ data: function(name, value) {
2231
+ name = "data-" + hyphen(name || "")
2232
+ switch (arguments.length) {
2233
+ case 2:
2234
+ this.attr(name, value)
2235
+ return this
2236
+ case 1:
2237
+ var val = this.attr(name)
2238
+ return parseData(val)
2239
+ case 0:
2240
+ var ret = {}
2241
+ ap.forEach.call(this[0].attributes, function(attr) {
2242
+ if (attr) {
2243
+ name = attr.name
2244
+ if (!name.indexOf("data-")) {
2245
+ name = camelize(name.slice(5))
2246
+ ret[name] = parseData(attr.value)
2247
+ }
2174
2248
  }
2175
- }
2249
+ })
2250
+ return ret
2251
+ }
2252
+ },
2253
+ removeData: function(name) {
2254
+ name = "data-" + hyphen(name)
2255
+ this[0].removeAttribute(name)
2256
+ return this
2257
+ },
2258
+ css: function(name, value) {
2259
+ if (avalon.isPlainObject(name)) {
2260
+ for (var i in name) {
2261
+ avalon.css(this, i, name[i])
2176
2262
  }
2263
+ } else {
2264
+ var ret = avalon.css(this, name, value)
2177
2265
  }
2266
+ return ret !== void 0 ? ret : this
2267
+ },
2268
+ position: function() {
2269
+ var offsetParent, offset,
2270
+ elem = this[0],
2271
+ parentOffset = {
2272
+ top: 0,
2273
+ left: 0
2274
+ }
2275
+ if (!elem) {
2276
+ return
2277
+ }
2278
+ if (this.css("position") === "fixed") {
2279
+ offset = elem.getBoundingClientRect()
2280
+ } else {
2281
+ offsetParent = this.offsetParent() //得到真正的offsetParent
2282
+ offset = this.offset() // 得到正确的offsetParent
2283
+ if (offsetParent[0].tagName !== "HTML") {
2284
+ parentOffset = offsetParent.offset()
2285
+ }
2286
+ parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true)
2287
+ parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true)
2288
+
2289
+ // Subtract offsetParent scroll positions
2290
+ parentOffset.top -= offsetParent.scrollTop()
2291
+ parentOffset.left -= offsetParent.scrollLeft()
2292
+ }
2293
+ return {
2294
+ top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true),
2295
+ left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true)
2296
+ }
2297
+ },
2298
+ offsetParent: function() {
2299
+ var offsetParent = this[0].offsetParent
2300
+ while (offsetParent && avalon.css(offsetParent, "position") === "static") {
2301
+ offsetParent = offsetParent.offsetParent;
2302
+ }
2303
+ return avalon(offsetParent || root)
2304
+ },
2305
+ bind: function(type, fn, phase) {
2306
+ if (this[0]) { //此方法不会链
2307
+ return avalon.bind(this[0], type, fn, phase)
2308
+ }
2309
+ },
2310
+ unbind: function(type, fn, phase) {
2311
+ if (this[0]) {
2312
+ avalon.unbind(this[0], type, fn, phase)
2313
+ }
2314
+ return this
2315
+ },
2316
+ val: function(value) {
2317
+ var node = this[0]
2318
+ if (node && node.nodeType === 1) {
2319
+ var get = arguments.length === 0
2320
+ var access = get ? ":get" : ":set"
2321
+ var fn = valHooks[getValType(node) + access]
2322
+ if (fn) {
2323
+ var val = fn(node, value)
2324
+ } else if (get) {
2325
+ return (node.value || "").replace(/\r/g, "")
2326
+ } else {
2327
+ node.value = value
2328
+ }
2329
+ }
2330
+ return get ? val : this
2178
2331
  }
2332
+ })
2333
+
2334
+ function parseData(data) {
2335
+ try {
2336
+ if (typeof data === "object")
2337
+ return data
2338
+ data = data === "true" ? true :
2339
+ data === "false" ? false :
2340
+ data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data
2341
+ } catch (e) {}
2342
+ return data
2179
2343
  }
2180
- bindings.sort(bindingSorter)
2181
- var control = elem.type
2182
- if (control && msData["ms-duplex"]) {
2183
- if (msData["ms-attr-checked"] && /radio|checkbox/.test(control)) {
2184
- log("warning!" + control + "控件不能同时定义ms-attr-checked与ms-duplex")
2185
- }
2186
- if (msData["ms-attr-value"] && /text|password/.test(control)) {
2187
- log("warning!" + control + "控件不能同时定义ms-attr-value与ms-duplex")
2188
- }
2189
- }
2190
- var scanNode = true
2191
- for (i = 0; binding = bindings[i]; i++) {
2192
- type = binding.type
2193
- if (rnoscanAttrBinding.test(type)) {
2194
- return executeBindings(bindings.slice(0, i + 1), vmodels)
2195
- } else if (scanNode) {
2196
- scanNode = !rnoscanNodeBinding.test(type)
2344
+ var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
2345
+ rvalidchars = /^[\],:{}\s]*$/,
2346
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
2347
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
2348
+ rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g
2349
+ avalon.parseJSON = window.JSON ? JSON.parse : function(data) {
2350
+ if (typeof data === "string") {
2351
+ data = data.trim();
2352
+ if (data) {
2353
+ if (rvalidchars.test(data.replace(rvalidescape, "@")
2354
+ .replace(rvalidtokens, "]")
2355
+ .replace(rvalidbraces, ""))) {
2356
+ return (new Function("return " + data))() // jshint ignore:line
2357
+ }
2197
2358
  }
2359
+ avalon.error("Invalid JSON: " + data)
2198
2360
  }
2199
- executeBindings(bindings, vmodels)
2200
- if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) {
2201
- mergeTextNodes && mergeTextNodes(elem)
2202
- scanNodeList(elem, vmodels) //扫描子孙元素
2203
- }
2361
+ return data
2204
2362
  }
2205
2363
 
2206
- var rnoscanAttrBinding = /^if|widget|repeat$/
2207
- var rnoscanNodeBinding = /^each|with|html|include$/
2208
- //IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支,
2209
- //但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面
2210
- if (!"1" [0]) {
2211
- var cacheAttrs = new Cache(512)
2212
- var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g,
2213
- rquote = /^['"]/,
2214
- rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i,
2215
- ramp = /&amp;/g
2216
- //IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点
2217
- //<body><section>ddd</section></body>
2218
- // window.onload = function() {
2219
- // var body = document.body
2220
- // for (var i = 0, el; el = body.children[i++]; ) {
2221
- // avalon.log(el.outerHTML)
2222
- // }
2223
- // }
2224
- //依次输出<SECTION>, </SECTION>
2225
- var getAttributes = function (elem) {
2226
- var html = elem.outerHTML
2227
- //处理IE6-8解析HTML5新标签的情况,及<br>等半闭合标签outerHTML为空的情况
2228
- if (html.slice(0, 2) === "</" || !html.trim()) {
2229
- return []
2230
- }
2231
- var str = html.match(rtag)[0]
2232
- var attributes = [],
2233
- match,
2234
- k, v
2235
- var ret = cacheAttrs.get(str)
2236
- if (ret) {
2237
- return ret
2238
- }
2239
- while (k = rattrs.exec(str)) {
2240
- v = k[2]
2241
- if (v) {
2242
- v = (rquote.test(v) ? v.slice(1, -1) : v).replace(ramp, "&")
2243
- }
2244
- var name = k[1].toLowerCase()
2245
- match = name.match(rmsAttr)
2246
- var binding = {
2247
- name: name,
2248
- specified: true,
2249
- value: v || ""
2364
+ //生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法
2365
+ avalon.each({
2366
+ scrollLeft: "pageXOffset",
2367
+ scrollTop: "pageYOffset"
2368
+ }, function(method, prop) {
2369
+ avalon.fn[method] = function(val) {
2370
+ var node = this[0] || {}, win = getWindow(node),
2371
+ top = method === "scrollTop"
2372
+ if (!arguments.length) {
2373
+ return win ? (prop in win) ? win[prop] : root[method] : node[method]
2374
+ } else {
2375
+ if (win) {
2376
+ win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop())
2377
+ } else {
2378
+ node[method] = val
2250
2379
  }
2251
- attributes.push(binding)
2252
2380
  }
2253
- return cacheAttrs.put(str, attributes)
2254
2381
  }
2255
- }
2382
+ })
2256
2383
 
2257
- function scanNodeList(parent, vmodels) {
2258
- var node = parent.firstChild
2259
- while (node) {
2260
- var nextNode = node.nextSibling
2261
- scanNode(node, node.nodeType, vmodels)
2262
- node = nextNode
2263
- }
2384
+ function getWindow(node) {
2385
+ return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false;
2386
+ }
2387
+ //=============================css相关=======================
2388
+ var cssHooks = avalon.cssHooks = {}
2389
+ var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"]
2390
+ var cssMap = {
2391
+ "float": W3C ? "cssFloat" : "styleFloat"
2264
2392
  }
2393
+ avalon.cssNumber = oneObject("animationIterationCount,columnCount,order,flex,flexGrow,flexShrink,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
2265
2394
 
2266
- function scanNodeArray(nodes, vmodels) {
2267
- for (var i = 0, node; node = nodes[i++]; ) {
2268
- scanNode(node, node.nodeType, vmodels)
2395
+ avalon.cssName = function(name, host, camelCase) {
2396
+ if (cssMap[name]) {
2397
+ return cssMap[name]
2269
2398
  }
2270
- }
2271
- function scanNode(node, nodeType, vmodels) {
2272
- if (nodeType === 1) {
2273
- scanTag(node, vmodels) //扫描元素节点
2274
- } else if (nodeType === 3 && rexpr.test(node.data)){
2275
- scanText(node, vmodels) //扫描文本节点
2276
- } else if (kernel.commentInterpolate && nodeType === 8 && !rexpr.test(node.nodeValue)) {
2277
- scanText(node, vmodels) //扫描注释节点
2399
+ host = host || root.style
2400
+ for (var i = 0, n = prefixes.length; i < n; i++) {
2401
+ camelCase = camelize(prefixes[i] + name)
2402
+ if (camelCase in host) {
2403
+ return (cssMap[name] = camelCase)
2404
+ }
2278
2405
  }
2406
+ return null
2279
2407
  }
2280
- function scanTag(elem, vmodels, node) {
2281
- //扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
2282
- //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
2283
- var a = elem.getAttribute("ms-skip")
2284
- //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形
2285
- if (!elem.getAttributeNode) {
2286
- return log("warning " + elem.tagName + " no getAttributeNode method")
2287
- }
2288
- var b = elem.getAttributeNode("ms-important")
2289
- var c = elem.getAttributeNode("ms-controller")
2290
- if (typeof a === "string") {
2291
- return
2292
- } else if (node = b || c) {
2293
- var newVmodel = avalon.vmodels[node.value]
2294
- if (!newVmodel) {
2295
- return
2296
- }
2297
- //ms-important不包含父VM,ms-controller相反
2298
- vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
2299
- var name = node.name
2300
- elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则
2301
- avalon(elem).removeClass(name)
2302
- createSignalTower(elem, newVmodel)
2303
- }
2304
- scanAttr(elem, vmodels) //扫描特性节点
2408
+ cssHooks["@:set"] = function(node, name, value) {
2409
+ try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常
2410
+ node.style[name] = value
2411
+ } catch (e) {}
2305
2412
  }
2306
- var rhasHtml = /\|\s*html\s*/,
2307
- r11a = /\|\|/g,
2308
- rlt = /&lt;/g,
2309
- rgt = /&gt;/g
2310
- rstringLiteral = /(['"])(\\\1|.)+?\1/g
2311
- function getToken(value) {
2312
- if (value.indexOf("|") > 0) {
2313
- var scapegoat = value.replace( rstringLiteral, function(_){
2314
- return Array(_.length+1).join("1")// jshint ignore:line
2315
- })
2316
- var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
2317
- if (index > -1) {
2318
- return {
2319
- filters: value.slice(index),
2320
- value: value.slice(0, index),
2321
- expr: true
2322
- }
2413
+ if (window.getComputedStyle) {
2414
+ cssHooks["@:get"] = function(node, name) {
2415
+ if (!node || !node.style) {
2416
+ throw new Error("getComputedStyle要求传入一个节点 " + node)
2323
2417
  }
2418
+ var ret, styles = getComputedStyle(node, null)
2419
+ if (styles) {
2420
+ ret = name === "filter" ? styles.getPropertyValue(name) : styles[name]
2421
+ if (ret === "") {
2422
+ ret = node.style[name] //其他浏览器需要我们手动取内联样式
2423
+ }
2424
+ }
2425
+ return ret
2324
2426
  }
2325
- return {
2326
- value: value,
2327
- filters: "",
2328
- expr: true
2427
+ cssHooks["opacity:get"] = function(node) {
2428
+ var ret = cssHooks["@:get"](node, "opacity")
2429
+ return ret === "" ? "1" : ret
2329
2430
  }
2330
- }
2331
-
2332
- function scanExpr(str) {
2333
- var tokens = [],
2334
- value, start = 0,
2335
- stop
2336
- do {
2337
- stop = str.indexOf(openTag, start)
2338
- if (stop === -1) {
2339
- break
2340
- }
2341
- value = str.slice(start, stop)
2342
- if (value) { // {{ 左边的文本
2343
- tokens.push({
2344
- value: value,
2345
- filters: "",
2346
- expr: false
2347
- })
2348
- }
2349
- start = stop + openTag.length
2350
- stop = str.indexOf(closeTag, start)
2351
- if (stop === -1) {
2352
- break
2431
+ } else {
2432
+ var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i
2433
+ var rposition = /^(top|right|bottom|left)$/
2434
+ var ralpha = /alpha\([^)]*\)/i
2435
+ var ie8 = !! window.XDomainRequest
2436
+ var salpha = "DXImageTransform.Microsoft.Alpha"
2437
+ var border = {
2438
+ thin: ie8 ? '1px' : '2px',
2439
+ medium: ie8 ? '3px' : '4px',
2440
+ thick: ie8 ? '5px' : '6px'
2441
+ }
2442
+ cssHooks["@:get"] = function(node, name) {
2443
+ //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位
2444
+ var currentStyle = node.currentStyle
2445
+ var ret = currentStyle[name]
2446
+ if ((rnumnonpx.test(ret) && !rposition.test(ret))) {
2447
+ //①,保存原有的style.left, runtimeStyle.left,
2448
+ var style = node.style,
2449
+ left = style.left,
2450
+ rsLeft = node.runtimeStyle.left
2451
+ //②由于③处的style.left = xxx会影响到currentStyle.left,
2452
+ //因此把它currentStyle.left放到runtimeStyle.left,
2453
+ //runtimeStyle.left拥有最高优先级,不会style.left影响
2454
+ node.runtimeStyle.left = currentStyle.left
2455
+ //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft
2456
+ //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760
2457
+ style.left = name === 'fontSize' ? '1em' : (ret || 0)
2458
+ ret = style.pixelLeft + "px"
2459
+ //④还原 style.left,runtimeStyle.left
2460
+ style.left = left
2461
+ node.runtimeStyle.left = rsLeft
2353
2462
  }
2354
- value = str.slice(start, stop)
2355
- if (value) { //处理{{ }}插值表达式
2356
- tokens.push(getToken(value))
2463
+ if (ret === "medium") {
2464
+ name = name.replace("Width", "Style")
2465
+ //border width 默认值为medium,即使其为0"
2466
+ if (currentStyle[name] === "none") {
2467
+ ret = "0px"
2468
+ }
2357
2469
  }
2358
- start = stop + closeTag.length
2359
- } while (1)
2360
- value = str.slice(start)
2361
- if (value) { //}} 右边的文本
2362
- tokens.push({
2363
- value: value,
2364
- expr: false,
2365
- filters: ""
2366
- })
2367
- }
2368
- return tokens
2369
- }
2370
-
2371
- function scanText(textNode, vmodels) {
2372
- var bindings = []
2373
- if (textNode.nodeType === 8) {
2374
- var token = getToken(textNode.nodeValue)
2375
- var tokens = [token]
2376
- } else {
2377
- tokens = scanExpr(textNode.data)
2470
+ return ret === "" ? "auto" : border[ret] || ret
2378
2471
  }
2379
- if (tokens.length) {
2380
- for (var i = 0; token = tokens[i++]; ) {
2381
- var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点
2382
- if (token.expr) {
2383
- token.type = "text"
2384
- token.element = node
2385
- token.filters = token.filters.replace(rhasHtml, function() {
2386
- token.type = "html"
2387
- token.group = 1
2388
- return ""
2389
- })// jshint ignore:line
2390
- bindings.push(token) //收集带有插值表达式的文本
2391
- }
2392
- hyperspace.appendChild(node)
2472
+ cssHooks["opacity:set"] = function(node, name, value) {
2473
+ var style = node.style
2474
+ var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : ""
2475
+ var filter = style.filter || "";
2476
+ style.zoom = 1
2477
+ //不能使用以下方式设置透明度
2478
+ //node.filters.alpha.opacity = value * 100
2479
+ style.filter = (ralpha.test(filter) ?
2480
+ filter.replace(ralpha, opacity) :
2481
+ filter + " " + opacity).trim()
2482
+ if (!style.filter) {
2483
+ style.removeAttribute("filter")
2393
2484
  }
2394
- textNode.parentNode.replaceChild(hyperspace, textNode)
2395
- if (bindings.length)
2396
- executeBindings(bindings, vmodels)
2397
2485
  }
2398
- }
2399
-
2400
- /*********************************************************************
2401
- * avalon的原型方法定义区 *
2402
- **********************************************************************/
2403
-
2404
- function hyphen(target) {
2405
- //转换为连字符线风格
2406
- return target.replace(/([a-z\d])([A-Z]+)/g, "$1-$2").toLowerCase()
2407
- }
2408
-
2409
- function camelize(target) {
2410
- //转换为驼峰风格
2411
- if (target.indexOf("-") < 0 && target.indexOf("_") < 0) {
2412
- return target //提前判断,提高getStyle等的效率
2486
+ cssHooks["opacity:get"] = function(node) {
2487
+ //这是最快的获取IE透明值的方式,不需要动用正则了!
2488
+ var alpha = node.filters.alpha || node.filters[salpha],
2489
+ op = alpha && alpha.enabled ? alpha.opacity : 100
2490
+ return (op / 100) + "" //确保返回的是字符串
2413
2491
  }
2414
- return target.replace(/[-_][^-_]/g, function(match) {
2415
- return match.charAt(1).toUpperCase()
2416
- })
2417
- }
2418
-
2419
- var fakeClassListMethods = {
2420
- _toString: function() {
2421
- var node = this.node
2422
- var cls = node.className
2423
- var str = typeof cls === "string" ? cls : cls.baseVal
2424
- return str.split(/\s+/).join(" ")
2425
- },
2426
- _contains: function(cls) {
2427
- return (" " + this + " ").indexOf(" " + cls + " ") > -1
2428
- },
2429
- _add: function(cls) {
2430
- if (!this.contains(cls)) {
2431
- this._set(this + " " + cls)
2432
- }
2433
- },
2434
- _remove: function(cls) {
2435
- this._set((" " + this + " ").replace(" " + cls + " ", " "))
2436
- },
2437
- __set: function(cls) {
2438
- cls = cls.trim()
2439
- var node = this.node
2440
- if (rsvg.test(node)) {
2441
- //SVG元素的className是一个对象 SVGAnimatedString { baseVal="", animVal=""},只能通过set/getAttribute操作
2442
- node.setAttribute("class", cls)
2443
- } else {
2444
- node.className = cls
2445
- }
2446
- } //toggle存在版本差异,因此不使用它
2447
2492
  }
2448
2493
 
2449
- function fakeClassList(node) {
2450
- if (!("classList" in node)) {
2451
- node.classList = {
2452
- node: node
2453
- }
2454
- for (var k in fakeClassListMethods) {
2455
- node.classList[k.slice(1)] = fakeClassListMethods[k]
2456
- }
2494
+ "top,left".replace(rword, function(name) {
2495
+ cssHooks[name + ":get"] = function(node) {
2496
+ var computed = cssHooks["@:get"](node, name)
2497
+ return /px$/.test(computed) ? computed :
2498
+ avalon(node).position()[name] + "px"
2457
2499
  }
2458
- return node.classList
2500
+ })
2501
+
2502
+ var cssShow = {
2503
+ position: "absolute",
2504
+ visibility: "hidden",
2505
+ display: "block"
2459
2506
  }
2460
2507
 
2508
+ var rdisplayswap = /^(none|table(?!-c[ea]).+)/
2461
2509
 
2462
- "add,remove".replace(rword, function(method) {
2463
- avalon.fn[method + "Class"] = function(cls) {
2464
- var el = this[0]
2465
- //https://developer.mozilla.org/zh-CN/docs/Mozilla/Firefox/Releases/26
2466
- if (cls && typeof cls === "string" && el && el.nodeType === 1) {
2467
- cls.replace(/\S+/g, function(c) {
2468
- fakeClassList(el)[method](c)
2469
- })
2510
+ function showHidden(node, array) {
2511
+ //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
2512
+ if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0
2513
+ if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) {
2514
+ var obj = {
2515
+ node: node
2516
+ }
2517
+ for (var name in cssShow) {
2518
+ obj[name] = node.style[name]
2519
+ node.style[name] = cssShow[name]
2520
+ }
2521
+ array.push(obj)
2522
+ }
2523
+ var parent = node.parentNode
2524
+ if (parent && parent.nodeType === 1) {
2525
+ showHidden(parent, array)
2526
+ }
2470
2527
  }
2471
- return this
2472
2528
  }
2473
- })
2474
- avalon.fn.mix({
2475
- hasClass: function(cls) {
2476
- var el = this[0] || {}
2477
- return el.nodeType === 1 && fakeClassList(el).contains(cls)
2478
- },
2479
- toggleClass: function(value, stateVal) {
2480
- var className, i = 0
2481
- var classNames = value.split(/\s+/)
2482
- var isBool = typeof stateVal === "boolean"
2483
- while ((className = classNames[i++])) {
2484
- var state = isBool ? stateVal : !this.hasClass(className)
2485
- this[state ? "addClass" : "removeClass"](className)
2486
- }
2487
- return this
2488
- },
2489
- attr: function(name, value) {
2490
- if (arguments.length === 2) {
2491
- this[0].setAttribute(name, value)
2492
- return this
2493
- } else {
2494
- return this[0].getAttribute(name)
2495
- }
2496
- },
2497
- data: function(name, value) {
2498
- name = "data-" + hyphen(name || "")
2499
- switch (arguments.length) {
2500
- case 2:
2501
- this.attr(name, value)
2502
- return this
2503
- case 1:
2504
- var val = this.attr(name)
2505
- return parseData(val)
2506
- case 0:
2507
- var ret = {}
2508
- ap.forEach.call(this[0].attributes, function(attr) {
2509
- if (attr) {
2510
- name = attr.name
2511
- if (!name.indexOf("data-")) {
2512
- name = camelize(name.slice(5))
2513
- ret[name] = parseData(attr.value)
2514
- }
2515
- }
2516
- })
2529
+ "Width,Height".replace(rword, function(name) { //fix 481
2530
+ var method = name.toLowerCase(),
2531
+ clientProp = "client" + name,
2532
+ scrollProp = "scroll" + name,
2533
+ offsetProp = "offset" + name
2534
+ cssHooks[method + ":get"] = function(node, which, override) {
2535
+ var boxSizing = -4
2536
+ if (typeof override === "number") {
2537
+ boxSizing = override
2538
+ }
2539
+ which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
2540
+ var ret = node[offsetProp] // border-box 0
2541
+ if (boxSizing === 2) { // margin-box 2
2542
+ return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
2543
+ }
2544
+ if (boxSizing < 0) { // padding-box -2
2545
+ ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
2546
+ }
2547
+ if (boxSizing === -4) { // content-box -4
2548
+ ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
2549
+ }
2517
2550
  return ret
2518
- }
2519
- },
2520
- removeData: function(name) {
2521
- name = "data-" + hyphen(name)
2522
- this[0].removeAttribute(name)
2523
- return this
2524
- },
2525
- css: function(name, value) {
2526
- if (avalon.isPlainObject(name)) {
2527
- for (var i in name) {
2528
- avalon.css(this, i, name[i])
2529
2551
  }
2530
- } else {
2531
- var ret = avalon.css(this, name, value)
2532
- }
2533
- return ret !== void 0 ? ret : this
2534
- },
2535
- position: function() {
2536
- var offsetParent, offset,
2537
- elem = this[0],
2538
- parentOffset = {
2539
- top: 0,
2540
- left: 0
2552
+ cssHooks[method + "&get"] = function(node) {
2553
+ var hidden = [];
2554
+ showHidden(node, hidden);
2555
+ var val = cssHooks[method + ":get"](node)
2556
+ for (var i = 0, obj; obj = hidden[i++];) {
2557
+ node = obj.node
2558
+ for (var n in obj) {
2559
+ if (typeof obj[n] === "string") {
2560
+ node.style[n] = obj[n]
2561
+ }
2541
2562
  }
2542
- if (!elem) {
2543
- return
2544
- }
2545
- if (this.css("position") === "fixed") {
2546
- offset = elem.getBoundingClientRect()
2547
- } else {
2548
- offsetParent = this.offsetParent() //得到真正的offsetParent
2549
- offset = this.offset() // 得到正确的offsetParent
2550
- if (offsetParent[0].tagName !== "HTML") {
2551
- parentOffset = offsetParent.offset()
2552
2563
  }
2553
- parentOffset.top += avalon.css(offsetParent[0], "borderTopWidth", true)
2554
- parentOffset.left += avalon.css(offsetParent[0], "borderLeftWidth", true)
2564
+ return val;
2555
2565
  }
2556
- return {
2557
- top: offset.top - parentOffset.top - avalon.css(elem, "marginTop", true),
2558
- left: offset.left - parentOffset.left - avalon.css(elem, "marginLeft", true)
2559
- }
2560
- },
2561
- offsetParent: function() {
2562
- var offsetParent = this[0].offsetParent
2563
- while (offsetParent && avalon.css(offsetParent, "position") === "static") {
2564
- offsetParent = offsetParent.offsetParent;
2566
+ avalon.fn[method] = function(value) { //会忽视其display
2567
+ var node = this[0]
2568
+ if (arguments.length === 0) {
2569
+ if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
2570
+ return node["inner" + name] || node.document.documentElement[clientProp]
2571
+ }
2572
+ if (node.nodeType === 9) { //取得页面尺寸
2573
+ var doc = node.documentElement
2574
+ //FF chrome html.scrollHeight< body.scrollHeight
2575
+ //IE 标准模式 : html.scrollHeight> body.scrollHeight
2576
+ //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
2577
+ return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp])
2578
+ }
2579
+ return cssHooks[method + "&get"](node)
2580
+ } else {
2581
+ return this.css(method, value)
2582
+ }
2565
2583
  }
2566
- return avalon(offsetParent || root)
2567
- },
2568
- bind: function(type, fn, phase) {
2569
- if (this[0]) { //此方法不会链
2570
- return avalon.bind(this[0], type, fn, phase)
2584
+ avalon.fn["inner" + name] = function() {
2585
+ return cssHooks[method + ":get"](this[0], void 0, -2)
2571
2586
  }
2572
- },
2573
- unbind: function(type, fn, phase) {
2574
- if (this[0]) {
2575
- avalon.unbind(this[0], type, fn, phase)
2587
+ avalon.fn["outer" + name] = function(includeMargin) {
2588
+ return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
2576
2589
  }
2577
- return this
2578
- },
2579
- val: function(value) {
2580
- var node = this[0]
2581
- if (node && node.nodeType === 1) {
2582
- var get = arguments.length === 0
2583
- var access = get ? ":get" : ":set"
2584
- var fn = valHooks[getValType(node) + access]
2585
- if (fn) {
2586
- var val = fn(node, value)
2587
- } else if (get) {
2588
- return (node.value || "").replace(/\r/g, "")
2589
- } else {
2590
- node.value = value
2591
- }
2592
- }
2593
- return get ? val : this
2594
- }
2595
- })
2596
-
2597
- function parseData(data) {
2598
- try {
2599
- if (typeof data === "object")
2600
- return data
2601
- data = data === "true" ? true :
2602
- data === "false" ? false :
2603
- data === "null" ? null : +data + "" === data ? +data : rbrace.test(data) ? avalon.parseJSON(data) : data
2604
- } catch (e) {
2605
- }
2606
- return data
2607
- }
2608
- var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
2609
- rvalidchars = /^[\],:{}\s]*$/,
2610
- rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
2611
- rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
2612
- rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d+\.|)\d+(?:[eE][+-]?\d+|)/g
2613
- avalon.parseJSON = window.JSON ? JSON.parse : function(data) {
2614
- if (typeof data === "string") {
2615
- data = data.trim();
2616
- if (data) {
2617
- if (rvalidchars.test(data.replace(rvalidescape, "@")
2618
- .replace(rvalidtokens, "]")
2619
- .replace(rvalidbraces, ""))) {
2620
- return (new Function("return " + data))()// jshint ignore:line
2621
- }
2622
- }
2623
- avalon.error("Invalid JSON: " + data)
2624
- }
2625
- return data
2626
- }
2627
-
2628
- //生成avalon.fn.scrollLeft, avalon.fn.scrollTop方法
2629
- avalon.each({
2630
- scrollLeft: "pageXOffset",
2631
- scrollTop: "pageYOffset"
2632
- }, function(method, prop) {
2633
- avalon.fn[method] = function(val) {
2634
- var node = this[0] || {}, win = getWindow(node),
2635
- top = method === "scrollTop"
2636
- if (!arguments.length) {
2637
- return win ? (prop in win) ? win[prop] : root[method] : node[method]
2638
- } else {
2639
- if (win) {
2640
- win.scrollTo(!top ? val : avalon(win).scrollLeft(), top ? val : avalon(win).scrollTop())
2641
- } else {
2642
- node[method] = val
2643
- }
2644
- }
2645
- }
2646
- })
2647
-
2648
- function getWindow(node) {
2649
- return node.window && node.document ? node : node.nodeType === 9 ? node.defaultView || node.parentWindow : false;
2650
- }
2651
- //=============================css相关=======================
2652
- var cssHooks = avalon.cssHooks = {}
2653
- var prefixes = ["", "-webkit-", "-o-", "-moz-", "-ms-"]
2654
- var cssMap = {
2655
- "float": W3C ? "cssFloat" : "styleFloat"
2656
- }
2657
- avalon.cssNumber = oneObject("columnCount,order,fillOpacity,fontWeight,lineHeight,opacity,orphans,widows,zIndex,zoom")
2658
-
2659
- avalon.cssName = function(name, host, camelCase) {
2660
- if (cssMap[name]) {
2661
- return cssMap[name]
2662
- }
2663
- host = host || root.style
2664
- for (var i = 0, n = prefixes.length; i < n; i++) {
2665
- camelCase = camelize(prefixes[i] + name)
2666
- if (camelCase in host) {
2667
- return (cssMap[name] = camelCase)
2668
- }
2669
- }
2670
- return null
2671
- }
2672
- cssHooks["@:set"] = function(node, name, value) {
2673
- try { //node.style.width = NaN;node.style.width = "xxxxxxx";node.style.width = undefine 在旧式IE下会抛异常
2674
- node.style[name] = value
2675
- } catch (e) {
2676
- }
2677
- }
2678
- if (window.getComputedStyle) {
2679
- cssHooks["@:get"] = function(node, name) {
2680
- if (!node || !node.style) {
2681
- throw new Error("getComputedStyle要求传入一个节点 " + node)
2682
- }
2683
- var ret, styles = getComputedStyle(node, null)
2684
- if (styles) {
2685
- ret = name === "filter" ? styles.getPropertyValue(name) : styles[name]
2686
- if (ret === "") {
2687
- ret = node.style[name] //其他浏览器需要我们手动取内联样式
2688
- }
2689
- }
2690
- return ret
2691
- }
2692
- cssHooks["opacity:get"] = function(node) {
2693
- var ret = cssHooks["@:get"](node, "opacity")
2694
- return ret === "" ? "1" : ret
2695
- }
2696
- } else {
2697
- var rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i
2698
- var rposition = /^(top|right|bottom|left)$/
2699
- var ralpha = /alpha\([^)]*\)/i
2700
- var ie8 = !!window.XDomainRequest
2701
- var salpha = "DXImageTransform.Microsoft.Alpha"
2702
- var border = {
2703
- thin: ie8 ? '1px' : '2px',
2704
- medium: ie8 ? '3px' : '4px',
2705
- thick: ie8 ? '5px' : '6px'
2706
- }
2707
- cssHooks["@:get"] = function(node, name) {
2708
- //取得精确值,不过它有可能是带em,pc,mm,pt,%等单位
2709
- var currentStyle = node.currentStyle
2710
- var ret = currentStyle[name]
2711
- if ((rnumnonpx.test(ret) && !rposition.test(ret))) {
2712
- //①,保存原有的style.left, runtimeStyle.left,
2713
- var style = node.style,
2714
- left = style.left,
2715
- rsLeft = node.runtimeStyle.left
2716
- //②由于③处的style.left = xxx会影响到currentStyle.left,
2717
- //因此把它currentStyle.left放到runtimeStyle.left,
2718
- //runtimeStyle.left拥有最高优先级,不会style.left影响
2719
- node.runtimeStyle.left = currentStyle.left
2720
- //③将精确值赋给到style.left,然后通过IE的另一个私有属性 style.pixelLeft
2721
- //得到单位为px的结果;fontSize的分支见http://bugs.jquery.com/ticket/760
2722
- style.left = name === 'fontSize' ? '1em' : (ret || 0)
2723
- ret = style.pixelLeft + "px"
2724
- //④还原 style.left,runtimeStyle.left
2725
- style.left = left
2726
- node.runtimeStyle.left = rsLeft
2727
- }
2728
- if (ret === "medium") {
2729
- name = name.replace("Width", "Style")
2730
- //border width 默认值为medium,即使其为0"
2731
- if (currentStyle[name] === "none") {
2732
- ret = "0px"
2733
- }
2734
- }
2735
- return ret === "" ? "auto" : border[ret] || ret
2736
- }
2737
- cssHooks["opacity:set"] = function(node, name, value) {
2738
- var style = node.style
2739
- var opacity = isFinite(value) && value <= 1 ? "alpha(opacity=" + value * 100 + ")" : ""
2740
- var filter = style.filter || "";
2741
- style.zoom = 1
2742
- //不能使用以下方式设置透明度
2743
- //node.filters.alpha.opacity = value * 100
2744
- style.filter = (ralpha.test(filter) ?
2745
- filter.replace(ralpha, opacity) :
2746
- filter + " " + opacity).trim()
2747
- if (!style.filter) {
2748
- style.removeAttribute("filter")
2749
- }
2750
- }
2751
- cssHooks["opacity:get"] = function(node) {
2752
- //这是最快的获取IE透明值的方式,不需要动用正则了!
2753
- var alpha = node.filters.alpha || node.filters[salpha],
2754
- op = alpha && alpha.enabled ? alpha.opacity : 100
2755
- return (op / 100) + "" //确保返回的是字符串
2756
- }
2757
- }
2758
-
2759
- "top,left".replace(rword, function(name) {
2760
- cssHooks[name + ":get"] = function(node) {
2761
- var computed = cssHooks["@:get"](node, name)
2762
- return /px$/.test(computed) ? computed :
2763
- avalon(node).position()[name] + "px"
2764
- }
2765
- })
2766
-
2767
- var cssShow = {
2768
- position: "absolute",
2769
- visibility: "hidden",
2770
- display: "block"
2771
- }
2772
-
2773
- var rdisplayswap = /^(none|table(?!-c[ea]).+)/
2774
-
2775
- function showHidden(node, array) {
2776
- //http://www.cnblogs.com/rubylouvre/archive/2012/10/27/2742529.html
2777
- if (node.offsetWidth <= 0) { //opera.offsetWidth可能小于0
2778
- if (rdisplayswap.test(cssHooks["@:get"](node, "display"))) {
2779
- var obj = {
2780
- node: node
2781
- }
2782
- for (var name in cssShow) {
2783
- obj[name] = node.style[name]
2784
- node.style[name] = cssShow[name]
2785
- }
2786
- array.push(obj)
2787
- }
2788
- var parent = node.parentNode
2789
- if (parent && parent.nodeType === 1) {
2790
- showHidden(parent, array)
2791
- }
2792
- }
2793
- }
2794
- "Width,Height".replace(rword, function(name) { //fix 481
2795
- var method = name.toLowerCase(),
2796
- clientProp = "client" + name,
2797
- scrollProp = "scroll" + name,
2798
- offsetProp = "offset" + name
2799
- cssHooks[method + ":get"] = function(node, which, override) {
2800
- var boxSizing = -4
2801
- if (typeof override === "number") {
2802
- boxSizing = override
2803
- }
2804
- which = name === "Width" ? ["Left", "Right"] : ["Top", "Bottom"]
2805
- var ret = node[offsetProp] // border-box 0
2806
- if (boxSizing === 2) { // margin-box 2
2807
- return ret + avalon.css(node, "margin" + which[0], true) + avalon.css(node, "margin" + which[1], true)
2808
- }
2809
- if (boxSizing < 0) { // padding-box -2
2810
- ret = ret - avalon.css(node, "border" + which[0] + "Width", true) - avalon.css(node, "border" + which[1] + "Width", true)
2811
- }
2812
- if (boxSizing === -4) { // content-box -4
2813
- ret = ret - avalon.css(node, "padding" + which[0], true) - avalon.css(node, "padding" + which[1], true)
2814
- }
2815
- return ret
2816
- }
2817
- cssHooks[method + "&get"] = function(node) {
2818
- var hidden = [];
2819
- showHidden(node, hidden);
2820
- var val = cssHooks[method + ":get"](node)
2821
- for (var i = 0, obj; obj = hidden[i++]; ) {
2822
- node = obj.node
2823
- for (var n in obj) {
2824
- if (typeof obj[n] === "string") {
2825
- node.style[n] = obj[n]
2826
- }
2827
- }
2828
- }
2829
- return val;
2830
- }
2831
- avalon.fn[method] = function(value) { //会忽视其display
2832
- var node = this[0]
2833
- if (arguments.length === 0) {
2834
- if (node.setTimeout) { //取得窗口尺寸,IE9后可以用node.innerWidth /innerHeight代替
2835
- return node["inner" + name] || node.document.documentElement[clientProp]
2836
- }
2837
- if (node.nodeType === 9) { //取得页面尺寸
2838
- var doc = node.documentElement
2839
- //FF chrome html.scrollHeight< body.scrollHeight
2840
- //IE 标准模式 : html.scrollHeight> body.scrollHeight
2841
- //IE 怪异模式 : html.scrollHeight 最大等于可视窗口多一点?
2842
- return Math.max(node.body[scrollProp], doc[scrollProp], node.body[offsetProp], doc[offsetProp], doc[clientProp])
2843
- }
2844
- return cssHooks[method + "&get"](node)
2845
- } else {
2846
- return this.css(method, value)
2847
- }
2848
- }
2849
- avalon.fn["inner" + name] = function() {
2850
- return cssHooks[method + ":get"](this[0], void 0, -2)
2851
- }
2852
- avalon.fn["outer" + name] = function(includeMargin) {
2853
- return cssHooks[method + ":get"](this[0], void 0, includeMargin === true ? 2 : 0)
2854
- }
2855
- })
2856
- avalon.fn.offset = function() { //取得距离页面左右角的坐标
2857
- var node = this[0],
2590
+ })
2591
+ avalon.fn.offset = function() { //取得距离页面左右角的坐标
2592
+ var node = this[0],
2858
2593
  box = {
2859
2594
  left: 0,
2860
2595
  top: 0
2861
2596
  }
2862
- if (!node || !node.tagName || !node.ownerDocument) {
2863
- return box
2864
- }
2865
- var doc = node.ownerDocument,
2597
+ if (!node || !node.tagName || !node.ownerDocument) {
2598
+ return box
2599
+ }
2600
+ var doc = node.ownerDocument,
2866
2601
  body = doc.body,
2867
2602
  root = doc.documentElement,
2868
2603
  win = doc.defaultView || doc.parentWindow
2869
- if (!avalon.contains(root, node)) {
2870
- return box
2871
- }
2872
- //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的
2873
- //我们可以通过getBoundingClientRect来获得元素相对于client的rect.
2874
- //http://msdn.microsoft.com/en-us/library/ms536433.aspx
2875
- if (node.getBoundingClientRect) {
2876
- box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone)
2877
- }
2878
- //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop
2879
- var clientTop = root.clientTop || body.clientTop,
2604
+ if (!avalon.contains(root, node)) {
2605
+ return box
2606
+ }
2607
+ //http://hkom.blog1.fc2.com/?mode=m&no=750 body的偏移量是不包含margin的
2608
+ //我们可以通过getBoundingClientRect来获得元素相对于client的rect.
2609
+ //http://msdn.microsoft.com/en-us/library/ms536433.aspx
2610
+ if (node.getBoundingClientRect) {
2611
+ box = node.getBoundingClientRect() // BlackBerry 5, iOS 3 (original iPhone)
2612
+ }
2613
+ //chrome/IE6: body.scrollTop, firefox/other: root.scrollTop
2614
+ var clientTop = root.clientTop || body.clientTop,
2880
2615
  clientLeft = root.clientLeft || body.clientLeft,
2881
2616
  scrollTop = Math.max(win.pageYOffset || 0, root.scrollTop, body.scrollTop),
2882
2617
  scrollLeft = Math.max(win.pageXOffset || 0, root.scrollLeft, body.scrollLeft)
2883
- // 把滚动距离加到left,top中去。
2884
- // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它
2885
- // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx
2886
- return {
2887
- top: box.top + scrollTop - clientTop,
2888
- left: box.left + scrollLeft - clientLeft
2618
+ // 把滚动距离加到left,top中去。
2619
+ // IE一些版本中会自动为HTML元素加上2px的border,我们需要去掉它
2620
+ // http://msdn.microsoft.com/en-us/library/ms533564(VS.85).aspx
2621
+ return {
2622
+ top: box.top + scrollTop - clientTop,
2623
+ left: box.left + scrollLeft - clientLeft
2624
+ }
2889
2625
  }
2890
- }
2891
2626
 
2892
- //==================================val相关============================
2627
+ //==================================val相关============================
2893
2628
 
2894
- function getValType(el) {
2895
- var ret = el.tagName.toLowerCase()
2896
- return ret === "input" && /checkbox|radio/.test(el.type) ? "checked" : ret
2897
- }
2629
+ function getValType(elem) {
2630
+ var ret = elem.tagName.toLowerCase()
2631
+ return ret === "input" && /checkbox|radio/.test(elem.type) ? "checked" : ret
2632
+ }
2898
2633
  var roption = /^<option(?:\s+\w+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s>]+))?)*\s+value[\s=]/i
2899
2634
  var valHooks = {
2900
2635
  "option:get": IEVersion ? function(node) {
@@ -2906,12 +2641,12 @@ var valHooks = {
2906
2641
  },
2907
2642
  "select:get": function(node, value) {
2908
2643
  var option, options = node.options,
2909
- index = node.selectedIndex,
2910
- getter = valHooks["option:get"],
2911
- one = node.type === "select-one" || index < 0,
2912
- values = one ? null : [],
2913
- max = one ? index + 1 : options.length,
2914
- i = index < 0 ? max : one ? index : 0
2644
+ index = node.selectedIndex,
2645
+ getter = valHooks["option:get"],
2646
+ one = node.type === "select-one" || index < 0,
2647
+ values = one ? null : [],
2648
+ max = one ? index + 1 : options.length,
2649
+ i = index < 0 ? max : one ? index : 0
2915
2650
  for (; i < max; i++) {
2916
2651
  option = options[i]
2917
2652
  //旧式IE在reset后不会改变selected,需要改用i === index判定
@@ -2931,7 +2666,7 @@ var valHooks = {
2931
2666
  "select:set": function(node, values, optionSet) {
2932
2667
  values = [].concat(values) //强制转换为数组
2933
2668
  var getter = valHooks["option:get"]
2934
- for (var i = 0, el; el = node.options[i++]; ) {
2669
+ for (var i = 0, el; el = node.options[i++];) {
2935
2670
  if ((el.selected = values.indexOf(getter(el)) > -1)) {
2936
2671
  optionSet = true
2937
2672
  }
@@ -2976,10 +2711,10 @@ var rsplit = /[^\w$]+/g
2976
2711
  var rkeywords = new RegExp(["\\b" + keywords.replace(/,/g, '\\b|\\b') + "\\b"].join('|'), 'g')
2977
2712
  var rnumber = /\b\d[^,]*/g
2978
2713
  var rcomma = /^,+|,+$/g
2979
- var cacheVars = new Cache(512)
2714
+ var variablePool = new Cache(512)
2980
2715
  var getVariables = function (code) {
2981
2716
  var key = "," + code.trim()
2982
- var ret = cacheVars.get(key)
2717
+ var ret = variablePool.get(key)
2983
2718
  if (ret) {
2984
2719
  return ret
2985
2720
  }
@@ -2990,7 +2725,7 @@ var getVariables = function (code) {
2990
2725
  .replace(rnumber, "")
2991
2726
  .replace(rcomma, "")
2992
2727
  .split(/^$|,+/)
2993
- return cacheVars.put(key, uniqSet(match))
2728
+ return variablePool.put(key, uniqSet(match))
2994
2729
  }
2995
2730
  /*添加赋值语句*/
2996
2731
 
@@ -3023,7 +2758,7 @@ function uniqSet(array) {
3023
2758
  return ret
3024
2759
  }
3025
2760
  //缓存求值函数,以便多次利用
3026
- var cacheExprs = new Cache(128)
2761
+ var evaluatorPool = new Cache(128)
3027
2762
  //取得求值函数及其传参
3028
2763
  var rduplex = /\w\[.*\]|\w\.\w/
3029
2764
  var rproxy = /(\$proxy\$[a-z]+)\d+$/
@@ -3047,7 +2782,7 @@ function parseFilter(val, filters) {
3047
2782
  .replace(rthimLeftParentheses, function () {
3048
2783
  return '",'
3049
2784
  }) + "]"
3050
- return "return avalon.filters.$filter(" + val + ", " + filters + ")"
2785
+ return "return this.filters.$filter(" + val + ", " + filters + ")"
3051
2786
  }
3052
2787
 
3053
2788
  function parseExpr(code, scopes, data) {
@@ -3079,9 +2814,11 @@ function parseExpr(code, scopes, data) {
3079
2814
  //https://github.com/RubyLouvre/avalon/issues/583
3080
2815
  data.vars.forEach(function (v) {
3081
2816
  var reg = new RegExp("\\b" + v + "(?:\\.\\w+|\\[\\w+\\])+", "ig")
3082
- code = code.replace(reg, function (_) {
2817
+ code = code.replace(reg, function (_, cap) {
3083
2818
  var c = _.charAt(v.length)
3084
- var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2819
+ //var r = IEVersion ? code.slice(arguments[1] + _.length) : RegExp.rightContext
2820
+ //https://github.com/RubyLouvre/avalon/issues/966
2821
+ var r = code.slice(cap + _.length)
3085
2822
  var method = /^\s*\(/.test(r)
3086
2823
  if (c === "." || c === "[" || method) {//比如v为aa,我们只匹配aa.bb,aa[cc],不匹配aaa.xxx
3087
2824
  var name = "var" + String(Math.random()).replace(/^0\./, "")
@@ -3106,7 +2843,8 @@ function parseExpr(code, scopes, data) {
3106
2843
  //---------------args----------------
3107
2844
  data.args = args
3108
2845
  //---------------cache----------------
3109
- var fn = cacheExprs.get(exprId) //直接从缓存,免得重复生成
2846
+ delete data.vars
2847
+ var fn = evaluatorPool.get(exprId) //直接从缓存,免得重复生成
3110
2848
  if (fn) {
3111
2849
  data.evaluator = fn
3112
2850
  return
@@ -3121,6 +2859,16 @@ function parseExpr(code, scopes, data) {
3121
2859
  }
3122
2860
  code = "\nvar ret" + expose + " = " + code + ";\r\n"
3123
2861
  code += parseFilter("ret" + expose, filters)
2862
+ try {
2863
+ fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
2864
+ data.evaluator = evaluatorPool.put(exprId, function() {
2865
+ return fn.apply(avalon, arguments)//确保可以在编译代码中使用this获取avalon对象
2866
+ })
2867
+ } catch (e) {
2868
+ log("debug: parse error," + e.message)
2869
+ }
2870
+ vars = assigns = names = null //释放内存
2871
+ return
3124
2872
  } else if (dataType === "duplex") { //双工绑定
3125
2873
  var _body = "'use strict';\nreturn function(vvv){\n\t" +
3126
2874
  prefix +
@@ -3130,10 +2878,11 @@ function parseExpr(code, scopes, data) {
3130
2878
  "= vvv;\n} "
3131
2879
  try {
3132
2880
  fn = Function.apply(noop, names.concat(_body))
3133
- data.evaluator = cacheExprs.put(exprId, fn)
2881
+ data.evaluator = evaluatorPool.put(exprId, fn)
3134
2882
  } catch (e) {
3135
2883
  log("debug: parse error," + e.message)
3136
2884
  }
2885
+ vars = assigns = names = null //释放内存
3137
2886
  return
3138
2887
  } else if (dataType === "on") { //事件绑定
3139
2888
  if (code.indexOf("(") === -1) {
@@ -3152,37 +2901,430 @@ function parseExpr(code, scopes, data) {
3152
2901
  }
3153
2902
  try {
3154
2903
  fn = Function.apply(noop, names.concat("'use strict';\n" + prefix + code))
3155
- data.evaluator = cacheExprs.put(exprId, fn)
2904
+ data.evaluator = evaluatorPool.put(exprId, fn)
3156
2905
  } catch (e) {
3157
2906
  log("debug: parse error," + e.message)
3158
- } finally {
3159
- vars = assigns = names = null //释放内存
3160
2907
  }
2908
+ vars = assigns = names = null //释放内存
3161
2909
  }
3162
-
3163
-
3164
- //parseExpr的智能引用代理
3165
-
3166
- function parseExprProxy(code, scopes, data, tokens, noregister) {
3167
- if (Array.isArray(tokens)) {
3168
- code = tokens.map(function (el) {
2910
+ function stringifyExpr(code) {
2911
+ var hasExpr = rexpr.test(code) //比如ms-class="width{{w}}"的情况
2912
+ if (hasExpr) {
2913
+ var array = scanExpr(code)
2914
+ if (array.length === 1) {
2915
+ return array[0].value
2916
+ }
2917
+ return array.map(function (el) {
3169
2918
  return el.expr ? "(" + el.value + ")" : quote(el.value)
3170
2919
  }).join(" + ")
2920
+ } else {
2921
+ return code
3171
2922
  }
2923
+ }
2924
+ //parseExpr的智能引用代理
2925
+
2926
+ function parseExprProxy(code, scopes, data, noRegister) {
2927
+ code = code || "" //code 可能未定义
3172
2928
  parseExpr(code, scopes, data)
3173
- if (data.evaluator && !noregister) {
2929
+ if (data.evaluator && !noRegister) {
3174
2930
  data.handler = bindingExecutors[data.handlerName || data.type]
3175
2931
  //方便调试
3176
2932
  //这里非常重要,我们通过判定视图刷新函数的element是否在DOM树决定
3177
2933
  //将它移出订阅者列表
3178
- registerSubscriber(data)
2934
+ avalon.injectBinding(data)
2935
+ }
2936
+ }
2937
+ avalon.parseExprProxy = parseExprProxy
2938
+ /*********************************************************************
2939
+ * 扫描系统 *
2940
+ **********************************************************************/
2941
+
2942
+ avalon.scan = function(elem, vmodel) {
2943
+ elem = elem || root
2944
+ var vmodels = vmodel ? [].concat(vmodel) : []
2945
+ scanTag(elem, vmodels)
2946
+ }
2947
+
2948
+ //http://www.w3.org/TR/html5/syntax.html#void-elements
2949
+ var stopScan = oneObject("area,base,basefont,br,col,command,embed,hr,img,input,link,meta,param,source,track,wbr,noscript,script,style,textarea".toUpperCase())
2950
+
2951
+ function checkScan(elem, callback, innerHTML) {
2952
+ var id = setTimeout(function() {
2953
+ var currHTML = elem.innerHTML
2954
+ clearTimeout(id)
2955
+ if (currHTML === innerHTML) {
2956
+ callback()
2957
+ } else {
2958
+ checkScan(elem, callback, currHTML)
2959
+ }
2960
+ })
2961
+ }
2962
+
2963
+
2964
+ function createSignalTower(elem, vmodel) {
2965
+ var id = elem.getAttribute("avalonctrl") || vmodel.$id
2966
+ elem.setAttribute("avalonctrl", id)
2967
+ vmodel.$events.expr = elem.tagName + '[avalonctrl="' + id + '"]'
2968
+ }
2969
+
2970
+ var getBindingCallback = function(elem, name, vmodels) {
2971
+ var callback = elem.getAttribute(name)
2972
+ if (callback) {
2973
+ for (var i = 0, vm; vm = vmodels[i++]; ) {
2974
+ if (vm.hasOwnProperty(callback) && typeof vm[callback] === "function") {
2975
+ return vm[callback]
2976
+ }
2977
+ }
2978
+ }
2979
+ }
2980
+
2981
+ function executeBindings(bindings, vmodels) {
2982
+ for (var i = 0, data; data = bindings[i++]; ) {
2983
+ data.vmodels = vmodels
2984
+ bindingHandlers[data.type](data, vmodels)
2985
+ if (data.evaluator && data.element && data.element.nodeType === 1) { //移除数据绑定,防止被二次解析
2986
+ //chrome使用removeAttributeNode移除不存在的特性节点时会报错 https://github.com/RubyLouvre/avalon/issues/99
2987
+ data.element.removeAttribute(data.name)
2988
+ }
2989
+ }
2990
+ bindings.length = 0
2991
+ }
2992
+
2993
+ //https://github.com/RubyLouvre/avalon/issues/636
2994
+ var mergeTextNodes = IEVersion && window.MutationObserver ? function (elem) {
2995
+ var node = elem.firstChild, text
2996
+ while (node) {
2997
+ var aaa = node.nextSibling
2998
+ if (node.nodeType === 3) {
2999
+ if (text) {
3000
+ text.nodeValue += node.nodeValue
3001
+ elem.removeChild(node)
3002
+ } else {
3003
+ text = node
3004
+ }
3005
+ } else {
3006
+ text = null
3007
+ }
3008
+ node = aaa
3009
+ }
3010
+ } : 0
3011
+ var roneTime = /^\s*::/
3012
+ var rmsAttr = /ms-(\w+)-?(.*)/
3013
+ var priorityMap = {
3014
+ "if": 10,
3015
+ "repeat": 90,
3016
+ "data": 100,
3017
+ "widget": 110,
3018
+ "each": 1400,
3019
+ "with": 1500,
3020
+ "duplex": 2000,
3021
+ "on": 3000
3022
+ }
3023
+
3024
+ var events = oneObject("animationend,blur,change,input,click,dblclick,focus,keydown,keypress,keyup,mousedown,mouseenter,mouseleave,mousemove,mouseout,mouseover,mouseup,scan,scroll,submit")
3025
+ var obsoleteAttrs = oneObject("value,title,alt,checked,selected,disabled,readonly,enabled")
3026
+ function bindingSorter(a, b) {
3027
+ return a.priority - b.priority
3028
+ }
3029
+
3030
+ function scanAttr(elem, vmodels, match) {
3031
+ var scanNode = true
3032
+ if (vmodels.length) {
3033
+ var attributes = getAttributes ? getAttributes(elem) : elem.attributes
3034
+ var bindings = []
3035
+ var fixAttrs = []
3036
+ var msData = {}
3037
+ var uniq = {}
3038
+ for (var i = 0, attr; attr = attributes[i++]; ) {
3039
+ if (attr.specified) {
3040
+ if (match = attr.name.match(rmsAttr)) {
3041
+ //如果是以指定前缀命名的
3042
+ var type = match[1]
3043
+ var param = match[2] || ""
3044
+ var value = attr.value
3045
+ var name = attr.name
3046
+ if (uniq[name]) {//IE8下ms-repeat,ms-with BUG
3047
+ continue
3048
+ }
3049
+ uniq[name] = 1
3050
+ if (events[type]) {
3051
+ param = type
3052
+ type = "on"
3053
+ } else if (obsoleteAttrs[type]) {
3054
+ if (type === "enabled") {//吃掉ms-enabled绑定,用ms-disabled代替
3055
+ log("warning!ms-enabled或ms-attr-enabled已经被废弃")
3056
+ type = "disabled"
3057
+ value = "!(" + value + ")"
3058
+ }
3059
+ param = type
3060
+ type = "attr"
3061
+ name = "ms-" + type + "-" + param
3062
+ fixAttrs.push([attr.name, name, value])
3063
+ }
3064
+ msData[name] = value
3065
+ if (typeof bindingHandlers[type] === "function") {
3066
+ var newValue = value.replace(roneTime, "")
3067
+ var oneTime = value !== newValue
3068
+ var binding = {
3069
+ type: type,
3070
+ param: param,
3071
+ element: elem,
3072
+ name: name,
3073
+ value: newValue,
3074
+ oneTime: oneTime,
3075
+ uuid: name + "-" + getUid(elem),
3076
+ //chrome与firefox下Number(param)得到的值不一样 #855
3077
+ priority: (priorityMap[type] || type.charCodeAt(0) * 10) + (Number(param.replace(/\D/g, "")) || 0)
3078
+ }
3079
+ if (type === "html" || type === "text") {
3080
+ var token = getToken(value)
3081
+ avalon.mix(binding, token)
3082
+ binding.filters = binding.filters.replace(rhasHtml, function () {
3083
+ binding.type = "html"
3084
+ binding.group = 1
3085
+ return ""
3086
+ })// jshint ignore:line
3087
+ } else if (type === "duplex") {
3088
+ var hasDuplex = name
3089
+ } else if (name === "ms-if-loop") {
3090
+ binding.priority += 100
3091
+ }
3092
+ bindings.push(binding)
3093
+ if (type === "widget") {
3094
+ elem.msData = elem.msData || msData
3095
+ }
3096
+ }
3097
+ }
3098
+ }
3099
+ }
3100
+ if (bindings.length) {
3101
+ bindings.sort(bindingSorter)
3102
+ fixAttrs.forEach(function (arr) {
3103
+ log("warning!请改用" + arr[1] + "代替" + arr[0] + "!")
3104
+ elem.removeAttribute(arr[0])
3105
+ elem.setAttribute(arr[1], arr[2])
3106
+ })
3107
+ //http://bugs.jquery.com/ticket/7071
3108
+ //在IE下对VML读取type属性,会让此元素所有属性都变成<Failed>
3109
+ if (hasDuplex && msData["ms-attr-value"] && !elem.scopeName && elem.type === "text") {
3110
+ log("warning!一个控件不能同时定义ms-attr-value与" + hasDuplex)
3111
+ }
3112
+ for (i = 0; binding = bindings[i]; i++) {
3113
+ type = binding.type
3114
+ if (rnoscanAttrBinding.test(type)) {
3115
+ return executeBindings(bindings.slice(0, i + 1), vmodels)
3116
+ } else if (scanNode) {
3117
+ scanNode = !rnoscanNodeBinding.test(type)
3118
+ }
3119
+ }
3120
+ executeBindings(bindings, vmodels)
3121
+ }
3122
+ }
3123
+ if (scanNode && !stopScan[elem.tagName] && rbind.test(elem.innerHTML.replace(rlt, "<").replace(rgt, ">"))) {
3124
+ mergeTextNodes && mergeTextNodes(elem)
3125
+ scanNodeList(elem, vmodels) //扫描子孙元素
3126
+ }
3127
+ }
3128
+ var rnoscanAttrBinding = /^if|widget|repeat$/
3129
+ var rnoscanNodeBinding = /^each|with|html|include$/
3130
+ //IE67下,在循环绑定中,一个节点如果是通过cloneNode得到,自定义属性的specified为false,无法进入里面的分支,
3131
+ //但如果我们去掉scanAttr中的attr.specified检测,一个元素会有80+个特性节点(因为它不区分固有属性与自定义属性),很容易卡死页面
3132
+ if (!W3C) {
3133
+ var attrPool = new Cache(512)
3134
+ var rattrs = /\s+(ms-[^=\s]+)(?:=("[^"]*"|'[^']*'|[^\s>]+))?/g,
3135
+ rquote = /^['"]/,
3136
+ rtag = /<\w+\b(?:(["'])[^"]*?(\1)|[^>])*>/i,
3137
+ ramp = /&amp;/g
3138
+ //IE6-8解析HTML5新标签,会将它分解两个元素节点与一个文本节点
3139
+ //<body><section>ddd</section></body>
3140
+ // window.onload = function() {
3141
+ // var body = document.body
3142
+ // for (var i = 0, el; el = body.children[i++]; ) {
3143
+ // avalon.log(el.outerHTML)
3144
+ // }
3145
+ // }
3146
+ //依次输出<SECTION>, </SECTION>
3147
+ var getAttributes = function (elem) {
3148
+ var html = elem.outerHTML
3149
+ //处理IE6-8解析HTML5新标签的情况,及<br>等半闭合标签outerHTML为空的情况
3150
+ if (html.slice(0, 2) === "</" || !html.trim()) {
3151
+ return []
3152
+ }
3153
+ var str = html.match(rtag)[0]
3154
+ var attributes = [],
3155
+ match,
3156
+ k, v
3157
+ var ret = attrPool.get(str)
3158
+ if (ret) {
3159
+ return ret
3160
+ }
3161
+ while (k = rattrs.exec(str)) {
3162
+ v = k[2]
3163
+ if (v) {
3164
+ v = (rquote.test(v) ? v.slice(1, -1) : v).replace(ramp, "&")
3165
+ }
3166
+ var name = k[1].toLowerCase()
3167
+ match = name.match(rmsAttr)
3168
+ var binding = {
3169
+ name: name,
3170
+ specified: true,
3171
+ value: v || ""
3172
+ }
3173
+ attributes.push(binding)
3174
+ }
3175
+ return attrPool.put(str, attributes)
3176
+ }
3177
+ }
3178
+
3179
+ function scanNodeList(parent, vmodels) {
3180
+ var nodes = avalon.slice(parent.childNodes)
3181
+ scanNodeArray(nodes, vmodels)
3182
+ }
3183
+
3184
+ function scanNodeArray(nodes, vmodels) {
3185
+ for (var i = 0, node; node = nodes[i++];) {
3186
+ switch (node.nodeType) {
3187
+ case 1:
3188
+ scanTag(node, vmodels) //扫描元素节点
3189
+ if (node.msCallback) {
3190
+ node.msCallback()
3191
+ node.msCallback = void 0
3192
+ }
3193
+ break
3194
+ case 3:
3195
+ if(rexpr.test(node.nodeValue)){
3196
+ scanText(node, vmodels, i) //扫描文本节点
3197
+ }
3198
+ break
3199
+ }
3200
+ }
3201
+ }
3202
+
3203
+
3204
+ function scanTag(elem, vmodels, node) {
3205
+ //扫描顺序 ms-skip(0) --> ms-important(1) --> ms-controller(2) --> ms-if(10) --> ms-repeat(100)
3206
+ //--> ms-if-loop(110) --> ms-attr(970) ...--> ms-each(1400)-->ms-with(1500)--〉ms-duplex(2000)垫后
3207
+ var a = elem.getAttribute("ms-skip")
3208
+ //#360 在旧式IE中 Object标签在引入Flash等资源时,可能出现没有getAttributeNode,innerHTML的情形
3209
+ if (!elem.getAttributeNode) {
3210
+ return log("warning " + elem.tagName + " no getAttributeNode method")
3211
+ }
3212
+ var b = elem.getAttributeNode("ms-important")
3213
+ var c = elem.getAttributeNode("ms-controller")
3214
+ if (typeof a === "string") {
3215
+ return
3216
+ } else if (node = b || c) {
3217
+ var newVmodel = avalon.vmodels[node.value]
3218
+ if (!newVmodel) {
3219
+ return
3220
+ }
3221
+ //ms-important不包含父VM,ms-controller相反
3222
+ vmodels = node === b ? [newVmodel] : [newVmodel].concat(vmodels)
3223
+ var name = node.name
3224
+ elem.removeAttribute(name) //removeAttributeNode不会刷新[ms-controller]样式规则
3225
+ avalon(elem).removeClass(name)
3226
+ createSignalTower(elem, newVmodel)
3227
+ }
3228
+ scanAttr(elem, vmodels) //扫描特性节点
3229
+ }
3230
+ var rhasHtml = /\|\s*html(?:\b|$)/,
3231
+ r11a = /\|\|/g,
3232
+ rlt = /&lt;/g,
3233
+ rgt = /&gt;/g,
3234
+ rstringLiteral = /(['"])(\\\1|.)+?\1/g
3235
+ function getToken(value) {
3236
+ if (value.indexOf("|") > 0) {
3237
+ var scapegoat = value.replace(rstringLiteral, function (_) {
3238
+ return Array(_.length + 1).join("1")// jshint ignore:line
3239
+ })
3240
+ var index = scapegoat.replace(r11a, "\u1122\u3344").indexOf("|") //干掉所有短路或
3241
+ if (index > -1) {
3242
+ return {
3243
+ filters: value.slice(index),
3244
+ value: value.slice(0, index),
3245
+ expr: true
3246
+ }
3247
+ }
3248
+ }
3249
+ return {
3250
+ value: value,
3251
+ filters: "",
3252
+ expr: true
3253
+ }
3254
+ }
3255
+
3256
+ function scanExpr(str) {
3257
+ var tokens = [],
3258
+ value, start = 0,
3259
+ stop
3260
+ do {
3261
+ stop = str.indexOf(openTag, start)
3262
+ if (stop === -1) {
3263
+ break
3264
+ }
3265
+ value = str.slice(start, stop)
3266
+ if (value) { // {{ 左边的文本
3267
+ tokens.push({
3268
+ value: value,
3269
+ filters: "",
3270
+ expr: false
3271
+ })
3272
+ }
3273
+ start = stop + openTag.length
3274
+ stop = str.indexOf(closeTag, start)
3275
+ if (stop === -1) {
3276
+ break
3277
+ }
3278
+ value = str.slice(start, stop)
3279
+ if (value) { //处理{{ }}插值表达式
3280
+ tokens.push(getToken(value, start))
3281
+ }
3282
+ start = stop + closeTag.length
3283
+ } while (1)
3284
+ value = str.slice(start)
3285
+ if (value) { //}} 右边的文本
3286
+ tokens.push({
3287
+ value: value,
3288
+ expr: false,
3289
+ filters: ""
3290
+ })
3291
+ }
3292
+ return tokens
3293
+ }
3294
+
3295
+ function scanText(textNode, vmodels, index) {
3296
+ var bindings = []
3297
+ tokens = scanExpr(textNode.data)
3298
+ if (tokens.length) {
3299
+ for (var i = 0; token = tokens[i++]; ) {
3300
+ var node = DOC.createTextNode(token.value) //将文本转换为文本节点,并替换原来的文本节点
3301
+ if (token.expr) {
3302
+ token.value = token.value.replace(roneTime, function () {
3303
+ token.oneTime = true
3304
+ return ""
3305
+ })// jshint ignore:line
3306
+ token.type = "text"
3307
+ token.element = node
3308
+ token.filters = token.filters.replace(rhasHtml, function (a, b,c) {
3309
+ token.type = "html"
3310
+ return ""
3311
+ })// jshint ignore:line
3312
+ token.pos = index * 1000 + i
3313
+ bindings.push(token) //收集带有插值表达式的文本
3314
+ }
3315
+ avalonFragment.appendChild(node)
3316
+ }
3317
+ textNode.parentNode.replaceChild(avalonFragment, textNode)
3318
+ if (bindings.length)
3319
+ executeBindings(bindings, vmodels)
3179
3320
  }
3180
3321
  }
3181
- avalon.parseExprProxy = parseExprProxy
3322
+
3182
3323
  var bools = ["autofocus,autoplay,async,allowTransparency,checked,controls",
3183
3324
  "declare,disabled,defer,defaultChecked,defaultSelected",
3184
3325
  "contentEditable,isMap,loop,multiple,noHref,noResize,noShade",
3185
- "open,readOnly,selected"].join(",")
3326
+ "open,readOnly,selected"
3327
+ ].join(",")
3186
3328
  var boolMap = {}
3187
3329
  bools.replace(rword, function (name) {
3188
3330
  boolMap[name.toLowerCase()] = name
@@ -3199,7 +3341,8 @@ var propMap = {//属性名映射
3199
3341
 
3200
3342
  var anomaly = ["accessKey,bgColor,cellPadding,cellSpacing,codeBase,codeType,colSpan",
3201
3343
  "dateTime,defaultValue,frameBorder,longDesc,maxLength,marginWidth,marginHeight",
3202
- "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"].join(",")
3344
+ "rowSpan,tabIndex,useMap,vSpace,valueType,vAlign"
3345
+ ].join(",")
3203
3346
  anomaly.replace(rword, function (name) {
3204
3347
  propMap[name.toLowerCase()] = name
3205
3348
  })
@@ -3208,21 +3351,13 @@ var rnoscripts = /<noscript.*?>(?:[\s\S]+?)<\/noscript>/img
3208
3351
  var rnoscriptText = /<noscript.*?>([\s\S]+?)<\/noscript>/im
3209
3352
 
3210
3353
  var getXHR = function () {
3211
- return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP")// jshint ignore:line
3354
+ return new (window.XMLHttpRequest || ActiveXObject)("Microsoft.XMLHTTP") // jshint ignore:line
3212
3355
  }
3213
3356
 
3214
- var cacheTmpls = avalon.templateCache = {}
3357
+ var templatePool = avalon.templateCache = {}
3215
3358
 
3216
3359
  bindingHandlers.attr = function (data, vmodels) {
3217
- var text = data.value.trim(),
3218
- simple = true
3219
- if (text.indexOf(openTag) > -1 && text.indexOf(closeTag) > 2) {
3220
- simple = false
3221
- if (rexpr.test(text) && RegExp.rightContext === "" && RegExp.leftContext === "") {
3222
- simple = true
3223
- text = RegExp.$1
3224
- }
3225
- }
3360
+ var value = stringifyExpr(data.value.trim())
3226
3361
  if (data.type === "include") {
3227
3362
  var elem = data.element
3228
3363
  data.includeRendered = getBindingCallback(elem, "data-include-rendered", vmodels)
@@ -3243,7 +3378,7 @@ bindingHandlers.attr = function (data, vmodels) {
3243
3378
  }
3244
3379
  }
3245
3380
  data.handlerName = "attr" //handleName用于处理多种绑定共用同一种bindingExecutor的情况
3246
- parseExprProxy(text, vmodels, data, (simple ? 0 : scanExpr(data.value)))
3381
+ parseExprProxy(value, vmodels, data)
3247
3382
  }
3248
3383
 
3249
3384
  bindingExecutors.attr = function (val, elem, data) {
@@ -3252,28 +3387,29 @@ bindingExecutors.attr = function (val, elem, data) {
3252
3387
  if (method === "css") {
3253
3388
  avalon(elem).css(attrName, val)
3254
3389
  } else if (method === "attr") {
3390
+
3255
3391
  // ms-attr-class="xxx" vm.xxx="aaa bbb ccc"将元素的className设置为aaa bbb ccc
3256
3392
  // ms-attr-class="xxx" vm.xxx=false 清空元素的所有类名
3257
3393
  // ms-attr-name="yyy" vm.yyy="ooo" 为元素设置name属性
3258
- if (boolMap[attrName]) {
3259
- var bool = boolMap[attrName]
3260
- if (typeof elem[bool] === "boolean") {
3261
- // IE6-11不支持动态设置fieldset的disabled属性,IE11下样式是生效了,但无法阻止用户对其底下的input元素进行设值……
3262
- return elem[bool] = !!val
3263
- }
3264
- }
3265
3394
  var toRemove = (val === false) || (val === null) || (val === void 0)
3266
3395
 
3267
3396
  if (!W3C && propMap[attrName]) { //旧式IE下需要进行名字映射
3268
3397
  attrName = propMap[attrName]
3269
3398
  }
3399
+ var bool = boolMap[attrName]
3400
+ if (typeof elem[bool] === "boolean") {
3401
+ elem[bool] = !!val //布尔属性必须使用el.xxx = true|false方式设值
3402
+ if (!val) { //如果为false, IE全系列下相当于setAttribute(xxx,''),会影响到样式,需要进一步处理
3403
+ toRemove = true
3404
+ }
3405
+ }
3270
3406
  if (toRemove) {
3271
3407
  return elem.removeAttribute(attrName)
3272
3408
  }
3273
3409
  //SVG只能使用setAttribute(xxx, yyy), VML只能使用elem.xxx = yyy ,HTML的固有属性必须elem.xxx = yyy
3274
3410
  var isInnate = rsvg.test(elem) ? false : (DOC.namespaces && isVML(elem)) ? true : attrName in elem.cloneNode(false)
3275
3411
  if (isInnate) {
3276
- elem[attrName] = val
3412
+ elem[attrName] = val + ""
3277
3413
  } else {
3278
3414
  elem.setAttribute(attrName, val)
3279
3415
  }
@@ -3285,7 +3421,9 @@ bindingExecutors.attr = function (val, elem, data) {
3285
3421
  var target = replace ? elem.parentNode : elem
3286
3422
  var scanTemplate = function (text) {
3287
3423
  if (loaded) {
3288
- text = loaded.apply(target, [text].concat(vmodels))
3424
+ var newText = loaded.apply(target, [text].concat(vmodels))
3425
+ if (typeof newText === "string")
3426
+ text = newText
3289
3427
  }
3290
3428
  if (rendered) {
3291
3429
  checkScan(target, function () {
@@ -3318,20 +3456,27 @@ bindingExecutors.attr = function (val, elem, data) {
3318
3456
  }
3319
3457
 
3320
3458
  if (data.param === "src") {
3321
- if (cacheTmpls[val]) {
3459
+ if (typeof templatePool[val] === "string") {
3322
3460
  avalon.nextTick(function () {
3323
- scanTemplate(cacheTmpls[val])
3461
+ scanTemplate(templatePool[val])
3324
3462
  })
3463
+ } else if (Array.isArray(templatePool[val])) { //#805 防止在循环绑定中发出许多相同的请求
3464
+ templatePool[val].push(scanTemplate)
3325
3465
  } else {
3326
3466
  var xhr = getXHR()
3327
3467
  xhr.onreadystatechange = function () {
3328
3468
  if (xhr.readyState === 4) {
3329
3469
  var s = xhr.status
3330
3470
  if (s >= 200 && s < 300 || s === 304 || s === 1223) {
3331
- scanTemplate(cacheTmpls[val] = xhr.responseText)
3471
+ var text = xhr.responseText
3472
+ for (var f = 0, fn; fn = templatePool[val][f++]; ) {
3473
+ fn(text)
3474
+ }
3475
+ templatePool[val] = text
3332
3476
  }
3333
3477
  }
3334
3478
  }
3479
+ templatePool[val] = [scanTemplate]
3335
3480
  xhr.open("GET", val, true)
3336
3481
  if ("withCredentials" in xhr) {
3337
3482
  xhr.withCredentials = true
@@ -3382,13 +3527,14 @@ bindingExecutors.attr = function (val, elem, data) {
3382
3527
  function getTemplateNodes(data, id, text) {
3383
3528
  var div = data.templateCache && data.templateCache[id]
3384
3529
  if (div) {
3385
- var dom = DOC.createDocumentFragment(), firstChild
3530
+ var dom = DOC.createDocumentFragment(),
3531
+ firstChild
3386
3532
  while (firstChild = div.firstChild) {
3387
3533
  dom.appendChild(firstChild)
3388
3534
  }
3389
3535
  return dom
3390
3536
  }
3391
- return avalon.parseHTML(text)
3537
+ return avalon.parseHTML(text)
3392
3538
  }
3393
3539
 
3394
3540
  //这几个指令都可以使用插值表达式,如ms-src="aaa/{{b}}/{{c}}.html"
@@ -3397,92 +3543,85 @@ function getTemplateNodes(data, id, text) {
3397
3543
  })
3398
3544
  //根据VM的属性值或表达式的值切换类名,ms-class="xxx yyy zzz:flag"
3399
3545
  //http://www.cnblogs.com/rubylouvre/archive/2012/12/17/2818540.html
3400
- bindingHandlers["class"] = function(data, vmodels) {
3401
- var oldStyle = data.param,
3402
- text = data.value,
3546
+ bindingHandlers["class"] = function (binding, vmodels) {
3547
+ var oldStyle = binding.param,
3548
+ text = binding.value,
3403
3549
  rightExpr
3404
- data.handlerName = "class"
3550
+ binding.handlerName = "class"
3405
3551
  if (!oldStyle || isFinite(oldStyle)) {
3406
- data.param = "" //去掉数字
3407
- var noExpr = text.replace(rexprg, function(a) {
3552
+ binding.param = "" //去掉数字
3553
+ var colonIndex = text.replace(rexprg, function (a) {
3408
3554
  return a.replace(/./g, "0")
3409
- //return Math.pow(10, a.length - 1) //将插值表达式插入10的N-1次方来占位
3410
- })
3411
- var colonIndex = noExpr.indexOf(":") //取得第一个冒号的位置
3555
+ }).indexOf(":") //取得第一个冒号的位置
3412
3556
  if (colonIndex === -1) { // 比如 ms-class="aaa bbb ccc" 的情况
3413
3557
  var className = text
3414
- } else { // 比如 ms-class-1="ui-state-active:checked" 的情况
3558
+ rightExpr = true
3559
+ } else { // 比如 ms-class-1="ui-state-active:checked" 的情况
3415
3560
  className = text.slice(0, colonIndex)
3416
3561
  rightExpr = text.slice(colonIndex + 1)
3417
- parseExpr(rightExpr, vmodels, data) //决定是添加还是删除
3418
- if (!data.evaluator) {
3419
- log("debug: ms-class '" + (rightExpr || "").trim() + "' 不存在于VM中")
3420
- return false
3421
- } else {
3422
- data._evaluator = data.evaluator
3423
- data._args = data.args
3424
- }
3425
3562
  }
3426
- var hasExpr = rexpr.test(className) //比如ms-class="width{{w}}"的情况
3427
- if (!hasExpr) {
3428
- data.immobileClass = className
3563
+ if (!rexpr.test(text)) {
3564
+ className = quote(className)
3565
+ } else {
3566
+ className = stringifyExpr(className)
3429
3567
  }
3430
- parseExprProxy("", vmodels, data, (hasExpr ? scanExpr(className) : 0))
3568
+ binding.expr = "[" + className + "," + rightExpr + "]"
3431
3569
  } else {
3432
- data.immobileClass = data.oldStyle = data.param
3433
- parseExprProxy(text, vmodels, data)
3570
+ binding.expr = '[' + quote(oldStyle) + "," + text + "]"
3571
+ binding.oldStyle = oldStyle
3572
+ }
3573
+ var method = binding.type
3574
+ if (method === "hover" || method === "active") { //确保只绑定一次
3575
+ if (!binding.hasBindEvent) {
3576
+ var elem = binding.element
3577
+ var $elem = avalon(elem)
3578
+ var activate = "mouseenter" //在移出移入时切换类名
3579
+ var abandon = "mouseleave"
3580
+ if (method === "active") { //在聚焦失焦中切换类名
3581
+ elem.tabIndex = elem.tabIndex || -1
3582
+ activate = "mousedown"
3583
+ abandon = "mouseup"
3584
+ var fn0 = $elem.bind("mouseleave", function () {
3585
+ binding.toggleClass && $elem.removeClass(binding.newClass)
3586
+ })
3587
+ }
3588
+ }
3589
+
3590
+ var fn1 = $elem.bind(activate, function () {
3591
+ binding.toggleClass && $elem.addClass(binding.newClass)
3592
+ })
3593
+ var fn2 = $elem.bind(abandon, function () {
3594
+ binding.toggleClass && $elem.removeClass(binding.newClass)
3595
+ })
3596
+ binding.rollback = function () {
3597
+ $elem.unbind("mouseleave", fn0)
3598
+ $elem.unbind(activate, fn1)
3599
+ $elem.unbind(abandon, fn2)
3600
+ }
3601
+ binding.hasBindEvent = true
3434
3602
  }
3603
+ parseExprProxy(binding.expr, vmodels, binding)
3435
3604
  }
3436
3605
 
3437
- bindingExecutors ["class"] = function(val, elem, data) {
3438
- var $elem = avalon(elem),
3439
- method = data.type
3440
- if (method === "class" && data.oldStyle) { //如果是旧风格
3441
- $elem.toggleClass(data.oldStyle, !!val)
3442
- } else {
3443
- //如果存在冒号就有求值函数
3444
- data.toggleClass = data._evaluator ? !!data._evaluator.apply(elem, data._args) : true
3445
- data.newClass = data.immobileClass || val
3446
- if (data.oldClass && data.newClass !== data.oldClass) {
3447
- $elem.removeClass(data.oldClass)
3448
- }
3449
- data.oldClass = data.newClass
3450
- switch (method) {
3451
- case "class":
3452
- $elem.toggleClass(data.newClass, data.toggleClass)
3453
- break
3454
- case "hover":
3455
- case "active":
3456
- if (!data.hasBindEvent) { //确保只绑定一次
3457
- var activate = "mouseenter" //在移出移入时切换类名
3458
- var abandon = "mouseleave"
3459
- if (method === "active") { //在聚焦失焦中切换类名
3460
- elem.tabIndex = elem.tabIndex || -1
3461
- activate = "mousedown"
3462
- abandon = "mouseup"
3463
- var fn0 = $elem.bind("mouseleave", function() {
3464
- data.toggleClass && $elem.removeClass(data.newClass)
3465
- })
3466
- }
3467
- var fn1 = $elem.bind(activate, function() {
3468
- data.toggleClass && $elem.addClass(data.newClass)
3469
- })
3470
- var fn2 = $elem.bind(abandon, function() {
3471
- data.toggleClass && $elem.removeClass(data.newClass)
3472
- })
3473
- data.rollback = function() {
3474
- $elem.unbind("mouseleave", fn0)
3475
- $elem.unbind(activate, fn1)
3476
- $elem.unbind(abandon, fn2)
3477
- }
3478
- data.hasBindEvent = true
3479
- }
3480
- break;
3606
+ bindingExecutors["class"] = function (arr, elem, binding) {
3607
+ var $elem = avalon(elem)
3608
+ binding.newClass = arr[0]
3609
+ binding.toggleClass = !!arr[1]
3610
+ if (binding.oldClass && binding.newClass !== binding.oldClass) {
3611
+ $elem.removeClass(binding.oldClass)
3612
+ }
3613
+ binding.oldClass = binding.newClass
3614
+ if (binding.type === "class") {
3615
+ if (binding.oldStyle) {
3616
+ $elem.toggleClass(binding.oldStyle, !!arr[1])
3617
+ } else {
3618
+ $elem.toggleClass(binding.newClass, binding.toggleClass)
3481
3619
  }
3482
3620
  }
3621
+
3483
3622
  }
3484
3623
 
3485
- "hover,active".replace(rword, function(method) {
3624
+ "hover,active".replace(rword, function (method) {
3486
3625
  bindingHandlers[method] = bindingHandlers["class"]
3487
3626
  })
3488
3627
  //ms-controller绑定已经在scanTag 方法中实现
@@ -3491,21 +3630,20 @@ bindingExecutors ["class"] = function(val, elem, data) {
3491
3630
 
3492
3631
  // bindingHandlers.data 定义在if.js
3493
3632
  bindingExecutors.data = function(val, elem, data) {
3494
- var key = "data-" + data.param
3495
- if (val && typeof val === "object") {
3496
- elem[key] = val
3497
- } else {
3498
- elem.setAttribute(key, String(val))
3499
- }
3633
+ var key = "data-" + data.param
3634
+ if (val && typeof val === "object") {
3635
+ elem[key] = val
3636
+ } else {
3637
+ elem.setAttribute(key, String(val))
3638
+ }
3500
3639
  }
3501
-
3502
3640
  //双工绑定
3503
- var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3641
+ var duplexBinding = bindingHandlers.duplex = function(data, vmodels) {
3504
3642
  var elem = data.element,
3505
- hasCast
3506
- parseExprProxy(data.value, vmodels, data, 0, 1)
3643
+ hasCast
3644
+ parseExprProxy(data.value, vmodels, data, 1)
3507
3645
 
3508
- data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
3646
+ data.changed = getBindingCallback(elem, "data-duplex-changed", vmodels) || noop
3509
3647
  if (data.evaluator && data.args) {
3510
3648
  var params = []
3511
3649
  var casting = oneObject("string,number,boolean,checked")
@@ -3515,7 +3653,7 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3515
3653
  if (elem.msData) {
3516
3654
  elem.msData["ms-duplex"] = data.value
3517
3655
  }
3518
- data.param.replace(/\w+/g, function (name) {
3656
+ data.param.replace(/\w+/g, function(name) {
3519
3657
  if (/^(checkbox|radio)$/.test(elem.type) && /^(radio|checked)$/.test(name)) {
3520
3658
  if (name === "radio")
3521
3659
  log("ms-duplex-radio已经更名为ms-duplex-checked")
@@ -3538,14 +3676,14 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3538
3676
  params.push("string")
3539
3677
  }
3540
3678
  data.param = params.join("-")
3541
- data.bound = function (type, callback) {
3679
+ data.bound = function(type, callback) {
3542
3680
  if (elem.addEventListener) {
3543
3681
  elem.addEventListener(type, callback, false)
3544
3682
  } else {
3545
3683
  elem.attachEvent("on" + type, callback)
3546
3684
  }
3547
3685
  var old = data.rollback
3548
- data.rollback = function () {
3686
+ data.rollback = function() {
3549
3687
  elem.avalonSetter = null
3550
3688
  avalon.unbind(elem, type, callback)
3551
3689
  old && old()
@@ -3562,29 +3700,30 @@ var duplexBinding = bindingHandlers.duplex = function (data, vmodels) {
3562
3700
  }
3563
3701
  }
3564
3702
  //不存在 bindingExecutors.duplex
3565
- function fixNull(val) {
3566
- return val == null ? "" : val
3567
- }
3703
+
3704
+ function fixNull(val) {
3705
+ return val == null ? "" : val
3706
+ }
3568
3707
  avalon.duplexHooks = {
3569
3708
  checked: {
3570
- get: function (val, data) {
3709
+ get: function(val, data) {
3571
3710
  return !data.element.oldValue
3572
3711
  }
3573
3712
  },
3574
3713
  string: {
3575
- get: function (val) { //同步到VM
3714
+ get: function(val) { //同步到VM
3576
3715
  return val
3577
3716
  },
3578
3717
  set: fixNull
3579
3718
  },
3580
3719
  "boolean": {
3581
- get: function (val) {
3720
+ get: function(val) {
3582
3721
  return val === "true"
3583
3722
  },
3584
3723
  set: fixNull
3585
3724
  },
3586
3725
  number: {
3587
- get: function (val, data) {
3726
+ get: function(val, data) {
3588
3727
  var number = parseFloat(val)
3589
3728
  if (-val === -number) {
3590
3729
  return number
@@ -3604,7 +3743,7 @@ avalon.duplexHooks = {
3604
3743
  }
3605
3744
 
3606
3745
  function pipe(val, data, action, e) {
3607
- data.param.replace(/\w+/g, function (name) {
3746
+ data.param.replace(/\w+/g, function(name) {
3608
3747
  var hook = avalon.duplexHooks[name]
3609
3748
  if (hook && typeof hook[action] === "function") {
3610
3749
  val = hook[action](val, data)
@@ -3615,44 +3754,41 @@ function pipe(val, data, action, e) {
3615
3754
 
3616
3755
  var TimerID, ribbon = []
3617
3756
 
3618
- avalon.tick = function (fn) {
3619
- if (ribbon.push(fn) === 1) {
3620
- TimerID = setInterval(ticker, 60)
3757
+ avalon.tick = function(fn) {
3758
+ if (ribbon.push(fn) === 1) {
3759
+ TimerID = setInterval(ticker, 60)
3760
+ }
3621
3761
  }
3622
- }
3623
3762
 
3624
- function ticker() {
3625
- for (var n = ribbon.length - 1; n >= 0; n--) {
3626
- var el = ribbon[n]
3627
- if (el() === false) {
3628
- ribbon.splice(n, 1)
3763
+ function ticker() {
3764
+ for (var n = ribbon.length - 1; n >= 0; n--) {
3765
+ var el = ribbon[n]
3766
+ if (el() === false) {
3767
+ ribbon.splice(n, 1)
3768
+ }
3769
+ }
3770
+ if (!ribbon.length) {
3771
+ clearInterval(TimerID)
3629
3772
  }
3630
3773
  }
3631
- if (!ribbon.length) {
3632
- clearInterval(TimerID)
3633
- }
3634
- }
3635
3774
 
3636
3775
  var watchValueInTimer = noop
3637
3776
  var rmsinput = /text|password|hidden/
3638
- new function () {// jshint ignore:line
3639
- try {//#272 IE9-IE11, firefox
3777
+ new function() { // jshint ignore:line
3778
+ try { //#272 IE9-IE11, firefox
3640
3779
  var setters = {}
3641
3780
  var aproto = HTMLInputElement.prototype
3642
3781
  var bproto = HTMLTextAreaElement.prototype
3643
- function newSetter(value) {// jshint ignore:line
3644
- if (avalon.contains(root, this)) {
3782
+ function newSetter(value) { // jshint ignore:line
3645
3783
  setters[this.tagName].call(this, value)
3646
- if (!rmsinput.test(this.type))
3647
- return
3648
- if (!this.msFocus && this.avalonSetter) {
3784
+ if (rmsinput.test(this.type) && !this.msFocus && this.avalonSetter) {
3649
3785
  this.avalonSetter()
3650
3786
  }
3651
- }
3652
3787
  }
3653
3788
  var inputProto = HTMLInputElement.prototype
3654
3789
  Object.getOwnPropertyNames(inputProto) //故意引发IE6-8等浏览器报错
3655
3790
  setters["INPUT"] = Object.getOwnPropertyDescriptor(aproto, "value").set
3791
+
3656
3792
  Object.defineProperty(aproto, "value", {
3657
3793
  set: newSetter
3658
3794
  })
@@ -3661,12 +3797,14 @@ new function () {// jshint ignore:line
3661
3797
  set: newSetter
3662
3798
  })
3663
3799
  } catch (e) {
3800
+ //在chrome 43中 ms-duplex终于不需要使用定时器实现双向绑定了
3801
+ // http://updates.html5rocks.com/2015/04/DOM-attributes-now-on-the-prototype
3802
+ // https://docs.google.com/document/d/1jwA8mtClwxI-QJuHT7872Z0pxpZz8PBkf2bGAbsUtqs/edit?pli=1
3664
3803
  watchValueInTimer = avalon.tick
3665
3804
  }
3666
- }// jshint ignore:line
3667
-
3805
+ } // jshint ignore:line
3668
3806
  if (IEVersion) {
3669
- avalon.bind(DOC, "selectionchange", function (e) {
3807
+ avalon.bind(DOC, "selectionchange", function(e) {
3670
3808
  var el = DOC.activeElement
3671
3809
  if (el && typeof el.avalonSetter === "function") {
3672
3810
  el.avalonSetter()
@@ -3675,61 +3813,56 @@ if (IEVersion) {
3675
3813
  }
3676
3814
 
3677
3815
  //处理radio, checkbox, text, textarea, password
3678
- duplexBinding.INPUT = function (element, evaluator, data) {
3816
+ duplexBinding.INPUT = function(element, evaluator, data) {
3679
3817
  var $type = element.type,
3680
- bound = data.bound,
3681
- $elem = avalon(element),
3682
- composing = false
3818
+ bound = data.bound,
3819
+ $elem = avalon(element),
3820
+ composing = false
3683
3821
 
3684
- function callback(value) {
3685
- data.changed.call(this, value, data)
3686
- }
3822
+ function callback(value) {
3823
+ data.changed.call(this, value, data)
3824
+ }
3687
3825
 
3688
- function compositionStart() {
3689
- composing = true
3690
- }
3826
+ function compositionStart() {
3827
+ composing = true
3828
+ }
3691
3829
 
3692
- function compositionEnd() {
3693
- composing = false
3694
- }
3695
- //当value变化时改变model的值
3696
- var updateVModel = function () {
3697
- if (composing) //处理中文输入法在minlengh下引发的BUG
3830
+ function compositionEnd() {
3831
+ composing = false
3832
+ }
3833
+ //当value变化时改变model的值
3834
+ var updateVModel = function() {
3835
+ var val = element.value //防止递归调用形成死循环
3836
+ if (composing || val === element.oldValue) //处理中文输入法在minlengh下引发的BUG
3698
3837
  return
3699
- var val = element.oldValue = element.value //防止递归调用形成死循环
3700
3838
  var lastValue = data.pipe(val, data, "get")
3701
- if ($elem.data("duplex-observe") !== false) {
3839
+ if ($elem.data("duplexObserve") !== false) {
3702
3840
  evaluator(lastValue)
3703
3841
  callback.call(element, lastValue)
3704
- if ($elem.data("duplex-focus")) {
3705
- avalon.nextTick(function () {
3706
- element.focus()
3707
- })
3708
- }
3709
3842
  }
3710
3843
  }
3711
3844
  //当model变化时,它就会改变value的值
3712
- data.handler = function () {
3713
- var val = data.pipe(evaluator(), data, "set") + ""//fix #673
3845
+ data.handler = function() {
3846
+ var val = data.pipe(evaluator(), data, "set") +"" //fix #673
3714
3847
  if (val !== element.oldValue) {
3715
- element.value = val
3848
+ element.value = element.oldValue = val
3716
3849
  }
3717
3850
  }
3718
3851
  if (data.isChecked || $type === "radio") {
3719
3852
  var IE6 = IEVersion === 6
3720
- updateVModel = function () {
3721
- if ($elem.data("duplex-observe") !== false) {
3853
+ updateVModel = function() {
3854
+ if ($elem.data("duplexObserve") !== false) {
3722
3855
  var lastValue = data.pipe(element.value, data, "get")
3723
3856
  evaluator(lastValue)
3724
3857
  callback.call(element, lastValue)
3725
3858
  }
3726
3859
  }
3727
- data.handler = function () {
3860
+ data.handler = function() {
3728
3861
  var val = evaluator()
3729
- var checked = data.isChecked ? !!val : val + "" === element.value
3862
+ var checked = data.isChecked ? !! val : val + "" === element.value
3730
3863
  element.oldValue = checked
3731
3864
  if (IE6) {
3732
- setTimeout(function () {
3865
+ setTimeout(function() {
3733
3866
  //IE8 checkbox, radio是使用defaultChecked控制选中状态,
3734
3867
  //并且要先设置defaultChecked后设置checked
3735
3868
  //并且必须设置延迟
@@ -3742,22 +3875,24 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3742
3875
  }
3743
3876
  bound("click", updateVModel)
3744
3877
  } else if ($type === "checkbox") {
3745
- updateVModel = function () {
3746
- if ($elem.data("duplex-observe") !== false) {
3878
+ updateVModel = function() {
3879
+ if ($elem.data("duplexObserve") !== false) {
3747
3880
  var method = element.checked ? "ensure" : "remove"
3748
3881
  var array = evaluator()
3749
3882
  if (!Array.isArray(array)) {
3750
3883
  log("ms-duplex应用于checkbox上要对应一个数组")
3751
3884
  array = [array]
3752
3885
  }
3753
- avalon.Array[method](array, data.pipe(element.value, data, "get"))
3886
+ var val = data.pipe(element.value, data, "get")
3887
+ avalon.Array[method](array, val)
3754
3888
  callback.call(element, array)
3755
3889
  }
3756
3890
  }
3757
3891
 
3758
- data.handler = function () {
3892
+ data.handler = function() {
3759
3893
  var array = [].concat(evaluator()) //强制转换为数组
3760
- element.checked = array.indexOf(data.pipe(element.value, data, "get")) > -1
3894
+ var val = data.pipe(element.value, data, "get")
3895
+ element.checked = array.indexOf(val) > -1
3761
3896
  }
3762
3897
  bound(W3C ? "change" : "click", updateVModel)
3763
3898
  } else {
@@ -3765,12 +3900,13 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3765
3900
  if (element.attributes["data-event"]) {
3766
3901
  log("data-event指令已经废弃,请改用data-duplex-event")
3767
3902
  }
3768
- function delay(e) {// jshint ignore:line
3769
- setTimeout(function () {
3903
+
3904
+ function delay(e) { // jshint ignore:line
3905
+ setTimeout(function() {
3770
3906
  updateVModel(e)
3771
3907
  })
3772
3908
  }
3773
- events.replace(rword, function (name) {
3909
+ events.replace(rword, function(name) {
3774
3910
  switch (name) {
3775
3911
  case "input":
3776
3912
  if (!IEVersion) { // W3C
@@ -3782,9 +3918,9 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3782
3918
  } else { //onpropertychange事件无法区分是程序触发还是用户触发
3783
3919
  // IE下通过selectionchange事件监听IE9+点击input右边的X的清空行为,及粘贴,剪切,删除行为
3784
3920
  if (IEVersion > 8) {
3785
- bound("input", updateVModel)//IE9使用propertychange无法监听中文输入改动
3921
+ bound("input", updateVModel) //IE9使用propertychange无法监听中文输入改动
3786
3922
  } else {
3787
- bound("propertychange", function (e) {//IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正
3923
+ bound("propertychange", function(e) { //IE6-8下第一次修改时不会触发,需要使用keydown或selectionchange修正
3788
3924
  if (e.propertyName === "value") {
3789
3925
  updateVModel()
3790
3926
  }
@@ -3800,15 +3936,15 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3800
3936
  break
3801
3937
  }
3802
3938
  })
3803
- bound("focus", function () {
3939
+ bound("focus", function() {
3804
3940
  element.msFocus = true
3805
3941
  })
3806
- bound("blur", function () {
3942
+ bound("blur", function() {
3807
3943
  element.msFocus = false
3808
3944
  })
3809
3945
 
3810
3946
  if (rmsinput.test($type)) {
3811
- watchValueInTimer(function () {
3947
+ watchValueInTimer(function() {
3812
3948
  if (root.contains(element)) {
3813
3949
  if (!element.msFocus && element.oldValue !== element.value) {
3814
3950
  updateVModel()
@@ -3819,34 +3955,33 @@ duplexBinding.INPUT = function (element, evaluator, data) {
3819
3955
  })
3820
3956
  }
3821
3957
 
3822
- element.avalonSetter = updateVModel//#765
3958
+ element.avalonSetter = updateVModel //#765
3823
3959
  }
3824
3960
 
3825
3961
  element.oldValue = element.value
3826
- registerSubscriber(data)
3962
+ avalon.injectBinding(data)
3827
3963
  callback.call(element, element.value)
3828
3964
  }
3829
3965
  duplexBinding.TEXTAREA = duplexBinding.INPUT
3830
-
3831
-
3832
3966
  duplexBinding.SELECT = function(element, evaluator, data) {
3833
3967
  var $elem = avalon(element)
3834
- function updateVModel() {
3835
- if ($elem.data("duplex-observe") !== false) {
3836
- var val = $elem.val() //字符串或字符串数组
3837
- if (Array.isArray(val)) {
3838
- val = val.map(function(v) {
3839
- return data.pipe(v, data, "get")
3840
- })
3841
- } else {
3842
- val = data.pipe(val, data, "get")
3843
- }
3844
- if (val + "" !== element.oldValue) {
3845
- evaluator(val)
3968
+
3969
+ function updateVModel() {
3970
+ if ($elem.data("duplexObserve") !== false) {
3971
+ var val = $elem.val() //字符串或字符串数组
3972
+ if (Array.isArray(val)) {
3973
+ val = val.map(function(v) {
3974
+ return data.pipe(v, data, "get")
3975
+ })
3976
+ } else {
3977
+ val = data.pipe(val, data, "get")
3978
+ }
3979
+ if (val + "" !== element.oldValue) {
3980
+ evaluator(val)
3981
+ }
3982
+ data.changed.call(element, val, data)
3846
3983
  }
3847
- data.changed.call(element, val, data)
3848
3984
  }
3849
- }
3850
3985
  data.handler = function() {
3851
3986
  var val = evaluator()
3852
3987
  val = val && val.$model || val
@@ -3867,74 +4002,77 @@ duplexBinding.SELECT = function(element, evaluator, data) {
3867
4002
  }
3868
4003
  }
3869
4004
  data.bound("change", updateVModel)
3870
- checkScan(element, function() {
3871
- registerSubscriber(data)
4005
+ element.msCallback = function() {
4006
+ avalon.injectBinding(data)
3872
4007
  data.changed.call(element, evaluator(), data)
3873
- }, NaN)
4008
+ }
3874
4009
  }
3875
-
3876
-
3877
4010
  // bindingHandlers.html 定义在if.js
3878
- bindingExecutors.html = function(val, elem, data) {
3879
- val = val == null ? "" : val
3880
- var isHtmlFilter = "group" in data
4011
+ bindingExecutors.html = function (val, elem, data) {
4012
+ var isHtmlFilter = elem.nodeType !== 1
3881
4013
  var parent = isHtmlFilter ? elem.parentNode : elem
3882
4014
  if (!parent)
3883
4015
  return
3884
- if (val.nodeType === 11) { //将val转换为文档碎片
3885
- var fragment = val
4016
+ val = val == null ? "" : val
4017
+ if (data.oldText !== val) {
4018
+ data.oldText = val
4019
+ } else {
4020
+ return
4021
+ }
4022
+ if (elem.nodeType === 3) {
4023
+ var signature = generateID("html")
4024
+ parent.insertBefore(DOC.createComment(signature), elem)
4025
+ data.element = DOC.createComment(signature + ":end")
4026
+ parent.replaceChild(data.element, elem)
4027
+ elem = data.element
4028
+ }
4029
+ if (typeof val !== "object") {//string, number, boolean
4030
+ var fragment = avalon.parseHTML(String(val))
4031
+ } else if (val.nodeType === 11) { //将val转换为文档碎片
4032
+ fragment = val
3886
4033
  } else if (val.nodeType === 1 || val.item) {
3887
- var nodes = val.nodeType === 1 ? val.childNodes : val.item ? val : []
3888
- fragment = hyperspace.cloneNode(true)
4034
+ var nodes = val.nodeType === 1 ? val.childNodes : val.item
4035
+ fragment = avalonFragment.cloneNode(true)
3889
4036
  while (nodes[0]) {
3890
4037
  fragment.appendChild(nodes[0])
3891
4038
  }
3892
- } else {
3893
- fragment = avalon.parseHTML(val)
3894
4039
  }
4040
+
4041
+ nodes = avalon.slice(fragment.childNodes)
3895
4042
  //插入占位符, 如果是过滤器,需要有节制地移除指定的数量,如果是html指令,直接清空
3896
- var comment = DOC.createComment("ms-html")
3897
4043
  if (isHtmlFilter) {
3898
- parent.insertBefore(comment, elem)
3899
- var n = data.group, i = 1
3900
- while (i < n) {
3901
- var node = elem.nextSibling
3902
- if (node) {
4044
+ var endValue = elem.nodeValue.slice(0, -4)
4045
+ while (true) {
4046
+ var node = elem.previousSibling
4047
+ if (!node || node.nodeType === 8 && node.nodeValue === endValue) {
4048
+ break
4049
+ } else {
3903
4050
  parent.removeChild(node)
3904
- i++
3905
4051
  }
3906
4052
  }
3907
- parent.removeChild(elem)
3908
- data.element = comment //防止被CG
4053
+ parent.insertBefore(fragment, elem)
3909
4054
  } else {
3910
- avalon.clearHTML(parent).appendChild(comment)
3911
- }
3912
- if (isHtmlFilter) {
3913
- data.group = fragment.childNodes.length || 1
3914
- }
3915
- nodes = avalon.slice(fragment.childNodes)
3916
- if (nodes[0]) {
3917
- if (comment.parentNode)
3918
- comment.parentNode.replaceChild(fragment, comment)
3919
- if (isHtmlFilter) {
3920
- data.element = nodes[0]
3921
- }
4055
+ avalon.clearHTML(elem).appendChild(fragment)
3922
4056
  }
3923
4057
  scanNodeArray(nodes, data.vmodels)
3924
4058
  }
3925
-
3926
4059
  bindingHandlers["if"] =
3927
- bindingHandlers.data =
3928
- bindingHandlers.text =
3929
- bindingHandlers.html =
3930
- function(data, vmodels) {
3931
- parseExprProxy(data.value, vmodels, data)
3932
- }
4060
+ bindingHandlers.data =
4061
+ bindingHandlers.text =
4062
+ bindingHandlers.html =
4063
+ function(data, vmodels) {
4064
+ parseExprProxy(data.value, vmodels, data)
4065
+ }
3933
4066
 
3934
4067
  bindingExecutors["if"] = function(val, elem, data) {
4068
+ try {
4069
+ if(!elem.parentNode) return
4070
+ } catch(e) {return}
3935
4071
  if (val) { //插回DOM树
3936
4072
  if (elem.nodeType === 8) {
3937
4073
  elem.parentNode.replaceChild(data.template, elem)
4074
+ elem.ifRemove = null
4075
+ // animate.enter(data.template, elem.parentNode)
3938
4076
  elem = data.element = data.template //这时可能为null
3939
4077
  }
3940
4078
  if (elem.getAttribute(data.name)) {
@@ -3946,6 +4084,8 @@ bindingExecutors["if"] = function(val, elem, data) {
3946
4084
  if (elem.nodeType === 1) {
3947
4085
  var node = data.element = DOC.createComment("ms-if")
3948
4086
  elem.parentNode.replaceChild(node, elem)
4087
+ elem.ifRemove = node
4088
+ // animate.leave(elem, node.parentNode, node)
3949
4089
  data.template = elem //元素节点
3950
4090
  ifGroup.appendChild(elem)
3951
4091
  data.rollback = function() {
@@ -3956,8 +4096,6 @@ bindingExecutors["if"] = function(val, elem, data) {
3956
4096
  }
3957
4097
  }
3958
4098
  }
3959
-
3960
-
3961
4099
  //ms-important绑定已经在scanTag 方法中实现
3962
4100
  //ms-include绑定已由ms-attr绑定实现
3963
4101
 
@@ -4001,11 +4139,9 @@ bindingExecutors.on = function(callback, elem, data) {
4001
4139
  }
4002
4140
  }
4003
4141
  }
4004
-
4005
-
4006
- bindingHandlers.repeat = function(data, vmodels) {
4142
+ bindingHandlers.repeat = function (data, vmodels) {
4007
4143
  var type = data.type
4008
- parseExprProxy(data.value, vmodels, data, 0, 1)
4144
+ parseExprProxy(data.value, vmodels, data, 1)
4009
4145
  data.proxies = []
4010
4146
  var freturn = false
4011
4147
  try {
@@ -4014,11 +4150,12 @@ bindingHandlers.repeat = function(data, vmodels) {
4014
4150
  if (xtype !== "object" && xtype !== "array") {
4015
4151
  freturn = true
4016
4152
  avalon.log("warning:" + data.value + "只能是对象或数组")
4153
+ } else {
4154
+ data.xtype = xtype
4017
4155
  }
4018
4156
  } catch (e) {
4019
4157
  freturn = true
4020
4158
  }
4021
-
4022
4159
  var arr = data.value.split(".") || []
4023
4160
  if (arr.length > 1) {
4024
4161
  arr.pop()
@@ -4032,41 +4169,43 @@ bindingHandlers.repeat = function(data, vmodels) {
4032
4169
  }
4033
4170
  }
4034
4171
  }
4172
+
4035
4173
  var elem = data.element
4036
- elem.removeAttribute(data.name)
4037
-
4038
- data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
4039
- data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
4040
- var signature = generateID(type)
4041
- var comment = data.element = DOC.createComment(signature + ":end")
4042
- data.clone = DOC.createComment(signature)
4043
- hyperspace.appendChild(comment)
4044
-
4045
- if (type === "each" || type === "with") {
4046
- data.template = elem.innerHTML.trim()
4047
- avalon.clearHTML(elem).appendChild(comment)
4048
- } else {
4049
- data.template = elem.outerHTML.trim()
4050
- elem.parentNode.replaceChild(comment, elem)
4051
- }
4052
- data.template = avalon.parseHTML(data.template)
4053
- data.rollback = function() {
4054
- var elem = data.element
4055
- if (!elem)
4056
- return
4057
- bindingExecutors.repeat.call(data, "clear")
4058
- var parentNode = elem.parentNode
4059
- var content = data.template
4060
- var target = content.firstChild
4061
- parentNode.replaceChild(content, elem)
4062
- var start = data.$stamp
4063
- start && start.parentNode && start.parentNode.removeChild(start)
4064
- target = data.element = data.type === "repeat" ? target : parentNode
4174
+ if (elem.nodeType === 1) {
4175
+ elem.removeAttribute(data.name)
4176
+ data.sortedCallback = getBindingCallback(elem, "data-with-sorted", vmodels)
4177
+ data.renderedCallback = getBindingCallback(elem, "data-" + type + "-rendered", vmodels)
4178
+ var signature = generateID(type)
4179
+ var start = DOC.createComment(signature)
4180
+ var end = DOC.createComment(signature + ":end")
4181
+ data.signature = signature
4182
+ data.template = avalonFragment.cloneNode(false)
4183
+ if (type === "repeat") {
4184
+ var parent = elem.parentNode
4185
+ parent.replaceChild(end, elem)
4186
+ parent.insertBefore(start, end)
4187
+ data.template.appendChild(elem)
4188
+ } else {
4189
+ while (elem.firstChild) {
4190
+ data.template.appendChild(elem.firstChild)
4191
+ }
4192
+ elem.appendChild(start)
4193
+ elem.appendChild(end)
4194
+ }
4195
+ data.element = end
4196
+ data.handler = bindingExecutors.repeat
4197
+ data.rollback = function () {
4198
+ var elem = data.element
4199
+ if (!elem)
4200
+ return
4201
+ data.handler("clear")
4202
+ }
4065
4203
  }
4204
+
4066
4205
  if (freturn) {
4067
4206
  return
4068
4207
  }
4069
- data.handler = bindingExecutors.repeat
4208
+
4070
4209
  data.$outer = {}
4071
4210
  var check0 = "$key"
4072
4211
  var check1 = "$val"
@@ -4074,6 +4213,7 @@ bindingHandlers.repeat = function(data, vmodels) {
4074
4213
  check0 = "$first"
4075
4214
  check1 = "$last"
4076
4215
  }
4216
+
4077
4217
  for (i = 0; v = vmodels[i++]; ) {
4078
4218
  if (v.hasOwnProperty(check0) && v.hasOwnProperty(check1)) {
4079
4219
  data.$outer = v
@@ -4082,81 +4222,101 @@ bindingHandlers.repeat = function(data, vmodels) {
4082
4222
  }
4083
4223
  var $events = $repeat.$events
4084
4224
  var $list = ($events || {})[subscribers]
4085
- if ($list && avalon.Array.ensure($list, data)) {
4086
- addSubscribers(data, $list)
4087
- }
4225
+ injectDependency($list, data)
4088
4226
  if (xtype === "object") {
4089
- data.$with = true
4090
- var pool = !$events ? {} : $events.$withProxyPool || ($events.$withProxyPool = {})
4091
- data.handler("append", $repeat, pool)
4227
+ data.handler("append")
4092
4228
  } else if ($repeat.length) {
4093
4229
  data.handler("add", 0, $repeat.length)
4094
4230
  }
4095
4231
  }
4096
4232
 
4097
- bindingExecutors.repeat = function(method, pos, el) {
4233
+ bindingExecutors.repeat = function (method, pos, el) {
4234
+ var data = this
4235
+ if (!method && data.xtype) {
4236
+ var old = data.$repeat
4237
+ var neo = data.evaluator.apply(0, data.args || [])
4238
+
4239
+ if (data.xtype === "array") {
4240
+ if (old.length === neo.length) {
4241
+ return
4242
+ }
4243
+ method = "add"
4244
+ pos = 0
4245
+ data.$repeat = neo
4246
+ el = neo.length
4247
+ } else {
4248
+ if (keysVM(old).join(";;") === keysVM(neo).join(";;")) {
4249
+ return
4250
+ }
4251
+ method = "append"
4252
+ data.$repeat = neo
4253
+ }
4254
+ }
4098
4255
  if (method) {
4099
- var data = this
4256
+ var start, fragment
4100
4257
  var end = data.element
4258
+ var comments = getComments(data)
4101
4259
  var parent = end.parentNode
4102
4260
  var proxies = data.proxies
4103
- var transation = hyperspace.cloneNode(false)
4261
+ var transation = avalonFragment.cloneNode(false)
4104
4262
  switch (method) {
4105
- case "add": //在pos位置后添加el数组(pos为数字,el为数组)
4263
+ case "add": //在pos位置后添加el数组(pos为插入位置,el为要插入的个数)
4106
4264
  var n = pos + el
4107
- var array = data.$repeat
4108
- var last = array.length - 1
4109
- var fragments = [], fragment
4110
- var start = locateNode(data, pos)
4265
+ var fragments = []
4111
4266
  for (var i = pos; i < n; i++) {
4112
4267
  var proxy = eachProxyAgent(i, data)
4113
4268
  proxies.splice(i, 0, proxy)
4114
4269
  shimController(data, transation, proxy, fragments)
4115
4270
  }
4116
- parent.insertBefore(transation, start)
4271
+ parent.insertBefore(transation, comments[pos] || end)
4117
4272
  for (i = 0; fragment = fragments[i++]; ) {
4118
4273
  scanNodeArray(fragment.nodes, fragment.vmodels)
4119
4274
  fragment.nodes = fragment.vmodels = null
4120
4275
  }
4276
+
4121
4277
  break
4122
4278
  case "del": //将pos后的el个元素删掉(pos, el都是数字)
4123
- start = proxies[pos].$stamp
4124
- end = locateNode(data, pos + el)
4125
- sweepNodes(start, end)
4279
+ sweepNodes(comments[pos], comments[pos + el] || end)
4126
4280
  var removed = proxies.splice(pos, el)
4127
4281
  recycleProxies(removed, "each")
4128
4282
  break
4129
4283
  case "clear":
4130
- var check = data.$stamp || proxies[0]
4131
- if (check) {
4132
- start = check.$stamp || check
4284
+ start = comments[0]
4285
+ if (start) {
4133
4286
  sweepNodes(start, end)
4287
+ if (data.xtype === "object") {
4288
+ parent.insertBefore(start, end)
4289
+ }else{
4290
+ recycleProxies(proxies, "each")
4291
+ }
4134
4292
  }
4135
- recycleProxies(proxies, "each")
4136
4293
  break
4137
4294
  case "move":
4138
- start = proxies[0].$stamp
4139
- var signature = start.nodeValue
4140
- var rooms = []
4141
- var room = [], node
4142
- sweepNodes(start, end, function() {
4143
- room.unshift(this)
4144
- if (this.nodeValue === signature) {
4145
- rooms.unshift(room)
4146
- room = []
4147
- }
4148
- })
4149
- sortByIndex(proxies, pos)
4150
- sortByIndex(rooms, pos)
4151
- while (room = rooms.shift()) {
4152
- while (node = room.shift()) {
4153
- transation.appendChild(node)
4295
+ start = comments[0]
4296
+ if (start) {
4297
+ var signature = start.nodeValue
4298
+ var rooms = []
4299
+ var room = [],
4300
+ node
4301
+ sweepNodes(start, end, function () {
4302
+ room.unshift(this)
4303
+ if (this.nodeValue === signature) {
4304
+ rooms.unshift(room)
4305
+ room = []
4306
+ }
4307
+ })
4308
+ sortByIndex(rooms, pos)
4309
+ sortByIndex(proxies, pos)
4310
+ while (room = rooms.shift()) {
4311
+ while (node = room.shift()) {
4312
+ transation.appendChild(node)
4313
+ }
4154
4314
  }
4315
+ parent.insertBefore(transation, end)
4155
4316
  }
4156
- parent.insertBefore(transation, end)
4157
4317
  break
4158
4318
  case "index": //将proxies中的第pos个起的所有元素重新索引
4159
- last = proxies.length - 1
4319
+ var last = proxies.length - 1
4160
4320
  for (; el = proxies[pos]; pos++) {
4161
4321
  el.$index = pos
4162
4322
  el.$first = pos === 0
@@ -4166,15 +4326,23 @@ bindingExecutors.repeat = function(method, pos, el) {
4166
4326
  case "set": //将proxies中的第pos个元素的VM设置为el(pos为数字,el任意)
4167
4327
  proxy = proxies[pos]
4168
4328
  if (proxy) {
4169
- notifySubscribers(proxy.$events.$index)
4329
+ fireDependencies(proxy.$events[data.param || "el"])
4170
4330
  }
4171
- return
4172
- case "append": //将pos的键值对从el中取出(pos为一个普通对象,el为预先生成好的代理VM对象池)
4173
- var pool = el
4331
+ break
4332
+ case "append":
4333
+ var object = data.$repeat //原来第2参数, 被循环对象
4334
+ var pool = Array.isArray(proxies) ||!proxies ? {}: proxies //代理对象组成的hash
4335
+ data.proxies = pool
4174
4336
  var keys = []
4175
4337
  fragments = []
4176
- for (var key in pos) { //得到所有键名
4177
- if (pos.hasOwnProperty(key) && key !== "hasOwnProperty") {
4338
+ for (var key in pool) {
4339
+ if (!object.hasOwnProperty(key)) {
4340
+ proxyRecycler(pool[key], withProxyPool) //去掉之前的代理VM
4341
+ delete(pool[key])
4342
+ }
4343
+ }
4344
+ for (key in object) { //得到所有键名
4345
+ if (object.hasOwnProperty(key) && key !== "hasOwnProperty") {
4178
4346
  keys.push(key)
4179
4347
  }
4180
4348
  }
@@ -4186,14 +4354,11 @@ bindingExecutors.repeat = function(method, pos, el) {
4186
4354
  }
4187
4355
  for (i = 0; key = keys[i++]; ) {
4188
4356
  if (key !== "hasOwnProperty") {
4189
- if (!pool[key]) {
4190
- pool[key] = withProxyAgent(key, data)
4191
- }
4357
+ pool[key] = withProxyAgent(pool[key], key, data)
4192
4358
  shimController(data, transation, pool[key], fragments)
4193
4359
  }
4194
4360
  }
4195
- var comment = data.$stamp = data.clone
4196
- parent.insertBefore(comment, end)
4361
+
4197
4362
  parent.insertBefore(transation, end)
4198
4363
  for (i = 0; fragment = fragments[i++]; ) {
4199
4364
  scanNodeArray(fragment.nodes, fragment.vmodels)
@@ -4201,29 +4366,26 @@ bindingExecutors.repeat = function(method, pos, el) {
4201
4366
  }
4202
4367
  break
4203
4368
  }
4369
+ if (!data.$repeat || data.$repeat.hasOwnProperty("$lock")) //IE6-8 VBScript对象会报错, 有时候data.$repeat不存在
4370
+ return
4204
4371
  if (method === "clear")
4205
4372
  method = "del"
4206
4373
  var callback = data.renderedCallback || noop,
4207
4374
  args = arguments
4208
- checkScan(parent, function() {
4209
- callback.apply(parent, args)
4210
- if (parent.oldValue && parent.tagName === "SELECT") { //fix #503
4211
- avalon(parent).val(parent.oldValue.split(","))
4212
- }
4213
- }, NaN)
4375
+ if (parent.oldValue && parent.tagName === "SELECT") { //fix #503
4376
+ avalon(parent).val(parent.oldValue.split(","))
4377
+ }
4378
+ callback.apply(parent, args)
4214
4379
  }
4215
4380
  }
4216
-
4217
- "with,each".replace(rword, function(name) {
4381
+ "with,each".replace(rword, function (name) {
4218
4382
  bindingHandlers[name] = bindingHandlers.repeat
4219
4383
  })
4220
4384
 
4221
4385
  function shimController(data, transation, proxy, fragments) {
4222
4386
  var content = data.template.cloneNode(true)
4223
4387
  var nodes = avalon.slice(content.childNodes)
4224
- if (proxy.$stamp) {
4225
- content.insertBefore(proxy.$stamp, content.firstChild)
4226
- }
4388
+ content.insertBefore(DOC.createComment(data.signature), content.firstChild)
4227
4389
  transation.appendChild(content)
4228
4390
  var nv = [proxy].concat(data.vmodels)
4229
4391
  var fragment = {
@@ -4233,11 +4395,21 @@ function shimController(data, transation, proxy, fragments) {
4233
4395
  fragments.push(fragment)
4234
4396
  }
4235
4397
 
4236
- function locateNode(data, pos) {
4237
- var proxy = data.proxies[pos]
4238
- return proxy ? proxy.$stamp : data.element
4398
+ function getComments(data) {
4399
+ var ret = []
4400
+ var nodes = data.element.parentNode.childNodes
4401
+ for (var i = 0, node; node = nodes[i++]; ) {
4402
+ if (node.nodeValue === data.signature) {
4403
+ ret.push(node)
4404
+ } else if (node.nodeValue === data.signature + ":end") {
4405
+ break
4406
+ }
4407
+ }
4408
+ return ret
4239
4409
  }
4240
4410
 
4411
+
4412
+ //移除掉start与end之间的节点(保留end)
4241
4413
  function sweepNodes(start, end, callback) {
4242
4414
  while (true) {
4243
4415
  var node = end.previousSibling
@@ -4254,24 +4426,89 @@ function sweepNodes(start, end, callback) {
4254
4426
  // 为ms-each,ms-with, ms-repeat会创建一个代理VM,
4255
4427
  // 通过它们保持一个下上文,让用户能调用$index,$first,$last,$remove,$key,$val,$outer等属性与方法
4256
4428
  // 所有代理VM的产生,消费,收集,存放通过xxxProxyFactory,xxxProxyAgent, recycleProxies,xxxProxyPool实现
4257
- var eachProxyPool = []
4258
4429
  var withProxyPool = []
4430
+ function withProxyFactory() {
4431
+ var proxy = modelFactory({
4432
+ $key: "",
4433
+ $outer: {},
4434
+ $host: {},
4435
+ $val: {
4436
+ get: function () {
4437
+ return this.$host[this.$key]
4438
+ },
4439
+ set: function (val) {
4440
+ this.$host[this.$key] = val
4441
+ }
4442
+ }
4443
+ }, {
4444
+ $val: 1
4445
+ })
4446
+ proxy.$id = generateID("$proxy$with")
4447
+ return proxy
4448
+ }
4449
+
4450
+ function withProxyAgent(proxy, key, data) {
4451
+ proxy = proxy || withProxyPool.pop()
4452
+ if (!proxy) {
4453
+ proxy = withProxyFactory()
4454
+ } else {
4455
+ proxy.$reinitialize()
4456
+ }
4457
+ var host = data.$repeat
4458
+ proxy.$key = key
4459
+
4460
+ proxy.$host = host
4461
+ proxy.$outer = data.$outer
4462
+ if (host.$events) {
4463
+ proxy.$events.$val = host.$events[key]
4464
+ } else {
4465
+ proxy.$events = {}
4466
+ }
4467
+ return proxy
4468
+ }
4469
+
4470
+
4471
+ function recycleProxies(proxies) {
4472
+ eachProxyRecycler(proxies)
4473
+ }
4474
+ function eachProxyRecycler(proxies) {
4475
+ proxies.forEach(function (proxy) {
4476
+ proxyRecycler(proxy, eachProxyPool)
4477
+ })
4478
+ proxies.length = 0
4479
+ }
4480
+
4481
+
4482
+ var eachProxyPool = []
4259
4483
  function eachProxyFactory(name) {
4260
4484
  var source = {
4261
4485
  $host: [],
4262
4486
  $outer: {},
4263
- $stamp: 1,
4264
4487
  $index: 0,
4265
4488
  $first: false,
4266
4489
  $last: false,
4267
4490
  $remove: avalon.noop
4268
4491
  }
4269
4492
  source[name] = {
4270
- get: function() {
4271
- return this.$host[this.$index]
4493
+ get: function () {
4494
+ var e = this.$events
4495
+ var array = e.$index
4496
+ e.$index = e[name] //#817 通过$index为el收集依赖
4497
+ try {
4498
+ return this.$host[this.$index]
4499
+ } finally {
4500
+ e.$index = array
4501
+ }
4272
4502
  },
4273
- set: function(val) {
4274
- this.$host.set(this.$index, val)
4503
+ set: function (val) {
4504
+ try {
4505
+ var e = this.$events
4506
+ var array = e.$index
4507
+ e.$index = []
4508
+ this.$host.set(this.$index, val)
4509
+ } finally {
4510
+ e.$index = array
4511
+ }
4275
4512
  }
4276
4513
  }
4277
4514
  var second = {
@@ -4280,14 +4517,13 @@ function eachProxyFactory(name) {
4280
4517
  $index: 1
4281
4518
  }
4282
4519
  var proxy = modelFactory(source, second)
4283
- var e = proxy.$events
4284
- e[name] = e.$first = e.$last = e.$index
4285
4520
  proxy.$id = generateID("$proxy$each")
4286
4521
  return proxy
4287
4522
  }
4288
4523
 
4289
4524
  function eachProxyAgent(index, data) {
4290
- var param = data.param || "el", proxy
4525
+ var param = data.param || "el",
4526
+ proxy
4291
4527
  for (var i = 0, n = eachProxyPool.length; i < n; i++) {
4292
4528
  var candidate = eachProxyPool[i]
4293
4529
  if (candidate && candidate.hasOwnProperty(param)) {
@@ -4305,76 +4541,30 @@ function eachProxyAgent(index, data) {
4305
4541
  proxy.$last = index === last
4306
4542
  proxy.$host = host
4307
4543
  proxy.$outer = data.$outer
4308
- proxy.$stamp = data.clone.cloneNode(false)
4309
- proxy.$remove = function() {
4544
+ proxy.$remove = function () {
4310
4545
  return host.removeAt(proxy.$index)
4311
4546
  }
4312
4547
  return proxy
4313
4548
  }
4314
4549
 
4315
- function withProxyFactory() {
4316
- var proxy = modelFactory({
4317
- $key: "",
4318
- $outer: {},
4319
- $host: {},
4320
- $val: {
4321
- get: function() {
4322
- return this.$host[this.$key]
4323
- },
4324
- set: function(val) {
4325
- this.$host[this.$key] = val
4326
- }
4327
- }
4328
- }, {
4329
- $val: 1
4330
- })
4331
- proxy.$id = generateID("$proxy$with")
4332
- return proxy
4333
- }
4334
4550
 
4335
- function withProxyAgent(key, data) {
4336
- var proxy = withProxyPool.pop()
4337
- if (!proxy) {
4338
- proxy = withProxyFactory()
4551
+ function proxyRecycler(proxy, proxyPool) {
4552
+ for (var i in proxy.$events) {
4553
+ var arr = proxy.$events[i]
4554
+ if (Array.isArray(arr)) {
4555
+ arr.forEach(function (data) {
4556
+ if (typeof data === "object")
4557
+ disposeData(data)
4558
+ })// jshint ignore:line
4559
+ arr.length = 0
4560
+ }
4339
4561
  }
4340
- var host = data.$repeat
4341
- proxy.$key = key
4342
- proxy.$host = host
4343
- proxy.$outer = data.$outer
4344
- if (host.$events) {
4345
- proxy.$events.$val = host.$events[key]
4346
- } else {
4347
- proxy.$events = {}
4562
+ proxy.$host = proxy.$outer = {}
4563
+ if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) {
4564
+ proxyPool.pop()
4348
4565
  }
4349
- return proxy
4350
- }
4351
-
4352
- function recycleProxies(proxies, type) {
4353
- var proxyPool = type === "each" ? eachProxyPool : withProxyPool
4354
- avalon.each(proxies, function(key, proxy) {
4355
- if (proxy.$events) {
4356
- for (var i in proxy.$events) {
4357
- if (Array.isArray(proxy.$events[i])) {
4358
- proxy.$events[i].forEach(function(data) {
4359
- if (typeof data === "object")
4360
- disposeData(data)
4361
- })// jshint ignore:line
4362
- proxy.$events[i].length = 0
4363
- }
4364
- }
4365
- proxy.$host = proxy.$outer = {}
4366
- if (proxyPool.unshift(proxy) > kernel.maxRepeatSize) {
4367
- proxyPool.pop()
4368
- }
4369
- }
4370
- })
4371
- if (type === "each")
4372
- proxies.length = 0
4373
4566
  }
4374
4567
 
4375
-
4376
-
4377
-
4378
4568
  /*********************************************************************
4379
4569
  * 各种指令 *
4380
4570
  **********************************************************************/
@@ -4385,8 +4575,7 @@ bindingExecutors.text = function(val, elem) {
4385
4575
  if (elem.nodeType === 3) { //绑定在文本节点上
4386
4576
  try { //IE对游离于DOM树外的节点赋值会报错
4387
4577
  elem.data = val
4388
- } catch (e) {
4389
- }
4578
+ } catch (e) {}
4390
4579
  } else { //绑定在特性节点上
4391
4580
  if ("textContent" in elem) {
4392
4581
  elem.textContent = val
@@ -4395,8 +4584,6 @@ bindingExecutors.text = function(val, elem) {
4395
4584
  }
4396
4585
  }
4397
4586
  }
4398
-
4399
-
4400
4587
  function parseDisplay(nodeName, val) {
4401
4588
  //用于取得此类标签的默认display值
4402
4589
  var key = "_" + nodeName
@@ -4416,42 +4603,33 @@ function parseDisplay(nodeName, val) {
4416
4603
 
4417
4604
  avalon.parseDisplay = parseDisplay
4418
4605
 
4419
- bindingHandlers.visible = function(data, vmodels) {
4420
- var elem = avalon(data.element)
4421
- var display = elem.css("display")
4422
- if (display === "none") {
4423
- var style = elem[0].style
4424
- var has = /visibility/i.test(style.cssText)
4425
- var visible = elem.css("visibility")
4426
- style.display = ""
4427
- style.visibility = "hidden"
4428
- display = elem.css("display")
4429
- if (display === "none") {
4430
- display = parseDisplay(elem[0].nodeName)
4431
- }
4432
- style.visibility = has ? visible : ""
4433
- }
4434
- data.display = display
4606
+ bindingHandlers.visible = function (data, vmodels) {
4435
4607
  parseExprProxy(data.value, vmodels, data)
4436
4608
  }
4437
4609
 
4438
- bindingExecutors.visible = function(val, elem, data) {
4439
- elem.style.display = val ? data.display : "none"
4610
+ bindingExecutors.visible = function (val, elem, binding) {
4611
+ if (val) {
4612
+ elem.style.display = binding.display || ""
4613
+ if (avalon(elem).css("display") === "none") {
4614
+ elem.style.display = binding.display = parseDisplay(elem.nodeName)
4615
+ }
4616
+ } else {
4617
+ elem.style.display = "none"
4618
+ }
4440
4619
  }
4441
-
4442
4620
  bindingHandlers.widget = function(data, vmodels) {
4443
4621
  var args = data.value.match(rword)
4444
4622
  var elem = data.element
4445
4623
  var widget = args[0]
4446
4624
  var id = args[1]
4447
- if (!id || id === "$") {//没有定义或为$时,取组件名+随机数
4625
+ if (!id || id === "$") { //没有定义或为$时,取组件名+随机数
4448
4626
  id = generateID(widget)
4449
4627
  }
4450
- var optName = args[2] || widget//没有定义,取组件名
4628
+ var optName = args[2] || widget //没有定义,取组件名
4451
4629
  var constructor = avalon.ui[widget]
4452
4630
  if (typeof constructor === "function") { //ms-widget="tabs,tabsAAA,optname"
4453
4631
  vmodels = elem.vmodels || vmodels
4454
- for (var i = 0, v; v = vmodels[i++]; ) {
4632
+ for (var i = 0, v; v = vmodels[i++];) {
4455
4633
  if (v.hasOwnProperty(optName) && typeof v[optName] === "object") {
4456
4634
  var vmOptions = v[optName]
4457
4635
  vmOptions = vmOptions.$model || vmOptions
@@ -4461,6 +4639,7 @@ bindingHandlers.widget = function(data, vmodels) {
4461
4639
  if (vmOptions) {
4462
4640
  var wid = vmOptions[widget + "Id"]
4463
4641
  if (typeof wid === "string") {
4642
+ log("warning!不再支持" + widget + "Id")
4464
4643
  id = wid
4465
4644
  }
4466
4645
  }
@@ -4476,27 +4655,26 @@ bindingHandlers.widget = function(data, vmodels) {
4476
4655
  if (vmodel.$id) {
4477
4656
  avalon.vmodels[id] = vmodel
4478
4657
  createSignalTower(elem, vmodel)
4479
- if (vmodel.hasOwnProperty("$init")) {
4658
+ try {
4480
4659
  vmodel.$init(function() {
4481
4660
  avalon.scan(elem, [vmodel].concat(vmodels))
4482
4661
  if (typeof options.onInit === "function") {
4483
4662
  options.onInit.call(elem, vmodel, options, vmodels)
4484
4663
  }
4485
4664
  })
4486
- }
4665
+ } catch (e) {}
4487
4666
  data.rollback = function() {
4488
4667
  try {
4489
- vmodel.widgetElement = null
4490
4668
  vmodel.$remove()
4491
- } catch (e) {
4492
- }
4669
+ vmodel.widgetElement = null // 放到$remove后边
4670
+ } catch (e) {}
4493
4671
  elem.msData = {}
4494
4672
  delete avalon.vmodels[vmodel.$id]
4495
4673
  }
4496
- addSubscribers(data, widgetList)
4674
+ injectDisposeQueue(data, widgetList)
4497
4675
  if (window.chrome) {
4498
4676
  elem.addEventListener("DOMNodeRemovedFromDocument", function() {
4499
- setTimeout(removeSubscribers)
4677
+ setTimeout(rejectDisposeQueue)
4500
4678
  })
4501
4679
  }
4502
4680
  } else {
@@ -4566,7 +4744,7 @@ var filters = avalon.filters = {
4566
4744
  truncate: function(str, length, truncation) {
4567
4745
  //length,新字符串长度,truncation,新字符串的结尾的字段,返回新字符串
4568
4746
  length = length || 30
4569
- truncation = truncation === void(0) ? "..." : truncation
4747
+ truncation = typeof truncation === "string" ? truncation : "..."
4570
4748
  return str.length > length ? str.slice(0, length - truncation.length) + truncation : String(str)
4571
4749
  },
4572
4750
  $filter: function(val) {
@@ -4859,33 +5037,33 @@ new function() {// jshint ignore:line
4859
5037
  /*********************************************************************
4860
5038
  * END *
4861
5039
  **********************************************************************/
4862
- new function() {
5040
+ new function () {
4863
5041
  avalon.config({
4864
5042
  loader: false
4865
5043
  })
4866
- var fns = [], fn, loaded
5044
+ var fns = [], loaded = DOC.readyState === "complete", fn
4867
5045
  function flush(f) {
4868
5046
  loaded = 1
4869
5047
  while (f = fns.shift())
4870
5048
  f()
4871
5049
  }
4872
- if (W3C) {
4873
- avalon.bind(DOC, "DOMContentLoaded", fn = function() {
4874
- avalon.unbind(DOC, "DOMContentLoaded", fn)
5050
+
5051
+ avalon.bind(DOC, "DOMContentLoaded", fn = function () {
5052
+ avalon.unbind(DOC, "DOMContentLoaded", fn)
5053
+ flush()
5054
+ })
5055
+
5056
+ var id = setInterval(function () {
5057
+ if (document.readyState === "complete" && document.body) {
5058
+ clearInterval(id)
4875
5059
  flush()
4876
- })
4877
- } else {
4878
- var id = setInterval(function() {
4879
- if (document.readyState === "complete" && document.body) {
4880
- clearInterval(id)
4881
- flush()
4882
- }
4883
- }, 50)
4884
- }
4885
- avalon.ready = function(fn) {
5060
+ }
5061
+ }, 50)
5062
+
5063
+ avalon.ready = function (fn) {
4886
5064
  loaded ? fn(avalon) : fns.push(fn)
4887
5065
  }
4888
- avalon.ready(function() {
5066
+ avalon.ready(function () {
4889
5067
  avalon.scan(DOC.body)
4890
5068
  })
4891
5069
  }
@@ -4921,6 +5099,7 @@ new function() {
4921
5099
  if (noGlobal === void 0) {
4922
5100
  window.avalon = avalon
4923
5101
  }
5102
+
4924
5103
  return avalon
4925
5104
 
4926
5105
  }));