@richscripts/richtexteditor 2.4.0 → 2.4.2

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.
Files changed (60) hide show
  1. package/migration/README.md +100 -0
  2. package/migration/cli.js +294 -0
  3. package/migration/index.d.ts +18 -0
  4. package/migration/index.js +242 -0
  5. package/migration/report-schema-v2.json +127 -0
  6. package/package.json +14 -2
  7. package/richtexteditor/CHANGELOG.md +326 -1
  8. package/richtexteditor/all_plugins.js +14982 -4831
  9. package/richtexteditor/all_plugins.min.js +62 -42
  10. package/richtexteditor/plugins/accessibilitychecker.js +8 -7
  11. package/richtexteditor/plugins/aitoolkit.css +17 -8
  12. package/richtexteditor/plugins/aitoolkit.js +8 -8
  13. package/richtexteditor/plugins/all_plugins.js +14982 -4831
  14. package/richtexteditor/plugins/all_plugins.min.js +62 -42
  15. package/richtexteditor/plugins/autocorrect.js +353 -0
  16. package/richtexteditor/plugins/bookmarkcard.js +12 -1
  17. package/richtexteditor/plugins/changecase.js +238 -0
  18. package/richtexteditor/plugins/comments.js +15 -28
  19. package/richtexteditor/plugins/contentminimap.js +6 -5
  20. package/richtexteditor/plugins/crdt-engine.js +2201 -198
  21. package/richtexteditor/plugins/crdt-engine.min.js +5 -5
  22. package/richtexteditor/plugins/crossreference.js +408 -0
  23. package/richtexteditor/plugins/documentimport.js +915 -21
  24. package/richtexteditor/plugins/documentoutline.js +5 -4
  25. package/richtexteditor/plugins/docxexport.js +960 -0
  26. package/richtexteditor/plugins/draghandle.js +309 -0
  27. package/richtexteditor/plugins/footnotes.js +398 -0
  28. package/richtexteditor/plugins/formatpainter.js +298 -0
  29. package/richtexteditor/plugins/formattingmarks.js +246 -0
  30. package/richtexteditor/plugins/html2pdf.js +108 -4
  31. package/richtexteditor/plugins/imageeditor.js +37 -7
  32. package/richtexteditor/plugins/insertgallery.js +553 -170
  33. package/richtexteditor/plugins/keyboarda11y.js +316 -0
  34. package/richtexteditor/plugins/linenumbers.js +352 -0
  35. package/richtexteditor/plugins/linkchecker.js +396 -0
  36. package/richtexteditor/plugins/mentionplugin.js +4 -3
  37. package/richtexteditor/plugins/mergefields.js +328 -0
  38. package/richtexteditor/plugins/multilevellist.js +137 -0
  39. package/richtexteditor/plugins/pagination.js +505 -0
  40. package/richtexteditor/plugins/pdfexport.js +1137 -0
  41. package/richtexteditor/plugins/permanentpen.js +211 -0
  42. package/richtexteditor/plugins/revisionhistory.js +47 -34
  43. package/richtexteditor/plugins/rtlui.js +150 -0
  44. package/richtexteditor/plugins/sanitizer.js +510 -0
  45. package/richtexteditor/plugins/slashcommand.js +142 -17
  46. package/richtexteditor/plugins/spellcheck.js +6 -3
  47. package/richtexteditor/plugins/tableofcontents.js +353 -0
  48. package/richtexteditor/plugins/tabletools.js +342 -0
  49. package/richtexteditor/plugins/textdirection.js +313 -0
  50. package/richtexteditor/plugins/trackedchanges.js +37 -6
  51. package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +1 -1
  52. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +1 -1
  53. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +1 -1
  54. package/richtexteditor/plugins/vendor/html2pdf.bundle.min.js +3 -0
  55. package/richtexteditor/plugins/watermark.js +214 -0
  56. package/richtexteditor/plugins/wordexport.js +0 -0
  57. package/richtexteditor/plugins/yjscollab.js +49 -11
  58. package/richtexteditor/rte-config.js +44 -44
  59. package/richtexteditor/rte.js +91 -63
  60. package/richtexteditor/rte_theme_default.css +483 -384
