/**
* 模板核心
*
* @file UI核心
* @author Brian Li(lbxxlht@163.com)
* @frozen 8/30/2015
* 1.封装模板和模板数据结构
* 2.负责负责模板编译,在tpl属性被设置时进行编译
* 3.负责数据源处理,在datasource属性被设置时调用数据源预处理,得到预处理结果后,用该结果渲染出html字符串
* 4.对外提供tpl接口、datasource接口、html接口,其中html接口只负责返回渲染好的html字符串,为只读
* 5.负责UI对象的自定义事件处理,对外提供on接口用来绑定事件,对内提供fire接口用来触发事件。
* 6.使用getter、setter,不兼容老浏览器
*/
var define = typeof define === 'function' && define.amd ? define : function (factory) {
typeof module === 'object' ? (module.exports = factory(require)) : '';
};
define(function (require) {
require('./extend');
var doT = require('./doT');
var tool = require('./tool');
var core = {};
// public protected/////////////////////////////////////////////////////////////////
/**
* 绑定事件
*
* @param {string} type 事件类型
* @param {Function} callback 事件触发时的回调
* @param {Object | null} me callback绑定的上下位
*/
core.on = function (type, callback, me) {
Eif (me === undefined) {
me = this;
}
callback = tool.bind(callback, me);
this._temp.handlers[type] = this._temp.handlers[type] || [];
this._temp.handlers[type].push(callback);
};
/**
* 触发事件
*
* @param {string} type 事件类型
* @param {Object | null} param callback的回传
*/
core.fire = function (type, param) {
param = param || {};
param.type = type;
param.target = this;
Eif (this._temp.handlers[type] instanceof Array) {
for (var n = 0; n < this._temp.handlers[type].length; n++) {
this._temp.handlers[type][n](param);
}
}
};
// 初始化数据结构
core._format = function () {
this._temp = {
// 皮肤样式,存储向container注入的class,用于dispose是移除
className: '',
// 事件hash1:此hash中的事件已绑定到DOM上
events: {},
// 事件hash2:此hash中为自定义事件,通过on从外部注入,通过fire从内部触发
handlers: {},
// 模板数组,存储最原始的模板
tpl: [],
// 编译结果,用于渲染
renderer: function () {},
// 数据源
datasource: {},
// 渲染结果,外部只读,内部可写
html: ''
};
Object.defineProperty(this, '_temp', {enumerable: false});
};
// 销毁数据结构
core._dispose = function () {
/* eslint-disable */
for (var key in this._temp) {
this._temp[key] = null;
}
this._temp = null;
/* eslint-enable */
};
Object.defineProperty(core, '_format', {enumerable: false});
Object.defineProperty(core, '_init', {enumerable: false});
Object.defineProperty(core, '_dispose', {enumerable: false});
// public //////////////////////////////////////////////////////////////////////////
/**
* tpl模板接口
*
* @attribute read and write
*/
Object.defineProperty(core, 'tpl', {
/**
* getter
*
* @return {Array} tpl数组
*/
get: function () {
return this._temp != null ? this._temp.tpl : null;
},
/**
* setter
*
* @param {Array|string} tpl 模板字符串或字符串数组
*/
set: function (tpl) {
if (tpl == null || this._temp == null) {
return;
}
if (typeof tpl === 'string') {
tpl = [tpl];
}
if (!(tpl instanceof Array)) {
return;
}
// 编译一次,确保模板是正确的
var func = compile(tpl);
if (func != null) {
this._temp.tpl = tpl;
this._temp.renderer = func;
}
}
});
/**
* datasource 数据源接口
*
* @attribute read and write
*/
Object.defineProperty(core, 'datasource', {
/**
* getter
*
* @return {Object} 数据源对象
*/
get: function () {
var result = null;
if (this._temp == null) {
return result;
}
var datasource = this._temp.datasource;
if (datasource.hasOwnProperty('__prepare__') && typeof datasource.__prepare__ === 'function') {
result = datasource.__datasource__;
}
else {
result = datasource;
}
return result;
},
/**
* setter
*
* @param {Object} datasource 数据源对象,在模板中被翻译成it
* 用编译结果将传入的数据源渲染,并保存渲染结果
*/
set: function (datasource) {
if (this._temp == null) {
return;
}
var oldData = this._temp.datasource;
var renderData = null;
if (
oldData != null
&& oldData.hasOwnProperty('__prepare__')
&& typeof oldData.__prepare__ === 'function'
) {
// 走此分支场景:
// 用户通过someUiObj.datasource = ... 再次操作数据源
// 该UI原始数据源和渲染数据源不同,并且UI指定了原始数据源处理函数
oldData.__datasource__ = datasource;
renderData = oldData.__prepare__(datasource);
}
else if (
datasource.hasOwnProperty('__prepare__')
&& typeof datasource.__prepare__ === 'function'
) {
// 走此分支场景:
// UI初始化时,将原始数据源和处理处理函数包装后,进行Caller
renderData = datasource.__prepare__(datasource.__datasource__);
this._temp.datasource = datasource;
}
else {
// 走此分支场景:
// UI原始数据源跟渲染数据源格式相同,不用处理
renderData = datasource;
this._temp.datasource = datasource;
}
this._temp.renderData = renderData;
this._temp.html = render(this._temp.renderer, renderData);
}
});
/**
* html 渲染结果接口
* @attribute readonly
*/
Object.defineProperty(core, 'html', {
/**
* get方法
*
* @return {string} 渲染结果
*/
get: function () {
return this._temp != null ? this._temp.html : null;
}
});
// private //////////////////////////////////////
/**
* 编译模板
*
* @param {Array<string>} tpl 模板数组
* @return {Function|null} 编辑结果
*/
function compile(tpl) {
var func = null;
var template = tpl.join('');
try {
func = doT.compile(template);
}
catch (e) {
/* eslint-disable */
console.error('Tpl can not be compiled, please check tpl\'s grammar.');
/* eslint-enable */
}
return func;
}
/**
* 渲染数据
*
* @param {Function} func 渲染函数
* @param {Object} datasource 数据源
* @return {string} 渲染结果
*/
function render(func, datasource) {
var str = '';
Eif (typeof func === 'function' && datasource != null) {
try {
str = func(datasource);
}
catch (e) {
/* eslint-disable */
console.error('Datasource can not match Renderer.');
/* eslint-enable */
}
}
return str;
}
return core;
});
|