280 lines
8.0 KiB
JavaScript
280 lines
8.0 KiB
JavaScript
/**
|
||
* 模板引擎预处理,对dojs框架有依赖
|
||
* @authors yutent (yutent@doui.cc)
|
||
* @date 2016-01-02 21:26:49
|
||
*
|
||
*/
|
||
|
||
"use strict";
|
||
|
||
let fs = require('fs')
|
||
|
||
class Tool {
|
||
|
||
constructor(conf){
|
||
this.conf = {
|
||
delimiter: ['<!--{', '}-->'], //模板界定符
|
||
labels:{ //支持的标签类型
|
||
inc: 'include([^\\{\\}\\(\\)]*?)', //引入其他文件
|
||
each: 'each([^\\{\\}\\(\\)]*?)', //each循环开始
|
||
done: '/each', //each循环结束
|
||
if: 'if([^\\{\\}\\/]*?)', //if开始
|
||
elif: 'elseif([^\\{\\}\\/]*?)', //elseif开始
|
||
else: 'else', //else开始
|
||
fi: '/if', //if结束
|
||
var: 'var([\\s\\S])*?', //定义变量
|
||
echo: '=([^\\{\\}]*?)', //普通变量
|
||
}
|
||
}
|
||
|
||
this.conf = this.conf.merge(conf)
|
||
|
||
//过滤器
|
||
this.filters = {
|
||
html: function(str = ''){
|
||
str += ''
|
||
return str.tohtml()
|
||
},
|
||
truncate: function(str, len = '', truncation = '...'){
|
||
str += ''
|
||
//防止模板里参数加了引号导致异常
|
||
len = len.replace(/['"]/g, '') - 0
|
||
if(str.length <= len || len < 1)
|
||
return str
|
||
|
||
//去除参数里多余的引号
|
||
truncation = truncation.replace(/^['"]/, '').replace(/['"]$/, '')
|
||
|
||
return str.slice(0, len) + truncation
|
||
},
|
||
lower: function(str){
|
||
str += ''
|
||
return str.toLowerCase()
|
||
},
|
||
upper: function(str){
|
||
str += ''
|
||
return str.toUpperCase()
|
||
},
|
||
date: function(str, format = ''){
|
||
//去除参数里多余的引号
|
||
format = format.replace(/^['"]/, '').replace(/['"]$/, '')
|
||
return gmdate(format, str)
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//设置 配置信息
|
||
config(key, val){
|
||
key += ''
|
||
if(empty(key) || empty(val))
|
||
return
|
||
this.conf[key] = val
|
||
}
|
||
|
||
//生成正则
|
||
exp(str){
|
||
return new RegExp(str, 'g')
|
||
}
|
||
|
||
//生成模板标签
|
||
label(id){
|
||
let conf = this.conf
|
||
let tag = conf.labels[id || 'inc']
|
||
return this.exp(conf.delimiter[0] + tag + conf.delimiter[1])
|
||
}
|
||
|
||
//解析普通字段
|
||
matchNormal(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + '[=\\s]?')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
.replace(/\|\|/g, "\t")
|
||
|
||
let matches = m.split('|')
|
||
let filter = matches.length == 1 ? '' : matches[1].trim()
|
||
let txt = matches[0].replace(/\t/g, '||').trim()
|
||
|
||
// 默认过滤HTML标签
|
||
txt = txt.htmlspecialchars()
|
||
|
||
if(filter){
|
||
|
||
let args = filter.split(':')
|
||
filter = args.splice(0, 1, txt) + ''
|
||
if(filter === 'date' && args.length > 2){
|
||
let tmp = args.splice(0, 1)
|
||
tmp.push(args.join(':'))
|
||
args = tmp
|
||
tmp = null
|
||
}
|
||
|
||
if(this.filters.hasOwnProperty(filter)){
|
||
args = args.map((it, i) => {
|
||
if(i === 0)
|
||
return it
|
||
return `'${it}'`
|
||
})
|
||
txt = `do_fn.${filter}(${args.join(', ')})`
|
||
}
|
||
|
||
}
|
||
return `\` + (${txt}); tpl += \``
|
||
}
|
||
|
||
//解析each循环
|
||
matchFor(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + 'each\\s+')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
|
||
m = m.trim()
|
||
if(empty(m) || !/\sin\s/.test(m))
|
||
return new Error('Wrong each loop')
|
||
|
||
let each = 'for (let '
|
||
let ms = m.split(' in ')
|
||
let mi = ms[0].trim().split(' ')
|
||
let mf = ms[1].trim() //要遍历的对象
|
||
|
||
if(mi.length === 1){
|
||
each += `d_idx in ${mf}) { let ${mi[0]} = ${mf}[d_idx]; tpl += \``;
|
||
}else{
|
||
each += `${mi[0]} in ${mf}) { let ${mi[1]} = ${mf}[${mi[0]}]; tpl += \``
|
||
}
|
||
|
||
return `\`; ${each}`
|
||
}
|
||
|
||
//解析条件语句
|
||
matchIf(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + 'if\\s+')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
|
||
m = m.trim()
|
||
if(empty(m))
|
||
return `\`; tpl += \``
|
||
|
||
return `\`; if (${m}){ tpl += \``
|
||
}
|
||
|
||
//解析条件语句
|
||
matchElseIf(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + 'elseif\\s+')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
|
||
m = m.trim()
|
||
if(empty(m))
|
||
return `\`;} else { tpl += \``
|
||
|
||
return `\`; } else if (${m}){ tpl += \``
|
||
}
|
||
|
||
//解析变量定义
|
||
matchVar(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + 'var\\s+')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
|
||
m = m.trim()
|
||
if(!empty(m) || /=/.test(m))
|
||
m = 'let ' + m
|
||
|
||
this.vars += ` ${m};`
|
||
|
||
return `\`; tpl += \``
|
||
}
|
||
|
||
//解析include
|
||
matchInclude(m){
|
||
let begin = this.exp('^' + this.conf.delimiter[0] + 'include\\s+')
|
||
let end = this.exp(this.conf.delimiter[1] + '$')
|
||
|
||
m = m.replace(begin, '')
|
||
.replace(end, '')
|
||
.replace(/^['"]/, '').replace(/['"]$/, '')
|
||
.replace(/\.tpl$/, '') //去掉可能出现的自带的模板后缀
|
||
|
||
m += '.tpl' //统一加上后缀
|
||
|
||
if(!fs.existsSync(this.conf.path + m))
|
||
return new Error('Can not find template "' + m + '"')
|
||
|
||
let tpl = fs.readFileSync(this.conf.path + m) + ''
|
||
//递归解析include
|
||
tpl = tpl.replace(/[\r\n\t]+/g, ' ') //去掉所有的换行/制表
|
||
.replace(/\\/g, '\\\\')
|
||
.replace(this.label(0), m1 => {
|
||
return this.matchInclude(m1)
|
||
})
|
||
|
||
return tpl
|
||
}
|
||
|
||
|
||
//解析模板
|
||
parse(str, data){
|
||
|
||
this.vars = `"use strict"; let do_fn = f; `
|
||
for(let i in data){
|
||
let tmp = JSON.stringify(data[i]) || ''
|
||
this.vars += `let ${i} = ${tmp}; `
|
||
}
|
||
|
||
str = str.replace(/[\r\n\t]+/g, ' ') //去掉所有的换行/制表
|
||
.replace(/\\/g, '\\\\')
|
||
.replace(/`/g, '\\`')
|
||
//解析include
|
||
.replace(this.label('inc'), m => {
|
||
return this.matchInclude(m)
|
||
})
|
||
//解析each循环
|
||
.replace(this.label('each'), m => {
|
||
return this.matchFor(m)
|
||
})
|
||
//解析循环结束标识
|
||
.replace(this.label('done'), '\` } tpl += \`')
|
||
//解析 if条件
|
||
.replace(this.label('if'), m => {
|
||
return this.matchIf(m)
|
||
})
|
||
.replace(this.label('elif'), m => {
|
||
return this.matchElseIf(m)
|
||
})
|
||
// parse the else
|
||
.replace(this.label('else'), '\`; } else { tpl += \`')
|
||
//解析if条件结束标识
|
||
.replace(this.label('fi'), '\`; } tpl += \`')
|
||
//解析临时变量的定义
|
||
.replace(this.label('var'), m => {
|
||
return this.matchVar(m)
|
||
})
|
||
//解析普通变量/字段
|
||
.replace(this.label('echo'), m => {
|
||
return this.matchNormal(m)
|
||
})
|
||
|
||
str = `${this.vars} let tpl=\`${str}\`; return tpl;`
|
||
|
||
return (new Function('f', str))(this.filters)
|
||
}
|
||
|
||
}
|
||
|
||
|
||
module.exports = Tool |