@@ -0,0 +1,408 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-07-27 Cross-references. "See clause 3.2", "Figure 4", "as described in
4
+ // Scope of Work (page 7)" — references that keep pointing at the right thing
5
+ // after the document is edited around them.
6
+ //
7
+ // This is the piece that makes the rest of the document suite hold together.
8
+ // Legal numbering renumbers clauses, the table of contents re-derives itself,
9
+ // footnotes renumber — and until now a sentence saying "see clause 3.2" stayed
10
+ // frozen at 3.2 while the clause it meant became 4.1. A stale cross-reference in
11
+ // a contract is not cosmetic; it changes what the contract says.
12
+ //
13
+ // Competitive position (checked 2026-07-27): CKEditor 5 ships Bookmarks
14
+ // (in-text anchors) and its own auto-updating table of contents, but describes
15
+ // auto-updating cross-references as a PLANNED enhancement rather than a current
16
+ // feature. TinyMCE documents no cross-reference plugin. Word has had this for
17
+ // decades and it is table stakes for contract and specification work.
18
+ //
19
+ // Design notes:
20
+ // - A reference is an atomic contenteditable=false chip carrying the target id
21
+ // and the display format. The visible text is REAL TEXT, recomputed on every
22
+ // mutation, so the saved HTML stands alone outside the editor (same reasoning
23
+ // as footnotes.js).
24
+ // - A reference whose target has been deleted renders a visible error rather
25
+ // than silently keeping its last value. Word does this too, and it is the
26
+ // right call: a wrong-but-plausible clause number is far more dangerous than
27
+ // an obviously broken one.
28
+ RTE_DefaultConfig.plugin_crossreference = RTE_Plugin_CrossReference;
29
+
30
+ // Shown when the target no longer exists.
31
+ if (typeof RTE_DefaultConfig.crossRefMissingText === "undefined") RTE_DefaultConfig.crossRefMissingText = "[reference not found]";
32
+ // Prefixes used when auto-numbering tables and figures.
33
+ if (typeof RTE_DefaultConfig.crossRefTableLabel === "undefined") RTE_DefaultConfig.crossRefTableLabel = "Table";
34
+ if (typeof RTE_DefaultConfig.crossRefFigureLabel === "undefined") RTE_DefaultConfig.crossRefFigureLabel = "Figure";
35
+
36
+ function RTE_Plugin_CrossReference() {
37
+ var obj = this;
38
+ var config, editor;
39
+ var boundDoc = null;
40
+ var queued = false;
41
+ var idSeq = 0;
42
+
43
+ obj.PluginName = "CrossReference";
44
+
45
+ obj.InitConfig = function (argconfig) { config = argconfig; };
46
+
47
+ obj.InitEditor = function (argeditor) {
48
+ editor = argeditor;
49
+
50
+ editor.attachEvent("exec_command_insertcrossreference", function (state) {
51
+ state.returnValue = true;
52
+ var v = state && state.value;
53
+ if (v && typeof v === "object") obj.Insert(v.target, v.format);
54
+ });
55
+
56
+ setup();
57
+ try { editor.attachEvent("ready", setup); } catch (e) {}
58
+ try { editor.attachEvent("aftersethtml", function () { setup(); queue(); }); } catch (e) {}
59
+ setTimeout(function () { setup(); queue(); }, 0);
60
+
61
+ // Public API.
62
+ editor.listCrossReferenceTargets = function () { return obj.Targets(); };
63
+ editor.insertCrossReference = function (targetId, format) { return obj.Insert(targetId, format); };
64
+ editor.updateCrossReferences = function () { return refresh(); };
65
+ editor.getCrossReferences = function () { return obj.List(); };
66
+ editor.getCrossReferenceCss = function () { return css(); };
67
+ };
68
+
69
+ function setup() {
70
+ var doc = getDoc();
71
+ if (!doc) return;
72
+ injectStyles(doc);
73
+ if (doc === boundDoc) return;
74
+ boundDoc = doc;
75
+ var editable = getEditable();
76
+ if (!editable) return;
77
+ editable.addEventListener("keyup", queue);
78
+ editable.addEventListener("cut", queue);
79
+ editable.addEventListener("paste", queue);
80
+ editable.addEventListener("drop", queue);
81
+ editable.addEventListener("click", function (e) {
82
+ var chip = closestClass(e.target, "rte-xref", editable);
83
+ if (!chip) return;
84
+ e.preventDefault();
85
+ var t = resolveTarget(chip.getAttribute("data-xref-target"));
86
+ if (!t) return;
87
+ try { t.el.scrollIntoView({ block: "center" }); } catch (e2) { t.el.scrollIntoView(); }
88
+ });
89
+ }
90
+
91
+ function getDoc() { try { return editor.getDocument(); } catch (e) { return null; } }
92
+ function getEditable() { try { return editor.getEditable(); } catch (e) { return null; } }
93
+
94
+ function queue() {
95
+ if (queued) return;
96
+ queued = true;
97
+ setTimeout(function () { queued = false; refresh(); }, 0);
98
+ }
99
+
100
+ function closestClass(node, cls, root) {
101
+ while (node && node !== root) {
102
+ if (node.nodeType === 1 && node.classList && node.classList.contains(cls)) return node;
103
+ node = node.parentNode;
104
+ }
105
+ return null;
106
+ }
107
+
108
+ function newId(prefix) {
109
+ return prefix + Math.floor(Math.random() * 1e9).toString(36) + (idSeq++).toString(36);
110
+ }
111
+
112
+ function ensureId(el, prefix) {
113
+ var id = el.getAttribute("id");
114
+ if (id) return id;
115
+ id = newId(prefix);
116
+ el.setAttribute("id", id);
117
+ return id;
118
+ }
119
+
120
+ // ---- targets ---------------------------------------------------------
121
+
122
+ // Everything in the document that can be referred to, in document order.
123
+ obj.Targets = function () {
124
+ var editable = getEditable();
125
+ if (!editable) return [];
126
+ var out = [];
127
+
128
+ var headings = editable.querySelectorAll("h1,h2,h3,h4,h5,h6");
129
+ for (var i = 0; i < headings.length; i++) {
130
+ var h = headings[i];
131
+ // Generated regions are not referable content.
132
+ if (h.closest && (h.closest("nav.rte-toc") || h.closest("section.rte-footnotes"))) continue;
133
+ var text = (h.textContent || "").trim();
134
+ if (!text) continue;
135
+ out.push({ id: ensureId(h, "rte-h-"), type: "heading", label: text, number: null });
136
+ }
137
+
138
+ // Legal-numbered clauses: the number is derived from position, exactly
139
+ // like the CSS counters that render it.
140
+ var roots = editable.querySelectorAll("ol.rte-legal-list");
141
+ for (var r = 0; r < roots.length; r++) collectClauses(roots[r], [], out);
142
+
143
+ var notes = editable.querySelectorAll("li.rte-fn-note[data-fn-id]");
144
+ for (var n = 0; n < notes.length; n++) {
145
+ out.push({
146
+ id: ensureId(notes[n], "rte-fn-"),
147
+ type: "footnote",
148
+ label: (noteText(notes[n]) || "footnote"),
149
+ number: notes[n].getAttribute("data-fn-number")
150
+ });
151
+ }
152
+
153
+ var tables = editable.querySelectorAll("table");
154
+ var tnum = 0;
155
+ for (var t = 0; t < tables.length; t++) {
156
+ tnum++;
157
+ var cap = tables[t].querySelector("caption");
158
+ out.push({
159
+ id: ensureId(tables[t], "rte-tbl-"),
160
+ type: "table",
161
+ label: (cap && cap.textContent.trim()) || (String(config.crossRefTableLabel) + " " + tnum),
162
+ number: String(tnum)
163
+ });
164
+ }
165
+
166
+ var figs = editable.querySelectorAll("figure, img");
167
+ var fnum = 0;
168
+ for (var f = 0; f < figs.length; f++) {
169
+ // An <img> inside a <figure> is the same figure, counted once.
170
+ if (figs[f].nodeName === "IMG" && figs[f].closest && figs[f].closest("figure")) continue;
171
+ fnum++;
172
+ var fc = figs[f].querySelector ? figs[f].querySelector("figcaption") : null;
173
+ out.push({
174
+ id: ensureId(figs[f], "rte-fig-"),
175
+ type: "figure",
176
+ label: (fc && fc.textContent.trim()) || (String(config.crossRefFigureLabel) + " " + fnum),
177
+ number: String(fnum)
178
+ });
179
+ }
180
+ return out;
181
+ };
182
+
183
+ function collectClauses(ol, path, out) {
184
+ var idx = 0;
185
+ for (var i = 0; i < ol.children.length; i++) {
186
+ var li = ol.children[i];
187
+ if (li.nodeName !== "LI") continue;
188
+ idx++;
189
+ var here = path.concat([idx]);
190
+ var first = firstLineText(li);
191
+ out.push({
192
+ id: ensureId(li, "rte-cl-"),
193
+ type: "clause",
194
+ label: first || ("clause " + here.join(".")),
195
+ number: here.join(".")
196
+ });
197
+ for (var c = 0; c < li.children.length; c++) {
198
+ if (li.children[c].nodeName === "OL") collectClauses(li.children[c], here, out);
199
+ }
200
+ }
201
+ }
202
+
203
+ // The clause's own text, excluding any nested sub-list.
204
+ function firstLineText(li) {
205
+ var s = "";
206
+ for (var i = 0; i < li.childNodes.length; i++) {
207
+ var n = li.childNodes[i];
208
+ if (n.nodeType === 1 && (n.nodeName === "OL" || n.nodeName === "UL")) break;
209
+ s += n.textContent || "";
210
+ }
211
+ return s.trim().replace(/\s+/g, " ").slice(0, 80);
212
+ }
213
+
214
+ function noteText(note) {
215
+ var clone = note.cloneNode(true);
216
+ var backs = clone.querySelectorAll ? clone.querySelectorAll("a.rte-fn-back") : [];
217
+ for (var i = 0; i < backs.length; i++) if (backs[i].parentNode) backs[i].parentNode.removeChild(backs[i]);
218
+ return (clone.textContent || "").replace(/↩/g, "").trim().slice(0, 80);
219
+ }
220
+
221
+ function resolveTarget(id) {
222
+ if (!id) return null;
223
+ var list = obj.Targets();
224
+ for (var i = 0; i < list.length; i++) {
225
+ if (list[i].id !== id) continue;
226
+ var editable = getEditable();
227
+ var el = editable ? editable.querySelector("#" + id.replace(/["\\\]\[]/g, "\\$&")) : null;
228
+ if (!el) return null;
229
+ var t = list[i];
230
+ t.el = el;
231
+ return t;
232
+ }
233
+ return null;
234
+ }
235
+
236
+ // ---- insert ----------------------------------------------------------
237
+
238
+ // format: "text" | "number" | "page" | "position" | "label"
239
+ obj.Insert = function (targetId, format) {
240
+ var doc = getDoc();
241
+ var editable = getEditable();
242
+ if (!doc || !editable) return false;
243
+ var target = resolveTarget(targetId);
244
+ if (!target) return false;
245
+ format = String(format || "text");
246
+
247
+ var chip = doc.createElement("span");
248
+ chip.className = "rte-xref";
249
+ chip.setAttribute("data-xref-target", targetId);
250
+ chip.setAttribute("data-xref-format", format);
251
+ chip.setAttribute("contenteditable", "false");
252
+ chip.textContent = displayFor(target, format, chip);
253
+
254
+ var placed = false;
255
+ try {
256
+ if (typeof editor.insertElement === "function") { editor.insertElement(chip); placed = true; }
257
+ } catch (e) {}
258
+ if (!placed) {
259
+ try {
260
+ var sel = editor.getSelection();
261
+ if (sel && sel.rangeCount) {
262
+ var r = sel.getRangeAt(0);
263
+ r.collapse(false);
264
+ r.insertNode(chip);
265
+ r.setStartAfter(chip); r.collapse(true);
266
+ sel.removeAllRanges(); sel.addRange(r);
267
+ placed = true;
268
+ }
269
+ } catch (e) {}
270
+ }
271
+ if (!placed) editable.appendChild(chip);
272
+ // Recompute now that the chip is actually IN the document. The text set
273
+ // above was computed while it was still detached, and the "position"
274
+ // format compares the chip against its target — on a detached node that
275
+ // comparison is meaningless, so a reference to something earlier in the
276
+ // document displayed "below" until the next refresh happened to fix it.
277
+ refresh();
278
+ fireChange();
279
+ return targetId;
280
+ };
281
+
282
+ function displayFor(target, format, chip) {
283
+ if (!target) return String(config.crossRefMissingText);
284
+ switch (format) {
285
+ case "number":
286
+ if (target.number) return target.number;
287
+ return target.label; // headings have no number
288
+ case "label":
289
+ // "Table 2" / "Figure 3" / "clause 3.2" style
290
+ if (target.type === "table") return String(config.crossRefTableLabel) + " " + target.number;
291
+ if (target.type === "figure") return String(config.crossRefFigureLabel) + " " + target.number;
292
+ if (target.type === "clause") return "clause " + target.number;
293
+ if (target.type === "footnote") return "footnote " + target.number;
294
+ return target.label;
295
+ case "page":
296
+ var p = null;
297
+ try {
298
+ if (typeof editor.getPageOfElement === "function") p = editor.getPageOfElement(target.el);
299
+ } catch (e) { p = null; }
300
+ // No page view means no honest page number; keep whatever was
301
+ // last computed rather than inventing or blanking one.
302
+ if (p) return String(p);
303
+ var prev = chip ? chip.getAttribute("data-xref-last-page") : null;
304
+ return prev || "?";
305
+ case "position":
306
+ return positionOf(target.el, chip);
307
+ default:
308
+ return target.label;
309
+ }
310
+ }
311
+
312
+ // "above" / "below" relative to the reference itself.
313
+ function positionOf(targetEl, chip) {
314
+ if (!targetEl || !chip) return "above";
315
+ try {
316
+ var pos = chip.compareDocumentPosition(targetEl);
317
+ if (pos & Node.DOCUMENT_POSITION_PRECEDING) return "above";
318
+ if (pos & Node.DOCUMENT_POSITION_FOLLOWING) return "below";
319
+ } catch (e) {}
320
+ return "above";
321
+ }
322
+
323
+ // ---- refresh ---------------------------------------------------------
324
+
325
+ function chips() {
326
+ var editable = getEditable();
327
+ if (!editable) return [];
328
+ return Array.prototype.slice.call(editable.querySelectorAll("span.rte-xref[data-xref-target]"));
329
+ }
330
+
331
+ function refresh() {
332
+ var list = chips();
333
+ if (!list.length) return [];
334
+ // Build the target index once rather than per chip.
335
+ var targets = obj.Targets();
336
+ var byId = {};
337
+ for (var t = 0; t < targets.length; t++) byId[targets[t].id] = targets[t];
338
+ var editable = getEditable();
339
+
340
+ var out = [];
341
+ for (var i = 0; i < list.length; i++) {
342
+ var chip = list[i];
343
+ var id = chip.getAttribute("data-xref-target");
344
+ var fmt = chip.getAttribute("data-xref-format") || "text";
345
+ var target = byId[id] || null;
346
+ if (target) {
347
+ target.el = editable.querySelector("#" + id.replace(/["\\\]\[]/g, "\\$&"));
348
+ }
349
+ var text = displayFor(target, fmt, chip);
350
+ if (chip.textContent !== text) chip.textContent = text;
351
+ if (target) {
352
+ chip.classList.remove("rte-xref-broken");
353
+ if (fmt === "page" && /^\d+$/.test(text)) chip.setAttribute("data-xref-last-page", text);
354
+ } else {
355
+ // Visible failure beats a plausible wrong number.
356
+ chip.classList.add("rte-xref-broken");
357
+ }
358
+ out.push({ target: id, format: fmt, text: text, resolved: !!target });
359
+ }
360
+ return out;
361
+ }
362
+
363
+ obj.List = function () {
364
+ var list = chips();
365
+ var out = [];
366
+ for (var i = 0; i < list.length; i++) {
367
+ out.push({
368
+ target: list[i].getAttribute("data-xref-target"),
369
+ format: list[i].getAttribute("data-xref-format") || "text",
370
+ text: list[i].textContent,
371
+ resolved: !list[i].classList.contains("rte-xref-broken")
372
+ });
373
+ }
374
+ return out;
375
+ };
376
+
377
+ function fireChange() {
378
+ try { if (typeof editor.updateDesign === "function") editor.updateDesign(); } catch (e) {}
379
+ try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) {}
380
+ }
381
+
382
+ // ---- styles ----------------------------------------------------------
383
+
384
+ function css() {
385
+ return (
386
+ "span.rte-xref{color:#1474ea;cursor:pointer;border-bottom:1px dotted #9dc2f5;" +
387
+ "user-select:none;}" +
388
+ "span.rte-xref:hover{border-bottom-style:solid;}" +
389
+ "span.rte-xref.rte-xref-broken{color:#c81e1e;background:rgba(200,30,30,.09);" +
390
+ "border-bottom:1px solid #e59b9b;border-radius:2px;padding:0 .15em;}"
391
+ );
392
+ }
393
+
394
+ function injectStyles(doc) {
395
+ if (!doc) return;
396
+ var existing = doc.getElementById("rte-xref-styles");
397
+ var text = css();
398
+ if (existing) {
399
+ if (existing.getAttribute("data-css") === text) return;
400
+ existing.parentNode && existing.parentNode.removeChild(existing);
401
+ }
402
+ var st = doc.createElement("style");
403
+ st.id = "rte-xref-styles";
404
+ st.setAttribute("data-css", text);
405
+ st.appendChild(doc.createTextNode(text));
406
+ (doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(st);
407
+ }
408
+ }