@kobalab/liulian 0.7.7 → 0.9.0
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/ChangeLog.md +19 -0
- package/README.md +4 -0
- package/bin/liulian.js +4 -1
- package/css/icon.png +0 -0
- package/css/liulian.css +34 -2
- package/lib/backup/git.js +96 -0
- package/lib/html/file.js +78 -5
- package/lib/html/folder.js +4 -33
- package/lib/html/index.js +17 -0
- package/lib/html/text.js +30 -0
- package/lib/http/request.js +6 -0
- package/lib/http/response.js +6 -0
- package/lib/module/core.js +12 -1
- package/lib/module/index.js +9 -9
- package/lib/module/paiga.js +124 -0
- package/lib/module/pinyin.js +51 -0
- package/lib/resource/file.js +32 -7
- package/lib/resource/index.js +4 -0
- package/lib/resource/liulian.js +11 -2
- package/lib/resource/text.js +22 -6
- package/lib/text/liulian.js +6 -0
- package/lib/util/str-tool.js +40 -0
- package/locale/en +10 -0
- package/locale/ja +10 -0
- package/locale/zh-CN +10 -0
- package/package.json +2 -2
package/ChangeLog.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
## v0.9.0 / 2022-04-03
|
|
2
|
+
|
|
3
|
+
- import モジュールで外部モジュールをインポートできるようにした
|
|
4
|
+
- pinyin モジュール(中国語のピンインを表示する)を追加
|
|
5
|
+
- paiga モジュール(麻雀の牌画像を表示する)を追加
|
|
6
|
+
- 脆弱性警告に対処(minimist 1.2.5 → 1.2.6)
|
|
7
|
+
|
|
8
|
+
### v0.8.1 / 2022-01-24
|
|
9
|
+
|
|
10
|
+
- 脆弱性警告に対処(mocha 9.1.3 → 9.2.0)
|
|
11
|
+
|
|
12
|
+
## v0.8.0 / 2021-12-30
|
|
13
|
+
|
|
14
|
+
- #4 自動バックアップの機能を追加
|
|
15
|
+
- redirect モジュールを追加
|
|
16
|
+
- アイコンを変更
|
|
17
|
+
- include モジュールの再帰的使用を検知できるようにした
|
|
18
|
+
- HEADとTAILを二重インクルードしないよう修正
|
|
19
|
+
|
|
1
20
|
### v0.7.7 / 2021-12-03
|
|
2
21
|
|
|
3
22
|
- | のみの行があると異常終了するバグを修正
|
package/README.md
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
<a href="https://kobalab.net/xiumai/"><img src="https://kobalab.net/xiumai/theme/xiumai.png" alt="XiuMai" height=24 valign=bottom></a>
|
|
4
4
|
の後継。Node.jsで動作するWebサイト作成ツール。
|
|
5
5
|
|
|
6
|
+
## デモ
|
|
7
|
+
https://kobalab.net/liulian/
|
|
8
|
+
|
|
6
9
|
## インストール
|
|
7
10
|
```sh
|
|
8
11
|
$ npm i -g @kobalab/liulian
|
|
@@ -33,6 +36,7 @@ $ liulian ~/Documents/LiuLian
|
|
|
33
36
|
- [README と index](https://kobalab.net/liulian/man/readme&index)
|
|
34
37
|
- [HEAD と TAIL](https://kobalab.net/liulian/man/head&tail)
|
|
35
38
|
- [リバースプロキシを使う](https://kobalab.net/liulian/man/proxy-setting)
|
|
39
|
+
- [Gitで自動バックアップする](https://kobalab.net/liulian/man/git)
|
|
36
40
|
|
|
37
41
|
## ライセンス
|
|
38
42
|
[MIT](https://github.com/kobalab/LiuLian/blob/master/LICENSE)
|
package/bin/liulian.js
CHANGED
|
@@ -17,6 +17,8 @@ const mount = argv.mount;
|
|
|
17
17
|
|
|
18
18
|
require('../lib/setup')(home);
|
|
19
19
|
|
|
20
|
+
const backup = require('../lib/backup/git')(path.join(home, 'docs'));
|
|
21
|
+
|
|
20
22
|
const locale = require('../lib/util/locale')(
|
|
21
23
|
path.join(__dirname, '../locale'),
|
|
22
24
|
'en');
|
|
@@ -44,7 +46,8 @@ const liulian = require('../lib/liulian')({
|
|
|
44
46
|
locale: locale,
|
|
45
47
|
mount: mount,
|
|
46
48
|
auth: auth,
|
|
47
|
-
passport: passport
|
|
49
|
+
passport: passport,
|
|
50
|
+
backup: backup });
|
|
48
51
|
|
|
49
52
|
const app = express();
|
|
50
53
|
|
package/css/icon.png
CHANGED
|
Binary file
|
package/css/liulian.css
CHANGED
|
@@ -95,7 +95,7 @@ pre {
|
|
|
95
95
|
background: #eee;
|
|
96
96
|
overflow: auto;
|
|
97
97
|
overflow-wrap: normal;
|
|
98
|
-
font-family: Osaka-Mono, "MS ゴシック", Courier, monospace;
|
|
98
|
+
font-family: Osaka-Mono, "HGゴシックM", "MS ゴシック", Courier, monospace;
|
|
99
99
|
font-size: 100%;
|
|
100
100
|
}
|
|
101
101
|
|
|
@@ -151,7 +151,7 @@ input[disabled] {
|
|
|
151
151
|
}
|
|
152
152
|
textarea {
|
|
153
153
|
padding: 4px;
|
|
154
|
-
font-family: Osaka-Mono, "MS ゴシック", Courier, monospace;
|
|
154
|
+
font-family: Osaka-Mono, "HGゴシックM", "MS ゴシック", Courier, monospace;
|
|
155
155
|
font-size: 100%;
|
|
156
156
|
border: solid 1px #999;
|
|
157
157
|
border-radius: 4px;
|
|
@@ -296,6 +296,38 @@ input[type="submit"] {
|
|
|
296
296
|
width: 100%;
|
|
297
297
|
height: 70vh;
|
|
298
298
|
}
|
|
299
|
+
#l-log .l-time {
|
|
300
|
+
text-align: right;
|
|
301
|
+
}
|
|
302
|
+
#l-diff {
|
|
303
|
+
border-radius: 0.2em;
|
|
304
|
+
border: solid 1px #080;
|
|
305
|
+
padding: 0;
|
|
306
|
+
background: #ddd;
|
|
307
|
+
}
|
|
308
|
+
#l-diff > * {
|
|
309
|
+
display: block;
|
|
310
|
+
padding: 2px 0.5em;
|
|
311
|
+
margin: 1px 0;
|
|
312
|
+
min-height: 1em;
|
|
313
|
+
font-family: Osaka-Mono, "MS ゴシック", Courier, monospace;
|
|
314
|
+
text-decoration: none;
|
|
315
|
+
white-space: pre-wrap;
|
|
316
|
+
color: #666;
|
|
317
|
+
background: #ffe;
|
|
318
|
+
}
|
|
319
|
+
#l-diff .l-head {
|
|
320
|
+
color: #999;
|
|
321
|
+
background: #cdf;
|
|
322
|
+
}
|
|
323
|
+
#l-diff ins {
|
|
324
|
+
color: #393;
|
|
325
|
+
background: #dfd;
|
|
326
|
+
}
|
|
327
|
+
#l-diff del {
|
|
328
|
+
color: #c33;
|
|
329
|
+
background: #fdd;
|
|
330
|
+
}
|
|
299
331
|
|
|
300
332
|
.l-error {
|
|
301
333
|
color: red;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* backup/git
|
|
3
|
+
*/
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const execSync = require('child_process').execFileSync;
|
|
9
|
+
const exec = require('util').promisify(require('child_process').execFile);
|
|
10
|
+
const spawn = require('child_process').spawn;
|
|
11
|
+
|
|
12
|
+
class Git {
|
|
13
|
+
|
|
14
|
+
constructor(repodir) {
|
|
15
|
+
let git_dir = path.join(repodir, '.git');
|
|
16
|
+
this._option = [ '--git-dir=' + git_dir, '--work-tree=' + repodir ];
|
|
17
|
+
fs.statSync(git_dir);
|
|
18
|
+
execSync('git', this._option.concat('init'));
|
|
19
|
+
try {
|
|
20
|
+
execSync('git', this._option.concat('log'));
|
|
21
|
+
}
|
|
22
|
+
catch(e) {
|
|
23
|
+
execSync('git', this._option.concat([
|
|
24
|
+
'commit','--allow-empty','-m','.']));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async checkIn(location, time, user) {
|
|
29
|
+
let path = location.replace(/^\//,'');
|
|
30
|
+
let log = await exec('git', this._option.concat([
|
|
31
|
+
'log','--oneline','--', path]));
|
|
32
|
+
let diff = await exec('git', this._option.concat(['diff','--', path]));
|
|
33
|
+
if (! log.stdout || diff.stdout) {
|
|
34
|
+
await exec('git', this._option.concat(['add', path]));
|
|
35
|
+
await exec('git', this._option.concat([
|
|
36
|
+
'commit','-m', `${time}:${user}`]));
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async checkOut(location, rev) {
|
|
41
|
+
let path = location.replace(/^\//,'');
|
|
42
|
+
let rv = await exec('git', this._option.concat([
|
|
43
|
+
'show',`${rev}:${path}`]));
|
|
44
|
+
return rv.stdout;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
checkOutStream(location, rev) {
|
|
48
|
+
let path = location.replace(/^\//,'');
|
|
49
|
+
let rv = spawn('git', this._option.concat([
|
|
50
|
+
'show',`${rev}:${path}`]));
|
|
51
|
+
return rv.stdout;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async log(location) {
|
|
55
|
+
let path = location.replace(/^\//,'');
|
|
56
|
+
let rv = await exec('git', this._option.concat([
|
|
57
|
+
'log','--pretty=format:%h\t%s','--', path]));
|
|
58
|
+
let log = [];
|
|
59
|
+
if (! rv.stdout) return log;
|
|
60
|
+
for (let line of rv.stdout.split(/\n/)) {
|
|
61
|
+
let [rev, comment] = line.split(/\t/);
|
|
62
|
+
let [time, user] = comment.split(/:/);
|
|
63
|
+
log.push({ rev: rev, time: +time, user: user });
|
|
64
|
+
}
|
|
65
|
+
return log;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async diff(location, rev1, rev2) {
|
|
69
|
+
let rv;
|
|
70
|
+
let path = location.replace(/^\//,'');
|
|
71
|
+
if (rev2) {
|
|
72
|
+
rv = await exec('git', this._option.concat([
|
|
73
|
+
'diff',`${rev1}..${rev2}`,'--', path]));
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
rv = await exec('git', this._option.concat([
|
|
77
|
+
'diff', rev1, '--', path]));
|
|
78
|
+
}
|
|
79
|
+
let diff = [], body;
|
|
80
|
+
for (let line of rv.stdout.split(/\n/)) {
|
|
81
|
+
if (line.match(/^@@/)) body = 1;
|
|
82
|
+
if (! body) continue;
|
|
83
|
+
diff.push(line);
|
|
84
|
+
}
|
|
85
|
+
return diff;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = function(repodir) {
|
|
90
|
+
try {
|
|
91
|
+
return new Git(repodir);
|
|
92
|
+
}
|
|
93
|
+
catch(e) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
package/lib/html/file.js
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"use strict";
|
|
5
5
|
|
|
6
6
|
const { cdata, fixpath } = require('../util/html-escape');
|
|
7
|
+
const { timeStr } = require('../util/str-tool');
|
|
7
8
|
|
|
8
9
|
const HTML = require('./');
|
|
9
10
|
|
|
@@ -15,19 +16,40 @@ module.exports = class File extends HTML {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
_editmenu() {
|
|
19
|
+
let path = fixpath(this._r.location, this._req.baseUrl);
|
|
20
|
+
let rev = this._req.param('rev');
|
|
21
|
+
let param = this._req.cmd == 'edit' ? ''
|
|
22
|
+
: this._req.cmd == 'diff' ? '?cmd=edit'
|
|
23
|
+
: rev ? `?cmd=edit&rev=${rev}`
|
|
24
|
+
: '?cmd=edit';
|
|
25
|
+
|
|
26
|
+
return `<li><a href="${cdata(path + param)}" accesskey="e">`
|
|
27
|
+
+ cdata(this.msg('toolbar.edit'))
|
|
28
|
+
+ `</a></li>\n`;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_logmenu() {
|
|
32
|
+
if (! this._r._backup) return ''
|
|
18
33
|
let path = fixpath(this._r.location, this._req.baseUrl);
|
|
19
34
|
return '<li><a href="'
|
|
20
35
|
+ cdata(path)
|
|
21
|
-
+ (this._req.cmd == '
|
|
22
|
-
+ '"
|
|
23
|
-
+ cdata(this.msg('toolbar.
|
|
36
|
+
+ (this._req.cmd == 'log' ? '' : '?cmd=log')
|
|
37
|
+
+ '">'
|
|
38
|
+
+ cdata(this.msg('toolbar.log'))
|
|
24
39
|
+ `</a></li>\n`;
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
_title() {
|
|
28
|
-
this.
|
|
43
|
+
const req = this._req;
|
|
44
|
+
const msg = req.msg;
|
|
45
|
+
|
|
46
|
+
let title = this._r.name;
|
|
47
|
+
if (req.cmd == 'log') title = msg('log.title', title);
|
|
48
|
+
else if (req.cmd == 'diff') title = msg('diff.title', title);
|
|
49
|
+
|
|
50
|
+
this.title(title);
|
|
29
51
|
return `<h1 id="l-title"><a href="${cdata(this._r.name)}">`
|
|
30
|
-
+ `${cdata(
|
|
52
|
+
+ `${cdata(title)}</a></h1>\n`;
|
|
31
53
|
}
|
|
32
54
|
|
|
33
55
|
_path() {
|
|
@@ -92,6 +114,50 @@ module.exports = class File extends HTML {
|
|
|
92
114
|
+ '</script>\n';
|
|
93
115
|
}
|
|
94
116
|
|
|
117
|
+
_log() {
|
|
118
|
+
|
|
119
|
+
const msg = this._req.msg;
|
|
120
|
+
|
|
121
|
+
let html = '<table id="l-log">\n';
|
|
122
|
+
html += '<tr>'
|
|
123
|
+
+ `<th>${cdata(msg('log.time'))}</th>`
|
|
124
|
+
+ (this._r.text != null
|
|
125
|
+
? `<th colspan="2">${cdata(msg('log.diff'))}</th>`
|
|
126
|
+
: '')
|
|
127
|
+
+ '<th></th>'
|
|
128
|
+
+ (this._r.text != null ? '<th></th>' : '')
|
|
129
|
+
+ '</tr>\n'
|
|
130
|
+
|
|
131
|
+
for (let i = 0; i < this._r.log.length; i++) {
|
|
132
|
+
|
|
133
|
+
let log = this._r.log[i];
|
|
134
|
+
let log2 = this._r.log[i + 1];
|
|
135
|
+
|
|
136
|
+
html += '<tr>'
|
|
137
|
+
+ `<td class="l-time">${timeStr(log.time)}</td>`
|
|
138
|
+
+ (this._r.text != null
|
|
139
|
+
? ((log2 ? (
|
|
140
|
+
'<td class="l-diff">'
|
|
141
|
+
+ `<a href="${cdata(
|
|
142
|
+
`?cmd=diff&rev=${log2.rev}&rev=${log.rev}`)}">`
|
|
143
|
+
+ `${cdata(msg('log.prev'))}</a></td>`
|
|
144
|
+
) : '<td></td>')
|
|
145
|
+
+ '<td class="l-diff">'
|
|
146
|
+
+ `<a href="${cdata(`?cmd=diff&rev=${log.rev}`)}">`
|
|
147
|
+
+ `${cdata(msg('log.curr'))}</a></td>`)
|
|
148
|
+
: '')
|
|
149
|
+
+ `<td><a href="?rev=${log.rev}">`
|
|
150
|
+
+ `${cdata(msg('log.show'))}</a></td>`
|
|
151
|
+
+ (this._r.text != null
|
|
152
|
+
? `<td><a href="?cmd=edit&rev=${log.rev}">`
|
|
153
|
+
+ `${cdata(msg('log.edit'))}</a></td>`
|
|
154
|
+
: '')
|
|
155
|
+
+ '</tr>\n';
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return html += '</table>\n';
|
|
159
|
+
}
|
|
160
|
+
|
|
95
161
|
edit() {
|
|
96
162
|
return this.stringify(
|
|
97
163
|
this._title()
|
|
@@ -99,4 +165,11 @@ module.exports = class File extends HTML {
|
|
|
99
165
|
+ this._form()
|
|
100
166
|
);
|
|
101
167
|
}
|
|
168
|
+
|
|
169
|
+
log() {
|
|
170
|
+
return this.stringify(
|
|
171
|
+
this._title()
|
|
172
|
+
+ this._log()
|
|
173
|
+
);
|
|
174
|
+
}
|
|
102
175
|
}
|
package/lib/html/folder.js
CHANGED
|
@@ -4,39 +4,8 @@
|
|
|
4
4
|
"use strict";
|
|
5
5
|
|
|
6
6
|
const File = require('./file');
|
|
7
|
-
const { cdata, fixpath }
|
|
8
|
-
|
|
9
|
-
function timeStr(time) {
|
|
10
|
-
|
|
11
|
-
const now = new Date().getTime();
|
|
12
|
-
|
|
13
|
-
const date = new Date(time);
|
|
14
|
-
const year = date.getFullYear();
|
|
15
|
-
const m = date.getMonth() + 1;
|
|
16
|
-
const mm = ('0' + m).substr(-2);
|
|
17
|
-
const d = date.getDate();
|
|
18
|
-
const dd = ('0' + d).substr(-2);
|
|
19
|
-
const hour = ('0' + date.getHours()).substr(-2);
|
|
20
|
-
const min = ('0' + date.getMinutes()).substr(-2);
|
|
21
|
-
|
|
22
|
-
if (now - time < 1000*60*60*12) return `${hour}:${min}`;
|
|
23
|
-
else if (now - time < 1000*60*60*24*365/2)
|
|
24
|
-
return `${m}/${d} ${hour}:${min}`;
|
|
25
|
-
else return `${year}/${mm}/${dd} `;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function sizeStr(size) {
|
|
29
|
-
|
|
30
|
-
if (size == null) return '-';
|
|
31
|
-
|
|
32
|
-
let str;
|
|
33
|
-
for (let unit of ['',' KB',' MB',' GB',' TB']) {
|
|
34
|
-
str = unit ? size.toFixed(1) + unit : size + unit;
|
|
35
|
-
if (size < 1024) return str;
|
|
36
|
-
size = size / 1024;
|
|
37
|
-
}
|
|
38
|
-
return str;
|
|
39
|
-
}
|
|
7
|
+
const { cdata, fixpath } = require('../util/html-escape');
|
|
8
|
+
const { timeStr, sizeStr } = require('../util/str-tool');
|
|
40
9
|
|
|
41
10
|
function cmp(key) {
|
|
42
11
|
return key == 'n' ? (x, y)=> ! x.type && y.type ? -1
|
|
@@ -94,6 +63,8 @@ function uc(c) { return c.toUpperCase() }
|
|
|
94
63
|
|
|
95
64
|
module.exports = class Folder extends File {
|
|
96
65
|
|
|
66
|
+
_logmenu() { return '' }
|
|
67
|
+
|
|
97
68
|
_table() {
|
|
98
69
|
|
|
99
70
|
const req = this._req;
|
package/lib/html/index.js
CHANGED
|
@@ -18,6 +18,7 @@ module.exports = class HTML {
|
|
|
18
18
|
style: '',
|
|
19
19
|
icon: DEFAULT_ICON,
|
|
20
20
|
script: [],
|
|
21
|
+
meta: [],
|
|
21
22
|
};
|
|
22
23
|
this.msg = req.msg;
|
|
23
24
|
}
|
|
@@ -58,6 +59,12 @@ module.exports = class HTML {
|
|
|
58
59
|
return this;
|
|
59
60
|
}
|
|
60
61
|
|
|
62
|
+
meta(attr) {
|
|
63
|
+
if (attr) this._.meta.push(attr);
|
|
64
|
+
else return this._.meta;
|
|
65
|
+
return this;
|
|
66
|
+
}
|
|
67
|
+
|
|
61
68
|
_head() {
|
|
62
69
|
const req = this._req;
|
|
63
70
|
|
|
@@ -83,10 +90,17 @@ module.exports = class HTML {
|
|
|
83
90
|
`<script>\n${opt.code}</script>\n`
|
|
84
91
|
).join('');
|
|
85
92
|
|
|
93
|
+
const meta = this._.meta.map(attr=>{
|
|
94
|
+
let attrs = Object.keys(attr).map(key=>
|
|
95
|
+
`${cdata(key)}="${cdata(attr[key])}"`).join(' ');
|
|
96
|
+
return `<meta ${attrs}>\n`;
|
|
97
|
+
}).join('');
|
|
98
|
+
|
|
86
99
|
return '<head>\n'
|
|
87
100
|
+ '<meta charset="utf-8">\n'
|
|
88
101
|
+ '<meta name="viewport" '
|
|
89
102
|
+ 'content="width=device-width, initial-scale=1">\n'
|
|
103
|
+
+ meta
|
|
90
104
|
+ `<title>${cref(this.title())}</title>\n`
|
|
91
105
|
+ scriptRef
|
|
92
106
|
+ icon
|
|
@@ -98,6 +112,8 @@ module.exports = class HTML {
|
|
|
98
112
|
|
|
99
113
|
_editmenu() { return '' }
|
|
100
114
|
|
|
115
|
+
_logmenu() { return '' }
|
|
116
|
+
|
|
101
117
|
_toolbar() {
|
|
102
118
|
const msg = this.msg;
|
|
103
119
|
const req = this._req;
|
|
@@ -109,6 +125,7 @@ module.exports = class HTML {
|
|
|
109
125
|
+ ` alt="${cdata(msg('toolbar.home'))}"></a>\n`
|
|
110
126
|
+ `<ul>\n`;
|
|
111
127
|
if (this._req.user) {
|
|
128
|
+
html += this._logmenu();
|
|
112
129
|
html += this._editmenu();
|
|
113
130
|
const url = '?cmd=logout&session_id='
|
|
114
131
|
+ encodeURIComponent(this._req.sessionID);
|
package/lib/html/text.js
CHANGED
|
@@ -20,4 +20,34 @@ module.exports = class Text extends File {
|
|
|
20
20
|
+ `<input type="submit" value="${cdata(msg('udtext.submit'))}">\n`
|
|
21
21
|
+ '</form>\n';
|
|
22
22
|
}
|
|
23
|
+
|
|
24
|
+
_diff() {
|
|
25
|
+
|
|
26
|
+
if (! this._r.diff.length) return '';
|
|
27
|
+
|
|
28
|
+
let html = '<div id="l-diff">\n';
|
|
29
|
+
|
|
30
|
+
for (let line of this._r.diff) {
|
|
31
|
+
|
|
32
|
+
if (! line.length) continue;
|
|
33
|
+
if (line[0] == '\\') continue;
|
|
34
|
+
|
|
35
|
+
html += line[0] == '@'
|
|
36
|
+
? `<span class="l-head">${cdata(line)}</span>\n`
|
|
37
|
+
: line[0] == '+'
|
|
38
|
+
? `<ins>${cdata(line.substr(1))}</ins>\n`
|
|
39
|
+
: line[0] == '-'
|
|
40
|
+
? `<del>${cdata(line.substr(1))}</del>\n`
|
|
41
|
+
: `<span>${cdata(line.substr(1))}</span>\n`;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return html += '</div>\n';
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
diff() {
|
|
48
|
+
return this.stringify(
|
|
49
|
+
this._title()
|
|
50
|
+
+ this._diff()
|
|
51
|
+
);
|
|
52
|
+
}
|
|
23
53
|
}
|
package/lib/http/request.js
CHANGED
|
@@ -15,6 +15,7 @@ module.exports = class Request {
|
|
|
15
15
|
this._liulian = liulian;
|
|
16
16
|
this._req = req;
|
|
17
17
|
this.msg = liulian._.locale(req.acceptsLanguages());
|
|
18
|
+
this._n_open = 0;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
get version() { return this._liulian._version }
|
|
@@ -62,4 +63,9 @@ module.exports = class Request {
|
|
|
62
63
|
: isAbsolute(path) ? base + path
|
|
63
64
|
: base + join(this.pathDir, path);
|
|
64
65
|
}
|
|
66
|
+
|
|
67
|
+
openFile() {
|
|
68
|
+
this._n_open++;
|
|
69
|
+
if (this._n_open > 1000) throw 508;
|
|
70
|
+
}
|
|
65
71
|
}
|
package/lib/http/response.js
CHANGED
|
@@ -39,6 +39,12 @@ module.exports = class Response {
|
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
sendStream(stream, type) {
|
|
43
|
+
this._res.type(type)
|
|
44
|
+
stream.pipe(this._res);
|
|
45
|
+
this._cleanUp();
|
|
46
|
+
}
|
|
47
|
+
|
|
42
48
|
sendText(text, type = 'text/html') {
|
|
43
49
|
this._res.type(type).send(text);
|
|
44
50
|
this._cleanUp();
|
package/lib/module/core.js
CHANGED
|
@@ -13,7 +13,7 @@ module.exports = class Core {
|
|
|
13
13
|
this._req = parser._r._req;
|
|
14
14
|
this._inline = ['img','color','size','br','clear','class','lang'];
|
|
15
15
|
this._block = ['title','contents','footnote','include','img','clear',
|
|
16
|
-
'class','style','icon','lang','script'];
|
|
16
|
+
'class','style','icon','lang','redirect','script'];
|
|
17
17
|
this._np = ['style','script'];
|
|
18
18
|
}
|
|
19
19
|
|
|
@@ -148,6 +148,17 @@ module.exports = class Core {
|
|
|
148
148
|
return '';
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
+
redirect(type, param, value) {
|
|
152
|
+
let [url, sec] = param.split(/,\s*/);
|
|
153
|
+
sec = sec || '0';
|
|
154
|
+
let attr = {
|
|
155
|
+
'http-equiv': 'refresh',
|
|
156
|
+
'content': `${cdata(sec)}; URL=${cdata(url)}`
|
|
157
|
+
};
|
|
158
|
+
this._r.meta(attr);
|
|
159
|
+
return '';
|
|
160
|
+
}
|
|
161
|
+
|
|
151
162
|
script(type, param, value) {
|
|
152
163
|
this._r.script({ url: param, code: value });
|
|
153
164
|
return '';
|
package/lib/module/index.js
CHANGED
|
@@ -15,16 +15,16 @@ module.exports = class Module {
|
|
|
15
15
|
this.import('core');
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
require(path) {
|
|
19
|
-
try { return require(path) }
|
|
20
|
-
catch(err) { console.log(err) }
|
|
21
|
-
}
|
|
22
|
-
|
|
23
18
|
import(module) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
try {
|
|
20
|
+
if (! modules[module]) modules[module] = require('./' + module);
|
|
21
|
+
this._modules.push(new modules[module](this._parser));
|
|
22
|
+
return modules[module];
|
|
23
|
+
}
|
|
24
|
+
catch(err) {
|
|
25
|
+
if (err.code) console.log(module, err.code);
|
|
26
|
+
else console.log(err);
|
|
27
|
+
}
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
callInlineModule(str, name, param, value) {
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* module/paiga
|
|
3
|
+
*/
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const imgbase = '//kobalab.github.io/paiga/';
|
|
7
|
+
const img = {
|
|
8
|
+
_: 'ura.gif',
|
|
9
|
+
|
|
10
|
+
m0: 'man5red.gif',
|
|
11
|
+
m1: 'man1.gif', m2: 'man2.gif', m3: 'man3.gif',
|
|
12
|
+
m4: 'man4.gif', m5: 'man5.gif', m6: 'man6.gif',
|
|
13
|
+
m7: 'man7.gif', m8: 'man8.gif', m9: 'man9.gif',
|
|
14
|
+
|
|
15
|
+
p0: 'pin5red.gif',
|
|
16
|
+
p1: 'pin1.gif', p2: 'pin2.gif', p3: 'pin3.gif',
|
|
17
|
+
p4: 'pin4.gif', p5: 'pin5.gif', p6: 'pin6.gif',
|
|
18
|
+
p7: 'pin7.gif', p8: 'pin8.gif', p9: 'pin9.gif',
|
|
19
|
+
|
|
20
|
+
s0: 'sou5red.gif',
|
|
21
|
+
s1: 'sou1.gif', s2: 'sou2.gif', s3: 'sou3.gif',
|
|
22
|
+
s4: 'sou4.gif', s5: 'sou5.gif', s6: 'sou6.gif',
|
|
23
|
+
s7: 'sou7.gif', s8: 'sou8.gif', s9: 'sou9.gif',
|
|
24
|
+
|
|
25
|
+
z1: 'ton.gif', z2: 'nan.gif', z3: 'sha.gif', z4: 'pei.gif',
|
|
26
|
+
z5: 'haku.gif', z6: 'hatu.gif', z7: 'tyun.gif',
|
|
27
|
+
|
|
28
|
+
m0_: 'yman5red.gif',
|
|
29
|
+
m1_: 'yman1.gif', m2_: 'yman2.gif', m3_: 'yman3.gif',
|
|
30
|
+
m4_: 'yman4.gif', m5_: 'yman5.gif', m6_: 'yman6.gif',
|
|
31
|
+
m7_: 'yman7.gif', m8_: 'yman8.gif', m9_: 'yman9.gif',
|
|
32
|
+
|
|
33
|
+
p0_: 'ypin5red.gif',
|
|
34
|
+
p1_: 'ypin1.gif', p2_: 'ypin2.gif', p3_: 'ypin3.gif',
|
|
35
|
+
p4_: 'ypin4.gif', p5_: 'ypin5.gif', p6_: 'ypin6.gif',
|
|
36
|
+
p7_: 'ypin7.gif', p8_: 'ypin8.gif', p9_: 'ypin9.gif',
|
|
37
|
+
|
|
38
|
+
s0_: 'ysou5red.gif',
|
|
39
|
+
s1_: 'ysou1.gif', s2_: 'ysou2.gif', s3_: 'ysou3.gif',
|
|
40
|
+
s4_: 'ysou4.gif', s5_: 'ysou5.gif', s6_: 'ysou6.gif',
|
|
41
|
+
s7_: 'ysou7.gif', s8_: 'ysou8.gif', s9_: 'ysou9.gif',
|
|
42
|
+
|
|
43
|
+
z1_: 'yton.gif', z2_: 'ynan.gif', z3_: 'ysha.gif', z4_: 'ypei.gif',
|
|
44
|
+
z5_: 'yhaku.gif', z6_: 'yhatu.gif', z7_: 'ytyun.gif'
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function markup(paistr, w, h) {
|
|
48
|
+
|
|
49
|
+
let url, v = 0;
|
|
50
|
+
let html = '<span class="l-mod-paiga" style="white-space:pre;">';
|
|
51
|
+
|
|
52
|
+
for (let pai of paistr.match(/[mpsz](?:\d+[\-\=]?)+|[ _]|.+/g)) {
|
|
53
|
+
|
|
54
|
+
if (pai == ' ') {
|
|
55
|
+
html += ' ';
|
|
56
|
+
}
|
|
57
|
+
else if (pai == '_') {
|
|
58
|
+
url = imgbase + img._;
|
|
59
|
+
html += `<img src="${url}" width="${w}" height="${h}"`
|
|
60
|
+
+ ` alt="${pai}">`;
|
|
61
|
+
}
|
|
62
|
+
else if (pai.match(/^[mpsz](?:\d+[\-\=]?)+/)) {
|
|
63
|
+
let s = pai[0];
|
|
64
|
+
for (let n of pai.match(/\d[\-\=]?/g)) {
|
|
65
|
+
let d = n[1]||''; n = n[0];
|
|
66
|
+
if (d == '=' && ! v) {
|
|
67
|
+
html += `<span style="display:inline-block;width:${h}px">`;
|
|
68
|
+
v = 1;
|
|
69
|
+
}
|
|
70
|
+
if (d || v) {
|
|
71
|
+
url = imgbase + img[s+n+'_'];
|
|
72
|
+
if (d == '=') {
|
|
73
|
+
html += `<img src="${url}" width="${h}" height="${w}"`
|
|
74
|
+
+ ` style="vertical-align:bottom;display:block"`
|
|
75
|
+
+ ` alt="${s+n+'='}">`;
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
html += `<img src="${url}" width="${h}" height="${w}"`
|
|
79
|
+
+ ` alt="${s+n+'-'}">`;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
url = imgbase + img[s+n];
|
|
84
|
+
html += `<img src="${url}" width="${w}" height="${h}"`
|
|
85
|
+
+ ` alt="${s+n}">`;
|
|
86
|
+
}
|
|
87
|
+
if (d != '=') {
|
|
88
|
+
if (v) html += '</span>';
|
|
89
|
+
v = 0;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
html += `<span style="color:red;">${pai}</span>`;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (v) html += '</span>';
|
|
98
|
+
html += '</span>';
|
|
99
|
+
return html;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
module.exports = class Paiga {
|
|
103
|
+
|
|
104
|
+
constructor(parser) {
|
|
105
|
+
this._parser = parser;
|
|
106
|
+
this._r = parser._r;
|
|
107
|
+
this._req = parser._r._req;
|
|
108
|
+
this._inline = ['paiga'];
|
|
109
|
+
this._block = [];
|
|
110
|
+
this._np = [];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
paiga(type, param, value) {
|
|
114
|
+
let w = 24, h = 34;
|
|
115
|
+
if (! param) { w = 24; h = 34 }
|
|
116
|
+
else if (param == 'L') { w = 24; h = 34 }
|
|
117
|
+
else if (param == 'M') { w = 19; h = 24 }
|
|
118
|
+
else if (param == 'S') { w = 16; h = 23 }
|
|
119
|
+
else if (param.match(/^\d+x\d+$/)) {
|
|
120
|
+
[ w, h ] = param.split(/x/);
|
|
121
|
+
}
|
|
122
|
+
return markup(value, w, h);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* module/pinyin
|
|
3
|
+
*/
|
|
4
|
+
"use strict";
|
|
5
|
+
|
|
6
|
+
const tone_letter = {
|
|
7
|
+
a: ['ā','á','ǎ','à'],
|
|
8
|
+
e: ['ē','é','ě','è'],
|
|
9
|
+
o: ['ō','ó','ǒ','ò'],
|
|
10
|
+
i: ['ī','í','ǐ','ì'],
|
|
11
|
+
u: ['ū','ú','ǔ','ù'],
|
|
12
|
+
v: ['ǖ','ǘ','ǚ','ǜ'],
|
|
13
|
+
n: ['n̄','ń','ň','ǹ'],
|
|
14
|
+
A: ['Ā','Á','Ǎ','À'],
|
|
15
|
+
E: ['Ē','É','Ě','È'],
|
|
16
|
+
O: ['Ō','Ó','Ǒ','Ò'],
|
|
17
|
+
I: ['Ī','Í','Ǐ','Ì'],
|
|
18
|
+
U: ['Ū','Ú','Ǔ','Ù'],
|
|
19
|
+
V: ['Ǖ','Ǘ','Ǚ','Ǜ'],
|
|
20
|
+
N: ['N̄','Ń','Ň','Ǹ'],
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function mark(str) {
|
|
24
|
+
const code = (c)=>tone_letter[c][n-1];
|
|
25
|
+
let [ , s, n ] = str.match(/^(.*?)(\d)$/);
|
|
26
|
+
if (s.match(/[aeo]/i)) return s.replace(/[aeo]/i, code);
|
|
27
|
+
if (s.match(/[iu]$/i)) return s.replace(/[iu]$/i, code);
|
|
28
|
+
if (s.match(/^[iuv]/i)) return s.replace(/^[iuv]/i, code);
|
|
29
|
+
else return s.replace(/n/i, code);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
module.exports = class Pinyin {
|
|
33
|
+
|
|
34
|
+
constructor(parser) {
|
|
35
|
+
this._parser = parser;
|
|
36
|
+
this._r = parser._r;
|
|
37
|
+
this._req = parser._r._req;
|
|
38
|
+
this._inline = ['pinyin'];
|
|
39
|
+
this._block = ['pinyin'];
|
|
40
|
+
this._np = [];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
pinyin(type, param, value) {
|
|
44
|
+
value = value.replace(/([aeouiv][1234])([aeo])/ig, '$1-$2');
|
|
45
|
+
value = value.replace(/[iuv]?[aeo]?(?:i|u|o|n|ng)?[1234]/ig, mark);
|
|
46
|
+
value = value.replace(/v/g, 'ü').replace(/V/g, 'Ü');
|
|
47
|
+
return type == '#'
|
|
48
|
+
? `<div class="l-mod-pinyin">${value}</div>`
|
|
49
|
+
: `<span class="l-mod-pinyin">${value}</span>`;
|
|
50
|
+
}
|
|
51
|
+
}
|
package/lib/resource/file.js
CHANGED
|
@@ -17,6 +17,7 @@ module.exports = class File {
|
|
|
17
17
|
this._stat = stat;
|
|
18
18
|
this._location = location;
|
|
19
19
|
this.openFile = openFile;
|
|
20
|
+
this._backup = req.config.backup;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
get path() { return this._path }
|
|
@@ -34,20 +35,32 @@ module.exports = class File {
|
|
|
34
35
|
return this._type;
|
|
35
36
|
}
|
|
36
37
|
get location() { return this._location }
|
|
37
|
-
|
|
38
38
|
get redirect() { return this._redirect }
|
|
39
|
+
get log() { return this._log }
|
|
39
40
|
|
|
40
41
|
async open() {
|
|
41
|
-
if (this._req.
|
|
42
|
-
|
|
42
|
+
if (! this._req.user && (this._req.cmd == 'edit' ||
|
|
43
|
+
this._req.cmd == 'log' ||
|
|
44
|
+
this._req.cmd == 'diff' )) throw 403;
|
|
43
45
|
try {
|
|
44
46
|
const fh = await fs.open(this._path);
|
|
45
47
|
fh.close();
|
|
46
|
-
return this;
|
|
47
48
|
}
|
|
48
49
|
catch(err) {
|
|
49
50
|
throw 403;
|
|
50
51
|
}
|
|
52
|
+
if (this._backup && (this._req.cmd == 'edit' ||
|
|
53
|
+
this._req.cmd == 'log'))
|
|
54
|
+
{
|
|
55
|
+
if (Date.now() - this.time > 1000*60*60*24) {
|
|
56
|
+
await this._backup.checkIn(this.location,
|
|
57
|
+
this.time, this._req.user);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (this._backup && this._req.cmd == 'log') {
|
|
61
|
+
this._log = await this._backup.log(this.location);
|
|
62
|
+
}
|
|
63
|
+
return this;
|
|
51
64
|
}
|
|
52
65
|
|
|
53
66
|
async update() {
|
|
@@ -66,8 +79,20 @@ module.exports = class File {
|
|
|
66
79
|
}
|
|
67
80
|
|
|
68
81
|
send(res) {
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
else
|
|
82
|
+
if (this._req.cmd == 'edit') res.sendText(new HTML(this).edit());
|
|
83
|
+
else if (this._req.cmd == 'log') res.sendText(new HTML(this).log());
|
|
84
|
+
else {
|
|
85
|
+
try {
|
|
86
|
+
let rev = this._req.param('rev');
|
|
87
|
+
if (rev) {
|
|
88
|
+
res.sendStream(
|
|
89
|
+
this._backup.checkOutStream(this.location, rev),
|
|
90
|
+
this.type);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (err) {}
|
|
95
|
+
res.sendFile(this._path, this.type);
|
|
96
|
+
}
|
|
72
97
|
}
|
|
73
98
|
}
|
package/lib/resource/index.js
CHANGED
|
@@ -21,7 +21,11 @@ async function resource(req, file) {
|
|
|
21
21
|
else location = decodeURIComponent(urljoin(req.pathDir, file));
|
|
22
22
|
}
|
|
23
23
|
else location = decodeURIComponent(req.path);
|
|
24
|
+
|
|
25
|
+
if (location.match(/^\/\.git(?:\/.*)?$/)) throw 404;
|
|
26
|
+
|
|
24
27
|
const path = join(req.config.home, '/docs/', location);
|
|
28
|
+
req.openFile();
|
|
25
29
|
|
|
26
30
|
try {
|
|
27
31
|
const stat = await fs.stat(path);
|
package/lib/resource/liulian.js
CHANGED
|
@@ -16,6 +16,7 @@ module.exports = class LiuLian extends Text {
|
|
|
16
16
|
icon(url) { this._html.icon(url) }
|
|
17
17
|
lang(lang) { this._html.lang(lang) }
|
|
18
18
|
script(script) { this._html.script(script) }
|
|
19
|
+
meta(attr) { this._html.meta(attr) }
|
|
19
20
|
|
|
20
21
|
async _seekToParent(filename) {
|
|
21
22
|
let pathDir = this._req.pathDir;
|
|
@@ -41,10 +42,18 @@ module.exports = class LiuLian extends Text {
|
|
|
41
42
|
if (this._req.cmd == 'edit') {
|
|
42
43
|
res.sendText(new HTML(this).edit());
|
|
43
44
|
}
|
|
45
|
+
else if (this._req.cmd == 'log') {
|
|
46
|
+
res.sendText(new HTML(this).log());
|
|
47
|
+
}
|
|
48
|
+
else if (this._req.cmd == 'diff') {
|
|
49
|
+
res.sendText(new HTML(this).diff());
|
|
50
|
+
}
|
|
44
51
|
else {
|
|
45
|
-
this._text =
|
|
52
|
+
this._text = (this.name != 'HEAD'
|
|
53
|
+
? await this._seekToParent('HEAD') + '\n' : '')
|
|
46
54
|
+ this._text + '\n'
|
|
47
|
-
+
|
|
55
|
+
+ (this.name != 'TAIL'
|
|
56
|
+
? await this._seekToParent('TAIL') : '');
|
|
48
57
|
this._html = new HTML(this);
|
|
49
58
|
res.sendText(this._html.stringify(await parse(this)));
|
|
50
59
|
}
|
package/lib/resource/text.js
CHANGED
|
@@ -11,17 +11,32 @@ const File = require('./file');
|
|
|
11
11
|
module.exports = class Text extends File {
|
|
12
12
|
|
|
13
13
|
get text() { return this._text }
|
|
14
|
+
get diff() { return this._diff }
|
|
14
15
|
|
|
15
16
|
async open() {
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
await super.open();
|
|
18
|
+
if (this._backup && this._req.cmd == 'diff') {
|
|
19
|
+
let rev = this._req.params('rev');
|
|
20
|
+
this._diff = await this._backup.diff(this.location, ...rev);
|
|
21
|
+
return this;
|
|
22
|
+
}
|
|
23
|
+
try {
|
|
24
|
+
let rev = this._req.param('rev');
|
|
25
|
+
if (rev) {
|
|
26
|
+
this._text = await this._backup.checkOut(this.location, rev);
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch(err) {
|
|
31
|
+
throw 404;
|
|
32
|
+
}
|
|
18
33
|
try {
|
|
19
34
|
this._text = await fs.readFile(this._path, 'utf-8');
|
|
20
|
-
return this;
|
|
21
35
|
}
|
|
22
36
|
catch(err) {
|
|
23
37
|
throw 403;
|
|
24
38
|
}
|
|
39
|
+
return this;
|
|
25
40
|
}
|
|
26
41
|
|
|
27
42
|
async update() {
|
|
@@ -39,8 +54,9 @@ module.exports = class Text extends File {
|
|
|
39
54
|
}
|
|
40
55
|
|
|
41
56
|
send(res) {
|
|
42
|
-
if
|
|
43
|
-
|
|
44
|
-
else
|
|
57
|
+
if (this._req.cmd == 'edit') res.sendText(new HTML(this).edit());
|
|
58
|
+
else if (this._req.cmd == 'log') res.sendText(new HTML(this).log());
|
|
59
|
+
else if (this._req.cmd == 'diff') res.sendText(new HTML(this).diff());
|
|
60
|
+
else res.sendFile(this._path, this.type);
|
|
45
61
|
}
|
|
46
62
|
}
|
package/lib/text/liulian.js
CHANGED
|
@@ -381,6 +381,7 @@ class LiuLian {
|
|
|
381
381
|
let [ , name, param, eod ]
|
|
382
382
|
= line.match(/^#(\w+)(?:\((.*?)\))?(?:<<(\S+))?$/);
|
|
383
383
|
if (name == '_') return await this.tag(line, param, eod);
|
|
384
|
+
if (name == 'import') return this.import(line, param);
|
|
384
385
|
|
|
385
386
|
return await this._module.callBlockModule(line, name, param, eod);
|
|
386
387
|
}
|
|
@@ -402,6 +403,11 @@ class LiuLian {
|
|
|
402
403
|
}
|
|
403
404
|
}
|
|
404
405
|
|
|
406
|
+
import(line, param) {
|
|
407
|
+
if (this._module.import(param)) return '';
|
|
408
|
+
return '<div style="color:red">' + cdata(line) + '</div>\n\n';
|
|
409
|
+
}
|
|
410
|
+
|
|
405
411
|
inlineModule(str, name, param, value) {
|
|
406
412
|
return this._module.callInlineModule(str, name, param, value);
|
|
407
413
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* util/str-tool
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
function timeStr(time) {
|
|
6
|
+
|
|
7
|
+
const now = new Date().getTime();
|
|
8
|
+
|
|
9
|
+
const date = new Date(time);
|
|
10
|
+
const year = date.getFullYear();
|
|
11
|
+
const m = date.getMonth() + 1;
|
|
12
|
+
const mm = ('0' + m).substr(-2);
|
|
13
|
+
const d = date.getDate();
|
|
14
|
+
const dd = ('0' + d).substr(-2);
|
|
15
|
+
const hour = ('0' + date.getHours()).substr(-2);
|
|
16
|
+
const min = ('0' + date.getMinutes()).substr(-2);
|
|
17
|
+
|
|
18
|
+
if (now - time < 1000*60*60*12) return `${hour}:${min}`;
|
|
19
|
+
else if (now - time < 1000*60*60*24*365/2)
|
|
20
|
+
return `${m}/${d} ${hour}:${min}`;
|
|
21
|
+
else return `${year}/${mm}/${dd}`;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function sizeStr(size) {
|
|
25
|
+
|
|
26
|
+
if (size == null) return '-';
|
|
27
|
+
|
|
28
|
+
let str;
|
|
29
|
+
for (let unit of ['',' KB',' MB',' GB',' TB']) {
|
|
30
|
+
str = unit ? size.toFixed(1) + unit : size + unit;
|
|
31
|
+
if (size < 1024) return str;
|
|
32
|
+
size = size / 1024;
|
|
33
|
+
}
|
|
34
|
+
return str;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
timeStr: timeStr,
|
|
39
|
+
sizeStr: sizeStr,
|
|
40
|
+
};
|
package/locale/en
CHANGED
|
@@ -13,6 +13,7 @@ toolbar.home:Home
|
|
|
13
13
|
toolbar.login:Login
|
|
14
14
|
toolbar.logout:Logout
|
|
15
15
|
toolbar.edit:Edit
|
|
16
|
+
toolbar.log:History
|
|
16
17
|
# -----------------------------------------------------------------------------
|
|
17
18
|
adduser.title:Sign Up
|
|
18
19
|
adduser.user:Login Name
|
|
@@ -45,3 +46,12 @@ rmdir.submit:REMOVE
|
|
|
45
46
|
# -----------------------------------------------------------------------------
|
|
46
47
|
udtext.submit:SAVE
|
|
47
48
|
# -----------------------------------------------------------------------------
|
|
49
|
+
log.title:Revision history of {$1}
|
|
50
|
+
log.rev:Revision
|
|
51
|
+
log.time:Modified
|
|
52
|
+
log.diff:Difference
|
|
53
|
+
log.prev:from previous
|
|
54
|
+
log.curr:to current
|
|
55
|
+
log.show:Show
|
|
56
|
+
log.edit:Edit
|
|
57
|
+
diff.title:Difference of {$1}
|
package/locale/ja
CHANGED
|
@@ -13,6 +13,7 @@ toolbar.home:ホーム
|
|
|
13
13
|
toolbar.login:ログイン
|
|
14
14
|
toolbar.logout:ログアウト
|
|
15
15
|
toolbar.edit:編集
|
|
16
|
+
toolbar.log:履歴
|
|
16
17
|
# -----------------------------------------------------------------------------
|
|
17
18
|
adduser.title:ユーザー登録
|
|
18
19
|
adduser.user:ログイン名
|
|
@@ -45,3 +46,12 @@ rmdir.submit:削除
|
|
|
45
46
|
# -----------------------------------------------------------------------------
|
|
46
47
|
udtext.submit:保存
|
|
47
48
|
# -----------------------------------------------------------------------------
|
|
49
|
+
log.title:{$1} の変更履歴
|
|
50
|
+
log.rev:リビジョン
|
|
51
|
+
log.time:変更日
|
|
52
|
+
log.diff:差分
|
|
53
|
+
log.prev:前の版との差分
|
|
54
|
+
log.curr:現在との差分
|
|
55
|
+
log.show:表示
|
|
56
|
+
log.edit:編集
|
|
57
|
+
diff.title:{$1} の差分
|
package/locale/zh-CN
CHANGED
|
@@ -13,6 +13,7 @@ toolbar.home:主页
|
|
|
13
13
|
toolbar.login:登录
|
|
14
14
|
toolbar.logout:注销
|
|
15
15
|
toolbar.edit:编辑
|
|
16
|
+
toolbar.log:历史
|
|
16
17
|
# -----------------------------------------------------------------------------
|
|
17
18
|
adduser.title:注册
|
|
18
19
|
adduser.user:账号
|
|
@@ -45,3 +46,12 @@ rmdir.submit:删除
|
|
|
45
46
|
# -----------------------------------------------------------------------------
|
|
46
47
|
udtext.submit:保存
|
|
47
48
|
# -----------------------------------------------------------------------------
|
|
49
|
+
log.title:{$1} 的历史纪录
|
|
50
|
+
log.rev:版本
|
|
51
|
+
log.time:修改日期
|
|
52
|
+
log.diff:差别
|
|
53
|
+
log.prev:从前版的
|
|
54
|
+
log.curr:到现在的
|
|
55
|
+
log.show:表示这版
|
|
56
|
+
log.edit:编辑这版
|
|
57
|
+
diff.title:{$1} 的差别
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kobalab/liulian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Node.jsで動作するWebサイト作成ツール",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"homepage": "https://kobalab.net/liulian/",
|
|
29
29
|
"devDependencies": {
|
|
30
|
-
"mocha": "^9.
|
|
30
|
+
"mocha": "^9.2.0",
|
|
31
31
|
"nyc": "^15.1.0"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|