mm_tpl 2.7.2 → 2.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -30
- package/README_EN.md +38 -29
- package/index.js +20 -38
- package/lib/asset.js +8 -4
- package/lib/base.js +84 -0
- package/lib/base_lang.js +200 -0
- package/lib/file.js +258 -0
- package/lib/hook.js +15 -8
- package/lib/lang.js +20 -16
- package/lib/memory.js +1 -3
- package/lib/sqlite.js +1 -3
- package/lib/template.js +14 -10
- package/lib/{engine.js → tpl.js} +121 -98
- package/lib/tpl_rule.js +1 -1
- package/lib/util.js +117 -0
- package/package.json +5 -3
package/lib/{engine.js → tpl.js}
RENAMED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const engine = require('art-template');
|
|
2
2
|
const {
|
|
3
3
|
Base
|
|
4
|
-
} = require('
|
|
4
|
+
} = require('./base.js');
|
|
5
|
+
const {
|
|
6
|
+
FileUtil
|
|
7
|
+
} = require('./file.js');
|
|
5
8
|
const Hook = require('./hook.js');
|
|
6
9
|
const {
|
|
7
10
|
nativeRule,
|
|
@@ -26,9 +29,7 @@ const {
|
|
|
26
29
|
const {
|
|
27
30
|
Lang
|
|
28
31
|
} = require('./lang.js');
|
|
29
|
-
const
|
|
30
|
-
Memory
|
|
31
|
-
} = require('./memory.js');
|
|
32
|
+
const Memory = require('./memory.js');
|
|
32
33
|
|
|
33
34
|
/**
|
|
34
35
|
* 模板引擎类
|
|
@@ -36,15 +37,16 @@ const {
|
|
|
36
37
|
* 渲染结果不做页面级缓存,页面缓存交由 web 中间件按完整请求上下文负责
|
|
37
38
|
* @class
|
|
38
39
|
*/
|
|
39
|
-
class
|
|
40
|
+
class Tpl extends Base {
|
|
40
41
|
/**
|
|
41
42
|
* 配置参数
|
|
42
43
|
*/
|
|
43
44
|
static config = {
|
|
44
45
|
/**
|
|
45
46
|
* 应用根目录,目录结构见 doc/目录结构.md
|
|
47
|
+
* 默认为运行根目录,宿主应用建议显式传入自身根目录
|
|
46
48
|
*/
|
|
47
|
-
root:
|
|
49
|
+
root: FileUtil.getRunPath(),
|
|
48
50
|
/**
|
|
49
51
|
* 主题目录,相对应用根目录
|
|
50
52
|
*/
|
|
@@ -116,7 +118,7 @@ class Engine extends Base {
|
|
|
116
118
|
rules: [nativeRule, artRule, htmlRule, jsRule, pyRule, pyRule2],
|
|
117
119
|
/**
|
|
118
120
|
* 复数规则,键为语言代码,语言代码统一用小写下划线(如 zh_cn、en_us)
|
|
119
|
-
*
|
|
121
|
+
* 内置 BaseLang 已含 zh_cn、en 规则,其他语言代码需在此登记才能按语言取复数形式
|
|
120
122
|
*/
|
|
121
123
|
plural_rules: {
|
|
122
124
|
'en_us': function (count) {
|
|
@@ -127,12 +129,33 @@ class Engine extends Base {
|
|
|
127
129
|
* 变换字符串
|
|
128
130
|
*/
|
|
129
131
|
change_str: ['\\#', '\\<', '\\${', '\\*', '\\"""'],
|
|
132
|
+
/**
|
|
133
|
+
* 日志对象,需含 warn / error 等方法,留空则用 console
|
|
134
|
+
* 实例的 this.log 与实例级钩子的错误输出都走该对象
|
|
135
|
+
*/
|
|
136
|
+
logger: null,
|
|
130
137
|
};
|
|
131
138
|
/**
|
|
132
139
|
* 语言包文件缓存
|
|
133
140
|
* 按语言包目录组缓存,形如 {目录组: {fingerprint, dict}},文件未变化时复用解析结果
|
|
134
141
|
*/
|
|
135
142
|
static lang_cache = {};
|
|
143
|
+
/**
|
|
144
|
+
* 全局钩子实例
|
|
145
|
+
* 所有引擎实例共享,与实例级 this.hook 一起参与执行,全局先执行、实例级后执行
|
|
146
|
+
* 宿主应用注册跨实例钩子时使用,如 Tpl.global_hook.addFunc(...)
|
|
147
|
+
*/
|
|
148
|
+
static global_hook = new Hook();
|
|
149
|
+
/**
|
|
150
|
+
* 全局数据袋
|
|
151
|
+
* 渲染时注入模型的 globalBag 字段,供模板跨实例共享数据
|
|
152
|
+
*/
|
|
153
|
+
static global_bag = {};
|
|
154
|
+
/**
|
|
155
|
+
* 全局语言包字典
|
|
156
|
+
* 实例未配置任何语言包时的回退字典,形如 { 语言代码: 词条对象 }
|
|
157
|
+
*/
|
|
158
|
+
static global_dict = {};
|
|
136
159
|
/**
|
|
137
160
|
* 创建引擎实例
|
|
138
161
|
* @param {Object} config - 配置选项
|
|
@@ -216,9 +239,9 @@ class Engine extends Base {
|
|
|
216
239
|
this.funcs = {};
|
|
217
240
|
|
|
218
241
|
/**
|
|
219
|
-
*
|
|
242
|
+
* 实例级钩子,仅作用于当前实例;与全局钩子 Tpl.global_hook 一起参与执行,全局先执行、实例级后执行
|
|
220
243
|
*/
|
|
221
|
-
this.hook = new Hook();
|
|
244
|
+
this.hook = new Hook(this.config.logger);
|
|
222
245
|
|
|
223
246
|
/**
|
|
224
247
|
* 初始化
|
|
@@ -230,7 +253,7 @@ class Engine extends Base {
|
|
|
230
253
|
/**
|
|
231
254
|
* 初始化模板
|
|
232
255
|
*/
|
|
233
|
-
|
|
256
|
+
Tpl.prototype._initTpl = function () {
|
|
234
257
|
var cg = this.config;
|
|
235
258
|
// 引擎默认值为全局配置,仅主实例写入,避免多实例互相覆盖
|
|
236
259
|
if (!cg.is_fork) {
|
|
@@ -245,7 +268,7 @@ Engine.prototype._initTpl = function () {
|
|
|
245
268
|
* 支持 config.js 的 { defaults: { theme, style, lang } } 写法
|
|
246
269
|
* @param {Object} config - 配置参数
|
|
247
270
|
*/
|
|
248
|
-
|
|
271
|
+
Tpl.prototype._setConfig = function (config) {
|
|
249
272
|
if (!config) {
|
|
250
273
|
return;
|
|
251
274
|
}
|
|
@@ -259,7 +282,7 @@ Engine.prototype._setConfig = function (config) {
|
|
|
259
282
|
* @param {Object} config - 配置参数
|
|
260
283
|
* @returns {Object} 规范化后的配置
|
|
261
284
|
*/
|
|
262
|
-
|
|
285
|
+
Tpl.prototype._normalizeConfig = function (config) {
|
|
263
286
|
if (!config || typeof (config.defaults) !== 'object' || !config.defaults) {
|
|
264
287
|
return config;
|
|
265
288
|
}
|
|
@@ -283,9 +306,9 @@ Engine.prototype._normalizeConfig = function (config) {
|
|
|
283
306
|
* @param {string} theme - 主题名称
|
|
284
307
|
* @returns {string} 主题根目录全路径
|
|
285
308
|
*/
|
|
286
|
-
|
|
309
|
+
Tpl.prototype.getThemeDir = function (theme) {
|
|
287
310
|
var cg = this.config;
|
|
288
|
-
return theme.fullname(cg.theme_dir
|
|
311
|
+
return FileUtil.fullname(theme, FileUtil.fullname(cg.theme_dir, this.dir));
|
|
289
312
|
};
|
|
290
313
|
|
|
291
314
|
/**
|
|
@@ -294,12 +317,12 @@ Engine.prototype.getThemeDir = function (theme) {
|
|
|
294
317
|
* @param {string} style - 风格名称,为空时取主题下的风格目录本身
|
|
295
318
|
* @returns {string} 风格目录全路径
|
|
296
319
|
*/
|
|
297
|
-
|
|
298
|
-
var dir = this.config.style_dir
|
|
320
|
+
Tpl.prototype.getStyleDir = function (theme, style) {
|
|
321
|
+
var dir = FileUtil.fullname(this.config.style_dir, this.getThemeDir(theme));
|
|
299
322
|
if (!style) {
|
|
300
323
|
return dir;
|
|
301
324
|
}
|
|
302
|
-
return
|
|
325
|
+
return FileUtil.fullname(style, dir);
|
|
303
326
|
};
|
|
304
327
|
|
|
305
328
|
/**
|
|
@@ -310,12 +333,12 @@ Engine.prototype.getStyleDir = function (theme, style) {
|
|
|
310
333
|
* @param {string} theme - 主题名称,不传时取当前主题
|
|
311
334
|
* @returns {Array<string>} 模板目录列表
|
|
312
335
|
*/
|
|
313
|
-
|
|
336
|
+
Tpl.prototype.getTemplateDirs = function (theme) {
|
|
314
337
|
var cg = this.config;
|
|
315
338
|
var cur_theme = theme || this.current_theme;
|
|
316
339
|
var dirs = [];
|
|
317
340
|
if (cg.diy_dir) {
|
|
318
|
-
dirs.push(cg.diy_dir
|
|
341
|
+
dirs.push(FileUtil.fullname(cg.diy_dir, this.dir));
|
|
319
342
|
}
|
|
320
343
|
if (cur_theme) {
|
|
321
344
|
dirs.push(this.getThemeDir(cur_theme));
|
|
@@ -333,7 +356,7 @@ Engine.prototype.getTemplateDirs = function (theme) {
|
|
|
333
356
|
* @param {string} theme - 主题名称,不传时取当前主题
|
|
334
357
|
* @returns {Array<string>} 组件目录列表
|
|
335
358
|
*/
|
|
336
|
-
|
|
359
|
+
Tpl.prototype.getComponentDirs = function (theme) {
|
|
337
360
|
var cg = this.config;
|
|
338
361
|
if (!cg.component_dir) {
|
|
339
362
|
return [];
|
|
@@ -341,13 +364,13 @@ Engine.prototype.getComponentDirs = function (theme) {
|
|
|
341
364
|
var cur_theme = theme || this.current_theme;
|
|
342
365
|
var dirs = [];
|
|
343
366
|
if (cg.diy_dir) {
|
|
344
|
-
dirs.push(cg.component_dir.fullname(cg.diy_dir
|
|
367
|
+
dirs.push(FileUtil.fullname(cg.component_dir, FileUtil.fullname(cg.diy_dir, this.dir)));
|
|
345
368
|
}
|
|
346
369
|
if (cur_theme) {
|
|
347
|
-
dirs.push(cg.component_dir
|
|
370
|
+
dirs.push(FileUtil.fullname(cg.component_dir, this.getThemeDir(cur_theme)));
|
|
348
371
|
}
|
|
349
372
|
if (cg.default_theme && cg.default_theme !== cur_theme) {
|
|
350
|
-
dirs.push(cg.component_dir
|
|
373
|
+
dirs.push(FileUtil.fullname(cg.component_dir, this.getThemeDir(cg.default_theme)));
|
|
351
374
|
}
|
|
352
375
|
return dirs;
|
|
353
376
|
};
|
|
@@ -361,7 +384,7 @@ Engine.prototype.getComponentDirs = function (theme) {
|
|
|
361
384
|
* @param {string} style - 风格名称,不传时取当前风格;显式传空字符串表示用主题下的风格目录本身
|
|
362
385
|
* @returns {Array<string>} 样式目录列表
|
|
363
386
|
*/
|
|
364
|
-
|
|
387
|
+
Tpl.prototype.getStyleDirs = function (theme, style) {
|
|
365
388
|
var cg = this.config;
|
|
366
389
|
var cur_theme = theme || this.current_theme;
|
|
367
390
|
// 风格取自入参或当前值,为空时以默认风格兜底,不做跨风格回退
|
|
@@ -377,7 +400,7 @@ Engine.prototype.getStyleDirs = function (theme, style) {
|
|
|
377
400
|
var base = cg.base_style || '';
|
|
378
401
|
var dirs = [];
|
|
379
402
|
if (cg.diy_dir) {
|
|
380
|
-
dirs.push(cg.diy_dir
|
|
403
|
+
dirs.push(FileUtil.fullname(cg.diy_dir, this.dir));
|
|
381
404
|
}
|
|
382
405
|
for (var i = 0; i < themes.length; i++) {
|
|
383
406
|
dirs.push(this.getStyleDir(themes[i], cur_style));
|
|
@@ -395,21 +418,21 @@ Engine.prototype.getStyleDirs = function (theme, style) {
|
|
|
395
418
|
* @param {string} theme - 主题名称,不传时取当前主题
|
|
396
419
|
* @returns {Array<string>} 脚本目录列表
|
|
397
420
|
*/
|
|
398
|
-
|
|
421
|
+
Tpl.prototype.getJsDirs = function (theme) {
|
|
399
422
|
var cg = this.config;
|
|
400
423
|
var cur_theme = theme || this.current_theme;
|
|
401
424
|
var dirs = [];
|
|
402
425
|
if (cg.diy_dir) {
|
|
403
|
-
dirs.push(cg.diy_dir
|
|
426
|
+
dirs.push(FileUtil.fullname(cg.diy_dir, this.dir));
|
|
404
427
|
}
|
|
405
428
|
if (cur_theme && cg.theme_js_dir) {
|
|
406
|
-
dirs.push(cg.theme_js_dir
|
|
429
|
+
dirs.push(FileUtil.fullname(cg.theme_js_dir, this.getThemeDir(cur_theme)));
|
|
407
430
|
}
|
|
408
431
|
if (cg.default_theme && cg.default_theme !== cur_theme && cg.theme_js_dir) {
|
|
409
|
-
dirs.push(cg.theme_js_dir
|
|
432
|
+
dirs.push(FileUtil.fullname(cg.theme_js_dir, this.getThemeDir(cg.default_theme)));
|
|
410
433
|
}
|
|
411
434
|
if (cg.js_dir) {
|
|
412
|
-
dirs.push(cg.js_dir
|
|
435
|
+
dirs.push(FileUtil.fullname(cg.js_dir, this.dir));
|
|
413
436
|
}
|
|
414
437
|
return dirs;
|
|
415
438
|
};
|
|
@@ -418,7 +441,7 @@ Engine.prototype.getJsDirs = function (theme) {
|
|
|
418
441
|
* 预设值
|
|
419
442
|
* @param {boolean} force - 是否跳过语言包缓存强制重新加载
|
|
420
443
|
*/
|
|
421
|
-
|
|
444
|
+
Tpl.prototype._preset = function (force) {
|
|
422
445
|
// 基类构造期间会触发一次预设,此时加载器尚未就绪
|
|
423
446
|
if (!this.lang_loader) {
|
|
424
447
|
return;
|
|
@@ -430,7 +453,7 @@ Engine.prototype._preset = function (force) {
|
|
|
430
453
|
* 获取当前语言
|
|
431
454
|
* @returns {string} 语言代码
|
|
432
455
|
*/
|
|
433
|
-
|
|
456
|
+
Tpl.prototype.getLang = function () {
|
|
434
457
|
if (this.lang) {
|
|
435
458
|
return this.lang.now;
|
|
436
459
|
}
|
|
@@ -442,7 +465,7 @@ Engine.prototype.getLang = function () {
|
|
|
442
465
|
* @param {string} lang - 语言代码
|
|
443
466
|
* @returns {string} 设置后的语言代码
|
|
444
467
|
*/
|
|
445
|
-
|
|
468
|
+
Tpl.prototype.setLang = function (lang) {
|
|
446
469
|
if (typeof (lang) !== 'string' || !lang) {
|
|
447
470
|
this.log('warn', '语言参数无效,保持当前语言:' + this.getLang());
|
|
448
471
|
return this.getLang();
|
|
@@ -458,7 +481,7 @@ Engine.prototype.setLang = function (lang) {
|
|
|
458
481
|
* @param {...any} args - 替换参数,对象按命名占位符,其余按位置占位符
|
|
459
482
|
* @returns {string} 翻译后的文本
|
|
460
483
|
*/
|
|
461
|
-
|
|
484
|
+
Tpl.prototype.t = function (key, ...args) {
|
|
462
485
|
return this._translate(key, args);
|
|
463
486
|
};
|
|
464
487
|
|
|
@@ -468,7 +491,7 @@ Engine.prototype.t = function (key, ...args) {
|
|
|
468
491
|
* @param {Array<any>} args - 替换参数
|
|
469
492
|
* @returns {string} 翻译后的文本
|
|
470
493
|
*/
|
|
471
|
-
|
|
494
|
+
Tpl.prototype._translate = function (key, args) {
|
|
472
495
|
var val = this.lang.get(key);
|
|
473
496
|
if (typeof (val) !== 'string' || !args.length) {
|
|
474
497
|
return val;
|
|
@@ -488,7 +511,7 @@ Engine.prototype._translate = function (key, args) {
|
|
|
488
511
|
* @param {Object} data - 键值对
|
|
489
512
|
* @returns {string} 填充后的文本
|
|
490
513
|
*/
|
|
491
|
-
|
|
514
|
+
Tpl.prototype._fillNamed = function (text, data) {
|
|
492
515
|
for (var key in data) {
|
|
493
516
|
var val = data[key];
|
|
494
517
|
if (val === undefined || val === null) {
|
|
@@ -508,7 +531,7 @@ Engine.prototype._fillNamed = function (text, data) {
|
|
|
508
531
|
* @param {...any} args - 额外替换参数,依次替换 {1}、{2} 等占位符
|
|
509
532
|
* @returns {string} 翻译后的文本
|
|
510
533
|
*/
|
|
511
|
-
|
|
534
|
+
Tpl.prototype.tc = function (key, count, ...args) {
|
|
512
535
|
return this.lang.tc(key, count, ...args);
|
|
513
536
|
};
|
|
514
537
|
|
|
@@ -517,7 +540,7 @@ Engine.prototype.tc = function (key, count, ...args) {
|
|
|
517
540
|
* 过滤掉无词条的语言,Lang 内置的 zh_cn、en 空字典不计入
|
|
518
541
|
* @returns {Array<string>} 语言代码数组
|
|
519
542
|
*/
|
|
520
|
-
|
|
543
|
+
Tpl.prototype.getLangs = function () {
|
|
521
544
|
if (!this.lang) {
|
|
522
545
|
return [];
|
|
523
546
|
}
|
|
@@ -539,7 +562,7 @@ Engine.prototype.getLangs = function () {
|
|
|
539
562
|
* @param {boolean} force - 是否跳过缓存强制重新读取语言包文件
|
|
540
563
|
* @returns {Array<string>} 重新加载后的语言列表
|
|
541
564
|
*/
|
|
542
|
-
|
|
565
|
+
Tpl.prototype.reloadLangs = function (force) {
|
|
543
566
|
var lang = this.getLang();
|
|
544
567
|
this._preset(force);
|
|
545
568
|
this.setLang(lang);
|
|
@@ -551,7 +574,7 @@ Engine.prototype.reloadLangs = function (force) {
|
|
|
551
574
|
* @param {string} lang - 语言代码
|
|
552
575
|
* @returns {Object|null} 语言包对象,不存在时返回null
|
|
553
576
|
*/
|
|
554
|
-
|
|
577
|
+
Tpl.prototype.loadLang = function (lang) {
|
|
555
578
|
if (typeof (lang) !== 'string' || !lang) {
|
|
556
579
|
this.log('warn', '语言参数无效:' + lang);
|
|
557
580
|
return null;
|
|
@@ -570,9 +593,9 @@ Engine.prototype.loadLang = function (lang) {
|
|
|
570
593
|
* 传入对象需实现 getUser / setUser / delUser / hasUser / listUsers / clear 六个同步方法
|
|
571
594
|
* 留空或接口不完整时回退为内置内存存储(Memory)
|
|
572
595
|
* @param {Object} store - 存储对象
|
|
573
|
-
* @returns {
|
|
596
|
+
* @returns {Tpl} 当前实例
|
|
574
597
|
*/
|
|
575
|
-
|
|
598
|
+
Tpl.prototype.setStore = function (store) {
|
|
576
599
|
if (!store || !this._isStore(store)) {
|
|
577
600
|
if (store) {
|
|
578
601
|
this.log('warn', 'user_store 接口不完整,回退为内置内存存储');
|
|
@@ -589,7 +612,7 @@ Engine.prototype.setStore = function (store) {
|
|
|
589
612
|
* 派生实例复用该对象即可共享同一份用户偏好
|
|
590
613
|
* @returns {Object} 存储对象
|
|
591
614
|
*/
|
|
592
|
-
|
|
615
|
+
Tpl.prototype.getStore = function () {
|
|
593
616
|
return this.store;
|
|
594
617
|
};
|
|
595
618
|
|
|
@@ -598,7 +621,7 @@ Engine.prototype.getStore = function () {
|
|
|
598
621
|
* @param {Object} store - 存储对象
|
|
599
622
|
* @returns {boolean} 六个同步方法是否齐备
|
|
600
623
|
*/
|
|
601
|
-
|
|
624
|
+
Tpl.prototype._isStore = function (store) {
|
|
602
625
|
var methods = ['getUser', 'setUser', 'delUser', 'hasUser', 'listUsers', 'clear'];
|
|
603
626
|
for (var i = 0; i < methods.length; i++) {
|
|
604
627
|
if (typeof (store[methods[i]]) !== 'function') {
|
|
@@ -614,7 +637,7 @@ Engine.prototype._isStore = function (store) {
|
|
|
614
637
|
* @param {Object} prefs - 用户偏好,形如 { style, lang }
|
|
615
638
|
* @returns {Object} 规范化后的偏好
|
|
616
639
|
*/
|
|
617
|
-
|
|
640
|
+
Tpl.prototype._prefs = function (prefs) {
|
|
618
641
|
if (!prefs || typeof (prefs) !== 'object') {
|
|
619
642
|
throw new TypeError('用户偏好必须是对象');
|
|
620
643
|
}
|
|
@@ -641,7 +664,7 @@ Engine.prototype._prefs = function (prefs) {
|
|
|
641
664
|
* @param {Object} prefs - 用户偏好,形如 { style, lang },只取这两个字段
|
|
642
665
|
* @returns {Object} 保存后的用户偏好
|
|
643
666
|
*/
|
|
644
|
-
|
|
667
|
+
Tpl.prototype.setUser = function (user_id, prefs) {
|
|
645
668
|
return this.store.setUser(user_id, this._prefs(prefs));
|
|
646
669
|
};
|
|
647
670
|
|
|
@@ -650,7 +673,7 @@ Engine.prototype.setUser = function (user_id, prefs) {
|
|
|
650
673
|
* @param {string|number} user_id - 用户标识
|
|
651
674
|
* @returns {Object|null} 用户偏好,未登记时返回 null
|
|
652
675
|
*/
|
|
653
|
-
|
|
676
|
+
Tpl.prototype.getUser = function (user_id) {
|
|
654
677
|
return this.store.getUser(user_id);
|
|
655
678
|
};
|
|
656
679
|
|
|
@@ -659,7 +682,7 @@ Engine.prototype.getUser = function (user_id) {
|
|
|
659
682
|
* @param {string|number} user_id - 用户标识
|
|
660
683
|
* @returns {boolean} 是否删除成功
|
|
661
684
|
*/
|
|
662
|
-
|
|
685
|
+
Tpl.prototype.removeUser = function (user_id) {
|
|
663
686
|
return this.store.delUser(user_id);
|
|
664
687
|
};
|
|
665
688
|
|
|
@@ -667,7 +690,7 @@ Engine.prototype.removeUser = function (user_id) {
|
|
|
667
690
|
* 获取已登记的用户标识列表
|
|
668
691
|
* @returns {Array<string>} 用户标识数组
|
|
669
692
|
*/
|
|
670
|
-
|
|
693
|
+
Tpl.prototype.getUsers = function () {
|
|
671
694
|
return this.store.listUsers();
|
|
672
695
|
};
|
|
673
696
|
|
|
@@ -679,7 +702,7 @@ Engine.prototype.getUsers = function () {
|
|
|
679
702
|
* @param {Object} options - 其他渲染参数,可省略
|
|
680
703
|
* @returns {Object} 渲染参数,含 style 与 lang
|
|
681
704
|
*/
|
|
682
|
-
|
|
705
|
+
Tpl.prototype.userOptions = function (user_id, options) {
|
|
683
706
|
var opt = Object.assign({}, options);
|
|
684
707
|
var record = this.store.getUser(user_id);
|
|
685
708
|
opt.style = (record && record.style !== undefined) ? record.style : this.current_style;
|
|
@@ -695,8 +718,8 @@ Engine.prototype.userOptions = function (user_id, options) {
|
|
|
695
718
|
* @param {...any} args - 附加参数
|
|
696
719
|
* @returns {Promise<any>} 执行结果
|
|
697
720
|
*/
|
|
698
|
-
|
|
699
|
-
var ret = await
|
|
721
|
+
Tpl.prototype.runFunc = async function (name, req, ...args) {
|
|
722
|
+
var ret = await Tpl.global_hook.runFunc(name, this.viewBag, req, ...args);
|
|
700
723
|
var ret_inst = await this.hook.runFunc(name, this.viewBag, req, ...args);
|
|
701
724
|
return ret_inst || ret;
|
|
702
725
|
};
|
|
@@ -708,19 +731,19 @@ Engine.prototype.runFunc = async function (name, req, ...args) {
|
|
|
708
731
|
* @param {...any} args - 附加参数
|
|
709
732
|
* @returns {Promise<any>} 执行结果
|
|
710
733
|
*/
|
|
711
|
-
|
|
734
|
+
Tpl.prototype.runHook = Tpl.prototype.runFunc;
|
|
712
735
|
|
|
713
736
|
/**
|
|
714
737
|
* 初始化前处理
|
|
715
738
|
* @param {Object} config - 配置参数
|
|
716
739
|
*/
|
|
717
|
-
|
|
740
|
+
Tpl.prototype._initBefore = function (config) { };
|
|
718
741
|
|
|
719
742
|
/**
|
|
720
743
|
* 初始化主处理
|
|
721
744
|
* @param {Object} config - 配置参数
|
|
722
745
|
*/
|
|
723
|
-
|
|
746
|
+
Tpl.prototype._initMain = function (config) {
|
|
724
747
|
this.setConfig(config);
|
|
725
748
|
this._initTpl();
|
|
726
749
|
};
|
|
@@ -730,7 +753,7 @@ Engine.prototype._initMain = function (config) {
|
|
|
730
753
|
* 构建字符转义规则,供渲染前后转义与还原使用
|
|
731
754
|
* @param {Object} config - 配置参数
|
|
732
755
|
*/
|
|
733
|
-
|
|
756
|
+
Tpl.prototype._initAfter = function (config) {
|
|
734
757
|
this.escape_rule = TplUtil.buildEscapeRule(this.config.change_str);
|
|
735
758
|
};
|
|
736
759
|
|
|
@@ -738,7 +761,7 @@ Engine.prototype._initAfter = function (config) {
|
|
|
738
761
|
* 初始化模板
|
|
739
762
|
* @param {Object} config - 配置参数
|
|
740
763
|
*/
|
|
741
|
-
|
|
764
|
+
Tpl.prototype._init = function (config) {
|
|
742
765
|
this._initBefore(config);
|
|
743
766
|
this._initMain(config);
|
|
744
767
|
this._initAfter(config);
|
|
@@ -748,7 +771,7 @@ Engine.prototype._init = function (config) {
|
|
|
748
771
|
* 获取当前主题
|
|
749
772
|
* @returns {string} 主题名称
|
|
750
773
|
*/
|
|
751
|
-
|
|
774
|
+
Tpl.prototype.getTheme = function () {
|
|
752
775
|
return this.current_theme;
|
|
753
776
|
};
|
|
754
777
|
|
|
@@ -760,7 +783,7 @@ Engine.prototype.getTheme = function () {
|
|
|
760
783
|
* @param {boolean} force - 是否跳过目录校验
|
|
761
784
|
* @returns {string} 设置后的主题名称
|
|
762
785
|
*/
|
|
763
|
-
|
|
786
|
+
Tpl.prototype.setTheme = function (theme, force) {
|
|
764
787
|
if (typeof (theme) !== 'string' || !theme) {
|
|
765
788
|
this.log('warn', '主题参数无效,保持当前主题:' + this.current_theme);
|
|
766
789
|
return this.current_theme;
|
|
@@ -782,7 +805,7 @@ Engine.prototype.setTheme = function (theme, force) {
|
|
|
782
805
|
* 获取当前风格
|
|
783
806
|
* @returns {string} 风格名称,未设置时返回空字符串
|
|
784
807
|
*/
|
|
785
|
-
|
|
808
|
+
Tpl.prototype.getStyle = function () {
|
|
786
809
|
return this.current_style;
|
|
787
810
|
};
|
|
788
811
|
|
|
@@ -792,7 +815,7 @@ Engine.prototype.getStyle = function () {
|
|
|
792
815
|
* @param {boolean} force - 是否跳过目录校验
|
|
793
816
|
* @returns {string} 设置后的风格名称
|
|
794
817
|
*/
|
|
795
|
-
|
|
818
|
+
Tpl.prototype.setStyle = function (style, force) {
|
|
796
819
|
if (typeof (style) !== 'string') {
|
|
797
820
|
this.log('warn', '风格参数无效,保持当前风格:' + this.current_style);
|
|
798
821
|
return this.current_style;
|
|
@@ -811,7 +834,7 @@ Engine.prototype.setStyle = function (style, force) {
|
|
|
811
834
|
* @param {boolean} restore 是否还原转义字符
|
|
812
835
|
* @returns {string} 转义后的字符串
|
|
813
836
|
*/
|
|
814
|
-
|
|
837
|
+
Tpl.prototype.escape = function (body, restore) {
|
|
815
838
|
return TplUtil.escape(body, this.escape_rule, restore);
|
|
816
839
|
};
|
|
817
840
|
|
|
@@ -820,7 +843,7 @@ Engine.prototype.escape = function (body, restore) {
|
|
|
820
843
|
* @param {Object} m - 模型对象
|
|
821
844
|
* @returns {Object} 处理后的模型
|
|
822
845
|
*/
|
|
823
|
-
|
|
846
|
+
Tpl.prototype._loadFuncBefore = function (m) {
|
|
824
847
|
// m.$i18n = function(key) {
|
|
825
848
|
// return $.i18n(key);
|
|
826
849
|
// }
|
|
@@ -832,7 +855,7 @@ Engine.prototype._loadFuncBefore = function (m) {
|
|
|
832
855
|
* @param {Object} m 模型对象
|
|
833
856
|
* @returns {Object} 处理后的模型
|
|
834
857
|
*/
|
|
835
|
-
|
|
858
|
+
Tpl.prototype.loadFunc = function (m) {
|
|
836
859
|
this._loadFuncBefore(m);
|
|
837
860
|
var _this = this;
|
|
838
861
|
m.view = function (file, model, options) {
|
|
@@ -856,13 +879,13 @@ Engine.prototype.loadFunc = function (m) {
|
|
|
856
879
|
};
|
|
857
880
|
m.hookAction = function (name, ...args) {
|
|
858
881
|
// 全局动作钩子先输出,实例级动作钩子后输出
|
|
859
|
-
return
|
|
882
|
+
return Tpl.global_hook.runAction(name, m, ...args) + _this.hook.runAction(name, m, ...args);
|
|
860
883
|
};
|
|
861
884
|
m.hookFilter = function (name, content, ...args) {
|
|
862
885
|
if (!content) {
|
|
863
886
|
content = '';
|
|
864
887
|
}
|
|
865
|
-
var ret =
|
|
888
|
+
var ret = Tpl.global_hook.runFilter(name, m, content, ...args);
|
|
866
889
|
return _this.hook.runFilter(name, m, ret, ...args);
|
|
867
890
|
};
|
|
868
891
|
if (this.lang) {
|
|
@@ -887,7 +910,7 @@ Engine.prototype.loadFunc = function (m) {
|
|
|
887
910
|
* @param {Function} func - 函数
|
|
888
911
|
* @returns {boolean} 是否设置成功
|
|
889
912
|
*/
|
|
890
|
-
|
|
913
|
+
Tpl.prototype.setFunc = function (name, func) {
|
|
891
914
|
if (typeof (name) !== 'string' || !name) {
|
|
892
915
|
this.log('warn', '模板函数名无效:' + name);
|
|
893
916
|
return false;
|
|
@@ -905,7 +928,7 @@ Engine.prototype.setFunc = function (name, func) {
|
|
|
905
928
|
* @param {string} name - 函数名
|
|
906
929
|
* @returns {boolean} 是否存在并删除成功
|
|
907
930
|
*/
|
|
908
|
-
|
|
931
|
+
Tpl.prototype.removeFunc = function (name) {
|
|
909
932
|
if (this.funcs[name] === undefined) {
|
|
910
933
|
return false;
|
|
911
934
|
}
|
|
@@ -917,7 +940,7 @@ Engine.prototype.removeFunc = function (name) {
|
|
|
917
940
|
* 获取模板函数表
|
|
918
941
|
* @returns {Object} 模板函数表
|
|
919
942
|
*/
|
|
920
|
-
|
|
943
|
+
Tpl.prototype.getFuncs = function () {
|
|
921
944
|
return this.funcs;
|
|
922
945
|
};
|
|
923
946
|
|
|
@@ -928,12 +951,12 @@ Engine.prototype.getFuncs = function () {
|
|
|
928
951
|
* @param {Object} options - 配置参数
|
|
929
952
|
* @returns {string} 渲染结果
|
|
930
953
|
*/
|
|
931
|
-
|
|
954
|
+
Tpl.prototype._renderMain = function (body, model, options) {
|
|
932
955
|
var ret = '';
|
|
933
956
|
if (body) {
|
|
934
957
|
var m = Object.assign({}, {
|
|
935
958
|
viewBag: this.viewBag,
|
|
936
|
-
globalBag:
|
|
959
|
+
globalBag: Tpl.global_bag
|
|
937
960
|
}, model);
|
|
938
961
|
this.loadFunc(m);
|
|
939
962
|
ret = this.engine.render(body, m, options);
|
|
@@ -948,7 +971,7 @@ Engine.prototype._renderMain = function (body, model, options) {
|
|
|
948
971
|
* @param {Object} options - 渲染参数
|
|
949
972
|
* @returns {Object|null} 原风格与原语言,未涉及临时选项时返回 null
|
|
950
973
|
*/
|
|
951
|
-
|
|
974
|
+
Tpl.prototype._applyTempOptions = function (options) {
|
|
952
975
|
if (!options) {
|
|
953
976
|
return null;
|
|
954
977
|
}
|
|
@@ -974,7 +997,7 @@ Engine.prototype._applyTempOptions = function (options) {
|
|
|
974
997
|
* 恢复临时选项
|
|
975
998
|
* @param {Object|null} prev - _applyTempOptions 返回的原值
|
|
976
999
|
*/
|
|
977
|
-
|
|
1000
|
+
Tpl.prototype._restoreTempOptions = function (prev) {
|
|
978
1001
|
if (!prev) {
|
|
979
1002
|
return;
|
|
980
1003
|
}
|
|
@@ -992,7 +1015,7 @@ Engine.prototype._restoreTempOptions = function (prev) {
|
|
|
992
1015
|
* @param {Object} options - 配置参数
|
|
993
1016
|
* @returns {string} 渲染结果
|
|
994
1017
|
*/
|
|
995
|
-
|
|
1018
|
+
Tpl.prototype.render = function (body, model, options) {
|
|
996
1019
|
var prev = this._applyTempOptions(options);
|
|
997
1020
|
try {
|
|
998
1021
|
body = this.escape(body);
|
|
@@ -1009,7 +1032,7 @@ Engine.prototype.render = function (body, model, options) {
|
|
|
1009
1032
|
* @param {Object} options - 配置参数
|
|
1010
1033
|
* @returns {Function} 渲染函数
|
|
1011
1034
|
*/
|
|
1012
|
-
|
|
1035
|
+
Tpl.prototype.compile = function (body, options) {
|
|
1013
1036
|
return this.engine.compile(body, options);
|
|
1014
1037
|
};
|
|
1015
1038
|
|
|
@@ -1019,7 +1042,7 @@ Engine.prototype.compile = function (body, options) {
|
|
|
1019
1042
|
* @param {Object} options - 配置参数,dir 为指定的查找目录
|
|
1020
1043
|
* @returns {string} 模板文件路径
|
|
1021
1044
|
*/
|
|
1022
|
-
|
|
1045
|
+
Tpl.prototype.getFile = function (file, options) {
|
|
1023
1046
|
return this.template.getFile(file, options);
|
|
1024
1047
|
};
|
|
1025
1048
|
|
|
@@ -1029,7 +1052,7 @@ Engine.prototype.getFile = function (file, options) {
|
|
|
1029
1052
|
* 结果按优先级升序排列:基础风格在前打底,当前风格与自定义目录在后覆盖
|
|
1030
1053
|
* @returns {Array<string>} 样式文件全路径列表
|
|
1031
1054
|
*/
|
|
1032
|
-
|
|
1055
|
+
Tpl.prototype.getCssFiles = function () {
|
|
1033
1056
|
return this.css.getFiles();
|
|
1034
1057
|
};
|
|
1035
1058
|
|
|
@@ -1041,7 +1064,7 @@ Engine.prototype.getCssFiles = function () {
|
|
|
1041
1064
|
* @param {string} style - 风格名称,不传时取当前风格
|
|
1042
1065
|
* @returns {Array<string>} 命中的样式文件全路径列表,顺序即引用顺序
|
|
1043
1066
|
*/
|
|
1044
|
-
|
|
1067
|
+
Tpl.prototype.matchCssFiles = function (pattern, theme, style) {
|
|
1045
1068
|
return this.css.matchFiles(pattern, theme, style);
|
|
1046
1069
|
};
|
|
1047
1070
|
|
|
@@ -1054,7 +1077,7 @@ Engine.prototype.matchCssFiles = function (pattern, theme, style) {
|
|
|
1054
1077
|
* @param {string} style - 风格名称,不传时取当前风格
|
|
1055
1078
|
* @returns {string|Array<string>|null} 样式文件全路径;无通配符且不存在时返回 null
|
|
1056
1079
|
*/
|
|
1057
|
-
|
|
1080
|
+
Tpl.prototype.getCssFile = function (name, theme, style) {
|
|
1058
1081
|
if (!name || typeof (name) !== 'string') {
|
|
1059
1082
|
return null;
|
|
1060
1083
|
}
|
|
@@ -1072,7 +1095,7 @@ Engine.prototype.getCssFile = function (name, theme, style) {
|
|
|
1072
1095
|
* @param {string} theme - 主题名称,不传时取当前主题
|
|
1073
1096
|
* @returns {Array<string>} 命中的脚本文件全路径列表,顺序即执行顺序
|
|
1074
1097
|
*/
|
|
1075
|
-
|
|
1098
|
+
Tpl.prototype.matchJsFiles = function (pattern, theme) {
|
|
1076
1099
|
return this.js.matchFiles(pattern, theme);
|
|
1077
1100
|
};
|
|
1078
1101
|
|
|
@@ -1082,7 +1105,7 @@ Engine.prototype.matchJsFiles = function (pattern, theme) {
|
|
|
1082
1105
|
* 结果按优先级升序排列:应用级公共脚本在前,主题脚本与自定义目录在后
|
|
1083
1106
|
* @returns {Array<string>} 脚本文件全路径列表
|
|
1084
1107
|
*/
|
|
1085
|
-
|
|
1108
|
+
Tpl.prototype.getJsFiles = function () {
|
|
1086
1109
|
return this.js.getFiles();
|
|
1087
1110
|
};
|
|
1088
1111
|
|
|
@@ -1094,7 +1117,7 @@ Engine.prototype.getJsFiles = function () {
|
|
|
1094
1117
|
* @param {string} theme - 主题名称,不传时取当前主题
|
|
1095
1118
|
* @returns {string|Array<string>|null} 脚本文件全路径;无通配符且不存在时返回 null
|
|
1096
1119
|
*/
|
|
1097
|
-
|
|
1120
|
+
Tpl.prototype.getJsFile = function (name, theme) {
|
|
1098
1121
|
if (!name || typeof (name) !== 'string') {
|
|
1099
1122
|
return null;
|
|
1100
1123
|
}
|
|
@@ -1112,7 +1135,7 @@ Engine.prototype.getJsFile = function (name, theme) {
|
|
|
1112
1135
|
* @param {Object} options - 配置参数,dir 为指定的查找目录
|
|
1113
1136
|
* @returns {string} 组件文件路径
|
|
1114
1137
|
*/
|
|
1115
|
-
|
|
1138
|
+
Tpl.prototype.getComponentFile = function (name, options) {
|
|
1116
1139
|
return this.template.getComponent(name, options);
|
|
1117
1140
|
};
|
|
1118
1141
|
|
|
@@ -1125,7 +1148,7 @@ Engine.prototype.getComponentFile = function (name, options) {
|
|
|
1125
1148
|
* @param {Object} options - 配置参数
|
|
1126
1149
|
* @returns {string} 渲染结果
|
|
1127
1150
|
*/
|
|
1128
|
-
|
|
1151
|
+
Tpl.prototype.component = function (name, model, options) {
|
|
1129
1152
|
var dirs = this.getComponentDirs();
|
|
1130
1153
|
if (!dirs.length) {
|
|
1131
1154
|
this.error = {
|
|
@@ -1135,14 +1158,14 @@ Engine.prototype.component = function (name, model, options) {
|
|
|
1135
1158
|
return '';
|
|
1136
1159
|
}
|
|
1137
1160
|
var f = this.getComponentFile(name, options);
|
|
1138
|
-
if (!
|
|
1161
|
+
if (!FileUtil.hasFile(f)) {
|
|
1139
1162
|
this.error = {
|
|
1140
1163
|
code: 10000,
|
|
1141
1164
|
message: f + '组件不存在!'
|
|
1142
1165
|
};
|
|
1143
1166
|
return '';
|
|
1144
1167
|
}
|
|
1145
|
-
var body =
|
|
1168
|
+
var body = FileUtil.loadText(f);
|
|
1146
1169
|
if (!body) {
|
|
1147
1170
|
return '';
|
|
1148
1171
|
}
|
|
@@ -1160,18 +1183,18 @@ Engine.prototype.component = function (name, model, options) {
|
|
|
1160
1183
|
* @param {Object} options - 配置参数
|
|
1161
1184
|
* @returns {string} 渲染结果
|
|
1162
1185
|
*/
|
|
1163
|
-
|
|
1186
|
+
Tpl.prototype.view = function (file, model, options) {
|
|
1164
1187
|
var prev = this._applyTempOptions(options);
|
|
1165
1188
|
try {
|
|
1166
1189
|
var f = this.getFile(file, options);
|
|
1167
|
-
if (!
|
|
1190
|
+
if (!FileUtil.hasFile(f)) {
|
|
1168
1191
|
this.error = {
|
|
1169
1192
|
code: 10000,
|
|
1170
1193
|
message: f + '文件不存在!'
|
|
1171
1194
|
};
|
|
1172
1195
|
}
|
|
1173
1196
|
else {
|
|
1174
|
-
var body =
|
|
1197
|
+
var body = FileUtil.loadText(f);
|
|
1175
1198
|
if (body) {
|
|
1176
1199
|
body = this.escape(body);
|
|
1177
1200
|
body = this._renderMain(body, model, options);
|
|
@@ -1189,15 +1212,15 @@ Engine.prototype.view = function (file, model, options) {
|
|
|
1189
1212
|
* 设置视图数据
|
|
1190
1213
|
* @param {Object} obj - 要设置的数据对象
|
|
1191
1214
|
*/
|
|
1192
|
-
|
|
1193
|
-
|
|
1215
|
+
Tpl.prototype.set = function (obj) {
|
|
1216
|
+
TplUtil.mergeDeep(this.viewBag, obj, true);
|
|
1194
1217
|
};
|
|
1195
1218
|
|
|
1196
1219
|
/**
|
|
1197
1220
|
* 删除视图数据
|
|
1198
1221
|
* @param {Array|string} keys - 要删除的键或键数组
|
|
1199
1222
|
*/
|
|
1200
|
-
|
|
1223
|
+
Tpl.prototype.del = function (keys) {
|
|
1201
1224
|
if (typeof (keys) == 'string') {
|
|
1202
1225
|
delete this.viewBag[keys];
|
|
1203
1226
|
} else {
|
|
@@ -1212,7 +1235,7 @@ Engine.prototype.del = function (keys) {
|
|
|
1212
1235
|
* @param {string} key - 数据键名
|
|
1213
1236
|
* @returns {any} 数据值
|
|
1214
1237
|
*/
|
|
1215
|
-
|
|
1238
|
+
Tpl.prototype.get = function (key) {
|
|
1216
1239
|
return this.viewBag[key];
|
|
1217
1240
|
};
|
|
1218
1241
|
|
|
@@ -1224,9 +1247,9 @@ Engine.prototype.get = function (key) {
|
|
|
1224
1247
|
* @param {string} config.theme - 主题名称
|
|
1225
1248
|
* @param {string} config.style - 风格名称
|
|
1226
1249
|
* @param {string} config.lang - 语言代码
|
|
1227
|
-
* @returns {
|
|
1250
|
+
* @returns {Tpl} 派生出的引擎实例
|
|
1228
1251
|
*/
|
|
1229
|
-
|
|
1252
|
+
Tpl.prototype.fork = function (config) {
|
|
1230
1253
|
var cg = config || {};
|
|
1231
1254
|
var lang = (cg.lang && this.lang.isSupported(cg.lang)) ? cg.lang : this.getLang();
|
|
1232
1255
|
var cfg = Object.assign({}, this.config, {
|
|
@@ -1237,7 +1260,7 @@ Engine.prototype.fork = function (config) {
|
|
|
1237
1260
|
// 用户偏好属于用户而非实例,派生实例复用同一份存储
|
|
1238
1261
|
user_store: this.getStore()
|
|
1239
1262
|
});
|
|
1240
|
-
var tpl = new
|
|
1263
|
+
var tpl = new Tpl(cfg);
|
|
1241
1264
|
tpl.dir = this.dir;
|
|
1242
1265
|
if (cg.theme) {
|
|
1243
1266
|
tpl.setTheme(cg.theme);
|
|
@@ -1245,12 +1268,12 @@ Engine.prototype.fork = function (config) {
|
|
|
1245
1268
|
if (cg.style) {
|
|
1246
1269
|
tpl.setStyle(cg.style);
|
|
1247
1270
|
}
|
|
1248
|
-
|
|
1271
|
+
TplUtil.mergeDeep(tpl.viewBag, this.viewBag, true);
|
|
1249
1272
|
// 模板函数属于站点级扩展,派生实例继承同一份
|
|
1250
1273
|
tpl.funcs = Object.assign({}, this.funcs);
|
|
1251
1274
|
return tpl;
|
|
1252
1275
|
};
|
|
1253
1276
|
|
|
1254
1277
|
module.exports = {
|
|
1255
|
-
|
|
1278
|
+
Tpl
|
|
1256
1279
|
};
|