ep_markdown_toc 1.0.5 → 1.0.8
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/README.md +2 -0
- package/assets/demo.gif +0 -0
- package/index.js +7 -1
- package/package.json +2 -2
- package/static/js/client_hooks.js +42 -1
- package/static/js/plain_paste.js +60 -0
- package/templates/tocSettings.ejs +6 -1
- package/templates/tocToggleButton.ejs +6 -0
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
## Markdown editing in Etherpad
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+
|
|
3
5
|
This plugin enhances Etherpad for Markdown-style writing.
|
|
4
6
|
It does this in multiple ways:
|
|
5
7
|
- It enables automatic generation of a Table of Contents based on Markdown-style hashsign headings (#, ##, etc.) in Etherpad documents (can be disabled through settings). === style headings are not supported at the moment, and likely will never be.
|
package/assets/demo.gif
ADDED
|
Binary file
|
package/index.js
CHANGED
|
@@ -12,7 +12,13 @@ exports.eejsBlock_styles = (hook, context) => {
|
|
|
12
12
|
|
|
13
13
|
exports.eejsBlock_editbarMenuRight = (hookName, args, cb) => {
|
|
14
14
|
// console.log("MENU RIGHT");
|
|
15
|
-
args.content = eejs.require('ep_markdown_toc/templates/
|
|
15
|
+
args.content = eejs.require('ep_markdown_toc/templates/tocToggleButton.ejs') +
|
|
16
|
+
eejs.require('ep_markdown_toc/templates/markdownButton.ejs') + args.content;
|
|
17
|
+
return cb();
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
exports.eejsBlock_dd_view = (hookName, args, cb) => {
|
|
21
|
+
args.content += '<li><a href="#" id="markdown-toc-toggle-menu">Markdown TOC</a></li>';
|
|
16
22
|
return cb();
|
|
17
23
|
};
|
|
18
24
|
|
package/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ep_markdown_toc",
|
|
3
3
|
"description": "Provides markdown-based heading detection and TOC for Etherpad, used in Octomode.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.8",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "hrk",
|
|
7
7
|
"email": "heerko@hackersanddesigners.nl"
|
|
8
8
|
},
|
|
9
9
|
"repository": {
|
|
10
10
|
"type": "git",
|
|
11
|
-
"url": "https://github.com/heerko/ep_markdown_toc"
|
|
11
|
+
"url": "git+https://github.com/heerko/ep_markdown_toc.git"
|
|
12
12
|
},
|
|
13
13
|
"dependencies": {
|
|
14
14
|
"showdown": "*"
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
let _editorInfo = null; // Both no longer used atm, but leaving them for now.
|
|
4
4
|
let _ace;
|
|
5
5
|
const tags = ['h1', 'h2', 'h3', 'h4'];
|
|
6
|
+
let plainPasteEnabled = true;
|
|
6
7
|
|
|
7
8
|
/* Load the css in the editor iframe */
|
|
8
9
|
exports.aceEditorCSS = function() {
|
|
@@ -15,6 +16,8 @@ exports.aceEditorCSS = function() {
|
|
|
15
16
|
exports.postAceInit = (hook, context) => {
|
|
16
17
|
// store a reference to the editor for later
|
|
17
18
|
_ace = context.ace;
|
|
19
|
+
// Initialize plain paste handler (if enabled).
|
|
20
|
+
maybeInitPlainPaste(context);
|
|
18
21
|
updateTOC();
|
|
19
22
|
};
|
|
20
23
|
|
|
@@ -71,6 +74,14 @@ exports.postToolbarInit = () => {
|
|
|
71
74
|
$('#markdown-cheat').toggleClass('popup-show');
|
|
72
75
|
});
|
|
73
76
|
|
|
77
|
+
const toggleToc = (event) => {
|
|
78
|
+
if (event) event.preventDefault();
|
|
79
|
+
$('#options-hideToc').click();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
$('#markdown-toc-toggle').on('click', toggleToc);
|
|
83
|
+
$('#markdown-toc-toggle-menu').on('click', toggleToc);
|
|
84
|
+
|
|
74
85
|
initSettingsUI();
|
|
75
86
|
};
|
|
76
87
|
|
|
@@ -153,6 +164,7 @@ but it maybe possible to manipulate the list from settings.json?
|
|
|
153
164
|
*/
|
|
154
165
|
function initSettingsUI() {
|
|
155
166
|
const padcookie = require('ep_etherpad-lite/static/js/pad_cookie').padcookie;
|
|
167
|
+
const plainPaste = require('./plain_paste');
|
|
156
168
|
|
|
157
169
|
let prefs = padcookie.getPref('userPrefs') || {};
|
|
158
170
|
|
|
@@ -169,6 +181,10 @@ function initSettingsUI() {
|
|
|
169
181
|
prefs.styleHeadings = true; // also true
|
|
170
182
|
padcookie.setPref('userPrefs', prefs);
|
|
171
183
|
}
|
|
184
|
+
if (typeof prefs.plainPaste === 'undefined') {
|
|
185
|
+
prefs.plainPaste = true; // default to on
|
|
186
|
+
padcookie.setPref('userPrefs', prefs);
|
|
187
|
+
}
|
|
172
188
|
|
|
173
189
|
// apply the headings class to the inner iframe if set
|
|
174
190
|
const padOuter = document.querySelector('iframe[name="ace_outer"]');
|
|
@@ -203,6 +219,22 @@ function initSettingsUI() {
|
|
|
203
219
|
bindToggleSetting('#options-hideToc', 'hide-toc', 'hideToc');
|
|
204
220
|
bindToggleSetting('#options-hideButtons', 'hide-buttons', 'hideButtons');
|
|
205
221
|
bindToggleSetting('#options-styleHeadings', 'style-md-headings', 'styleHeadings');
|
|
222
|
+
|
|
223
|
+
// Plain paste toggle
|
|
224
|
+
const plainPasteCheckbox = $('#options-plainPaste');
|
|
225
|
+
plainPasteCheckbox.prop('checked', prefs.plainPaste === true);
|
|
226
|
+
plainPasteEnabled = prefs.plainPaste === true;
|
|
227
|
+
plainPasteCheckbox.on('change', function () {
|
|
228
|
+
const isChecked = $(this).is(':checked');
|
|
229
|
+
prefs.plainPaste = isChecked;
|
|
230
|
+
plainPasteEnabled = isChecked;
|
|
231
|
+
padcookie.setPref('userPrefs', prefs);
|
|
232
|
+
if (isChecked) {
|
|
233
|
+
plainPaste.ensure(context);
|
|
234
|
+
} else {
|
|
235
|
+
plainPaste.disable();
|
|
236
|
+
}
|
|
237
|
+
});
|
|
206
238
|
}
|
|
207
239
|
|
|
208
240
|
/* functions stolen from ep_headings2 */
|
|
@@ -217,4 +249,13 @@ exports.aceAttribsToClasses = (hookName, context) => {
|
|
|
217
249
|
if (context.key === 'heading') {
|
|
218
250
|
return [`heading:${context.value}`];
|
|
219
251
|
}
|
|
220
|
-
};
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/*
|
|
255
|
+
Plain paste integration: init if enabled and available.
|
|
256
|
+
*/
|
|
257
|
+
function maybeInitPlainPaste(context) {
|
|
258
|
+
if (!plainPasteEnabled) return;
|
|
259
|
+
//const plainPaste = require('./plain_paste');
|
|
260
|
+
//plainPaste.ensure(context);
|
|
261
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Force plain-text paste: intercept paste and insert text/plain only.
|
|
2
|
+
let bound = false;
|
|
3
|
+
let currentBody = null;
|
|
4
|
+
let currentHandler = null;
|
|
5
|
+
|
|
6
|
+
// Locate the inner editor window either from context or via DOM.
|
|
7
|
+
const getInnerWindow = (context) => {
|
|
8
|
+
if (context?.editorInfo?.ace_innerWin) return context.editorInfo.ace_innerWin;
|
|
9
|
+
const outer = document.querySelector('iframe[name="ace_outer"]');
|
|
10
|
+
const inner = outer?.contentDocument?.querySelector('iframe[name="ace_inner"]');
|
|
11
|
+
return inner?.contentWindow || null;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
const install = (context) => {
|
|
15
|
+
if (bound) return;
|
|
16
|
+
|
|
17
|
+
const editor = context?.editorInfo?.editor;
|
|
18
|
+
const callWithAce =
|
|
19
|
+
editor && typeof editor.callWithAce === 'function'
|
|
20
|
+
? editor.callWithAce.bind(editor)
|
|
21
|
+
: null;
|
|
22
|
+
const innerWin = getInnerWindow(context);
|
|
23
|
+
const body = innerWin?.document?.body;
|
|
24
|
+
|
|
25
|
+
if (!callWithAce || !body) return;
|
|
26
|
+
|
|
27
|
+
const handlePaste = (e) => {
|
|
28
|
+
const clipboard = e.clipboardData || (e.originalEvent && e.originalEvent.clipboardData);
|
|
29
|
+
const text = clipboard
|
|
30
|
+
? clipboard.getData('text/plain') || clipboard.getData('text')
|
|
31
|
+
: '';
|
|
32
|
+
if (!text) return;
|
|
33
|
+
|
|
34
|
+
e.preventDefault();
|
|
35
|
+
e.stopPropagation();
|
|
36
|
+
e.stopImmediatePropagation?.();
|
|
37
|
+
callWithAce((ace) => {
|
|
38
|
+
if (!ace || typeof ace.ace_getRep !== 'function' || typeof ace.ace_replaceRange !== 'function') return;
|
|
39
|
+
const rep = ace.ace_getRep();
|
|
40
|
+
if (!rep || !rep.selStart || !rep.selEnd) return;
|
|
41
|
+
ace.ace_replaceRange(rep.selStart, rep.selEnd, text);
|
|
42
|
+
}, 'plain-paste', true);
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
body.addEventListener('paste', handlePaste, true);
|
|
46
|
+
bound = true;
|
|
47
|
+
currentBody = body;
|
|
48
|
+
currentHandler = handlePaste;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
exports.ensure = (context) => install(context);
|
|
52
|
+
|
|
53
|
+
exports.disable = () => {
|
|
54
|
+
if (bound && currentBody && currentHandler) {
|
|
55
|
+
currentBody.removeEventListener('paste', currentHandler, true);
|
|
56
|
+
}
|
|
57
|
+
bound = false;
|
|
58
|
+
currentBody = null;
|
|
59
|
+
currentHandler = null;
|
|
60
|
+
};
|
|
@@ -14,4 +14,9 @@
|
|
|
14
14
|
<input type="checkbox" id="options-styleHeadings" />
|
|
15
15
|
<label for="options-styleHeadings" aria-label="Enable/Disable styling of the markdown headings">Style Markdown headings</label>
|
|
16
16
|
</p>
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
<p>
|
|
19
|
+
<input type="checkbox" id="options-plainPaste" />
|
|
20
|
+
<label for="options-plainPaste" aria-label="Force plain-text paste">Force plain-text paste</label>
|
|
21
|
+
</p>
|
|
22
|
+
</fieldset>
|