smarty/lib/tool.js

280 lines
8.0 KiB
JavaScript
Raw Normal View History

2017-03-17 18:07:35 +08:00
/**
* 模板引擎预处理对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