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