nodebb-plugin-markdown 9.0.10 → 10.1.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/.eslintrc +1 -126
- package/index.js +27 -30
- package/lib/controllers.js +1 -1
- package/package.json +8 -8
- package/plugin.json +3 -4
- package/public/js/.eslintrc +3 -0
- package/public/js/admin.js +1 -1
- package/public/js/client.js +74 -41
- package/upgrades/use_unminified_hljs_theme.js +14 -0
- package/.eslintignore +0 -2
- package/public/js/highlightjs-line-numbers.js +0 -373
package/.eslintrc
CHANGED
|
@@ -1,128 +1,3 @@
|
|
|
1
1
|
{
|
|
2
|
-
"extends": "
|
|
3
|
-
"parserOptions": {
|
|
4
|
-
"sourceType": "script"
|
|
5
|
-
},
|
|
6
|
-
|
|
7
|
-
"rules": {
|
|
8
|
-
// Customized
|
|
9
|
-
"handle-callback-err": [ "error","^(e$|(e|(.*(_e|E)))rr)" ],
|
|
10
|
-
"comma-dangle": ["error", {
|
|
11
|
-
"arrays": "always-multiline",
|
|
12
|
-
"objects": "always-multiline",
|
|
13
|
-
"imports": "always-multiline",
|
|
14
|
-
"exports": "always-multiline",
|
|
15
|
-
"functions": "never"
|
|
16
|
-
}],
|
|
17
|
-
"no-empty": ["error", { "allowEmptyCatch": true }],
|
|
18
|
-
"no-underscore-dangle": "off",
|
|
19
|
-
"newline-per-chained-call": "off",
|
|
20
|
-
"no-console": "off",
|
|
21
|
-
"no-mixed-operators": ["error", { "allowSamePrecedence": true }],
|
|
22
|
-
"strict": ["error", "global"],
|
|
23
|
-
"consistent-return": "off",
|
|
24
|
-
"func-names": "off",
|
|
25
|
-
"no-tabs": "off",
|
|
26
|
-
"indent": ["error", "tab"],
|
|
27
|
-
"no-eq-null": "off",
|
|
28
|
-
"camelcase": "off",
|
|
29
|
-
"no-new": "off",
|
|
30
|
-
"no-shadow": "off",
|
|
31
|
-
"no-use-before-define": ["error", "nofunc"],
|
|
32
|
-
"no-prototype-builtins": "off",
|
|
33
|
-
"new-cap": "off",
|
|
34
|
-
"no-plusplus": ["error", { "allowForLoopAfterthoughts": true }],
|
|
35
|
-
"import/no-unresolved": "error",
|
|
36
|
-
|
|
37
|
-
// ES6
|
|
38
|
-
"prefer-rest-params": "off",
|
|
39
|
-
"prefer-spread": "off",
|
|
40
|
-
"prefer-arrow-callback": "off",
|
|
41
|
-
"prefer-template": "off",
|
|
42
|
-
"no-var": "off",
|
|
43
|
-
"object-shorthand": "off",
|
|
44
|
-
"vars-on-top": "off",
|
|
45
|
-
|
|
46
|
-
// TODO
|
|
47
|
-
"import/no-extraneous-dependencies": "off",
|
|
48
|
-
"import/no-dynamic-require": "off",
|
|
49
|
-
"import/newline-after-import": "off",
|
|
50
|
-
"no-bitwise": "off",
|
|
51
|
-
"global-require": "off",
|
|
52
|
-
"max-len": "off",
|
|
53
|
-
"no-param-reassign": "off",
|
|
54
|
-
"no-restricted-syntax": "off",
|
|
55
|
-
"no-script-url": "off",
|
|
56
|
-
"default-case": "off",
|
|
57
|
-
"linebreak-style": "off",
|
|
58
|
-
|
|
59
|
-
// "no-multi-assign": "off",
|
|
60
|
-
// "one-var": "off",
|
|
61
|
-
// "no-undef": "off",
|
|
62
|
-
// "max-nested-callbacks": "off",
|
|
63
|
-
// "no-mixed-requires": "off",
|
|
64
|
-
// "brace-style": "off",
|
|
65
|
-
// "max-statements-per-line": "off",
|
|
66
|
-
// "no-unused-vars": "off",
|
|
67
|
-
// "no-mixed-spaces-and-tabs": "off",
|
|
68
|
-
// "no-useless-concat": "off",
|
|
69
|
-
// "require-jsdoc": "off",
|
|
70
|
-
// "eqeqeq": "off",
|
|
71
|
-
// "no-negated-condition": "off",
|
|
72
|
-
// "one-var-declaration-per-line": "off",
|
|
73
|
-
// "no-lonely-if": "off",
|
|
74
|
-
// "radix": "off",
|
|
75
|
-
// "no-else-return": "off",
|
|
76
|
-
// "no-useless-escape": "off",
|
|
77
|
-
// "block-scoped-var": "off",
|
|
78
|
-
// "operator-assignment": "off",
|
|
79
|
-
// "yoda": "off",
|
|
80
|
-
// "no-loop-func": "off",
|
|
81
|
-
// "no-void": "off",
|
|
82
|
-
// "valid-jsdoc": "off",
|
|
83
|
-
// "no-cond-assign": "off",
|
|
84
|
-
// "no-redeclare": "off",
|
|
85
|
-
// "no-unreachable": "off",
|
|
86
|
-
// "no-nested-ternary": "off",
|
|
87
|
-
// "operator-linebreak": "off",
|
|
88
|
-
// "guard-for-in": "off",
|
|
89
|
-
// "no-unneeded-ternary": "off",
|
|
90
|
-
// "no-sequences": "off",
|
|
91
|
-
// "no-extend-native": "off",
|
|
92
|
-
// "no-shadow-restricted-names": "off",
|
|
93
|
-
// "no-extra-boolean-cast": "off",
|
|
94
|
-
// "no-path-concat": "off",
|
|
95
|
-
// "no-unused-expressions": "off",
|
|
96
|
-
// "no-return-assign": "off",
|
|
97
|
-
// "no-restricted-modules": "off",
|
|
98
|
-
// "object-curly-spacing": "off",
|
|
99
|
-
// "indent": "off",
|
|
100
|
-
// "padded-blocks": "off",
|
|
101
|
-
// "eol-last": "off",
|
|
102
|
-
// "lines-around-directive": "off",
|
|
103
|
-
// "strict": "off",
|
|
104
|
-
// "comma-dangle": "off",
|
|
105
|
-
// "no-multi-spaces": "off",
|
|
106
|
-
// "quotes": "off",
|
|
107
|
-
// "keyword-spacing": "off",
|
|
108
|
-
// "no-mixed-operators": "off",
|
|
109
|
-
// "comma-spacing": "off",
|
|
110
|
-
// "no-trailing-spaces": "off",
|
|
111
|
-
// "key-spacing": "off",
|
|
112
|
-
// "no-multiple-empty-lines": "off",
|
|
113
|
-
// "spaced-comment": "off",
|
|
114
|
-
// "space-in-parens": "off",
|
|
115
|
-
// "block-spacing": "off",
|
|
116
|
-
// "quote-props": "off",
|
|
117
|
-
// "space-unary-ops": "off",
|
|
118
|
-
// "no-empty": "off",
|
|
119
|
-
// "dot-notation": "off",
|
|
120
|
-
// "func-call-spacing": "off",
|
|
121
|
-
// "array-bracket-spacing": "off",
|
|
122
|
-
// "object-property-newline": "off",
|
|
123
|
-
// "no-continue": "off",
|
|
124
|
-
// "no-extra-semi": "off",
|
|
125
|
-
// "no-spaced-func": "off",
|
|
126
|
-
// "no-useless-return": "off"
|
|
127
|
-
}
|
|
2
|
+
"extends": "nodebb"
|
|
128
3
|
}
|
package/index.js
CHANGED
|
@@ -27,7 +27,9 @@ const Markdown = {
|
|
|
27
27
|
onLoad: async function (params) {
|
|
28
28
|
const controllers = require('./lib/controllers');
|
|
29
29
|
const hostMiddleware = require.main.require('./src/middleware');
|
|
30
|
-
const middlewares = [
|
|
30
|
+
const middlewares = [
|
|
31
|
+
hostMiddleware.maintenanceMode, hostMiddleware.registrationComplete, hostMiddleware.pluginHooks,
|
|
32
|
+
];
|
|
31
33
|
|
|
32
34
|
params.router.get('/admin/plugins/markdown', params.middleware.admin.buildHeader, controllers.renderAdmin);
|
|
33
35
|
params.router.get('/api/admin/plugins/markdown', controllers.renderAdmin);
|
|
@@ -47,7 +49,7 @@ const Markdown = {
|
|
|
47
49
|
config.markdown = {
|
|
48
50
|
highlight: Markdown.highlight ? 1 : 0,
|
|
49
51
|
highlightLinesLanguageList: Markdown.config.highlightLinesLanguageList,
|
|
50
|
-
theme: highlightTheme || 'default.
|
|
52
|
+
theme: highlightTheme || 'default.css',
|
|
51
53
|
defaultHighlightLanguage: defaultHighlightLanguage || '',
|
|
52
54
|
};
|
|
53
55
|
|
|
@@ -60,13 +62,10 @@ const Markdown = {
|
|
|
60
62
|
hookData.links.push({
|
|
61
63
|
rel: 'prefetch stylesheet',
|
|
62
64
|
type: '',
|
|
63
|
-
href: `${nconf.get('relative_path')}/assets/plugins/nodebb-plugin-markdown/
|
|
65
|
+
href: `${nconf.get('relative_path')}/assets/plugins/nodebb-plugin-markdown/styles/${highlightTheme || 'default.css'}`,
|
|
64
66
|
}, {
|
|
65
67
|
rel: 'prefetch',
|
|
66
68
|
href: `${nconf.get('relative_path')}/assets/language/${meta.config.defaultLang || 'en-GB'}/markdown.json?${meta.config['cache-buster']}`,
|
|
67
|
-
}, {
|
|
68
|
-
rel: 'prefetch',
|
|
69
|
-
href: `${nconf.get('relative_path')}/assets/src/modules/highlight.js`,
|
|
70
69
|
});
|
|
71
70
|
|
|
72
71
|
return hookData;
|
|
@@ -81,7 +80,7 @@ const Markdown = {
|
|
|
81
80
|
langPrefix: 'language-',
|
|
82
81
|
highlight: true,
|
|
83
82
|
highlightLinesLanguageList: [],
|
|
84
|
-
highlightTheme: 'default.
|
|
83
|
+
highlightTheme: 'default.css',
|
|
85
84
|
|
|
86
85
|
probe: true,
|
|
87
86
|
probeCacheSize: 256,
|
|
@@ -98,12 +97,12 @@ const Markdown = {
|
|
|
98
97
|
};
|
|
99
98
|
const notCheckboxes = ['langPrefix', 'highlightTheme', 'highlightLinesLanguageList', 'probeCacheSize'];
|
|
100
99
|
|
|
101
|
-
meta.settings.get('markdown',
|
|
100
|
+
meta.settings.get('markdown', (err, options) => {
|
|
102
101
|
if (err) {
|
|
103
102
|
winston.warn(`[plugin/markdown] Unable to retrieve settings, assuming defaults: ${err.message}`);
|
|
104
103
|
}
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
Object.keys(defaults).forEach((field) => {
|
|
107
106
|
// If not set in config (nil)
|
|
108
107
|
if (!options.hasOwnProperty(field)) {
|
|
109
108
|
_self.config[field] = defaults[field];
|
|
@@ -112,7 +111,7 @@ const Markdown = {
|
|
|
112
111
|
} else {
|
|
113
112
|
_self.config[field] = options[field];
|
|
114
113
|
}
|
|
115
|
-
}
|
|
114
|
+
});
|
|
116
115
|
|
|
117
116
|
_self.highlight = _self.config.highlight;
|
|
118
117
|
delete _self.config.highlight;
|
|
@@ -137,8 +136,7 @@ const Markdown = {
|
|
|
137
136
|
Markdown._externalImageCache = cacheCreate({
|
|
138
137
|
name: 'markdown.externalImageCache',
|
|
139
138
|
max: parseInt(_self.config.probeCacheSize, 10) || 256,
|
|
140
|
-
|
|
141
|
-
maxAge: 1000 * 60 * 60 * 24, // 1 day
|
|
139
|
+
ttl: 1000 * 60 * 60 * 24, // 1 day
|
|
142
140
|
});
|
|
143
141
|
}
|
|
144
142
|
});
|
|
@@ -146,13 +144,11 @@ const Markdown = {
|
|
|
146
144
|
|
|
147
145
|
loadThemes: async () => {
|
|
148
146
|
try {
|
|
149
|
-
const files = await fs.promises.readdir(path.
|
|
147
|
+
const files = await fs.promises.readdir(path.join(require.resolve('highlight.js'), '../../styles'));
|
|
150
148
|
const isStylesheet = /\.css$/;
|
|
151
|
-
Markdown.themes = files.filter(
|
|
152
|
-
return isStylesheet.test(file);
|
|
153
|
-
});
|
|
149
|
+
Markdown.themes = files.filter(file => isStylesheet.test(file));
|
|
154
150
|
} catch (err) {
|
|
155
|
-
winston.error(
|
|
151
|
+
winston.error(`[plugin/markdown] Could not load Markdown themes: ${err.message}`);
|
|
156
152
|
Markdown.themes = [];
|
|
157
153
|
}
|
|
158
154
|
},
|
|
@@ -206,7 +202,7 @@ const Markdown = {
|
|
|
206
202
|
// eslint-disable-next-line no-cond-assign
|
|
207
203
|
while ((current = matcher.exec(data.postData.content)) !== null) {
|
|
208
204
|
const match = current[1];
|
|
209
|
-
if (match && Markdown.isExternalLink(match)) {
|
|
205
|
+
if (match && Markdown.isExternalLink(match)) { // for security only parse external images
|
|
210
206
|
const parsedUrl = url.parse(match);
|
|
211
207
|
const filename = path.basename(parsedUrl.pathname);
|
|
212
208
|
const size = Markdown._externalImageCache.get(match);
|
|
@@ -231,7 +227,6 @@ const Markdown = {
|
|
|
231
227
|
[width, height] = [height, width];
|
|
232
228
|
}
|
|
233
229
|
|
|
234
|
-
env.images.set(filename, { width, height });
|
|
235
230
|
Markdown._externalImageCache.set(match, { width, height });
|
|
236
231
|
}).catch(() => {
|
|
237
232
|
// Likely an issue getting the external image size, ignore in the future
|
|
@@ -254,13 +249,9 @@ const Markdown = {
|
|
|
254
249
|
const execute = function (html) {
|
|
255
250
|
// Replace all italicised mentions back to regular mentions
|
|
256
251
|
if (italicMention.test(html)) {
|
|
257
|
-
html = html.replace(italicMention,
|
|
258
|
-
return '@_' + slug + '_';
|
|
259
|
-
});
|
|
252
|
+
html = html.replace(italicMention, (match, slug) => `@_${slug}_`);
|
|
260
253
|
} else if (boldMention.test(html)) {
|
|
261
|
-
html = html.replace(boldMention,
|
|
262
|
-
return '@__' + slug + '__';
|
|
263
|
-
});
|
|
254
|
+
html = html.replace(boldMention, (match, slug) => `@__${slug}__`);
|
|
264
255
|
}
|
|
265
256
|
|
|
266
257
|
return html;
|
|
@@ -339,12 +330,15 @@ const Markdown = {
|
|
|
339
330
|
|
|
340
331
|
// Update renderer to add some classes to all images
|
|
341
332
|
const renderImage = parser.renderer.rules.image || function (tokens, idx, options, env, self) {
|
|
333
|
+
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
|
342
334
|
return self.renderToken.apply(self, arguments);
|
|
343
335
|
};
|
|
344
336
|
const renderLink = parser.renderer.rules.link_open || function (tokens, idx, options, env, self) {
|
|
337
|
+
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
|
345
338
|
return self.renderToken.apply(self, arguments);
|
|
346
339
|
};
|
|
347
340
|
const renderTable = parser.renderer.rules.table_open || function (tokens, idx, options, env, self) {
|
|
341
|
+
// eslint-disable-next-line prefer-spread,prefer-rest-params
|
|
348
342
|
return self.renderToken.apply(self, arguments);
|
|
349
343
|
};
|
|
350
344
|
|
|
@@ -356,7 +350,7 @@ const Markdown = {
|
|
|
356
350
|
// Validate the url
|
|
357
351
|
if (!Markdown.isUrlValid(attributes.get('src'))) { return ''; }
|
|
358
352
|
|
|
359
|
-
token.attrSet('class',
|
|
353
|
+
token.attrSet('class', `${token.attrGet('class') || ''} img-responsive img-markdown`);
|
|
360
354
|
|
|
361
355
|
// Append sizes to images
|
|
362
356
|
if (parsedSrc.pathname) {
|
|
@@ -437,7 +431,7 @@ const Markdown = {
|
|
|
437
431
|
* get updated.
|
|
438
432
|
*/
|
|
439
433
|
const allowedRoots = [nconf.get('upload_url'), '/uploads'];
|
|
440
|
-
const allowed =
|
|
434
|
+
const allowed = pathname => allowedRoots.some(root => pathname.toString().startsWith(root) || pathname.toString().startsWith(nconf.get('relative_path') + root));
|
|
441
435
|
|
|
442
436
|
try {
|
|
443
437
|
const urlObj = url.parse(src, false, true);
|
|
@@ -458,9 +452,12 @@ const Markdown = {
|
|
|
458
452
|
}
|
|
459
453
|
|
|
460
454
|
if (
|
|
461
|
-
urlObj.host === null
|
|
462
|
-
|
|
463
|
-
|
|
455
|
+
urlObj.host === null || // Relative paths are always internal links...
|
|
456
|
+
(
|
|
457
|
+
urlObj.host === baseUrlObj.host &&
|
|
458
|
+
urlObj.protocol === baseUrlObj.protocol && // Otherwise need to check that protocol and host match
|
|
459
|
+
(nconf.get('relative_path').length > 0 ? urlObj.pathname.indexOf(nconf.get('relative_path')) === 0 : true) // Subfolder installs need this additional check
|
|
460
|
+
)
|
|
464
461
|
) {
|
|
465
462
|
return false;
|
|
466
463
|
}
|
package/lib/controllers.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodebb-plugin-markdown",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.1.0",
|
|
4
4
|
"description": "A Markdown parser for NodeBB",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"repository": {
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
},
|
|
20
20
|
"husky": {
|
|
21
21
|
"hooks": {
|
|
22
|
-
"pre-commit": "lint-staged",
|
|
23
|
-
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
|
|
22
|
+
"pre-commit": "npx lint-staged",
|
|
23
|
+
"commit-msg": "npx commitlint -E HUSKY_GIT_PARAMS"
|
|
24
24
|
}
|
|
25
25
|
},
|
|
26
26
|
"lint-staged": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
]
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"
|
|
32
|
+
"highlight.js": "11.4.0",
|
|
33
33
|
"highlightjs-line-numbers.js": "^2.8.0",
|
|
34
34
|
"markdown-it": "^12.0.6",
|
|
35
35
|
"markdown-it-checkbox": "^1.1.0",
|
|
@@ -37,14 +37,14 @@
|
|
|
37
37
|
"probe-image-size": "^7.2.1"
|
|
38
38
|
},
|
|
39
39
|
"nbbpm": {
|
|
40
|
-
"compatibility": "^
|
|
40
|
+
"compatibility": "^2.4.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@commitlint/cli": "16.2.1",
|
|
44
44
|
"@commitlint/config-angular": "16.2.1",
|
|
45
|
-
"eslint": "8.
|
|
46
|
-
"eslint-config-
|
|
47
|
-
"eslint-plugin-import": "2.
|
|
45
|
+
"eslint": "8.x",
|
|
46
|
+
"eslint-config-nodebb": "0.1.1",
|
|
47
|
+
"eslint-plugin-import": "2.x",
|
|
48
48
|
"husky": "7.0.4",
|
|
49
49
|
"lint-staged": "12.3.5"
|
|
50
50
|
}
|
package/plugin.json
CHANGED
|
@@ -11,11 +11,9 @@
|
|
|
11
11
|
"public/js/client.js"
|
|
12
12
|
],
|
|
13
13
|
"staticDirs": {
|
|
14
|
-
"
|
|
14
|
+
"styles": "node_modules/highlight.js/styles"
|
|
15
15
|
},
|
|
16
16
|
"modules": {
|
|
17
|
-
"highlight.js": "../@highlightjs/cdn-assets/highlight.min.js",
|
|
18
|
-
"highlightjs-line-numbers.js": "./public/js/highlightjs-line-numbers.js",
|
|
19
17
|
"../admin/plugins/markdown.js": "./public/js/admin.js"
|
|
20
18
|
},
|
|
21
19
|
"languages": "public/languages",
|
|
@@ -36,6 +34,7 @@
|
|
|
36
34
|
{ "hook": "filter:sanitize.config", "method": "updateSanitizeConfig" }
|
|
37
35
|
],
|
|
38
36
|
"upgrades": [
|
|
39
|
-
"upgrades/reset_md_hljs_theme.js"
|
|
37
|
+
"upgrades/reset_md_hljs_theme.js",
|
|
38
|
+
"upgrades/use_unminified_hljs_theme.js"
|
|
40
39
|
]
|
|
41
40
|
}
|
package/public/js/admin.js
CHANGED
|
@@ -17,7 +17,7 @@ define('admin/plugins/markdown', ['settings', 'alerts'], function (Settings, ale
|
|
|
17
17
|
langPrefix: 'language-',
|
|
18
18
|
highlight: true,
|
|
19
19
|
highlightLinesLanguageList: [],
|
|
20
|
-
highlightTheme: 'default.
|
|
20
|
+
highlightTheme: 'default.css',
|
|
21
21
|
|
|
22
22
|
probe: true,
|
|
23
23
|
probeCacheSize: 256,
|
package/public/js/client.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/* global window, jQuery, $, config, socket */
|
|
4
|
-
|
|
5
3
|
(function () {
|
|
6
4
|
var Markdown = {};
|
|
7
5
|
|
|
@@ -33,13 +31,14 @@
|
|
|
33
31
|
// Otherwise, edit the post to reflect state change
|
|
34
32
|
var _this = this;
|
|
35
33
|
var pid = $(this).parents('li[data-pid]').attr('data-pid');
|
|
36
|
-
var index = $(this).parents('.content').find('input[type="checkbox"]').toArray()
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
var index = $(this).parents('.content').find('input[type="checkbox"]').toArray()
|
|
35
|
+
.reduce(function (memo, cur, index) {
|
|
36
|
+
if (cur === _this) {
|
|
37
|
+
memo = index;
|
|
38
|
+
}
|
|
40
39
|
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
return memo;
|
|
41
|
+
}, null);
|
|
43
42
|
|
|
44
43
|
socket.emit('plugins.markdown.checkbox.edit', {
|
|
45
44
|
pid: pid,
|
|
@@ -119,11 +118,15 @@
|
|
|
119
118
|
controls.updateTextareaSelection(textarea, selectionStart + 2, selectionStart + 2);
|
|
120
119
|
} else {
|
|
121
120
|
controls.insertIntoTextarea(textarea, '**' + strings.bold + '**');
|
|
122
|
-
controls.updateTextareaSelection(
|
|
121
|
+
controls.updateTextareaSelection(
|
|
122
|
+
textarea, selectionStart + 2, selectionStart + strings.bold.length + 2
|
|
123
|
+
);
|
|
123
124
|
}
|
|
124
125
|
} else {
|
|
125
126
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '**');
|
|
126
|
-
controls.updateTextareaSelection(
|
|
127
|
+
controls.updateTextareaSelection(
|
|
128
|
+
textarea, selectionStart + 2 + wrapDelta[0], selectionEnd + 2 - wrapDelta[1]
|
|
129
|
+
);
|
|
127
130
|
}
|
|
128
131
|
});
|
|
129
132
|
|
|
@@ -136,11 +139,15 @@
|
|
|
136
139
|
controls.updateTextareaSelection(textarea, selectionStart + 1, selectionStart + 1);
|
|
137
140
|
} else {
|
|
138
141
|
controls.insertIntoTextarea(textarea, '*' + strings.italic + '*');
|
|
139
|
-
controls.updateTextareaSelection(
|
|
142
|
+
controls.updateTextareaSelection(
|
|
143
|
+
textarea, selectionStart + 1, selectionStart + strings.italic.length + 1
|
|
144
|
+
);
|
|
140
145
|
}
|
|
141
146
|
} else {
|
|
142
147
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '*');
|
|
143
|
-
controls.updateTextareaSelection(
|
|
148
|
+
controls.updateTextareaSelection(
|
|
149
|
+
textarea, selectionStart + 1 + wrapDelta[0], selectionEnd + 1 - wrapDelta[1]
|
|
150
|
+
);
|
|
144
151
|
}
|
|
145
152
|
});
|
|
146
153
|
|
|
@@ -149,10 +156,14 @@
|
|
|
149
156
|
controls.insertIntoTextarea(textarea, '\n* ' + strings.list_item);
|
|
150
157
|
|
|
151
158
|
// Highlight "list item"
|
|
152
|
-
controls.updateTextareaSelection(
|
|
159
|
+
controls.updateTextareaSelection(
|
|
160
|
+
textarea, selectionStart + 3, selectionStart + strings.list_item.length + 3
|
|
161
|
+
);
|
|
153
162
|
} else {
|
|
154
163
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '\n* ', '');
|
|
155
|
-
controls.updateTextareaSelection(
|
|
164
|
+
controls.updateTextareaSelection(
|
|
165
|
+
textarea, selectionStart + 3 + wrapDelta[0], selectionEnd + 3 - wrapDelta[1]
|
|
166
|
+
);
|
|
156
167
|
}
|
|
157
168
|
});
|
|
158
169
|
|
|
@@ -165,41 +176,61 @@
|
|
|
165
176
|
controls.updateTextareaSelection(textarea, selectionStart + 2, selectionStart + 2);
|
|
166
177
|
} else {
|
|
167
178
|
controls.insertIntoTextarea(textarea, '~~' + strings.strikethrough_text + '~~');
|
|
168
|
-
controls.updateTextareaSelection(
|
|
179
|
+
controls.updateTextareaSelection(
|
|
180
|
+
textarea, selectionStart + 2, selectionEnd + strings.strikethrough_text.length + 2
|
|
181
|
+
);
|
|
169
182
|
}
|
|
170
183
|
} else {
|
|
171
184
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '~~', '~~');
|
|
172
|
-
controls.updateTextareaSelection(
|
|
185
|
+
controls.updateTextareaSelection(
|
|
186
|
+
textarea, selectionStart + 2 + wrapDelta[0], selectionEnd + 2 - wrapDelta[1]
|
|
187
|
+
);
|
|
173
188
|
}
|
|
174
189
|
});
|
|
175
190
|
|
|
176
191
|
formatting.addButtonDispatch('code', function (textarea, selectionStart, selectionEnd) {
|
|
177
192
|
if (selectionStart === selectionEnd) {
|
|
178
193
|
controls.insertIntoTextarea(textarea, '```\n' + strings.code_text + '\n```');
|
|
179
|
-
controls.updateTextareaSelection(
|
|
194
|
+
controls.updateTextareaSelection(
|
|
195
|
+
textarea, selectionStart + 4, selectionEnd + strings.code_text.length + 4
|
|
196
|
+
);
|
|
180
197
|
} else {
|
|
181
198
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '```\n', '\n```');
|
|
182
|
-
controls.updateTextareaSelection(
|
|
199
|
+
controls.updateTextareaSelection(
|
|
200
|
+
textarea, selectionStart + 4 + wrapDelta[0], selectionEnd + 4 - wrapDelta[1]
|
|
201
|
+
);
|
|
183
202
|
}
|
|
184
203
|
});
|
|
185
204
|
|
|
186
205
|
formatting.addButtonDispatch('link', function (textarea, selectionStart, selectionEnd) {
|
|
187
206
|
if (selectionStart === selectionEnd) {
|
|
188
207
|
controls.insertIntoTextarea(textarea, '[' + strings.link_text + '](' + strings.link_url + ')');
|
|
189
|
-
controls.updateTextareaSelection(
|
|
208
|
+
controls.updateTextareaSelection(
|
|
209
|
+
textarea,
|
|
210
|
+
selectionStart + strings.link_text.length + 3,
|
|
211
|
+
selectionEnd + strings.link_text.length + strings.link_url.length + 3
|
|
212
|
+
);
|
|
190
213
|
} else {
|
|
191
214
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '[', '](' + strings.link_url + ')');
|
|
192
|
-
controls.updateTextareaSelection(
|
|
215
|
+
controls.updateTextareaSelection(
|
|
216
|
+
textarea, selectionEnd + 3 - wrapDelta[1], selectionEnd + strings.link_url.length + 3 - wrapDelta[1]
|
|
217
|
+
);
|
|
193
218
|
}
|
|
194
219
|
});
|
|
195
220
|
|
|
196
221
|
formatting.addButtonDispatch('picture-o', function (textarea, selectionStart, selectionEnd) {
|
|
197
222
|
if (selectionStart === selectionEnd) {
|
|
198
223
|
controls.insertIntoTextarea(textarea, '');
|
|
199
|
-
controls.updateTextareaSelection(
|
|
224
|
+
controls.updateTextareaSelection(
|
|
225
|
+
textarea,
|
|
226
|
+
selectionStart + strings.picture_text.length + 4,
|
|
227
|
+
selectionEnd + strings.picture_text.length + strings.picture_url.length + 4
|
|
228
|
+
);
|
|
200
229
|
} else {
|
|
201
230
|
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '');
|
|
202
|
-
controls.updateTextareaSelection(
|
|
231
|
+
controls.updateTextareaSelection(
|
|
232
|
+
textarea, selectionEnd + 4 - wrapDelta[1], selectionEnd + strings.picture_url.length + 4 - wrapDelta[1]
|
|
233
|
+
);
|
|
203
234
|
}
|
|
204
235
|
});
|
|
205
236
|
});
|
|
@@ -207,30 +238,32 @@
|
|
|
207
238
|
});
|
|
208
239
|
};
|
|
209
240
|
|
|
210
|
-
function highlight(elements) {
|
|
241
|
+
async function highlight(elements) {
|
|
211
242
|
if (parseInt(config.markdown.highlight, 10)) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
243
|
+
const { default: hljs } = await import('highlight.js/lib/common');
|
|
244
|
+
window.hljs = hljs;
|
|
245
|
+
require('highlightjs-line-numbers.js');
|
|
215
246
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
block.classList.add(`language-${config.markdown.defaultHighlightLanguage}`);
|
|
219
|
-
}
|
|
247
|
+
elements.each(function (i, block) {
|
|
248
|
+
$(block.parentNode).addClass('markdown-highlight');
|
|
220
249
|
|
|
221
|
-
|
|
250
|
+
// Default language if set in ACP
|
|
251
|
+
if (!Array.prototype.some.call(block.classList, (className) => className.startsWith('language-')) && config.markdown.defaultHighlightLanguage) {
|
|
252
|
+
block.classList.add(`language-${config.markdown.defaultHighlightLanguage}`);
|
|
253
|
+
}
|
|
222
254
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
}).some(Boolean)) {
|
|
230
|
-
$(block).attr('data-lines', 1);
|
|
231
|
-
window.hljs.lineNumbersBlock(block);
|
|
255
|
+
window.hljs.highlightElement(block);
|
|
256
|
+
|
|
257
|
+
// Check detected language against whitelist and add lines if enabled
|
|
258
|
+
if (block.className.split(' ').map(function (className) {
|
|
259
|
+
if (className.indexOf('language-') === 0) {
|
|
260
|
+
className = className.slice(9);
|
|
232
261
|
}
|
|
233
|
-
|
|
262
|
+
return config.markdown.highlightLinesLanguageList.includes(className) || config.markdown.highlightLinesLanguageList.includes(className);
|
|
263
|
+
}).some(Boolean)) {
|
|
264
|
+
$(block).attr('data-lines', 1);
|
|
265
|
+
window.hljs.lineNumbersBlock(block);
|
|
266
|
+
}
|
|
234
267
|
});
|
|
235
268
|
}
|
|
236
269
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const meta = require.main.require('./src/meta');
|
|
4
|
+
|
|
5
|
+
module.exports = {
|
|
6
|
+
name: 'Update Markdown Theme to point to unminified file',
|
|
7
|
+
timestamp: Date.UTC(2022, 1, 17),
|
|
8
|
+
method: async () => {
|
|
9
|
+
const { highlightTheme } = await meta.settings.get('markdown');
|
|
10
|
+
if (highlightTheme) {
|
|
11
|
+
await meta.settings.setOne('markdown', 'highlightTheme', highlightTheme.replace('.min.css', '.css'));
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
package/.eslintignore
DELETED
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* STOP! This file was edited from the version found in the repo
|
|
3
|
-
* https://github.com/wcoder/highlightjs-line-numbers.js
|
|
4
|
-
*
|
|
5
|
-
* Changes:
|
|
6
|
-
* - Made into a requirejs module
|
|
7
|
-
* - Instead of passing window, document in as w and d, they
|
|
8
|
-
* are defined at top of module
|
|
9
|
-
*/
|
|
10
|
-
define('highlightjs-line-numbers', ['highlight'], function () {
|
|
11
|
-
var w = window;
|
|
12
|
-
var d = document;
|
|
13
|
-
|
|
14
|
-
var TABLE_NAME = 'hljs-ln',
|
|
15
|
-
LINE_NAME = 'hljs-ln-line',
|
|
16
|
-
CODE_BLOCK_NAME = 'hljs-ln-code',
|
|
17
|
-
NUMBERS_BLOCK_NAME = 'hljs-ln-numbers',
|
|
18
|
-
NUMBER_LINE_NAME = 'hljs-ln-n',
|
|
19
|
-
DATA_ATTR_NAME = 'data-line-number',
|
|
20
|
-
BREAK_LINE_REGEXP = /\r\n|\r|\n/g;
|
|
21
|
-
|
|
22
|
-
if (w.hljs) {
|
|
23
|
-
w.hljs.initLineNumbersOnLoad = initLineNumbersOnLoad;
|
|
24
|
-
w.hljs.lineNumbersBlock = lineNumbersBlock;
|
|
25
|
-
w.hljs.lineNumbersValue = lineNumbersValue;
|
|
26
|
-
|
|
27
|
-
addStyles();
|
|
28
|
-
} else {
|
|
29
|
-
w.console.error('highlight.js not detected!');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function isHljsLnCodeDescendant(domElt) {
|
|
33
|
-
var curElt = domElt;
|
|
34
|
-
while (curElt) {
|
|
35
|
-
if (curElt.className && curElt.className.indexOf('hljs-ln-code') !== -1) {
|
|
36
|
-
return true;
|
|
37
|
-
}
|
|
38
|
-
curElt = curElt.parentNode;
|
|
39
|
-
}
|
|
40
|
-
return false;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getHljsLnTable(hljsLnDomElt) {
|
|
44
|
-
var curElt = hljsLnDomElt;
|
|
45
|
-
while (curElt.nodeName !== 'TABLE') {
|
|
46
|
-
curElt = curElt.parentNode;
|
|
47
|
-
}
|
|
48
|
-
return curElt;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Function to workaround a copy issue with Microsoft Edge.
|
|
52
|
-
// Due to hljs-ln wrapping the lines of code inside a <table> element,
|
|
53
|
-
// itself wrapped inside a <pre> element, window.getSelection().toString()
|
|
54
|
-
// does not contain any line breaks. So we need to get them back using the
|
|
55
|
-
// rendered code in the DOM as reference.
|
|
56
|
-
function edgeGetSelectedCodeLines(selection) {
|
|
57
|
-
// current selected text without line breaks
|
|
58
|
-
var selectionText = selection.toString();
|
|
59
|
-
|
|
60
|
-
// get the <td> element wrapping the first line of selected code
|
|
61
|
-
var tdAnchor = selection.anchorNode;
|
|
62
|
-
while (tdAnchor.nodeName !== 'TD') {
|
|
63
|
-
tdAnchor = tdAnchor.parentNode;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// get the <td> element wrapping the last line of selected code
|
|
67
|
-
var tdFocus = selection.focusNode;
|
|
68
|
-
while (tdFocus.nodeName !== 'TD') {
|
|
69
|
-
tdFocus = tdFocus.parentNode;
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
// extract line numbers
|
|
73
|
-
var firstLineNumber = parseInt(tdAnchor.dataset.lineNumber);
|
|
74
|
-
var lastLineNumber = parseInt(tdFocus.dataset.lineNumber);
|
|
75
|
-
|
|
76
|
-
// multi-lines copied case
|
|
77
|
-
if (firstLineNumber != lastLineNumber) {
|
|
78
|
-
|
|
79
|
-
var firstLineText = tdAnchor.textContent;
|
|
80
|
-
var lastLineText = tdFocus.textContent;
|
|
81
|
-
|
|
82
|
-
// if the selection was made backward, swap values
|
|
83
|
-
if (firstLineNumber > lastLineNumber) {
|
|
84
|
-
var tmp = firstLineNumber;
|
|
85
|
-
firstLineNumber = lastLineNumber;
|
|
86
|
-
lastLineNumber = tmp;
|
|
87
|
-
tmp = firstLineText;
|
|
88
|
-
firstLineText = lastLineText;
|
|
89
|
-
lastLineText = tmp;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// discard not copied characters in first line
|
|
93
|
-
while (selectionText.indexOf(firstLineText) !== 0) {
|
|
94
|
-
firstLineText = firstLineText.slice(1);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// discard not copied characters in last line
|
|
98
|
-
while (selectionText.lastIndexOf(lastLineText) === -1) {
|
|
99
|
-
lastLineText = lastLineText.slice(0, -1);
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// reconstruct and return the real copied text
|
|
103
|
-
var selectedText = firstLineText;
|
|
104
|
-
var hljsLnTable = getHljsLnTable(tdAnchor);
|
|
105
|
-
for (var i = firstLineNumber + 1 ; i < lastLineNumber ; ++i) {
|
|
106
|
-
var codeLineSel = format('.{0}[{1}="{2}"]', [CODE_BLOCK_NAME, DATA_ATTR_NAME, i]);
|
|
107
|
-
var codeLineElt = hljsLnTable.querySelector(codeLineSel);
|
|
108
|
-
selectedText += '\n' + codeLineElt.textContent;
|
|
109
|
-
}
|
|
110
|
-
selectedText += '\n' + lastLineText;
|
|
111
|
-
return selectedText;
|
|
112
|
-
// single copied line case
|
|
113
|
-
} else {
|
|
114
|
-
return selectionText;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ensure consistent code copy/paste behavior across all browsers
|
|
119
|
-
// (see https://github.com/wcoder/highlightjs-line-numbers.js/issues/51)
|
|
120
|
-
document.addEventListener('copy', function(e) {
|
|
121
|
-
// get current selection
|
|
122
|
-
var selection = window.getSelection();
|
|
123
|
-
// override behavior when one wants to copy line of codes
|
|
124
|
-
if (isHljsLnCodeDescendant(selection.anchorNode)) {
|
|
125
|
-
var selectionText;
|
|
126
|
-
// workaround an issue with Microsoft Edge as copied line breaks
|
|
127
|
-
// are removed otherwise from the selection string
|
|
128
|
-
if (window.navigator.userAgent.indexOf('Edge') !== -1) {
|
|
129
|
-
selectionText = edgeGetSelectedCodeLines(selection);
|
|
130
|
-
} else {
|
|
131
|
-
// other browsers can directly use the selection string
|
|
132
|
-
selectionText = selection.toString();
|
|
133
|
-
}
|
|
134
|
-
e.clipboardData.setData('text/plain', selectionText);
|
|
135
|
-
e.preventDefault();
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
function addStyles () {
|
|
140
|
-
var css = d.createElement('style');
|
|
141
|
-
css.type = 'text/css';
|
|
142
|
-
css.innerHTML = format(
|
|
143
|
-
'.{0}{border-collapse:collapse}' +
|
|
144
|
-
'.{0} td{padding:0}' +
|
|
145
|
-
'.{1}:before{content:attr({2})}',
|
|
146
|
-
[
|
|
147
|
-
TABLE_NAME,
|
|
148
|
-
NUMBER_LINE_NAME,
|
|
149
|
-
DATA_ATTR_NAME
|
|
150
|
-
]);
|
|
151
|
-
d.getElementsByTagName('head')[0].appendChild(css);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function initLineNumbersOnLoad (options) {
|
|
155
|
-
if (d.readyState === 'interactive' || d.readyState === 'complete') {
|
|
156
|
-
documentReady(options);
|
|
157
|
-
} else {
|
|
158
|
-
w.addEventListener('DOMContentLoaded', function () {
|
|
159
|
-
documentReady(options);
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function documentReady (options) {
|
|
165
|
-
try {
|
|
166
|
-
var blocks = d.querySelectorAll('code.hljs,code.nohighlight');
|
|
167
|
-
|
|
168
|
-
for (var i in blocks) {
|
|
169
|
-
if (blocks.hasOwnProperty(i)) {
|
|
170
|
-
if (!isPluginDisabledForBlock(blocks[i])) {
|
|
171
|
-
lineNumbersBlock(blocks[i], options);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
} catch (e) {
|
|
176
|
-
w.console.error('LineNumbers error: ', e);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function isPluginDisabledForBlock(element) {
|
|
181
|
-
return element.classList.contains('nohljsln');
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function lineNumbersBlock (element, options) {
|
|
185
|
-
if (typeof element !== 'object') return;
|
|
186
|
-
|
|
187
|
-
async(function () {
|
|
188
|
-
element.innerHTML = lineNumbersInternal(element, options);
|
|
189
|
-
});
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function lineNumbersValue (value, options) {
|
|
193
|
-
if (typeof value !== 'string') return;
|
|
194
|
-
|
|
195
|
-
var element = document.createElement('code')
|
|
196
|
-
element.innerHTML = value
|
|
197
|
-
|
|
198
|
-
return lineNumbersInternal(element, options);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function lineNumbersInternal (element, options) {
|
|
202
|
-
|
|
203
|
-
var internalOptions = mapOptions(element, options);
|
|
204
|
-
|
|
205
|
-
duplicateMultilineNodes(element);
|
|
206
|
-
|
|
207
|
-
return addLineNumbersBlockFor(element.innerHTML, internalOptions);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
function addLineNumbersBlockFor (inputHtml, options) {
|
|
211
|
-
var lines = getLines(inputHtml);
|
|
212
|
-
|
|
213
|
-
// if last line contains only carriage return remove it
|
|
214
|
-
if (lines[lines.length-1].trim() === '') {
|
|
215
|
-
lines.pop();
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
if (lines.length > 1 || options.singleLine) {
|
|
219
|
-
var html = '';
|
|
220
|
-
|
|
221
|
-
for (var i = 0, l = lines.length; i < l; i++) {
|
|
222
|
-
html += format(
|
|
223
|
-
'<tr>' +
|
|
224
|
-
'<td class="{0} {1}" {3}="{5}">' +
|
|
225
|
-
'<div class="{2}" {3}="{5}"></div>' +
|
|
226
|
-
'</td>' +
|
|
227
|
-
'<td class="{0} {4}" {3}="{5}">' +
|
|
228
|
-
'{6}' +
|
|
229
|
-
'</td>' +
|
|
230
|
-
'</tr>',
|
|
231
|
-
[
|
|
232
|
-
LINE_NAME,
|
|
233
|
-
NUMBERS_BLOCK_NAME,
|
|
234
|
-
NUMBER_LINE_NAME,
|
|
235
|
-
DATA_ATTR_NAME,
|
|
236
|
-
CODE_BLOCK_NAME,
|
|
237
|
-
i + options.startFrom,
|
|
238
|
-
lines[i].length > 0 ? lines[i] : ' '
|
|
239
|
-
]);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
return format('<table class="{0}">{1}</table>', [ TABLE_NAME, html ]);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return inputHtml;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* @param {HTMLElement} element Code block.
|
|
250
|
-
* @param {Object} options External API options.
|
|
251
|
-
* @returns {Object} Internal API options.
|
|
252
|
-
*/
|
|
253
|
-
function mapOptions (element, options) {
|
|
254
|
-
options = options || {};
|
|
255
|
-
return {
|
|
256
|
-
singleLine: getSingleLineOption(options),
|
|
257
|
-
startFrom: getStartFromOption(element, options)
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function getSingleLineOption (options) {
|
|
262
|
-
var defaultValue = false;
|
|
263
|
-
if (!!options.singleLine) {
|
|
264
|
-
return options.singleLine;
|
|
265
|
-
}
|
|
266
|
-
return defaultValue;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function getStartFromOption (element, options) {
|
|
270
|
-
var defaultValue = 1;
|
|
271
|
-
var startFrom = defaultValue;
|
|
272
|
-
|
|
273
|
-
if (isFinite(options.startFrom)) {
|
|
274
|
-
startFrom = options.startFrom;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
// can be overridden because local option is priority
|
|
278
|
-
var value = getAttribute(element, 'data-ln-start-from');
|
|
279
|
-
if (value !== null) {
|
|
280
|
-
startFrom = toNumber(value, defaultValue);
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
return startFrom;
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
/**
|
|
287
|
-
* Recursive method for fix multi-line elements implementation in highlight.js
|
|
288
|
-
* Doing deep passage on child nodes.
|
|
289
|
-
* @param {HTMLElement} element
|
|
290
|
-
*/
|
|
291
|
-
function duplicateMultilineNodes (element) {
|
|
292
|
-
var nodes = element.childNodes;
|
|
293
|
-
for (var node in nodes) {
|
|
294
|
-
if (nodes.hasOwnProperty(node)) {
|
|
295
|
-
var child = nodes[node];
|
|
296
|
-
if (getLinesCount(child.textContent) > 0) {
|
|
297
|
-
if (child.childNodes.length > 0) {
|
|
298
|
-
duplicateMultilineNodes(child);
|
|
299
|
-
} else {
|
|
300
|
-
duplicateMultilineNode(child.parentNode);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Method for fix multi-line elements implementation in highlight.js
|
|
309
|
-
* @param {HTMLElement} element
|
|
310
|
-
*/
|
|
311
|
-
function duplicateMultilineNode (element) {
|
|
312
|
-
var className = element.className;
|
|
313
|
-
|
|
314
|
-
if ( ! /hljs-/.test(className)) return;
|
|
315
|
-
|
|
316
|
-
var lines = getLines(element.innerHTML);
|
|
317
|
-
|
|
318
|
-
for (var i = 0, result = ''; i < lines.length; i++) {
|
|
319
|
-
var lineText = lines[i].length > 0 ? lines[i] : ' ';
|
|
320
|
-
result += format('<span class="{0}">{1}</span>\n', [ className, lineText ]);
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
element.innerHTML = result.trim();
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function getLines (text) {
|
|
327
|
-
if (text.length === 0) return [];
|
|
328
|
-
return text.split(BREAK_LINE_REGEXP);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function getLinesCount (text) {
|
|
332
|
-
return (text.trim().match(BREAK_LINE_REGEXP) || []).length;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
///
|
|
336
|
-
/// HELPERS
|
|
337
|
-
///
|
|
338
|
-
|
|
339
|
-
function async (func) {
|
|
340
|
-
w.setTimeout(func, 0);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* {@link https://wcoder.github.io/notes/string-format-for-string-formating-in-javascript}
|
|
345
|
-
* @param {string} format
|
|
346
|
-
* @param {array} args
|
|
347
|
-
*/
|
|
348
|
-
function format (format, args) {
|
|
349
|
-
return format.replace(/\{(\d+)\}/g, function(m, n){
|
|
350
|
-
return args[n] !== undefined ? args[n] : m;
|
|
351
|
-
});
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* @param {HTMLElement} element Code block.
|
|
356
|
-
* @param {String} attrName Attribute name.
|
|
357
|
-
* @returns {String} Attribute value or empty.
|
|
358
|
-
*/
|
|
359
|
-
function getAttribute (element, attrName) {
|
|
360
|
-
return element.hasAttribute(attrName) ? element.getAttribute(attrName) : null;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
/**
|
|
364
|
-
* @param {String} str Source string.
|
|
365
|
-
* @param {Number} fallback Fallback value.
|
|
366
|
-
* @returns Parsed number or fallback value.
|
|
367
|
-
*/
|
|
368
|
-
function toNumber (str, fallback) {
|
|
369
|
-
if (!str) return fallback;
|
|
370
|
-
var number = Number(str);
|
|
371
|
-
return isFinite(number) ? number : fallback;
|
|
372
|
-
}
|
|
373
|
-
});
|