avalon-rails 1.4.1.1.20150404164109 → 1.4.6.0.20150915133100

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,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
  }));