forked from ecomfe/saber-firework
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathView.js
411 lines (364 loc) · 9.67 KB
/
View.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
/**
* @file View
* @author treelite([email protected])
*/
define(function (require) {
var inherits = require('saber-lang/inherits');
var dom = require('saber-dom');
var etpl = require('etpl');
var widget = require('saber-widget');
var router = require('saber-router');
var eventHelper = require('./event');
var globalConfig = require('./config');
var Abstract = require('./Abstract');
/**
* 代理DOM事件KEY
*
* @const
* @type{string}
*/
var KEY_DELEGATE = '__delegate__';
/*
* 代理DOM事件
* 调整this指针
*
* @inner
* @param {View} view
* @param {function} fn
* @return {function}
*/
function delegateDomEvent(view, fn) {
return fn[KEY_DELEGATE] = function (e) {
return fn.call(view, this, e);
};
}
/**
* 绑定DOM事件
*
* @inner
* @param {View} view
*/
function bindDomEvents(view) {
var type;
var selector;
var fn;
var events = view.domEvents || {};
Object.keys(events).forEach(function (name) {
fn = events[name];
name = name.split(':');
type = name[0].trim();
selector = name[1] ? name[1].trim() : undefined;
view.addDomEvent(view.main, type, selector, fn);
});
}
/**
* 编译模版
*
* @public
* @param {View} view
* @param {string|Array.<string>} str 模版
*/
function compileTemplate(view, str) {
if (!Array.isArray(str)) {
str = [str];
}
// 添加全局的模版
str = str.concat(globalConfig.template || []);
str = str.join('');
// 新建模版引擎
var tplEngine = new etpl.Engine();
// 拷贝etpl命名空间的filter、配置
tplEngine.options = etpl.options;
tplEngine.filters = etpl.filters;
// 保存默认render
var defaultRender = tplEngine.compile(str);
// 保存原始的render
var orgRender = tplEngine.render;
view.template = tplEngine;
// 重载render以支持无target的情况
view.template.render = function (name, data) {
var res = '';
// 如果只有一个参数 或者target为null
// 则使用默认render
if (arguments.length < 2 || !name) {
res = defaultRender(name || data);
}
else {
res = orgRender.call(this, name, data);
}
return res;
};
}
/**
* View
*
* @constructor
* @param {Object} options 配置信息
* @param {string|Array.<string>} options.template 模版字符串
* @param {string=} options.templateMainTarget 模版主target 用于初始化视图
* @param {string=} options.className 容器元素附加className
* @param {Object=} events view事件
* @param {Object=} domEvents DOM事件
*/
function View(options) {
options = options || {};
Abstract.call(this, options);
this.init();
// 修改原始配置项
// 只在第一次加载view的时候才编译模版
options.template = this.template;
}
inherits(View, Abstract);
/**
* 初始化
*
* @public
*/
View.prototype.init = function () {
this.template = this.template || '';
// 如果是字符串或者数组
// 则表示模版还未编译
if (Array.isArray(this.template)
|| typeof this.template === 'string'
) {
compileTemplate(this, this.template);
}
// 绑定了事件的DOM元素集合
// 用于View销毁时卸载事件绑定
this.bindElements = [];
Abstract.prototype.init.call(this);
};
/**
* 设置容器元素
*
* @public
* @param {HTMLElement} ele 视图容器元素
*/
View.prototype.setMain = function (ele) {
this.main = ele;
};
/**
* 渲染视图
*
* @public
* @param {Object} data
*
* @fires View#beforerender
* View#afterrender
*/
View.prototype.render = function (data) {
if (!this.main) {
return;
}
if (this.className) {
this.main.className += ' ' + this.className;
}
/**
* 渲染前事件
*
* @event
* @param {Object} 渲染数据
*/
this.emit('beforerender', data);
this.main.innerHTML =
this.template.render(this.templateMainTarget, data);
/**
* 渲染后事件
*
* @event
* @param {Object} 渲染数据
*/
this.emit('afterrender', data);
};
/**
* 视图就绪
* 主要进行事件绑定
*
* @public
* @fires View#ready
*/
View.prototype.ready = function () {
bindDomEvents(this);
/**
* 试图就绪事件
*
* @event
*/
this.emit('ready');
};
/**
* 选取视图中的DOM元素
*
* @public
* @param {string} selector 选择器
* @param {HTMLElement=} context 上下文
* @return {HTMLElement|Array.<HTMLElement>}
*/
View.prototype.query = function (selector, context) {
context = context || this.main || document.body;
return dom.query(selector, context);
};
/**
* 选取视图中的DOM元素
*
* @public
* @param {string} selector 选择器
* @param {HTMLElement=} context 上下文
* @return {HTMLElement|Array.<HTMLElement>}
*/
View.prototype.queryAll = function (selector, context) {
context = context || this.main || document.body;
return dom.queryAll(selector, context);
};
/**
* 页面跳转
*
* @public
* @param {string} url 跳转地址
* @param {Object=} query 查询条件
* @param {Object=} options 跳转参数
* @param {boolean} options.force 强制跳转(url相同时)
* @param {boolean} options.noCache 不使用缓存的action
*/
View.prototype.redirect = function (url, query, options) {
router.redirect(url, query, options);
};
/**
* Superseded by `addDomEvent`
*
* 绑定DOM事件
* 会对进行绑定的DOM元素进行管理,方便自动卸载
*
* @public
* @param {HTMLElement} ele
* @param {string} type 事件类型
* @param {string=} selector 子元素选择器
* @param {function} fn 事件处理函数
*/
View.prototype.attachEvent = function (ele, type, selector, fn) {
if (this.bindElements.indexOf(ele) < 0) {
this.bindElements.push(ele);
}
eventHelper.on(ele, type, selector, fn);
};
/**
* Superseded by `removeDomEvent`
*
* 卸载DOM事件
*
* @public
* @param {HTMLElement} ele
* @param {string} type 事件类型
* @param {string=} selector 子元素选择器
* @param {function} fn 事件处理函数
*/
View.prototype.detachEvent = function (ele, type, selector, fn) {
eventHelper.off(ele, type, selector, fn);
};
/*
* 绑定DOM事件
* 会对进行绑定的DOM元素进行管理,方便自动卸载
*
* @public
* @param {HTMLElement} ele
* @param {string} type 事件类型
* @param {string=} selector 子元素选择器
* @param {function(element,event)} fn 事件处理函数,this指针为View对象
*/
View.prototype.addDomEvent = function (ele, type, selector, fn) {
if (this.bindElements.indexOf(ele) < 0) {
this.bindElements.push(ele);
}
if (!fn) {
fn = selector;
selector = undefined;
}
eventHelper.on(ele, type, selector, delegateDomEvent(this, fn));
};
/*
* 卸载DOM事件
*
* @public
* @param {HTMLElement} ele
* @param {string} type 事件类型
* @param {string=} selector 子元素选择器
* @param {function} fn 事件处理函数
*/
View.prototype.removeDomEvent = function (ele, type, selector, fn) {
if (!fn) {
fn = selector;
selector = undefined;
}
if (fn[KEY_DELEGATE]) {
eventHelper.off(ele, type, selector, fn[KEY_DELEGATE]);
}
};
/**
* 视图销毁
*
* @public
* @fires View#dispose
*/
View.prototype.dispose = function () {
/**
* 视图销毁事件
*
* @event
*/
this.emit('dispose');
// 解除事件绑定
this.bindElements.forEach(function (ele) {
eventHelper.clear(ele);
});
this.bindElements = [];
// 销毁页面的widget
widget.dispose(this.main);
// 解除元素引用
this.main = null;
};
/**
* 视图离开
*
* @public
* @fires View#leave
*/
View.prototype.leave = function () {
/**
* 视图离开事件
*
* @event
*/
this.emit('leave');
};
/**
* 视图休眠
*
* @public
* @fires View#sleep
*/
View.prototype.sleep = function () {
/**
* 视图休眠事件
*
* @event
*/
this.emit('sleep');
};
/**
* 视图唤醒
*
* @public
* @param {Object} data
*
* @fires View#wakeup
*/
View.prototype.wakeup = function (data) {
/**
* 视图唤醒事件
*
* @event
*/
this.emit('wakeup', data);
};
return View;
});