wikity 1.3.2 → 1.3.4
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/dist/compile.js +6 -11
- package/dist/parse.js +234 -129
- package/dist/wiki.css.js +10 -3
- package/package.json +1 -1
- package/readme.md +1 -0
package/dist/compile.js
CHANGED
|
@@ -58,15 +58,15 @@ function compile(dir = '.', config = {}) {
|
|
|
58
58
|
toc += `${`<ol>`.repeat(lvl - 1)} <li> <a href="#${encodeURI(text.replace(/ /g, '_'))}">${text}</a> </li> ${`</ol>`.repeat(lvl - 1)}`;
|
|
59
59
|
});
|
|
60
60
|
const tocElem = (0, dedent_1.default) `
|
|
61
|
-
<div id="toc">
|
|
62
|
-
<span id="toc
|
|
61
|
+
<div id="page+toc">
|
|
62
|
+
<span id="page+toc+heading">
|
|
63
63
|
<strong>Contents</strong>
|
|
64
64
|
[<a href="javascript:void(0)" onclick="
|
|
65
|
-
document.
|
|
65
|
+
document.getElementById('page+toc+contents').setAttribute('style', this.innerText === 'hide' ? 'display: none;' : '');
|
|
66
66
|
this.innerText = this.innerText === 'hide' ? 'show' : 'hide';
|
|
67
67
|
">hide</a>]
|
|
68
68
|
</span>
|
|
69
|
-
<ol>${toc}</ol>
|
|
69
|
+
<ol id="page+toc+contents">${toc}</ol>
|
|
70
70
|
</div>
|
|
71
71
|
`;
|
|
72
72
|
// Set TOC on page
|
|
@@ -106,7 +106,7 @@ function compile(dir = '.', config = {}) {
|
|
|
106
106
|
if (!fs_1.default.existsSync(path_1.default.dirname(outFilePath))) {
|
|
107
107
|
fs_1.default.mkdirSync(path_1.default.dirname(outFilePath));
|
|
108
108
|
}
|
|
109
|
-
const renderedHtml = formatter.render(html
|
|
109
|
+
const renderedHtml = formatter.render(html.replace(/(<\/\w+>) +(\S)/g, '$1 $2'));
|
|
110
110
|
fs_1.default.writeFileSync(outFilePath, frontMatter + '\n' + renderedHtml, 'utf8');
|
|
111
111
|
// Move images
|
|
112
112
|
(0, glob_1.default)(imagesFolder + '/*', {}, (err, files) => {
|
|
@@ -131,12 +131,7 @@ function compile(dir = '.', config = {}) {
|
|
|
131
131
|
if (config.customStyles) {
|
|
132
132
|
styles += config.customStyles;
|
|
133
133
|
}
|
|
134
|
-
const cssOutput = config.eleventy ?
|
|
135
|
-
---
|
|
136
|
-
permalink: /wiki.css
|
|
137
|
-
---
|
|
138
|
-
${styles}
|
|
139
|
-
` : styles;
|
|
134
|
+
const cssOutput = config.eleventy ? ['---', 'permalink: /wiki.css', '---', styles].join('\n') : styles;
|
|
140
135
|
const cssOutFilename = config.eleventy ? 'wiki.css.njk' : 'wiki.css';
|
|
141
136
|
fs_1.default.writeFileSync(path_1.default.join(outputFolder, cssOutFilename), cssOutput);
|
|
142
137
|
}
|
package/dist/parse.js
CHANGED
|
@@ -6,22 +6,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.parse = exports.rawParse = void 0;
|
|
7
7
|
const fs_1 = __importDefault(require("fs"));
|
|
8
8
|
const path_1 = __importDefault(require("path"));
|
|
9
|
-
const escape_html_1 = __importDefault(require("escape-html"));
|
|
10
9
|
const dateformat_1 = __importDefault(require("dateformat"));
|
|
11
10
|
const common_1 = require("./common");
|
|
12
11
|
const r = String.raw;
|
|
13
12
|
const MAX_RECURSION = 20;
|
|
14
13
|
const arg = r `\s*([^|}]+?)\s*`;
|
|
15
|
-
function
|
|
16
|
-
return (
|
|
17
|
-
.replace(/{/g, '{'); // avoid keeping plain {{}} which is parsed as a template call
|
|
14
|
+
function cleanLink(link) {
|
|
15
|
+
return encodeURI(link.replace(/ /g, '_'));
|
|
18
16
|
}
|
|
17
|
+
function parseDimensions(dimStr) {
|
|
18
|
+
const regex = /(\d*)(?:x(\d*))?px/;
|
|
19
|
+
const match = dimStr.match(regex);
|
|
20
|
+
if (!match)
|
|
21
|
+
return { width: 'auto', height: 'auto' };
|
|
22
|
+
const [, width, height] = match;
|
|
23
|
+
return {
|
|
24
|
+
width: width || 'auto',
|
|
25
|
+
height: height || 'auto',
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
const escaper = (text, n = 0) => `%${text}#${n}`;
|
|
19
29
|
function rawParse(data, config = {}) {
|
|
20
30
|
return parse(data, config).data;
|
|
21
31
|
}
|
|
22
32
|
exports.rawParse = rawParse;
|
|
23
33
|
function parse(data, config = {}) {
|
|
24
34
|
var _a, _b, _c;
|
|
35
|
+
const KEY = Math.random().toString().slice(2); // key used to allow certain disallowed HTML elements
|
|
25
36
|
const templatesFolder = (_a = config.templatesFolder) !== null && _a !== void 0 ? _a : 'templates';
|
|
26
37
|
const imagesFolder = (_b = config.imagesFolder) !== null && _b !== void 0 ? _b : 'images';
|
|
27
38
|
const outputFolder = (_c = config.outputFolder) !== null && _c !== void 0 ? _c : 'wikity-out';
|
|
@@ -31,7 +42,6 @@ function parse(data, config = {}) {
|
|
|
31
42
|
const refs = [];
|
|
32
43
|
let nowikiCount = 0;
|
|
33
44
|
let rawExtLinkCount = 0;
|
|
34
|
-
let refCount = 0;
|
|
35
45
|
let outText = data;
|
|
36
46
|
for (let l = 0, last = ''; l < MAX_RECURSION; l++) {
|
|
37
47
|
if (last === outText)
|
|
@@ -39,24 +49,122 @@ function parse(data, config = {}) {
|
|
|
39
49
|
last = outText;
|
|
40
50
|
outText = outText
|
|
41
51
|
// Nowiki: <nowiki></nowiki>
|
|
42
|
-
.replace((0, common_1.RegExpBuilder)(r `<nowiki> ([^]+?) </nowiki>`), (_, m) =>
|
|
52
|
+
.replace((0, common_1.RegExpBuilder)(r `<nowiki> ([^]+?) </nowiki>`), (_, m) => (nowikis.push(m), escaper('NOWIKI', nowikiCount++)))
|
|
43
53
|
// Sanitise unacceptable HTML
|
|
44
|
-
.replace((0, common_1.RegExpBuilder)(r `<
|
|
54
|
+
.replace((0, common_1.RegExpBuilder)(r `< \s* (?= (?: script|link|meta|iframe|frameset|object|embed|applet|form|input|button|textarea ) (?! \s* key.{0,10}${KEY}) )`), '<')
|
|
45
55
|
.replace((0, common_1.RegExpBuilder)(r `(?<= <[^>]+ ) (\bon(\w+))`), 'data-$2')
|
|
46
56
|
// Comments: <!-- -->
|
|
47
57
|
.replace(/<!--[^]+?-->/g, '')
|
|
48
58
|
// Lines: ----
|
|
49
59
|
.replace(/^-{4,}/gm, '<hr>')
|
|
60
|
+
// Images: [[File:Image.png|options|caption]]
|
|
61
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ (?:File|Image): (.*?) (\|.+?)? \]\]`), (_, file, params = '') => {
|
|
62
|
+
if (params.includes('{{'))
|
|
63
|
+
return _;
|
|
64
|
+
if (!file)
|
|
65
|
+
return '';
|
|
66
|
+
const path = path_1.default.join(imagesFolder, file.trim().replace(/ /g, '_'));
|
|
67
|
+
let caption = '';
|
|
68
|
+
let imageData = {};
|
|
69
|
+
let imageArgs = params.split('|').map((arg) => arg.replace(/"/g, '"'));
|
|
70
|
+
for (const param of imageArgs) {
|
|
71
|
+
if (['left', 'right', 'center', 'none'].includes(param)) {
|
|
72
|
+
imageData.float = param;
|
|
73
|
+
}
|
|
74
|
+
if (['baseline', 'sub', 'super', 'top', 'text-bottom', 'middle', 'bottom', 'text-bottom'].includes(param)) {
|
|
75
|
+
imageData.align = param;
|
|
76
|
+
}
|
|
77
|
+
else if (['border', 'frameless', 'frame', 'framed', 'thumb', 'thumbnail'].includes(param)) {
|
|
78
|
+
imageData.type = { framed: 'frame', thumbnail: 'thumb' }[param] || param;
|
|
79
|
+
if (imageData.type === 'thumb') {
|
|
80
|
+
imageData.hasCaption = true;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
else if (param.endsWith('px')) {
|
|
84
|
+
const { width, height } = parseDimensions(param);
|
|
85
|
+
imageData.width = width;
|
|
86
|
+
imageData.height = height;
|
|
87
|
+
}
|
|
88
|
+
else if (param.startsWith('upright=')) {
|
|
89
|
+
imageData.width = +param.replace('upright=', '') * 300;
|
|
90
|
+
}
|
|
91
|
+
else if (param.startsWith('link=')) {
|
|
92
|
+
imageData.link = param.replace('link=', '');
|
|
93
|
+
}
|
|
94
|
+
else if (param.startsWith('alt=')) {
|
|
95
|
+
imageData.alt = param.replace('alt=', '');
|
|
96
|
+
}
|
|
97
|
+
else if (param.startsWith('style=')) {
|
|
98
|
+
imageData.style = param.replace('style=', '');
|
|
99
|
+
}
|
|
100
|
+
else if (param.startsWith('class=')) {
|
|
101
|
+
imageData.class = param.replace('class=', '');
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
caption = param;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
let content = `
|
|
108
|
+
<figure
|
|
109
|
+
class="
|
|
110
|
+
${imageData.class || ''}
|
|
111
|
+
image-container
|
|
112
|
+
image-${imageData.type || 'default'}
|
|
113
|
+
"
|
|
114
|
+
style="
|
|
115
|
+
display: inline-block;
|
|
116
|
+
float: ${imageData.float || 'none'};
|
|
117
|
+
vertical-align: ${imageData.align || 'unset'};
|
|
118
|
+
${imageData.style || ''}
|
|
119
|
+
"
|
|
120
|
+
>
|
|
121
|
+
<img
|
|
122
|
+
src="${path_1.default.basename(imagesFolder)}/${path_1.default.relative(imagesFolder, path)}"
|
|
123
|
+
alt="${imageData.alt || file}"
|
|
124
|
+
width="${imageData.width || 300}"
|
|
125
|
+
height="${imageData.height || 300}"
|
|
126
|
+
>
|
|
127
|
+
${imageData.hasCaption ? `<figcaption>${caption}</figcaption>` : ''}
|
|
128
|
+
</figure>
|
|
129
|
+
`;
|
|
130
|
+
const imageLink = imageData.link;
|
|
131
|
+
if (imageLink) {
|
|
132
|
+
content = `<a href="${cleanLink(imageLink)}" title="${imageLink}">${content}</a>`;
|
|
133
|
+
}
|
|
134
|
+
return content;
|
|
135
|
+
})
|
|
136
|
+
// Internal links: [[Page]] and [[Page|Text]]
|
|
137
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \]\]`), (_, link) => {
|
|
138
|
+
if (_.includes('{{'))
|
|
139
|
+
return _;
|
|
140
|
+
const content = `<a class="internal-link" title="${link}" href="./${cleanLink(link)}">${link}</a>`;
|
|
141
|
+
return content;
|
|
142
|
+
})
|
|
143
|
+
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \| ([^\]]+?) \]\]`), (_, link, text) => {
|
|
144
|
+
if (link.includes('{{'))
|
|
145
|
+
return _;
|
|
146
|
+
const content = `<a class="internal-link" title="${link}" href="./${cleanLink(link)}">${text}</a>`;
|
|
147
|
+
return content;
|
|
148
|
+
})
|
|
149
|
+
.replace((0, common_1.RegExpBuilder)(r `(</a>)([a-z]+)`), '$2$1')
|
|
150
|
+
// External links: [href Page] and just [href]
|
|
151
|
+
.replace((0, common_1.RegExpBuilder)(r `\[ ((?:\w+:)?\/\/ [^\s\]]+) (\s [^\]]+?)? \]`), (_, href, txt) => {
|
|
152
|
+
if (_.includes('{{'))
|
|
153
|
+
return _;
|
|
154
|
+
const content = `<a class="external-link" href="${href}">${txt || '[' + (++rawExtLinkCount) + ']'}</a>`;
|
|
155
|
+
return content;
|
|
156
|
+
})
|
|
157
|
+
// Magic words: {{!}}, {{reflist}}, etc
|
|
158
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* ! \s* }}`), escaper('VERT'))
|
|
159
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* !! \s* }}`), escaper('VERT').repeat(2))
|
|
160
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* = \s* }}`), escaper('EQUALS'))
|
|
161
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* [Rr]eflist \s* }}`), '<references/>')
|
|
50
162
|
// Metadata: displayTitle, __NOTOC__, etc
|
|
51
163
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* displayTitle: ([^}]+) }}`), (_, title) => (metadata.displayTitle = title, ''))
|
|
52
164
|
.replace((0, common_1.RegExpBuilder)(r `__NOINDEX__`), () => (metadata.noindex = true, ''))
|
|
53
165
|
.replace((0, common_1.RegExpBuilder)(r `__NOTOC__`), () => (metadata.notoc = true, ''))
|
|
54
166
|
.replace((0, common_1.RegExpBuilder)(r `__FORCETOC__`), () => (metadata.toc = true, ''))
|
|
55
167
|
.replace((0, common_1.RegExpBuilder)(r `__TOC__`), () => (metadata.toc = true, `<toc></toc>`))
|
|
56
|
-
// Magic words: {{!}}, {{reflist}}, etc
|
|
57
|
-
.replace((0, common_1.RegExpBuilder)(r `{{ \s* ! \s* }}`), '|')
|
|
58
|
-
.replace((0, common_1.RegExpBuilder)(r `{{ \s* = \s* }}`), '=')
|
|
59
|
-
.replace((0, common_1.RegExpBuilder)(r `{{ \s* [Rr]eflist \s* }}`), '<references/>')
|
|
60
168
|
// String functions: {{lc:}}, {{ucfirst:}}, {{len:}}, etc
|
|
61
169
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? urlencode: ${arg} }}`), (_, m) => encodeURI(m))
|
|
62
170
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? urldecode: ${arg} }}`), (_, m) => decodeURI(m))
|
|
@@ -71,9 +179,40 @@ function parse(data, config = {}) {
|
|
|
71
179
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? padright: ${arg} \|${arg} \|${arg} }}`), (_, str, n, char) => str.padEnd(+n, char))
|
|
72
180
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? replace: ${arg} \|${arg} \|${arg} }}`), (_, str, find, rep) => str.split(find).join(rep))
|
|
73
181
|
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? explode: ${arg} \|${arg} \|${arg} }}`), (_, str, delim, pos) => str.split(delim)[+pos])
|
|
182
|
+
// Magic functions: {{#ev:youtube}}, etc
|
|
183
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* #? ev: \s* (\w+) \s* \| \s* ( [^{}]+ ) \s* }} ( ?!} )`), (_, platform, args) => {
|
|
184
|
+
var _a;
|
|
185
|
+
// See mediawiki.org/wiki/Extension:EmbedVideo_(fork) for docs
|
|
186
|
+
const params = args.split('|');
|
|
187
|
+
for (let i = 0; i < 10; i++)
|
|
188
|
+
(_a = params[i]) !== null && _a !== void 0 ? _a : (params[i] = ''); // fill up with empty strings
|
|
189
|
+
const [id, dimensions, alignment, description, container, urlargs, autoresize] = params;
|
|
190
|
+
const { width, height } = parseDimensions(dimensions);
|
|
191
|
+
const source = {
|
|
192
|
+
// Add platforms
|
|
193
|
+
'youtube': `//www.youtube.com/embed/${id}`,
|
|
194
|
+
'vimeo': `//player.vimeo.com/video/${id}`,
|
|
195
|
+
}[platform];
|
|
196
|
+
if (!source)
|
|
197
|
+
return `<code>Failed to load video ${id} from ${platform}.</code>`;
|
|
198
|
+
return `
|
|
199
|
+
<iframe key="${KEY}"
|
|
200
|
+
src="${source}"
|
|
201
|
+
width="${width}"
|
|
202
|
+
height="${height}"
|
|
203
|
+
frameborder="0"
|
|
204
|
+
allowfullscreen="true"
|
|
205
|
+
loading="lazy"
|
|
206
|
+
title="${description !== null && description !== void 0 ? description : 'Play video'}"
|
|
207
|
+
${alignment ? `style="float: ${alignment};"` : ''}
|
|
208
|
+
>
|
|
209
|
+
</iframe>
|
|
210
|
+
${description ? `<figcaption>${description}</figcaption>` : ''}
|
|
211
|
+
`;
|
|
212
|
+
})
|
|
74
213
|
// Parser functions: {{#if:}}, {{#switch:}}, etc
|
|
75
|
-
.replace((0, common_1.RegExpBuilder)(r `{{ \s* (#\w+) \s* : \s* ( [^{}]+ ) \s* }}
|
|
76
|
-
if (
|
|
214
|
+
.replace((0, common_1.RegExpBuilder)(r `{{ \s* (#\w+) \s* : \s* ( [^{}]+ ) \s* }} ( ?!} )`), (_, name, content) => {
|
|
215
|
+
if (content.includes('{{'))
|
|
77
216
|
return _;
|
|
78
217
|
const args = content.trim().split(/\s*\|\s*/);
|
|
79
218
|
switch (name) {
|
|
@@ -105,8 +244,11 @@ function parse(data, config = {}) {
|
|
|
105
244
|
}
|
|
106
245
|
})
|
|
107
246
|
// Templates: {{template}}
|
|
108
|
-
.replace((0, common_1.RegExpBuilder)(r `(?<!{) {{ \s* ([^#{}|]+?) (\|[^{}]+)? }} (?!})`), (_, title, params = '') => {
|
|
109
|
-
|
|
247
|
+
.replace((0, common_1.RegExpBuilder)(r `(?<!{) {{ \s* ([^#{}|]+?) \s* (\|[^{}]+)? }} (?!})`), (_, title, params = '') => {
|
|
248
|
+
if (params.includes('{{'))
|
|
249
|
+
return _;
|
|
250
|
+
const templateFile = title.trim().replace(/ /g, '_');
|
|
251
|
+
const page = path_1.default.join(templatesFolder, templateFile);
|
|
110
252
|
let content = '';
|
|
111
253
|
// Try retrieve template content
|
|
112
254
|
try {
|
|
@@ -118,153 +260,116 @@ function parse(data, config = {}) {
|
|
|
118
260
|
return `<a class="internal-link redlink" title="${title}" href="${relPage}">${title}</a>`;
|
|
119
261
|
}
|
|
120
262
|
// Remove non-template sections
|
|
121
|
-
content = content
|
|
263
|
+
content = content.trim()
|
|
122
264
|
.replace(/<noinclude>.*?<\/noinclude>/gs, '')
|
|
123
265
|
.replace(/.*<(includeonly|onlyinclude)>|<\/(includeonly|onlyinclude)>.*/gs, '');
|
|
124
266
|
// Substitute arguments
|
|
125
267
|
const argMatch = (arg) => (0, common_1.RegExpBuilder)(r `{{{ \s* ${arg} (?:\|([^}]*))? \s* }}}`);
|
|
126
268
|
const args = params.split('|');
|
|
127
|
-
// provided key=value template arguments
|
|
269
|
+
// parse provided key=value template arguments
|
|
128
270
|
for (let i = 1; i < args.length; i++) {
|
|
129
|
-
const
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
271
|
+
const data = args[i];
|
|
272
|
+
const parts = data.split('=');
|
|
273
|
+
const isNamed = parts.length > 1;
|
|
274
|
+
const arg = isNamed ? parts[0] : i.toString();
|
|
275
|
+
const val = isNamed ? parts.slice(1).join('=') : data;
|
|
276
|
+
content = content.replace(argMatch(arg), (_, defaultVal) => (val || defaultVal || '').trim());
|
|
134
277
|
}
|
|
135
278
|
return content;
|
|
136
279
|
})
|
|
137
280
|
// Unparsed arguments
|
|
138
|
-
.replace((0, common_1.RegExpBuilder)(r `{{{ \s* [^{}|]+? (?:\|([^}]*))? \s* }}}`), (_,
|
|
281
|
+
.replace((0, common_1.RegExpBuilder)(r `{{{ \s* [^{}|]+? (?:\|([^}]*))? \s* }}}`), (_, defaultVal) => {
|
|
139
282
|
return defaultVal !== null && defaultVal !== void 0 ? defaultVal : '';
|
|
140
|
-
})
|
|
141
|
-
// Images: [[File:Image.png|options|caption]]
|
|
142
|
-
.replace((0, common_1.RegExpBuilder)(r `\[\[ (?:File|Image): (.+?) (\|.+?)? \]\]`), (_, file, params = '') => {
|
|
143
|
-
if (/{{/.test(params))
|
|
144
|
-
return _;
|
|
145
|
-
const path = path_1.default.join(imagesFolder, file.trim().replace(/ /g, '_'));
|
|
146
|
-
let caption = '';
|
|
147
|
-
let imageData = {};
|
|
148
|
-
let imageArgs = params.split('|').map((arg) => arg.replace(/"/g, '"'));
|
|
149
|
-
for (const param of imageArgs) {
|
|
150
|
-
if (['left', 'right', 'center', 'none'].includes(param)) {
|
|
151
|
-
imageData.float = param;
|
|
152
|
-
}
|
|
153
|
-
if (['baseline', 'sub', 'super', 'top', 'text-bottom', 'middle', 'bottom', 'text-bottom'].includes(param)) {
|
|
154
|
-
imageData.align = param;
|
|
155
|
-
}
|
|
156
|
-
else if (['border', 'frameless', 'frame', 'framed', 'thumb', 'thumbnail'].includes(param)) {
|
|
157
|
-
imageData.type = { framed: 'frame', thumbnail: 'thumb' }[param] || param;
|
|
158
|
-
if (imageData.type === 'thumb') {
|
|
159
|
-
imageData.hasCaption = true;
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
else if (param.endsWith('px')) {
|
|
163
|
-
param.replace(/(?:(\w+)?(x))?(\w+)px/, (_, size1, auto, size2) => {
|
|
164
|
-
if (size1) {
|
|
165
|
-
Object.assign(imageData, { width: size1, height: size2 });
|
|
166
|
-
}
|
|
167
|
-
else if (auto) {
|
|
168
|
-
Object.assign(imageData, { width: 'auto', height: size2 });
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
Object.assign(imageData, { width: size2, height: 'auto' });
|
|
172
|
-
}
|
|
173
|
-
return '';
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
else if (param.startsWith('upright=')) {
|
|
177
|
-
imageData.width = +param.replace('upright=', '') * 300;
|
|
178
|
-
}
|
|
179
|
-
else if (param.startsWith('link=')) {
|
|
180
|
-
imageData.link = param.replace('link=', '');
|
|
181
|
-
}
|
|
182
|
-
else if (param.startsWith('alt=')) {
|
|
183
|
-
imageData.alt = param.replace('alt=', '');
|
|
184
|
-
}
|
|
185
|
-
else if (param.startsWith('style=')) {
|
|
186
|
-
imageData.style = param.replace('style=', '');
|
|
187
|
-
}
|
|
188
|
-
else if (param.startsWith('class=')) {
|
|
189
|
-
imageData.class = param.replace('class=', '');
|
|
190
|
-
}
|
|
191
|
-
else {
|
|
192
|
-
caption = param;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
let content = `
|
|
196
|
-
<figure
|
|
197
|
-
class="
|
|
198
|
-
${imageData.class || ''}
|
|
199
|
-
image-container
|
|
200
|
-
image-${imageData.type || 'default'}
|
|
201
|
-
"
|
|
202
|
-
style="
|
|
203
|
-
float: ${imageData.float || 'none'};
|
|
204
|
-
vertical-align: ${imageData.align || 'unset'};
|
|
205
|
-
${imageData.style || ''}
|
|
206
|
-
"
|
|
207
|
-
>
|
|
208
|
-
<img
|
|
209
|
-
src="${path_1.default.basename(imagesFolder)}/${path_1.default.relative(imagesFolder, path)}"
|
|
210
|
-
alt="${imageData.alt || file}"
|
|
211
|
-
width="${imageData.width || 300}"
|
|
212
|
-
height="${imageData.height || 300}"
|
|
213
|
-
>
|
|
214
|
-
${imageData.hasCaption ? `<figcaption>${caption}</figcaption>` : ''}
|
|
215
|
-
</figure>
|
|
216
|
-
`;
|
|
217
|
-
if (imageData.link) {
|
|
218
|
-
content = `<a href="/${imageData.link}" title="${imageData.link}">${content}</a>`;
|
|
219
|
-
}
|
|
220
|
-
return content;
|
|
221
283
|
})
|
|
222
284
|
// Markup: '''bold''' and '''italic'''
|
|
223
285
|
.replace((0, common_1.RegExpBuilder)(r `''' ([^']+?) '''`), '<b>$1</b>')
|
|
224
286
|
.replace((0, common_1.RegExpBuilder)(r `'' ([^']+?) ''`), '<i>$1</i>')
|
|
225
287
|
// Headings: ==heading==
|
|
226
288
|
.replace((0, common_1.RegExpBuilder)(r `^ (=+) \s* (.+?) \s* \1 \s* $`), (_, lvl, txt) => `<h${lvl.length} id="${encodeURI(txt.replace(/ /g, '_'))}">${txt}</h${lvl.length}>`)
|
|
227
|
-
// Internal links: [[Page]] and [[Page|Text]]
|
|
228
|
-
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \]\]`), `<a class="internal-link" title="$1" href="$1">$1</a>`)
|
|
229
|
-
.replace((0, common_1.RegExpBuilder)(r `\[\[ ([^\]|]+?) \| ([^\]]+?) \]\]`), `<a class="internal-link" title="$1" href="/$1">$2</a>`)
|
|
230
|
-
.replace((0, common_1.RegExpBuilder)(r `(</a>)([a-z]+)`), '$2$1')
|
|
231
|
-
// External links: [href Page] and just [href]
|
|
232
|
-
.replace((0, common_1.RegExpBuilder)(r `\[ ((?:\w+:)?\/\/ [^\s\]]+) (\s [^\]]+?)? \]`), (_, href, txt) => `<a class="external-link" href="${href}">${txt || '[' + (++rawExtLinkCount) + ']'}</a>`)
|
|
233
289
|
// Bulleted list: *item
|
|
234
|
-
.replace((0, common_1.RegExpBuilder)(r `^ (\*+) (.+?) $`), (_, lvl,
|
|
290
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (\*+) (.+?) $`), (_, lvl, content) => {
|
|
291
|
+
if (content.includes('{{'))
|
|
292
|
+
return _;
|
|
293
|
+
const depth = lvl.length;
|
|
294
|
+
return `${'<ul>'.repeat(depth)}<li>${content}</li>${'</ul>'.repeat(depth)}`;
|
|
295
|
+
})
|
|
235
296
|
.replace((0, common_1.RegExpBuilder)(r `</ul> (\s*?) <ul>`), '$1')
|
|
236
297
|
// Numbered list: #item
|
|
237
|
-
.replace((0, common_1.RegExpBuilder)(r `^ (#+) (.+?) $`), (_, lvl,
|
|
298
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (#+) (.+?) $`), (_, lvl, content) => {
|
|
299
|
+
if (content.includes('{{'))
|
|
300
|
+
return _;
|
|
301
|
+
const depth = lvl.length;
|
|
302
|
+
return `${'<ol>'.repeat(depth)}<li>${content}</li>${'</ol>'.repeat(depth)}`;
|
|
303
|
+
})
|
|
238
304
|
.replace((0, common_1.RegExpBuilder)(r `</ol> (\s*?) <ol>`), '$1')
|
|
239
305
|
// Definition list: ;head, :item
|
|
306
|
+
.replace((0, common_1.RegExpBuilder)(r `^ ; (.+?) : (.+?) $`), `<dl><dt>$1</td><dd>$2</dd></dl>`)
|
|
307
|
+
.replace((0, common_1.RegExpBuilder)(r `^ (:+) (.+?) $`), (_, lvl, content) => {
|
|
308
|
+
if (content.includes('{{'))
|
|
309
|
+
return _;
|
|
310
|
+
const depth = lvl.length;
|
|
311
|
+
return `${'<dl>'.repeat(depth)}<dd>${content}</dd>${'</dl>'.repeat(depth)}`;
|
|
312
|
+
})
|
|
240
313
|
.replace((0, common_1.RegExpBuilder)(r `^ ; (.+) $`), '<dl><dt>$1</dt></dl>')
|
|
241
|
-
.replace((0, common_1.RegExpBuilder)(r `^ (:+) (.+?) $`), (_, lvl, txt) => `${'<dl>'.repeat(lvl.length)}<dd>${txt}</dd>${'</dl>'.repeat(lvl.length)}`)
|
|
242
314
|
.replace((0, common_1.RegExpBuilder)(r `</dl> (\s*?) <dl>`), '$1')
|
|
243
315
|
// Tables: {|, |+, !, |-, |, |}
|
|
244
|
-
.replace(
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
316
|
+
.replace(/\{\|.+\|\}/gs, (tableInner) => {
|
|
317
|
+
return tableInner
|
|
318
|
+
// {| data (open table)
|
|
319
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \{\| (.*?) $`), (_, attrs) => `<table ${attrs}><tr>`)
|
|
320
|
+
// |+ data
|
|
321
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|\+ (.*?) $`), (_, content) => `<caption>${content}</caption>`)
|
|
322
|
+
// |- (new row)
|
|
323
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|- (.*?) $`), (_, attrs) => `</tr><tr ${attrs}>`)
|
|
324
|
+
// |} (close)
|
|
325
|
+
.replace((0, common_1.RegExpBuilder)(r `^ \|\}`), `</tr></table>`)
|
|
326
|
+
// content: !head, !data|head, |text, |data|text, !!head, ||data
|
|
327
|
+
.replace((0, common_1.RegExpBuilder)(r `( ^! | ^\| | !! | \|\| ) (?: ( [^|\n]+? ) \|)? ( [^|\n]*? ) (?= $ | !! | \|\| )`), (_, type, data, content) => {
|
|
328
|
+
const elem = /!/.test(type) ? 'th' : 'td';
|
|
329
|
+
return `<${elem} ${data !== null && data !== void 0 ? data : ''}>${content}</${elem}>`;
|
|
330
|
+
});
|
|
331
|
+
})
|
|
332
|
+
// References: <ref></ref>
|
|
333
|
+
.replace((0, common_1.RegExpBuilder)(r `< ref \s* (?: name \s* = \s* ["']? ([^>'"]+) ["']? [^>]* )?> (.+?) </ ref >`), (_, refname, text) => {
|
|
334
|
+
if (_.includes('{{'))
|
|
335
|
+
return _;
|
|
336
|
+
const refData = { ref: text, n: refs.length + 1, name: refname };
|
|
337
|
+
refs.push(refData);
|
|
338
|
+
return `<sup class="refnote"><a id="cite-${refData.n}" class="ref" href="#ref-${refData.n}">[${refData.n}]</a></sup>`;
|
|
339
|
+
})
|
|
340
|
+
.replace((0, common_1.RegExpBuilder)(r `< ref \s* name \s* = \s* ["']? ( [^>"']+ ) ["']? \s* (?: /> | > .* </ref> )`), (_, refname) => {
|
|
341
|
+
const ref = refs.find(ref => ref.name === refname);
|
|
342
|
+
if (!ref)
|
|
343
|
+
return '';
|
|
344
|
+
return `<sup class="refnote"><a id="cite-${ref.n}" class="ref" href="#ref-${ref.n}">[${ref.n}]</a></sup>`;
|
|
256
345
|
})
|
|
257
|
-
.replace((0, common_1.RegExpBuilder)(r `<references \s* /?>`), '<ol>' + refs.map((ref, i) => `<li id="ref-${+i + 1}"> <a href="#cite-${+i + 1}">↑</a> ${ref} </li>`).join('\n') + '</ol>')
|
|
258
346
|
// Nonstandard: ``code`` and ```code blocks```
|
|
259
347
|
.replace((0, common_1.RegExpBuilder)(r ` \`\`\` ([^\`]+?) \`\`\` `), '<pre>$1</pre>')
|
|
260
348
|
.replace((0, common_1.RegExpBuilder)(r ` \`\` ([^\`]+?) \`\` `), '<code>$1</code>')
|
|
261
349
|
// Spacing
|
|
262
350
|
.replace(/(\r?\n){2}/g, '\n</p><p>\n');
|
|
263
351
|
}
|
|
264
|
-
// Final
|
|
352
|
+
// Final (one-time) substitutions
|
|
353
|
+
for (let i = 0; i < nowikis.length; i++) {
|
|
354
|
+
outText = outText
|
|
355
|
+
// Restore nowiki contents
|
|
356
|
+
.replace(escaper('NOWIKI', i), nowikis[i]);
|
|
357
|
+
}
|
|
358
|
+
outText = outText
|
|
359
|
+
// References: <references />
|
|
360
|
+
.replace((0, common_1.RegExpBuilder)(r `<references \s* /?>`), () => {
|
|
361
|
+
const references = refs.map(({ ref, n }) => {
|
|
362
|
+
const refline = `<li id="ref-${n}"> <a href="#cite-${n}">↑</a> ${ref} </li>`;
|
|
363
|
+
return refline;
|
|
364
|
+
}).join('\n');
|
|
365
|
+
return `<ol>${references}</ol>`;
|
|
366
|
+
})
|
|
367
|
+
// Magic word functions
|
|
368
|
+
.replaceAll(escaper('VERT'), '|')
|
|
369
|
+
.replaceAll(escaper('EQUALS'), '=');
|
|
370
|
+
// Escape all {{ to avoid crashes
|
|
265
371
|
outText = outText
|
|
266
|
-
|
|
267
|
-
.replace(/%NOWIKI#(\d+)%/g, (_, n) => fullyEscape(nowikis[n]));
|
|
372
|
+
.replaceAll('{{', '{{');
|
|
268
373
|
const result = { data: outText, metadata: metadata };
|
|
269
374
|
return result;
|
|
270
375
|
}
|
package/dist/wiki.css.js
CHANGED
|
@@ -10,6 +10,7 @@ dd, dl dl {margin-block: 0; margin-inline-start: 30px;}
|
|
|
10
10
|
|
|
11
11
|
figure {margin: 1em;}
|
|
12
12
|
.image-thumb, .image-frame {padding: 6px; border: 1px solid gray;}
|
|
13
|
+
.image-default {margin: 0;}
|
|
13
14
|
figcaption {padding-top: 6px;}
|
|
14
15
|
|
|
15
16
|
table.wikitable {border-collapse: collapse;}
|
|
@@ -25,7 +26,13 @@ a.external-link::after {content: '\1f855';}
|
|
|
25
26
|
a.redlink {color: #d33;}
|
|
26
27
|
a.redlink:visited {color: #b44;}
|
|
27
28
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#toc
|
|
29
|
+
sup.refnote {margin-left: -5px;}
|
|
30
|
+
|
|
31
|
+
#page+toc {border: 1px solid #aab; padding: 8px; width: fit-content; background-color: #f8f8f8; font-size: 95%;}
|
|
32
|
+
#page+toc-heading {display: block; text-align: center;}
|
|
33
|
+
#page+toc ol {margin: 0 0 0 1.3em;}
|
|
34
|
+
|
|
35
|
+
#infobox {float: right; clear: right; margin: 0 0 1em 1em; width: 300px; padding: 2px; border: 1px solid #CCC; overflow: auto; font-size: 90%;}
|
|
36
|
+
#infobox tr:first-child :first-child {padding: 10px 10px 0; text-align: center; font-weight: bold; font-size: 120%;}
|
|
37
|
+
#infobox th {padding-left: 10px; text-align: left;}
|
|
31
38
|
`;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -162,6 +162,7 @@ Your wikitext (`*.wiki`) files go in the root directory by default.
|
|
|
162
162
|
| `{{#explode:A-B-C-D\|-\|2}}` | C |
|
|
163
163
|
| `{{#urlencode:t e x t}}` | t%20e%20x%20t |
|
|
164
164
|
| `{{#urldecode:a%20b%27c}}` | a b'c |
|
|
165
|
+
| `{{#ev:youtube\|dQw4w9WgXcQ}}` | *(YouTube embed)* |
|
|
165
166
|
| `<noinclude>No</noinclude>` | *(blank outside a template)* |
|
|
166
167
|
| `<onlyinclude>Yes</onlyinclude>` | Yes |
|
|
167
168
|
| `<includeonly>Yes</includeonly>` | Yes *(blank inside a template)* |
|