nodebb-plugin-markdown 12.1.7 → 12.2.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/.jshintrc +85 -85
- package/README.md +9 -9
- package/index.js +11 -7
- package/package-lock.json +7333 -0
- package/package.json +1 -1
- package/plugin.json +2 -1
- package/public/js/admin.js +1 -26
- package/public/js/client.js +12 -330
- package/public/js/markdown.js +337 -0
- package/public/languages/de/markdown.json +11 -11
- package/public/languages/en-GB/markdown.json +11 -11
- package/public/languages/fa-IR/markdown.json +11 -11
- package/public/languages/fr/markdown.json +11 -11
- package/public/languages/he/markdown.json +12 -12
- package/public/languages/pl/markdown.json +11 -11
- package/public/languages/tr/markdown.json +11 -11
- package/public/languages/zh-CN/markdown.json +11 -11
- package/public/templates/admin/plugins/markdown.tpl +184 -180
package/package.json
CHANGED
package/plugin.json
CHANGED
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
"styles": "node_modules/highlight.js/styles"
|
|
15
15
|
},
|
|
16
16
|
"modules": {
|
|
17
|
-
"../admin/plugins/markdown.js": "./public/js/admin.js"
|
|
17
|
+
"../admin/plugins/markdown.js": "./public/js/admin.js",
|
|
18
|
+
"markdown.js": "./public/js/markdown.js"
|
|
18
19
|
},
|
|
19
20
|
"languages": "public/languages",
|
|
20
21
|
"defaultLang": "en_GB",
|
package/public/js/admin.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
/* globals define, $, socket, bootbox */
|
|
4
|
-
|
|
5
3
|
define('admin/plugins/markdown', ['settings', 'alerts'], function (Settings, alerts) {
|
|
6
4
|
var Markdown = {};
|
|
7
5
|
|
|
@@ -45,30 +43,7 @@ define('admin/plugins/markdown', ['settings', 'alerts'], function (Settings, ale
|
|
|
45
43
|
});
|
|
46
44
|
|
|
47
45
|
$('#save').on('click', function () {
|
|
48
|
-
Settings.save('markdown', $('.markdown-settings')
|
|
49
|
-
alerts.alert({
|
|
50
|
-
type: 'success',
|
|
51
|
-
alert_id: 'markdown-saved',
|
|
52
|
-
title: 'Reload Required',
|
|
53
|
-
message: 'Please reload your NodeBB to have your changes take effect',
|
|
54
|
-
clickfn: function () {
|
|
55
|
-
socket.emit('admin.reload');
|
|
56
|
-
},
|
|
57
|
-
timeout: 2500,
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Warning for "html" option
|
|
63
|
-
$('#html').on('change', function () {
|
|
64
|
-
var inputEl = $(this);
|
|
65
|
-
if (inputEl.prop('checked')) {
|
|
66
|
-
bootbox.confirm('Are you sure you wish to disable sanitisation of HTML? <strong>Doing so compromises your forum's client-side security, and allows malicious users to execute arbitrary javascript on other users' browsers.</strong>', function (result) {
|
|
67
|
-
if (!result) {
|
|
68
|
-
inputEl.prop('checked', false);
|
|
69
|
-
}
|
|
70
|
-
});
|
|
71
|
-
}
|
|
46
|
+
Settings.save('markdown', $('.markdown-settings'));
|
|
72
47
|
});
|
|
73
48
|
};
|
|
74
49
|
|
package/public/js/client.js
CHANGED
|
@@ -1,339 +1,21 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
(function () {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
Markdown.prepareFormattingTools();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
Markdown.enhanceCheckbox = function (ev, data) {
|
|
13
|
-
if (!data.posts && !data.post) {
|
|
14
|
-
return;
|
|
15
|
-
} if (data.hasOwnProperty('post')) {
|
|
16
|
-
data.posts = [data.post];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
var disable;
|
|
20
|
-
var checkboxEls;
|
|
21
|
-
data.posts.forEach(function (post) {
|
|
22
|
-
disable = !post.display_edit_tools;
|
|
23
|
-
checkboxEls = $('.posts li[data-pid="' + post.pid + '"] .content div.plugin-markdown input[type="checkbox"]');
|
|
24
|
-
|
|
25
|
-
checkboxEls.on('click', function (e) {
|
|
26
|
-
if (disable) {
|
|
27
|
-
// Find the post's checkboxes in DOM and make them readonly
|
|
28
|
-
e.preventDefault();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Otherwise, edit the post to reflect state change
|
|
32
|
-
var _this = this;
|
|
33
|
-
var pid = $(this).parents('li[data-pid]').attr('data-pid');
|
|
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
|
-
}
|
|
39
|
-
|
|
40
|
-
return memo;
|
|
41
|
-
}, null);
|
|
42
|
-
|
|
43
|
-
socket.emit('plugins.markdown.checkbox.edit', {
|
|
44
|
-
pid: pid,
|
|
45
|
-
index: index,
|
|
46
|
-
state: $(_this).prop('checked'),
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
Markdown.capturePaste = function (targetEl) {
|
|
53
|
-
targetEl.on('paste', function (e) {
|
|
54
|
-
var triggers = [/^>\s*/, /^\s*\*\s+/, /^\s*\d+\.\s+/, /^\s{4,}/];
|
|
55
|
-
var start = e.target.selectionStart;
|
|
56
|
-
var line = getLine(targetEl.val(), start);
|
|
57
|
-
|
|
58
|
-
var trigger = triggers.reduce(function (regexp, cur) {
|
|
59
|
-
if (regexp) {
|
|
60
|
-
return regexp;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return cur.test(line) ? cur : false;
|
|
64
|
-
}, false);
|
|
65
|
-
|
|
66
|
-
var prefix = line.match(trigger);
|
|
67
|
-
if (prefix) {
|
|
68
|
-
prefix = prefix.shift();
|
|
69
|
-
|
|
70
|
-
var payload = e.originalEvent.clipboardData.getData('text');
|
|
71
|
-
var fixed = payload.replace(/^/gm, prefix).slice(prefix.length);
|
|
72
|
-
|
|
73
|
-
setTimeout(function () {
|
|
74
|
-
var replacement = targetEl.val().slice(0, start) + fixed + targetEl.val().slice(start + payload.length);
|
|
75
|
-
targetEl.val(replacement);
|
|
76
|
-
}, 0);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
function getLine(text, selectionStart) {
|
|
81
|
-
// Break apart into lines, return the line the cursor is in
|
|
82
|
-
var lines = text.split('\n');
|
|
83
|
-
|
|
84
|
-
return lines.reduce(function (memo, cur) {
|
|
85
|
-
if (typeof memo !== 'number') {
|
|
86
|
-
return memo;
|
|
87
|
-
} if (selectionStart > (memo + cur.length)) {
|
|
88
|
-
return memo + cur.length + 1;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
return cur;
|
|
92
|
-
}, 0);
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
Markdown.highlight = function (data) {
|
|
97
|
-
if (data instanceof jQuery.Event) {
|
|
98
|
-
highlight($(data.data.selector));
|
|
99
|
-
} else {
|
|
100
|
-
highlight(data);
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
Markdown.prepareFormattingTools = function () {
|
|
105
|
-
require([
|
|
106
|
-
'composer/formatting',
|
|
107
|
-
'composer/controls',
|
|
108
|
-
'translator',
|
|
109
|
-
], function (formatting, controls, translator) {
|
|
110
|
-
if (formatting && controls) {
|
|
111
|
-
translator.getTranslations(window.config.userLang || window.config.defaultLang, 'markdown', function (strings) {
|
|
112
|
-
formatting.addButtonDispatch('bold', function (textarea, selectionStart, selectionEnd) {
|
|
113
|
-
if (selectionStart === selectionEnd) {
|
|
114
|
-
var block = controls.getBlockData(textarea, '**', selectionStart);
|
|
115
|
-
|
|
116
|
-
if (block.in && block.atEnd) {
|
|
117
|
-
// At end of bolded string, move cursor past delimiters
|
|
118
|
-
controls.updateTextareaSelection(textarea, selectionStart + 2, selectionStart + 2);
|
|
119
|
-
} else {
|
|
120
|
-
controls.insertIntoTextarea(textarea, '**' + strings.bold + '**');
|
|
121
|
-
controls.updateTextareaSelection(
|
|
122
|
-
textarea, selectionStart + 2, selectionStart + strings.bold.length + 2
|
|
123
|
-
);
|
|
124
|
-
}
|
|
125
|
-
} else {
|
|
126
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '**');
|
|
127
|
-
controls.updateTextareaSelection(
|
|
128
|
-
textarea, selectionStart + 2 + wrapDelta[0], selectionEnd + 2 - wrapDelta[1]
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
formatting.addButtonDispatch('italic', function (textarea, selectionStart, selectionEnd) {
|
|
134
|
-
if (selectionStart === selectionEnd) {
|
|
135
|
-
var block = controls.getBlockData(textarea, '*', selectionStart);
|
|
136
|
-
|
|
137
|
-
if (block.in && block.atEnd) {
|
|
138
|
-
// At end of italicised string, move cursor past delimiters
|
|
139
|
-
controls.updateTextareaSelection(textarea, selectionStart + 1, selectionStart + 1);
|
|
140
|
-
} else {
|
|
141
|
-
controls.insertIntoTextarea(textarea, '*' + strings.italic + '*');
|
|
142
|
-
controls.updateTextareaSelection(
|
|
143
|
-
textarea, selectionStart + 1, selectionStart + strings.italic.length + 1
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '*');
|
|
148
|
-
controls.updateTextareaSelection(
|
|
149
|
-
textarea, selectionStart + 1 + wrapDelta[0], selectionEnd + 1 - wrapDelta[1]
|
|
150
|
-
);
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
formatting.addButtonDispatch('list', function (textarea, selectionStart, selectionEnd) {
|
|
155
|
-
if (selectionStart === selectionEnd) {
|
|
156
|
-
controls.insertIntoTextarea(textarea, '\n* ' + strings.list_item);
|
|
157
|
-
|
|
158
|
-
// Highlight "list item"
|
|
159
|
-
controls.updateTextareaSelection(
|
|
160
|
-
textarea, selectionStart + 3, selectionStart + strings.list_item.length + 3
|
|
161
|
-
);
|
|
162
|
-
} else {
|
|
163
|
-
const selectedText = $(textarea).val().substring(selectionStart, selectionEnd);
|
|
164
|
-
const newText = '* ' + selectedText.split('\n').join('\n* ');
|
|
165
|
-
controls.replaceSelectionInTextareaWith(textarea, newText);
|
|
166
|
-
controls.updateTextareaSelection(textarea, selectionStart, selectionEnd + (newText.length - selectedText.length))
|
|
167
|
-
}
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
formatting.addButtonDispatch('strikethrough', function (textarea, selectionStart, selectionEnd) {
|
|
171
|
-
if (selectionStart === selectionEnd) {
|
|
172
|
-
var block = controls.getBlockData(textarea, '~~', selectionStart);
|
|
173
|
-
|
|
174
|
-
if (block.in && block.atEnd) {
|
|
175
|
-
// At end of bolded string, move cursor past delimiters
|
|
176
|
-
controls.updateTextareaSelection(textarea, selectionStart + 2, selectionStart + 2);
|
|
177
|
-
} else {
|
|
178
|
-
controls.insertIntoTextarea(textarea, '~~' + strings.strikethrough_text + '~~');
|
|
179
|
-
controls.updateTextareaSelection(
|
|
180
|
-
textarea, selectionStart + 2, selectionEnd + strings.strikethrough_text.length + 2
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
} else {
|
|
184
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '~~', '~~');
|
|
185
|
-
controls.updateTextareaSelection(
|
|
186
|
-
textarea, selectionStart + 2 + wrapDelta[0], selectionEnd + 2 - wrapDelta[1]
|
|
187
|
-
);
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
formatting.addButtonDispatch('code', function (textarea, selectionStart, selectionEnd) {
|
|
192
|
-
if (selectionStart === selectionEnd) {
|
|
193
|
-
controls.insertIntoTextarea(textarea, '```\n' + strings.code_text + '\n```');
|
|
194
|
-
controls.updateTextareaSelection(
|
|
195
|
-
textarea, selectionStart + 4, selectionEnd + strings.code_text.length + 4
|
|
196
|
-
);
|
|
197
|
-
} else {
|
|
198
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '```\n', '\n```');
|
|
199
|
-
controls.updateTextareaSelection(
|
|
200
|
-
textarea, selectionStart + 4 + wrapDelta[0], selectionEnd + 4 - wrapDelta[1]
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
formatting.addButtonDispatch('link', function (textarea, selectionStart, selectionEnd) {
|
|
206
|
-
if (selectionStart === selectionEnd) {
|
|
207
|
-
controls.insertIntoTextarea(textarea, '[' + strings.link_text + '](' + strings.link_url + ')');
|
|
208
|
-
controls.updateTextareaSelection(
|
|
209
|
-
textarea,
|
|
210
|
-
selectionStart + strings.link_text.length + 3,
|
|
211
|
-
selectionEnd + strings.link_text.length + strings.link_url.length + 3
|
|
212
|
-
);
|
|
213
|
-
} else {
|
|
214
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '[', '](' + strings.link_url + ')');
|
|
215
|
-
controls.updateTextareaSelection(
|
|
216
|
-
textarea, selectionEnd + 3 - wrapDelta[1], selectionEnd + strings.link_url.length + 3 - wrapDelta[1]
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
formatting.addButtonDispatch('picture-o', function (textarea, selectionStart, selectionEnd) {
|
|
222
|
-
if (selectionStart === selectionEnd) {
|
|
223
|
-
controls.insertIntoTextarea(textarea, '');
|
|
224
|
-
controls.updateTextareaSelection(
|
|
225
|
-
textarea,
|
|
226
|
-
selectionStart + strings.picture_text.length + 4,
|
|
227
|
-
selectionEnd + strings.picture_text.length + strings.picture_url.length + 4
|
|
228
|
-
);
|
|
229
|
-
} else {
|
|
230
|
-
var wrapDelta = controls.wrapSelectionInTextareaWith(textarea, '');
|
|
231
|
-
controls.updateTextareaSelection(
|
|
232
|
-
textarea, selectionEnd + 4 - wrapDelta[1], selectionEnd + strings.picture_url.length + 4 - wrapDelta[1]
|
|
233
|
-
);
|
|
234
|
-
}
|
|
235
|
-
});
|
|
236
|
-
});
|
|
237
|
-
}
|
|
4
|
+
require(['markdown', 'components'], (markdown, components) => {
|
|
5
|
+
$(window).on('action:composer.enhanced', function (evt, data) {
|
|
6
|
+
var textareaEl = data.postContainer.find('textarea');
|
|
7
|
+
markdown.capturePaste(textareaEl);
|
|
8
|
+
markdown.prepareFormattingTools();
|
|
238
9
|
});
|
|
239
|
-
};
|
|
240
|
-
|
|
241
|
-
async function highlight(elements) {
|
|
242
|
-
if (parseInt(config.markdown.highlight, 10)) {
|
|
243
|
-
console.debug('[plugin/markdown] Initializing highlight.js');
|
|
244
|
-
let hljs;
|
|
245
|
-
let list;
|
|
246
|
-
let aliasMap = new Map();
|
|
247
|
-
switch(true) {
|
|
248
|
-
case config.markdown.hljsLanguages.includes('common'): {
|
|
249
|
-
({ default: hljs} = await import(`highlight.js/lib/common`));
|
|
250
|
-
list = 'common';
|
|
251
|
-
break;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
case config.markdown.hljsLanguages.includes('all'): {
|
|
255
|
-
({ default: hljs} = await import(`highlight.js`));
|
|
256
|
-
list = 'all';
|
|
257
|
-
break;
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
default: {
|
|
261
|
-
({ default: hljs} = await import(`highlight.js/lib/core`));
|
|
262
|
-
list = 'core';
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
console.debug(`[plugins/markdown] Loaded ${list} hljs library`);
|
|
266
|
-
|
|
267
|
-
if (list !== 'all') {
|
|
268
|
-
await Promise.all(config.markdown.hljsLanguages.map(async (language) => {
|
|
269
|
-
if (['common', 'all'].includes(language)) {
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
console.debug(`[plugins/markdown] Loading ${language} support`);
|
|
274
|
-
const { default: lang } = await import('../../node_modules/highlight.js/lib/languages/' + language + '.js');
|
|
275
|
-
hljs.registerLanguage(language, lang);
|
|
276
|
-
}));
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// Build alias set
|
|
280
|
-
hljs.listLanguages().forEach((language) => {
|
|
281
|
-
const { aliases } = hljs.getLanguage(language);
|
|
282
|
-
if (aliases && Array.isArray(aliases)) {
|
|
283
|
-
aliases.forEach((alias) => {
|
|
284
|
-
aliasMap.set(alias, language);
|
|
285
|
-
});
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
aliasMap.set(language, language);
|
|
289
|
-
});
|
|
290
|
-
|
|
291
|
-
console.debug(`[plugins/markdown] Loading support for line numbers`);
|
|
292
|
-
window.hljs = hljs;
|
|
293
|
-
require('highlightjs-line-numbers.js');
|
|
294
|
-
|
|
295
|
-
elements.each(function (i, block) {
|
|
296
|
-
const parentNode = $(block.parentNode);
|
|
297
|
-
if (parentNode.hasClass('markdown-highlight')) {
|
|
298
|
-
return;
|
|
299
|
-
}
|
|
300
|
-
parentNode.addClass('markdown-highlight');
|
|
301
|
-
|
|
302
|
-
// Default language if set in ACP
|
|
303
|
-
if (!Array.prototype.some.call(block.classList, (className) => className.startsWith('language-')) && config.markdown.defaultHighlightLanguage) {
|
|
304
|
-
block.classList.add(`language-${config.markdown.defaultHighlightLanguage}`);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
window.hljs.highlightElement(block);
|
|
308
|
-
|
|
309
|
-
// Check detected language against whitelist and add lines if enabled
|
|
310
|
-
const classIterator = block.classList.values();
|
|
311
|
-
for(className of classIterator) {
|
|
312
|
-
if (className.startsWith('language-')) {
|
|
313
|
-
const language = className.split('-')[1];
|
|
314
|
-
const list = config.markdown.highlightLinesLanguageList;
|
|
315
|
-
if (aliasMap.has(language) && list && list.includes(aliasMap.get(language))) {
|
|
316
|
-
$(block).attr('data-lines', 1);
|
|
317
|
-
window.hljs.lineNumbersBlock(block);
|
|
318
|
-
}
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
$(window).on('action:composer.preview', {
|
|
327
|
-
selector: '.composer .preview pre code',
|
|
328
|
-
}, Markdown.highlight);
|
|
329
10
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
11
|
+
$(window).on('action:composer.preview', {
|
|
12
|
+
selector: '.composer .preview pre code',
|
|
13
|
+
}, markdown.highlight);
|
|
333
14
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
15
|
+
$(window).on('action:posts.loaded action:topic.loaded action:posts.edited', function (ev, data) {
|
|
16
|
+
markdown.highlight(components.get('post/content').find('pre code'));
|
|
17
|
+
markdown.enhanceCheckbox(ev, data);
|
|
18
|
+
markdown.markExternalLinks();
|
|
337
19
|
});
|
|
338
20
|
});
|
|
339
21
|
}());
|