-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathindex.js
423 lines (333 loc) · 14.2 KB
/
index.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
412
413
414
415
416
417
418
419
420
421
422
423
// 唱吧模板引擎 for 浏览器
(function(root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define([], factory);
}
else {
// Browser globals
root.cbT = factory();
}
}(this, function() {
'use strict';
const VERSION = '1.1.8';
const TEMPLATE_OUT = '__templateOut__';
const TEMPLATE_VAR_NAME = '__templateVarName__';
const TEMPLATE_SUB = '__templateSub__';
const TEMPLATE_OBJECT = '__templateObject__';
const TEMPLATE_NAME = '__templateName__';
const TEMPLATE_HELPER = '__templateHelper__';
const SUB_TEMPLATE = '__subTemplate__';
const FOREACH_INDEX = 'Index';
// 转义影响正则的字符
const encodeReg = (source) => String(source).replace(/([.*+?^=!:${}()|[\]/\\])/g, '\\$1');
// 判断是不是 DOM 元素
const isElement = (o) => (
typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2
o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName === "string"
);
// textarea 或 input 则取 value,其它情况取 innerHTML
const getContent = (element) => /^(textarea|input)$/i.test(element.nodeName) ? element.value : element.innerHTML;
// 辅助函数
const helpers = {
// run wrapper
run(func) {
func.call(this);
},
trim(str) {
if (str == null) {
return '';
}
if (!String.prototype.trim) {
return str.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '');
}
else {
return String.prototype.trim.call(str);
}
},
// HTML转义
encodeHTML(source) {
return String(source)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/\\/g, '\')
.replace(/"/g, '"')
.replace(/'/g, ''');
},
replaceUrlProtocol(source) {
return String(source).replace(/^https?:(\/\/.+?)$/i, document.location.protocol + '$1');
},
// 转义UI UI变量使用在HTML页面标签onclick等事件函数参数中
encodeEventHTML(source) {
return String(source)
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(/\\\\/g, '\\')
.replace(/\\\//g, '/')
.replace(/\\n/g, '\n')
.replace(/\\r/g, '\r');
},
forEachArray(source, sep = '<br>') {
const result = [];
if (this.isArray(source)) {
for (let i = 0, len = source.length; i < len; i++) {
const tmp = this.trim(String(source[i]));
if (tmp !== '') {
result.push(this.encodeHTML(tmp));
}
}
}
return result.join(sep);
},
// 判断是否是 Object 类型
isObject(source) {
return typeof source === 'function' || !!(source && typeof source === 'object');
},
// 判断是否是 Array 类型
isArray(args) {
if (!Array.isArray) {
return Object.prototype.toString.call(args) === '[object Array]';
}
else {
return Array.isArray(args);
}
},
isEmptyObject(obj) {
for (const prop in obj) {
if (obj.hasOwnProperty(prop)) {
return false;
}
}
return true;
},
each(obj, callback) {
let length, i = 0;
if (this.isArray(obj)) {
length = obj.length;
for (; i < length; i++) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
else {
for (i in obj) {
if (callback.call(obj[i], i, obj[i]) === false) {
break;
}
}
}
return obj;
}
};
const core = {
// 标记当前版本
version: VERSION,
// 自定义分隔符,可以含有正则中的字符,可以是HTML注释开头 <! !>
leftDelimiter: '<%',
rightDelimiter: '%>',
// 自定义默认是否转义,默认为自动转义
escape: true,
cache: {},
// 编译模板
compile(str) {
// 检查 str 参数是否是 DOM 元素,如果是则获取元素的 innerHTML/value,否则认为字符串为模板
if (isElement(str)) {
// str 是 DOM 元素实例
return this._compile(getContent(str));
}
else {
const element = document.getElementById(str);
if (element) {
// 取到对应 id 的 dom,缓存其编译后的 HTML 模板函数
if (this.cache[str]) {
return this.cache[str];
}
else {
this.cache[str] = this._compile(getContent(element));
return this.cache[str];
}
}
else {
// 是模板字符串,则生成一个函数
// 如果直接传入字符串作为模板,则可能变化过多,因此不考虑缓存
return this._compile(str);
}
}
},
// 渲染模板函数
render(str, data, subtemplate) {
return this.compile(str)(data, subtemplate);
},
// 将字符串拼接生成函数,即编译过程(compile)
_compile(str) {
return this._buildTemplateFunction(this._parse(str));
},
_buildTemplateFunction(str) {
let funcBody = `
if (${SUB_TEMPLATE}) {
${TEMPLATE_OBJECT} = { value: ${TEMPLATE_OBJECT} };
}
if (${TEMPLATE_HELPER}.isObject(${TEMPLATE_OBJECT})) {
var ${TEMPLATE_VAR_NAME} = '';
for (var ${TEMPLATE_NAME} in ${TEMPLATE_OBJECT}) {
${TEMPLATE_VAR_NAME} += 'var ' + ${TEMPLATE_NAME} + ' = ${TEMPLATE_OBJECT}["' + ${TEMPLATE_NAME} + '"];';
}
if (${TEMPLATE_VAR_NAME} !== '') {
eval(${TEMPLATE_VAR_NAME});
}
${TEMPLATE_VAR_NAME} = null;
}
var ${TEMPLATE_SUB} = {};
var ${TEMPLATE_OUT} = '${str}';
return ${TEMPLATE_OUT};
`;
// console.log(funcBody.replace(/\\n/g, '\n'));
// 删除无效指令
funcBody = funcBody.replace(new RegExp(`${TEMPLATE_OUT}\\s*\\+?=\\s*'';`, 'g'), '');
const func = new Function(TEMPLATE_HELPER, TEMPLATE_OBJECT, SUB_TEMPLATE, funcBody);
return (templateObject, subTemplate) => func(helpers, templateObject, subTemplate);
},
// 解析模板字符串
_parse(str) {
//取得分隔符
const _left_ = this.leftDelimiter;
const _right_ = this.rightDelimiter;
//对分隔符进行转义,支持正则中的元字符,可以是HTML注释 <! !>
const _left = encodeReg(_left_);
const _right = encodeReg(_right_);
str = String(str)
//去掉分隔符中js注释
.replace(new RegExp("(" + _left + "[^" + _right + "]*)//.*\n", "g"), "$1")
//默认支持HTML注释,将HTML注释匹配掉的原因是用户有可能用 <! !>来做分割符
//.replace(/<!--[\s\S]*?-->/g, '')
//去掉注释内容 <%* 这里可以任意的注释 *%>
.replace(new RegExp(_left + '\\*[\\s\\S]*?\\*' + _right, 'gm'), '')
//用来处理非分隔符内部的内容中含有 斜杠 \ 单引号 ‘
.replace(new RegExp(_left + "(?:(?!" + _right + ")[\\s\\S])*" + _right + "|((?:(?!" + _left + ")[\\s\\S])+)", "g"), (item, $1) => {
let str = '';
if ($1) {
//将 斜杠 单引 转义
str = $1.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
}
else {
str = item;
}
return str;
})
//把所有换行去掉 \r回车符 \t制表符 \n换行符
.replace(/\r/g, '\\r')
.replace(/\t/g, '\\t')
.replace(/\n/g, '\\n');
str = str
//定义变量,如果没有分号,需要容错 <%let val='test'%>
.replace(new RegExp("(" + _left + "\\s*?let\\s*?.+?\\s*?[^;])\\s*?" + _right, "g"),
`$1;${_right_}`)
//对变量后面的分号做容错(包括转义模式 如<%:h=value%>) <%=value;%> 排除掉函数的情况 <%fun1();%> 排除定义变量情况 <%var val='test';%>
.replace(new RegExp("(" + _left + ":?[hvu]?\\s*?=\\s*?[^;|" + _right + "]*?);\\s*?" + _right, "g"),
`$1${_right_}`)
// foreach循环 <% foreach (x in arr) %>
.replace(new RegExp(_left + "\\s*?foreach\\s*?\\((.+?)\\s+in\\s+(.+?)\\)\\s*?" + _right, "g"),
`${_left_}if/*-*/(typeof($2)!=='undefined'&&(${TEMPLATE_HELPER}.isArray($2)&&$2.length>0||${TEMPLATE_HELPER}.isObject($2)&&!${TEMPLATE_HELPER}.isEmptyObject($2))){${TEMPLATE_HELPER}.each($2,($1${FOREACH_INDEX},$1)=>{${_right_}`)
// foreachelse指令 <% foreachelse %>
.replace(new RegExp(_left + "\\s*?foreachelse\\s*?" + _right, "g"),
`${_left_}})}else{${TEMPLATE_HELPER}.run(()=>{${_right_}`)
// foreachbreak指令 <% foreachbreak %>
.replace(new RegExp(_left + "\\s*?foreachbreak\\s*?" + _right, "g"),
`${_left_}return false;${_right_}`)
// foreach循环结束 <% /foreach %>
.replace(new RegExp(_left + "\\s*?/foreach\\s*?" + _right, "g"),
`${_left_}})}${_right_}`)
// if 指令 <% if (x == 1) %>
.replace(new RegExp(_left + "\\s*?if\\s*?\\((.+?)\\)\\s*?" + _right, "g"),
`${_left_}if($1){${_right_}`)
// elseif 指令 <% elseif (x == 1) %>
.replace(new RegExp(_left + "\\s*?else\\s*?if\\s*?\\((.+?)\\)\\s*?" + _right, "g"),
`${_left_}}else if($1){${_right_}`)
// else 指令 <% else %>
.replace(new RegExp(_left + "\\s*?else\\s*?" + _right, "g"),
`${_left_}}else{${_right_}`)
// if 指令结束 <% /if %>
.replace(new RegExp(_left + "\\s*?/if\\s*?" + _right, "g"),
`${_left_}}${_right_}`)
// 注意:必须在原生指令编译完毕再编译其他指令
// 定义子模板 <% define value(param) %>
.replace(new RegExp(_left + "\\s*?define\\s+?([a-z0-9_$]+?)\\s*?\\((.*?)\\)\\s*?" + _right, "g"),
`${_left_}${TEMPLATE_SUB}['$1']=($2)=>{${_right_}`)
// 在最后子模板结束的位置增加直接调用子模板的代码!
.replace(new RegExp(_left + "\\s*?/define\\s*?(?![\\s\\S]*\\s*?/define\\s*?)" + _right, "g"),
`${_left_} /define ${_right_}${_left_}if(${SUB_TEMPLATE}){${TEMPLATE_OUT}='';if(${TEMPLATE_SUB}[${SUB_TEMPLATE}]){${TEMPLATE_SUB}[${SUB_TEMPLATE}](value)}${TEMPLATE_VAR_NAME}=null;return}${_right_}`)
// 定义子模板结束 <% /define %>
.replace(new RegExp(_left + "\\s*?/define\\s*?" + _right, "g"),
`${_left_}};${_right_}`)
// 调用子模板 <% run value() %>
.replace(new RegExp(_left + "\\s*?run\\s+?([a-zA-Z0-9_$]+?)\\s*?\\((.*?)\\)\\s*?" + _right, "g"),
`${_left_}if(${TEMPLATE_SUB}['$1']){${TEMPLATE_SUB}['$1']($2)}${_right_}`)
//按照 <% 分割为一个个数组,再用 \t 和在一起,相当于将 <% 替换为 \t
//将模板按照<%分为一段一段的,再在每段的结尾加入 \t,即用 \t 将每个模板片段前面分隔开
.split(_left_).join("\t");
//支持用户配置默认是否自动转义
if (this.escape) {
str = str
//找到 \t=任意一个字符%> 替换为 ‘,任意字符,'
//即替换简单变量 \t=data%> 替换为 ',data,'
//默认HTML转义 也支持HTML转义写法<%:h=value%>
.replace(new RegExp("\\t=(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.encodeHTML($1))+'`);
}
else {
str = str
//默认不转义HTML转义
.replace(new RegExp("\\t=(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':$1)+'`);
};
str = str
//支持HTML转义写法<%:h=value%>
.replace(new RegExp("\\t:h=(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.encodeHTML($1))+'`)
//支持不转义写法 <%:=value%>和<%-value%>
.replace(new RegExp("\\t(?::=|-)(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':$1)+'`)
//支持url转义 <%:u=value%>
.replace(new RegExp("\\t:u=(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':encodeURIComponent($1))+'`)
//支持UI 变量使用在HTML页面标签onclick等事件函数参数中 <%:v=value%>
.replace(new RegExp("\\t:v=(.*?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.encodeEventHTML($1))+'`)
//支持迭代数组 <%:a=value|分隔符%>
.replace(new RegExp("\\t:a=(.+?)(?:\\|(.*?))?" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.forEachArray($1,'$2'))+'`)
//支持格式化钱数 <%:m=value%>
.replace(new RegExp("\\t:m=(.+?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null)||isNaN($1))?'':Number(Math.round(($1)*100)/100).toFixed(2))+'`)
//字符串截取补... <%:s=value|位数%>
.replace(new RegExp("\\t:s=(.+?)\\|(\\d+?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.encodeHTML($1.length>$2?$1.substr(0,$2)+'...':$1))+'`)
//HTTP协议自适应 <%:p=value%>
.replace(new RegExp("\\t:p=(.+?)" + _right, "g"),
`'+((typeof($1)==='undefined'||(typeof($1)==='object'&&$1===null))?'':${TEMPLATE_HELPER}.encodeHTML(${TEMPLATE_HELPER}.replaceUrlProtocol($1)))+'`)
// <%:func=value%>
.replace(new RegExp("\\t:func=(.*?)" + _right, "g"),
`'+${TEMPLATE_HELPER}.encodeHTML($1)+'`)
// <%:func-value%>
.replace(new RegExp("\\t:func-(.*?)" + _right, "g"),
`'+($1)+'`)
// 将字符串按照 \t 分成为数组,在用'; 将其合并,即替换掉结尾的 \t 为 ';
.split("\t").join("';")
// 将 %> 替换为 ${TEMPLATE_OUT}+='
// 即去掉结尾符,生成字符串拼接
.split(_right_).join(`${TEMPLATE_OUT}+='`);
//console.log(str);
return str;
}
};
return Object.assign({
getInstance() {
return Object.assign({}, core);
}
}, core);
}));