网页模板是把动态的数据和静态的表现组装到一起的工具,使得内容与表现方式可以分离,是 Web 开发中的重要技术手段。早期的 Web 开发很简单,还没“模板 ”的概念。只需把数据提取出来,放在 HTML 里面显示就达到程序的目的。HTML代码混合于逻辑代码之中,HTML 就是直接显示的内容,内嵌在 HMTL 中<% ... %>表示为后台(服务端)执行代码,但很多情况<% ... %>中又有 HTML 的片段,至于怎么拼凑出 HTML 片段的方式方法各式各样、与多种的后台语言掺杂一起( ASP、PHP、JSP )各显神通。
$$.tpl = {
fillData : function(tpl, data){
// 匹配闭合tag的正则。
var divBlock = '<{0}\\s*[^>]*>((?:(?=([^<]+))\\2|<(?!{0}\\s*[^>]*>))*?)<\/({0})>';
var matchValue = (function(){
var
regexp = /{([^}\n]*)}/ig
,execJS = /^\[([^\]]*)\]$/
,getParent = /^parent\.\b(\w*)\b$/i
,getRoot = /^root\.\b(\w*)\b$/i;
/**
* @private
* @param {mixed} v
* @return {Boolean} 是否hash(是否对象)。
*/
function isObject(v){
return !!v && isObject.toString.call(v) == isObject.token;
}
isObject.toString = Object.prototype.toString
isObject.token = '[object Object]';
/**
* 替换置,如果未发现匹配的值,返回什么?一般原模版的格式({xxx}),即m1。
* 注意那些无意义的值除外。
*/
function falsey(value, tpl){
switch(value){
case false:
return 'false';
case '':
return value;
case 0:
return value;
}
return !!value ? value : tpl;
}
function replace(m1, m2){
var
jsCode
,parent = arguments.callee.parent
,root = arguments.callee.root;
if(execJS.test(m2)){
execJS.lastIndex = 0;
jsCode = execJS.exec(m2);
jsCode = jsCode[1];
try{ // 写try之目的为容错性的设计。
with(this){
jsCode = eval(jsCode);
}
return jsCode;
}catch(e){
return '空值';
}
}
if(isObject(parent) && getParent.test(m2)){ /* 父级寻址 */
getParent.lastIndex = 0;
m2 = m2.match(getParent);
m2 = m2[1];
return falsey(parent[m2], m1);
}else if(isObject(root) && getRoot.test(m2)){ /* 全称寻址 */
getRoot.lastIndex = 0;
m2 = m2.match(getRoot);
m2 = m2[1];
return falsey(root[m2], m1);
}else{
return falsey(this[m2], m1);
}
}
/**
* Lazy function
* @param {String} tpl
* @param {Object} dataObj
* @param {Object} parentObj
* @return {String}
*/
return function(tpl, dataObj, parentObj){
if(!replace.root){
replace.root = $$.tpl.root;
}
replace.parent = parentObj || 'itself'; // 无else部分,即表示全部就是trueBlock
replace.scope = dataObj;
return tpl.replace(regexp, replace.delegate());// set scope object!
}
})();
var matchIf = (function(){
var
elseReg = /<else\s*\/?>/i
,evalReg = /<if\s*(?:eval="([\w\d=<>\.!'\|\(\)]*)")[^>]*>/i
,tabReg = /\t{2}/g
,ifReg = new RegExp(divBlock.format('if'), 'ig');
/**
* 运算<if eval="exp">内容。
* @param {Object} data
* @param {Object} parent
* @param {String} ifTag
* @return {Boolean}
*/
function evalExp(data, parent, ifTag){
var exp;
evalReg.lastIndex = 0;
if(!evalReg.test(ifTag)){
$$.console.error('输入的标签{0}不是一个标准的if tag'.format(ifTag));
throw '不是一个标准的if tag';
}
evalReg.lastIndex = 0;
exp = evalReg.exec(ifTag);
exp = exp[1];
exp = '(' + exp +')'; // make it as the Expression 表达式
// debugger;
// 通过with(data){}给定对象可见的范围。如果没有matchedObj,则为this。这也是有可能的。
with(data || parent || this){
try{
exp = !!eval(exp);
}catch(e){
exp = false;
}
}
return exp;
}
/**
* @this {OBject} 值对象
*/
function replace(m1, m2){
var
evalResult // 运算表达式后的结果
,trueBlock // true块
,falseBlock = null // false块,默认没有
elseReg.lastIndex = 0;
if(elseReg.test(m2)){// 有else部分
var arr = m2.split(elseReg);
if(!arr || arr.length < 2){
$$.console.error('if-else不完整');
throw "if-else不完整";
};
trueBlock = arr[0];
falseBlock = arr[1];
}else{
trueBlock = m2; // 无else部分,即表示全部就是trueBlock
}
trueBlock = $$.trim(trueBlock);
trueBlock = trueBlock.replace(tabReg, '\t');// 消除多余tab符。
// 求 if 的表达式
evalResult = evalExp(this, arguments.callee.parent, m1);
if(evalResult){
return trueBlock;
}else if(!evalResult && falseBlock == null){
return '';
}else if(!evalResult && falseBlock){
return falseBlock;
}else{
// 不应该会走到这里的。若真的走到便抛出一个异常。
$$.console.error('求if-else块时发生致命错误!');
throw '求if-else块时发生致命错误!';
}
}
return function(tpl, data, parentData){
ifReg.lastIndex = 0;// 有global时需要注意 lastIndex的问题。
if(ifReg.test(tpl)){
replace.parent = parentData;
replace.scope = data;
return tpl.replace(ifReg, replace.delegate());
}else{
return tpl;
}
}
})();
function replace(m1, m2, m3, m4){
var
parentData = this
,values = parentData[m4]
,callee = $$.tpl.fillData
,str = ''
,arrTpl;
if(values && !values.pop){
return callee(m2, values); // 递归 for sub
}else if(values && values.pop){/* 有时候会因为大小写问题,无法匹配。请检查key即(m4)是否一致 */
m2 = callee(m2, values); // 递归 for sub
for(var i = 0, j = values.length; i < j; i++){
value = values[i];
m2 = matchIf(m2, value, parentData);
arrTpl = callee(m2, value);
str += matchValue(arrTpl, value, parentData);
}
return str;
}else if(values == false /* 特定为false的情况 */ ){
return str;
}else{
$$.console.warn('No data provided');// 怎么数据源没提供数据? 宜debug之
return 'edkTpl_nothing';
}
}
/**
* @param {String} tpl
* @param {Object} data
* @return {String}
*/
$$.tpl.fillData = function (tpl, data){
if(!$$.tpl.root){
$$.tpl.root = data;
}
if(data && !data.pop){
replace.scope = data;
var _replace = replace.delegate();
for(var i in data){
tpl = tpl.replace(new RegExp(divBlock.format(i), 'i'), _replace);
}
tpl = matchIf(tpl, data, data);
return matchValue(tpl, data);
}else if(data && data.pop){
return tpl;
}else if(!data){
// throw '没有输入数据的参数 data?';
}
return '';
}
return $$.tpl.fillData(tpl, data);
}
};
这是我第一次写的模板,纯练手,因此非常粗糙。