@richscripts/richtexteditor 2.4.0 → 2.4.1
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/migration/README.md +100 -0
- package/migration/cli.js +294 -0
- package/migration/index.d.ts +18 -0
- package/migration/index.js +242 -0
- package/migration/report-schema-v2.json +127 -0
- package/package.json +11 -1
- package/richtexteditor/CHANGELOG.md +326 -1
- package/richtexteditor/all_plugins.js +14959 -4832
- package/richtexteditor/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/accessibilitychecker.js +8 -7
- package/richtexteditor/plugins/aitoolkit.css +17 -8
- package/richtexteditor/plugins/aitoolkit.js +8 -8
- package/richtexteditor/plugins/all_plugins.js +14959 -4832
- package/richtexteditor/plugins/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/autocorrect.js +353 -0
- package/richtexteditor/plugins/bookmarkcard.js +12 -1
- package/richtexteditor/plugins/changecase.js +238 -0
- package/richtexteditor/plugins/comments.js +15 -28
- package/richtexteditor/plugins/contentminimap.js +6 -5
- package/richtexteditor/plugins/crossreference.js +408 -0
- package/richtexteditor/plugins/documentimport.js +915 -21
- package/richtexteditor/plugins/documentoutline.js +5 -4
- package/richtexteditor/plugins/docxexport.js +960 -0
- package/richtexteditor/plugins/draghandle.js +309 -0
- package/richtexteditor/plugins/footnotes.js +398 -0
- package/richtexteditor/plugins/formatpainter.js +298 -0
- package/richtexteditor/plugins/formattingmarks.js +246 -0
- package/richtexteditor/plugins/html2pdf.js +108 -4
- package/richtexteditor/plugins/imageeditor.js +37 -7
- package/richtexteditor/plugins/insertgallery.js +553 -170
- package/richtexteditor/plugins/keyboarda11y.js +316 -0
- package/richtexteditor/plugins/linenumbers.js +352 -0
- package/richtexteditor/plugins/linkchecker.js +396 -0
- package/richtexteditor/plugins/mentionplugin.js +4 -3
- package/richtexteditor/plugins/mergefields.js +328 -0
- package/richtexteditor/plugins/multilevellist.js +137 -0
- package/richtexteditor/plugins/pagination.js +505 -0
- package/richtexteditor/plugins/pdfexport.js +1137 -0
- package/richtexteditor/plugins/permanentpen.js +211 -0
- package/richtexteditor/plugins/revisionhistory.js +47 -34
- package/richtexteditor/plugins/rtlui.js +150 -0
- package/richtexteditor/plugins/sanitizer.js +486 -0
- package/richtexteditor/plugins/slashcommand.js +142 -17
- package/richtexteditor/plugins/spellcheck.js +6 -3
- package/richtexteditor/plugins/tableofcontents.js +353 -0
- package/richtexteditor/plugins/tabletools.js +342 -0
- package/richtexteditor/plugins/textdirection.js +313 -0
- package/richtexteditor/plugins/trackedchanges.js +37 -6
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +1 -1
- package/richtexteditor/plugins/vendor/html2pdf.bundle.min.js +3 -0
- package/richtexteditor/plugins/watermark.js +214 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +49 -11
- package/richtexteditor/rte-config.js +44 -44
- package/richtexteditor/rte.js +91 -63
- package/richtexteditor/rte_theme_default.css +462 -384
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-07-27 Footnotes. Inline reference markers plus an auto-numbered notes
|
|
4
|
+
// section at the end of the document — the citation apparatus legal briefs,
|
|
5
|
+
// academic papers, standards and long-form publishing are written with.
|
|
6
|
+
//
|
|
7
|
+
// Both premium competitors charge for this:
|
|
8
|
+
// - CKEditor 5 footnotes: "To use this premium feature, you need to activate
|
|
9
|
+
// it with proper credentials"; available in the Essential, Professional and
|
|
10
|
+
// Custom plans.
|
|
11
|
+
// - TinyMCE footnotes: "This plugin is only available for paid TinyMCE
|
|
12
|
+
// subscriptions" (premium plugin, TinyMCE 6.2+).
|
|
13
|
+
// Froala, Quill and Lexical ship nothing equivalent.
|
|
14
|
+
//
|
|
15
|
+
// Design notes:
|
|
16
|
+
// - The marker is an ATOMIC contenteditable=false <sup>, so arrow keys and
|
|
17
|
+
// backspace treat it as one unit and the caret can never land inside the
|
|
18
|
+
// number and corrupt it.
|
|
19
|
+
// - Numbers are written as LITERAL TEXT, not CSS counters. multilevellist.js
|
|
20
|
+
// deliberately went the other way, but the trade-off inverts here: a
|
|
21
|
+
// footnote's whole job is to survive leaving the editor (publishing, PDF,
|
|
22
|
+
// an RSS feed, someone else's page), and a CSS-counter number is invisible
|
|
23
|
+
// the moment the stylesheet does not travel with it. So we renumber on
|
|
24
|
+
// every mutation instead — see syncFootnotes().
|
|
25
|
+
// - Marker order is DOCUMENT order, and the <li> notes are re-sorted to match,
|
|
26
|
+
// so cutting a paragraph and pasting it earlier renumbers both ends.
|
|
27
|
+
// - Deleting a marker deletes its note; deleting the last marker removes the
|
|
28
|
+
// whole section. No orphans are left in the saved HTML.
|
|
29
|
+
// - The notes section IS content and is meant to persist in getHTMLCode()
|
|
30
|
+
// output — same contract as multilevellist's root class, and unlike the
|
|
31
|
+
// presentational overlays (pagination/typewriter) which are stripped around
|
|
32
|
+
// every serialize.
|
|
33
|
+
RTE_DefaultConfig.plugin_footnotes = RTE_Plugin_Footnotes;
|
|
34
|
+
|
|
35
|
+
// Heading rendered above the notes section. Set to "" to omit the heading.
|
|
36
|
+
if (typeof RTE_DefaultConfig.footnotesTitle === "undefined") RTE_DefaultConfig.footnotesTitle = "Footnotes";
|
|
37
|
+
// "decimal" | "lower-roman" | "upper-roman" | "lower-alpha" | "upper-alpha".
|
|
38
|
+
if (typeof RTE_DefaultConfig.footnotesNumbering === "undefined") RTE_DefaultConfig.footnotesNumbering = "decimal";
|
|
39
|
+
// Prefix/suffix wrapped around the marker number, e.g. "[" and "]".
|
|
40
|
+
if (typeof RTE_DefaultConfig.footnotesMarkerPrefix === "undefined") RTE_DefaultConfig.footnotesMarkerPrefix = "";
|
|
41
|
+
if (typeof RTE_DefaultConfig.footnotesMarkerSuffix === "undefined") RTE_DefaultConfig.footnotesMarkerSuffix = "";
|
|
42
|
+
|
|
43
|
+
function RTE_Plugin_Footnotes() {
|
|
44
|
+
var obj = this;
|
|
45
|
+
var config, editor;
|
|
46
|
+
var boundDoc = null;
|
|
47
|
+
var syncQueued = false;
|
|
48
|
+
|
|
49
|
+
obj.PluginName = "Footnotes";
|
|
50
|
+
|
|
51
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
52
|
+
|
|
53
|
+
obj.InitEditor = function (argeditor) {
|
|
54
|
+
editor = argeditor;
|
|
55
|
+
|
|
56
|
+
editor.attachEvent("exec_command_insertfootnote", function (state) {
|
|
57
|
+
state.returnValue = true;
|
|
58
|
+
obj.InsertFootnote();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
setup();
|
|
62
|
+
try { editor.attachEvent("ready", setup); } catch (e) {}
|
|
63
|
+
// Content replaced wholesale (setHTMLCode / load): re-bind and renumber,
|
|
64
|
+
// because the incoming HTML may already carry markers from a prior save.
|
|
65
|
+
try { editor.attachEvent("aftersethtml", function () { setup(); queueSync(); }); } catch (e) {}
|
|
66
|
+
setTimeout(function () { setup(); queueSync(); }, 0);
|
|
67
|
+
|
|
68
|
+
// Public API.
|
|
69
|
+
editor.insertFootnote = function (text) { return obj.InsertFootnote(text); };
|
|
70
|
+
editor.getFootnotes = function () { return obj.List(); };
|
|
71
|
+
editor.syncFootnotes = function () { return syncFootnotes(); };
|
|
72
|
+
editor.getFootnotesCss = function () { return css(); };
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
function setup() {
|
|
76
|
+
var doc = getDoc();
|
|
77
|
+
if (!doc) return;
|
|
78
|
+
injectStyles(doc);
|
|
79
|
+
if (doc !== boundDoc) { bindEditable(); boundDoc = doc; }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getDoc() { try { return editor.getDocument(); } catch (e) { return null; } }
|
|
83
|
+
function getEditable() { try { return editor.getEditable(); } catch (e) { return null; } }
|
|
84
|
+
|
|
85
|
+
function bindEditable() {
|
|
86
|
+
var editable = getEditable();
|
|
87
|
+
if (!editable) return;
|
|
88
|
+
// Any content mutation can add, remove or move a marker. Renumbering is
|
|
89
|
+
// cheap (a querySelectorAll plus text writes) so we just resync rather
|
|
90
|
+
// than trying to detect which edit mattered.
|
|
91
|
+
editable.addEventListener("keyup", queueSync);
|
|
92
|
+
editable.addEventListener("cut", queueSync);
|
|
93
|
+
editable.addEventListener("paste", queueSync);
|
|
94
|
+
editable.addEventListener("drop", queueSync);
|
|
95
|
+
// Clicking a marker jumps to its note, and clicking the backlink returns.
|
|
96
|
+
editable.addEventListener("click", function (e) {
|
|
97
|
+
var ref = closestClass(e.target, "rte-fn-ref", editable);
|
|
98
|
+
var back = closestClass(e.target, "rte-fn-back", editable);
|
|
99
|
+
var target = null;
|
|
100
|
+
if (ref) target = noteFor(ref.getAttribute("data-fn-id"));
|
|
101
|
+
else if (back) target = refFor(back.getAttribute("data-fn-id"));
|
|
102
|
+
if (!target) return;
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
try { target.scrollIntoView({ block: "center" }); } catch (e2) { target.scrollIntoView(); }
|
|
105
|
+
flash(target);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Coalesce bursts of edits into one renumber. setTimeout, not rAF: the
|
|
110
|
+
// editor may live in a background tab or a hidden iframe, where rAF never
|
|
111
|
+
// fires and the numbering would silently stop updating.
|
|
112
|
+
function queueSync() {
|
|
113
|
+
if (syncQueued) return;
|
|
114
|
+
syncQueued = true;
|
|
115
|
+
setTimeout(function () { syncQueued = false; syncFootnotes(); }, 0);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function closestClass(node, cls, root) {
|
|
119
|
+
while (node && node !== root) {
|
|
120
|
+
if (node.nodeType === 1 && node.classList && node.classList.contains(cls)) return node;
|
|
121
|
+
node = node.parentNode;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function newId() {
|
|
127
|
+
return "fn" + Math.floor(Math.random() * 1e9).toString(36) + (idSeq++).toString(36);
|
|
128
|
+
}
|
|
129
|
+
var idSeq = 0;
|
|
130
|
+
|
|
131
|
+
// ---- lookups ---------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
function refs() {
|
|
134
|
+
var editable = getEditable();
|
|
135
|
+
if (!editable) return [];
|
|
136
|
+
// Document order is exactly what querySelectorAll returns, which is what
|
|
137
|
+
// the numbering is defined by.
|
|
138
|
+
return Array.prototype.slice.call(editable.querySelectorAll("sup.rte-fn-ref[data-fn-id]"));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function section() {
|
|
142
|
+
var editable = getEditable();
|
|
143
|
+
return editable ? editable.querySelector("section.rte-footnotes") : null;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function noteFor(id) {
|
|
147
|
+
var sec = section();
|
|
148
|
+
return sec ? sec.querySelector('li.rte-fn-note[data-fn-id="' + cssEscape(id) + '"]') : null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function refFor(id) {
|
|
152
|
+
var editable = getEditable();
|
|
153
|
+
return editable ? editable.querySelector('sup.rte-fn-ref[data-fn-id="' + cssEscape(id) + '"]') : null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function cssEscape(s) { return String(s).replace(/["\\]/g, "\\$&"); }
|
|
157
|
+
|
|
158
|
+
// ---- insert ----------------------------------------------------------
|
|
159
|
+
|
|
160
|
+
obj.InsertFootnote = function (text) {
|
|
161
|
+
var doc = getDoc();
|
|
162
|
+
var editable = getEditable();
|
|
163
|
+
if (!doc || !editable) return null;
|
|
164
|
+
|
|
165
|
+
var id = newId();
|
|
166
|
+
var sup = doc.createElement("sup");
|
|
167
|
+
sup.className = "rte-fn-ref";
|
|
168
|
+
sup.setAttribute("data-fn-id", id);
|
|
169
|
+
sup.setAttribute("contenteditable", "false");
|
|
170
|
+
sup.setAttribute("id", "fnref-" + id);
|
|
171
|
+
sup.textContent = "0"; // placeholder; syncFootnotes writes the real number
|
|
172
|
+
|
|
173
|
+
// Drop the marker at the caret. insertElement keeps the editor's own
|
|
174
|
+
// undo/selection bookkeeping honest, which hand-splicing would not.
|
|
175
|
+
var placed = false;
|
|
176
|
+
try {
|
|
177
|
+
if (typeof editor.insertElement === "function") { editor.insertElement(sup); placed = true; }
|
|
178
|
+
} catch (e) {}
|
|
179
|
+
if (!placed) {
|
|
180
|
+
try {
|
|
181
|
+
var sel = editor.getSelection();
|
|
182
|
+
if (sel && sel.rangeCount) {
|
|
183
|
+
var range = sel.getRangeAt(0);
|
|
184
|
+
range.collapse(false);
|
|
185
|
+
range.insertNode(sup);
|
|
186
|
+
range.setStartAfter(sup);
|
|
187
|
+
range.collapse(true);
|
|
188
|
+
sel.removeAllRanges();
|
|
189
|
+
sel.addRange(range);
|
|
190
|
+
placed = true;
|
|
191
|
+
}
|
|
192
|
+
} catch (e) {}
|
|
193
|
+
}
|
|
194
|
+
if (!placed) { editable.appendChild(sup); }
|
|
195
|
+
|
|
196
|
+
var note = ensureSection(doc).querySelector("ol.rte-fn-list");
|
|
197
|
+
var li = doc.createElement("li");
|
|
198
|
+
li.className = "rte-fn-note";
|
|
199
|
+
li.setAttribute("data-fn-id", id);
|
|
200
|
+
li.setAttribute("id", "fn-" + id);
|
|
201
|
+
// The note text sits DIRECTLY in the <li>. An earlier revision wrapped it
|
|
202
|
+
// in <span class="rte-fn-text">, which was actively harmful: an empty
|
|
203
|
+
// inline element is a caret trap — the caret at offset 0 of an empty span
|
|
204
|
+
// resolves to the parent, so the first thing the user typed landed
|
|
205
|
+
// OUTSIDE the span and getFootnotes() reported the note as empty while
|
|
206
|
+
// the note visibly had text.
|
|
207
|
+
if (text != null && String(text) !== "") li.appendChild(doc.createTextNode(String(text)));
|
|
208
|
+
var back = doc.createElement("a");
|
|
209
|
+
back.className = "rte-fn-back";
|
|
210
|
+
back.setAttribute("data-fn-id", id);
|
|
211
|
+
back.setAttribute("href", "#fnref-" + id);
|
|
212
|
+
back.setAttribute("contenteditable", "false");
|
|
213
|
+
back.setAttribute("title", "Back to reference");
|
|
214
|
+
back.textContent = "\u21a9";
|
|
215
|
+
li.appendChild(back);
|
|
216
|
+
note.appendChild(li);
|
|
217
|
+
|
|
218
|
+
syncFootnotes();
|
|
219
|
+
// Put the caret at the START of the new note so the user can just type
|
|
220
|
+
// the citation. Offset 0 of the <li> itself, which is a block and so
|
|
221
|
+
// holds a caret reliably, unlike an empty inline wrapper.
|
|
222
|
+
try {
|
|
223
|
+
var r = doc.createRange();
|
|
224
|
+
r.setStart(li, 0);
|
|
225
|
+
r.collapse(true);
|
|
226
|
+
var s2 = editor.getSelection();
|
|
227
|
+
s2.removeAllRanges();
|
|
228
|
+
s2.addRange(r);
|
|
229
|
+
if (typeof editor.focus === "function") editor.focus();
|
|
230
|
+
} catch (e) {}
|
|
231
|
+
fireChange();
|
|
232
|
+
return id;
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
function ensureSection(doc) {
|
|
236
|
+
var editable = getEditable();
|
|
237
|
+
var sec = section();
|
|
238
|
+
if (sec) return sec;
|
|
239
|
+
sec = doc.createElement("section");
|
|
240
|
+
sec.className = "rte-footnotes";
|
|
241
|
+
var title = String(config.footnotesTitle == null ? "" : config.footnotesTitle);
|
|
242
|
+
if (title) {
|
|
243
|
+
var h = doc.createElement("h2");
|
|
244
|
+
h.className = "rte-fn-title";
|
|
245
|
+
h.textContent = title;
|
|
246
|
+
sec.appendChild(h);
|
|
247
|
+
}
|
|
248
|
+
var ol = doc.createElement("ol");
|
|
249
|
+
ol.className = "rte-fn-list";
|
|
250
|
+
sec.appendChild(ol);
|
|
251
|
+
editable.appendChild(sec);
|
|
252
|
+
return sec;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// ---- the renumber / reconcile pass -----------------------------------
|
|
256
|
+
|
|
257
|
+
// Single source of truth: the markers present in the document, in order.
|
|
258
|
+
// Everything else (note order, note existence, both sets of numbers) is
|
|
259
|
+
// derived from that list, so there is no state to drift.
|
|
260
|
+
function syncFootnotes() {
|
|
261
|
+
var doc = getDoc();
|
|
262
|
+
var sec = section();
|
|
263
|
+
var list = refs();
|
|
264
|
+
|
|
265
|
+
if (!list.length) {
|
|
266
|
+
// Last marker went away: drop the section rather than leaving an
|
|
267
|
+
// empty "Footnotes" heading in the saved HTML.
|
|
268
|
+
if (sec && sec.parentNode) sec.parentNode.removeChild(sec);
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
if (!doc) return [];
|
|
272
|
+
if (!sec) sec = ensureSection(doc);
|
|
273
|
+
var ol = sec.querySelector("ol.rte-fn-list");
|
|
274
|
+
if (!ol) { ol = doc.createElement("ol"); ol.className = "rte-fn-list"; sec.appendChild(ol); }
|
|
275
|
+
|
|
276
|
+
var seen = {};
|
|
277
|
+
var out = [];
|
|
278
|
+
for (var i = 0; i < list.length; i++) {
|
|
279
|
+
var ref = list[i];
|
|
280
|
+
var id = ref.getAttribute("data-fn-id");
|
|
281
|
+
// A copy/paste of a marker duplicates its id; re-key the duplicate so
|
|
282
|
+
// the two markers do not fight over one note.
|
|
283
|
+
if (seen[id]) {
|
|
284
|
+
var fresh = newId();
|
|
285
|
+
ref.setAttribute("data-fn-id", fresh);
|
|
286
|
+
ref.setAttribute("id", "fnref-" + fresh);
|
|
287
|
+
id = fresh;
|
|
288
|
+
}
|
|
289
|
+
seen[id] = true;
|
|
290
|
+
|
|
291
|
+
var num = String(i + 1);
|
|
292
|
+
var label = String(config.footnotesMarkerPrefix || "") + num + String(config.footnotesMarkerSuffix || "");
|
|
293
|
+
if (ref.textContent !== label) ref.textContent = label;
|
|
294
|
+
ref.setAttribute("data-fn-number", num);
|
|
295
|
+
if (ref.getAttribute("contenteditable") !== "false") ref.setAttribute("contenteditable", "false");
|
|
296
|
+
if (ref.getAttribute("id") !== "fnref-" + id) ref.setAttribute("id", "fnref-" + id);
|
|
297
|
+
|
|
298
|
+
var note = ol.querySelector('li.rte-fn-note[data-fn-id="' + cssEscape(id) + '"]');
|
|
299
|
+
if (!note) {
|
|
300
|
+
// Marker with no note — a paste from elsewhere, or a note the
|
|
301
|
+
// user deleted by hand. Give it an empty note to write into.
|
|
302
|
+
note = doc.createElement("li");
|
|
303
|
+
note.className = "rte-fn-note";
|
|
304
|
+
note.setAttribute("data-fn-id", id);
|
|
305
|
+
note.setAttribute("id", "fn-" + id);
|
|
306
|
+
}
|
|
307
|
+
note.setAttribute("data-fn-number", num);
|
|
308
|
+
// Append in marker order — this both inserts new notes and re-sorts
|
|
309
|
+
// existing ones to match a reordered document.
|
|
310
|
+
ol.appendChild(note);
|
|
311
|
+
out.push({ id: id, number: i + 1, text: noteText(note) });
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Drop notes whose marker is gone.
|
|
315
|
+
var notes = Array.prototype.slice.call(ol.querySelectorAll("li.rte-fn-note"));
|
|
316
|
+
for (var j = 0; j < notes.length; j++) {
|
|
317
|
+
if (!seen[notes[j].getAttribute("data-fn-id")]) ol.removeChild(notes[j]);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (ol.getAttribute("data-numbering") !== config.footnotesNumbering) {
|
|
321
|
+
ol.setAttribute("data-numbering", config.footnotesNumbering);
|
|
322
|
+
}
|
|
323
|
+
return out;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Read the WHOLE <li> minus the backlink rather than one designated child.
|
|
327
|
+
// The user can type anywhere in the note, and documents saved by an earlier
|
|
328
|
+
// revision still carry a .rte-fn-text wrapper, so anything that trusts a
|
|
329
|
+
// single container reports empty text for notes that plainly have some.
|
|
330
|
+
function noteText(note) {
|
|
331
|
+
if (!note) return "";
|
|
332
|
+
var clone = note.cloneNode(true);
|
|
333
|
+
var backs = clone.querySelectorAll ? clone.querySelectorAll("a.rte-fn-back") : [];
|
|
334
|
+
for (var i = 0; i < backs.length; i++) {
|
|
335
|
+
if (backs[i].parentNode) backs[i].parentNode.removeChild(backs[i]);
|
|
336
|
+
}
|
|
337
|
+
return (clone.textContent || "").replace(/\u21a9/g, "").trim();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
obj.List = function () {
|
|
341
|
+
var sec = section();
|
|
342
|
+
if (!sec) return [];
|
|
343
|
+
var out = [];
|
|
344
|
+
var notes = sec.querySelectorAll("li.rte-fn-note");
|
|
345
|
+
for (var i = 0; i < notes.length; i++) {
|
|
346
|
+
out.push({
|
|
347
|
+
id: notes[i].getAttribute("data-fn-id"),
|
|
348
|
+
number: parseInt(notes[i].getAttribute("data-fn-number"), 10) || (i + 1),
|
|
349
|
+
text: noteText(notes[i])
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
return out;
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
function flash(el) {
|
|
356
|
+
if (!el || !el.classList) return;
|
|
357
|
+
el.classList.add("rte-fn-flash");
|
|
358
|
+
setTimeout(function () { try { el.classList.remove("rte-fn-flash"); } catch (e) {} }, 900);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function fireChange() {
|
|
362
|
+
try { if (typeof editor.updateDesign === "function") editor.updateDesign(); } catch (e) {}
|
|
363
|
+
try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) {}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// ---- styles ----------------------------------------------------------
|
|
367
|
+
|
|
368
|
+
function css() {
|
|
369
|
+
var numbering = String(config.footnotesNumbering || "decimal");
|
|
370
|
+
return (
|
|
371
|
+
"sup.rte-fn-ref{cursor:pointer;font-size:.72em;line-height:0;vertical-align:super;" +
|
|
372
|
+
"color:#1474ea;font-weight:700;padding:0 .12em;user-select:none;}" +
|
|
373
|
+
"sup.rte-fn-ref:hover{text-decoration:underline;}" +
|
|
374
|
+
"section.rte-footnotes{margin-top:2em;padding-top:.85em;border-top:1px solid #ddd;" +
|
|
375
|
+
"font-size:.88em;color:#333;}" +
|
|
376
|
+
"section.rte-footnotes h2.rte-fn-title{font-size:1em;font-weight:700;margin:0 0 .5em;}" +
|
|
377
|
+
"ol.rte-fn-list{list-style:" + numbering + ";padding-left:1.6em;margin:0;}" +
|
|
378
|
+
"ol.rte-fn-list>li{margin:.25em 0;}" +
|
|
379
|
+
"a.rte-fn-back{text-decoration:none;color:#1474ea;cursor:pointer;user-select:none;}" +
|
|
380
|
+
".rte-fn-flash{background:#fff3b0;transition:background .5s ease;}"
|
|
381
|
+
);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
function injectStyles(doc) {
|
|
385
|
+
if (!doc) return;
|
|
386
|
+
var existing = doc.getElementById("rte-footnotes-styles");
|
|
387
|
+
var text = css();
|
|
388
|
+
if (existing) {
|
|
389
|
+
if (existing.getAttribute("data-css") === text) return;
|
|
390
|
+
existing.parentNode && existing.parentNode.removeChild(existing);
|
|
391
|
+
}
|
|
392
|
+
var st = doc.createElement("style");
|
|
393
|
+
st.id = "rte-footnotes-styles";
|
|
394
|
+
st.setAttribute("data-css", text);
|
|
395
|
+
st.appendChild(doc.createTextNode(text));
|
|
396
|
+
(doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(st);
|
|
397
|
+
}
|
|
398
|
+
}
|