@richscripts/richtexteditor 2.4.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.
Files changed (184) hide show
  1. package/README.md +360 -0
  2. package/admin/index.d.ts +100 -0
  3. package/admin/index.js +94 -0
  4. package/angular/component.ts +284 -0
  5. package/angular/index.d.ts +80 -0
  6. package/angular/index.ts +24 -0
  7. package/headless/index.d.ts +65 -0
  8. package/headless/index.js +272 -0
  9. package/index.d.ts +1412 -0
  10. package/integrations/shared.d.ts +138 -0
  11. package/integrations/shared.js +5182 -0
  12. package/package.json +130 -0
  13. package/react/index.d.ts +104 -0
  14. package/react/index.js +310 -0
  15. package/richtexteditor/CHANGELOG.md +1876 -0
  16. package/richtexteditor/all_plugins.js +31125 -0
  17. package/richtexteditor/all_plugins.min.js +78 -0
  18. package/richtexteditor/lang/rte-lang-af.js +193 -0
  19. package/richtexteditor/lang/rte-lang-ar.js +193 -0
  20. package/richtexteditor/lang/rte-lang-bg.js +193 -0
  21. package/richtexteditor/lang/rte-lang-bn.js +193 -0
  22. package/richtexteditor/lang/rte-lang-bs.js +193 -0
  23. package/richtexteditor/lang/rte-lang-ca.js +193 -0
  24. package/richtexteditor/lang/rte-lang-cs.js +193 -0
  25. package/richtexteditor/lang/rte-lang-cy.js +193 -0
  26. package/richtexteditor/lang/rte-lang-da.js +193 -0
  27. package/richtexteditor/lang/rte-lang-de.js +193 -0
  28. package/richtexteditor/lang/rte-lang-el.js +193 -0
  29. package/richtexteditor/lang/rte-lang-en.js +6 -0
  30. package/richtexteditor/lang/rte-lang-es.js +193 -0
  31. package/richtexteditor/lang/rte-lang-et.js +193 -0
  32. package/richtexteditor/lang/rte-lang-fa.js +193 -0
  33. package/richtexteditor/lang/rte-lang-fi.js +193 -0
  34. package/richtexteditor/lang/rte-lang-fil.js +193 -0
  35. package/richtexteditor/lang/rte-lang-fj.js +193 -0
  36. package/richtexteditor/lang/rte-lang-fr.js +193 -0
  37. package/richtexteditor/lang/rte-lang-ga.js +193 -0
  38. package/richtexteditor/lang/rte-lang-gu.js +193 -0
  39. package/richtexteditor/lang/rte-lang-he.js +193 -0
  40. package/richtexteditor/lang/rte-lang-hi.js +193 -0
  41. package/richtexteditor/lang/rte-lang-hr.js +193 -0
  42. package/richtexteditor/lang/rte-lang-ht.js +193 -0
  43. package/richtexteditor/lang/rte-lang-hu.js +193 -0
  44. package/richtexteditor/lang/rte-lang-id.js +193 -0
  45. package/richtexteditor/lang/rte-lang-is.js +193 -0
  46. package/richtexteditor/lang/rte-lang-it.js +193 -0
  47. package/richtexteditor/lang/rte-lang-ja.js +193 -0
  48. package/richtexteditor/lang/rte-lang-kn.js +193 -0
  49. package/richtexteditor/lang/rte-lang-ko.js +193 -0
  50. package/richtexteditor/lang/rte-lang-lt.js +193 -0
  51. package/richtexteditor/lang/rte-lang-lv.js +193 -0
  52. package/richtexteditor/lang/rte-lang-mg.js +193 -0
  53. package/richtexteditor/lang/rte-lang-mi.js +193 -0
  54. package/richtexteditor/lang/rte-lang-ml.js +193 -0
  55. package/richtexteditor/lang/rte-lang-mr.js +193 -0
  56. package/richtexteditor/lang/rte-lang-ms.js +193 -0
  57. package/richtexteditor/lang/rte-lang-mt.js +193 -0
  58. package/richtexteditor/lang/rte-lang-mww.js +193 -0
  59. package/richtexteditor/lang/rte-lang-nb.js +193 -0
  60. package/richtexteditor/lang/rte-lang-nl.js +193 -0
  61. package/richtexteditor/lang/rte-lang-otq.js +193 -0
  62. package/richtexteditor/lang/rte-lang-pa.js +193 -0
  63. package/richtexteditor/lang/rte-lang-pl.js +193 -0
  64. package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
  65. package/richtexteditor/lang/rte-lang-pt.js +193 -0
  66. package/richtexteditor/lang/rte-lang-ro.js +193 -0
  67. package/richtexteditor/lang/rte-lang-ru.js +193 -0
  68. package/richtexteditor/lang/rte-lang-sk.js +193 -0
  69. package/richtexteditor/lang/rte-lang-sl.js +193 -0
  70. package/richtexteditor/lang/rte-lang-sm.js +193 -0
  71. package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
  72. package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
  73. package/richtexteditor/lang/rte-lang-sv.js +193 -0
  74. package/richtexteditor/lang/rte-lang-sw.js +193 -0
  75. package/richtexteditor/lang/rte-lang-ta.js +193 -0
  76. package/richtexteditor/lang/rte-lang-te.js +193 -0
  77. package/richtexteditor/lang/rte-lang-th.js +193 -0
  78. package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
  79. package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
  80. package/richtexteditor/lang/rte-lang-to.js +193 -0
  81. package/richtexteditor/lang/rte-lang-tr.js +193 -0
  82. package/richtexteditor/lang/rte-lang-ty.js +193 -0
  83. package/richtexteditor/lang/rte-lang-uk.js +193 -0
  84. package/richtexteditor/lang/rte-lang-ur.js +193 -0
  85. package/richtexteditor/lang/rte-lang-vi.js +193 -0
  86. package/richtexteditor/lang/rte-lang-yua.js +193 -0
  87. package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
  88. package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
  89. package/richtexteditor/lang/rte-lang.js +1 -0
  90. package/richtexteditor/plugins/-combine-plugin.txt +2 -0
  91. package/richtexteditor/plugins/-combine-plugins.bat +20 -0
  92. package/richtexteditor/plugins/a11yenhance.js +90 -0
  93. package/richtexteditor/plugins/accessibilitychecker.js +579 -0
  94. package/richtexteditor/plugins/aitoolkit.css +15830 -0
  95. package/richtexteditor/plugins/aitoolkit.js +19171 -0
  96. package/richtexteditor/plugins/all_plugins.js +31125 -0
  97. package/richtexteditor/plugins/all_plugins.min.js +78 -0
  98. package/richtexteditor/plugins/autoembed.js +174 -0
  99. package/richtexteditor/plugins/blocktypes.js +175 -0
  100. package/richtexteditor/plugins/bookmarkcard.js +226 -0
  101. package/richtexteditor/plugins/charlimit.js +195 -0
  102. package/richtexteditor/plugins/comments.js +672 -0
  103. package/richtexteditor/plugins/contentminimap.js +369 -0
  104. package/richtexteditor/plugins/crdt-engine.js +8905 -0
  105. package/richtexteditor/plugins/crdt-engine.min.js +5 -0
  106. package/richtexteditor/plugins/dialoga11y.js +174 -0
  107. package/richtexteditor/plugins/dictation.js +268 -0
  108. package/richtexteditor/plugins/documentimport.js +369 -0
  109. package/richtexteditor/plugins/documentoutline.js +341 -0
  110. package/richtexteditor/plugins/emailtoolkit.js +165 -0
  111. package/richtexteditor/plugins/emojiautocomplete.js +241 -0
  112. package/richtexteditor/plugins/foldheadings.js +181 -0
  113. package/richtexteditor/plugins/ghostcomplete.js +297 -0
  114. package/richtexteditor/plugins/html2pdf.js +158 -0
  115. package/richtexteditor/plugins/imageeditor.js +159 -0
  116. package/richtexteditor/plugins/insertcode.js +319 -0
  117. package/richtexteditor/plugins/insertemoji.js +229 -0
  118. package/richtexteditor/plugins/insertgallery.js +362 -0
  119. package/richtexteditor/plugins/inserttemplate.js +261 -0
  120. package/richtexteditor/plugins/localdraft.js +213 -0
  121. package/richtexteditor/plugins/markdownexport.js +110 -0
  122. package/richtexteditor/plugins/mathdialog.js +153 -0
  123. package/richtexteditor/plugins/mentionplugin.js +534 -0
  124. package/richtexteditor/plugins/mermaiddiagram.js +294 -0
  125. package/richtexteditor/plugins/readabilitystats.js +148 -0
  126. package/richtexteditor/plugins/readaloud.js +243 -0
  127. package/richtexteditor/plugins/restrictedediting.js +208 -0
  128. package/richtexteditor/plugins/revisionhistory.js +670 -0
  129. package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
  130. package/richtexteditor/plugins/slashcommand.js +705 -0
  131. package/richtexteditor/plugins/smartchips.js +250 -0
  132. package/richtexteditor/plugins/spellcheck.js +181 -0
  133. package/richtexteditor/plugins/todolist.js +137 -0
  134. package/richtexteditor/plugins/trackedchanges.js +658 -0
  135. package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
  136. package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
  137. package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
  138. package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
  139. package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
  140. package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
  141. package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
  142. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
  143. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
  144. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
  145. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
  146. package/richtexteditor/plugins/typewriter.js +203 -0
  147. package/richtexteditor/plugins/wordexport.js +0 -0
  148. package/richtexteditor/plugins/yjscollab.js +693 -0
  149. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
  150. package/richtexteditor/rte-config.js +933 -0
  151. package/richtexteditor/rte.js +943 -0
  152. package/richtexteditor/rte_mobile.css +234 -0
  153. package/richtexteditor/rte_theme_default.css +4819 -0
  154. package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
  155. package/richtexteditor/runtime/help.htm +162 -0
  156. package/richtexteditor/runtime/images/1x1.gif +0 -0
  157. package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
  158. package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
  159. package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
  160. package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
  161. package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
  162. package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
  163. package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
  164. package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
  165. package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
  166. package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
  167. package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
  168. package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
  169. package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
  170. package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
  171. package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
  172. package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
  173. package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
  174. package/richtexteditor/runtime/images/formbn.gif +0 -0
  175. package/richtexteditor/runtime/images/multiclavier.gif +0 -0
  176. package/richtexteditor/runtime/richtexteditor_content.css +362 -0
  177. package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
  178. package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
  179. package/server/index.d.ts +21 -0
  180. package/server/index.js +23 -0
  181. package/svelte/index.d.ts +19 -0
  182. package/svelte/index.js +59 -0
  183. package/vue/index.d.ts +94 -0
  184. package/vue/index.js +291 -0
@@ -0,0 +1,213 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-06-06 Local draft autosave + crash recovery. Complements the core
4
+ // server-oriented config.autoSave by persisting the document to localStorage as
5
+ // you type, so a refresh / crash / accidental navigation never loses work — the
6
+ // next load offers to restore the unsaved draft (Gmail / Notion style).
7
+ //
8
+ // Opt-in: enable by setting config.localDraft to `true` or to an options object:
9
+ // config.localDraft = {
10
+ // key: "my-doc", // storage key (default: editor id or page path)
11
+ // intervalMs: 2500, // debounce between saves (default 2500)
12
+ // ttlMs: 1209600000, // drop drafts older than this (default 14 days)
13
+ // promptRestore: true // show the built-in restore bar (default true)
14
+ // }
15
+ //
16
+ // The host app can also drive it via the API and clear the draft after a real
17
+ // save: editor.saveLocalDraft(), editor.hasLocalDraft(), editor.getLocalDraft(),
18
+ // editor.restoreLocalDraft(), editor.clearLocalDraft().
19
+ RTE_DefaultConfig.plugin_localdraft = RTE_Plugin_LocalDraft;
20
+ if (typeof RTE_DefaultConfig.localDraft === "undefined") RTE_DefaultConfig.localDraft = false;
21
+
22
+ function RTE_Plugin_LocalDraft() {
23
+ var obj = this;
24
+ var config, editor;
25
+ var opts = null, key = null, timer = null, baseline = "", bar = null, attached = false, boundDoc = null;
26
+
27
+ obj.PluginName = "LocalDraft";
28
+
29
+ obj.InitConfig = function (argconfig) { config = argconfig; };
30
+
31
+ obj.InitEditor = function (argeditor) {
32
+ editor = argeditor;
33
+
34
+ // Public API works regardless of the opt-in flag.
35
+ editor.saveLocalDraft = function () { return save(true); };
36
+ editor.hasLocalDraft = function () { return !!read(); };
37
+ editor.getLocalDraft = function () { return read(); };
38
+ editor.clearLocalDraft = function () { try { store().removeItem(resolveKey()); } catch (e) {} hideBar(); return true; };
39
+ editor.restoreLocalDraft = function () {
40
+ var d = read(); if (!d) return false;
41
+ try { editor.setHTMLCode(d.html); } catch (e) { return false; }
42
+ try { if (typeof editor.fireChange === "function") editor.fireChange(); } catch (e) {}
43
+ hideBar();
44
+ return true;
45
+ };
46
+
47
+ if (!config.localDraft) return; // disabled — API still available
48
+ opts = (typeof config.localDraft === "object") ? config.localDraft : {};
49
+ key = resolveKey();
50
+
51
+ // Capture the initial (server-provided) content as the baseline so we
52
+ // only offer a restore when the draft actually differs from it.
53
+ var startup = function () {
54
+ if (attached) return; // fires from both "ready" and the timeout — run once
55
+ baseline = safeGetHTML();
56
+ maybeOfferRestore();
57
+ attach();
58
+ };
59
+ try { editor.attachEvent("ready", startup); } catch (e) {}
60
+ // The editor recreates its document/body after init and on aftersethtml;
61
+ // re-bind the doc-level input listener each time so autosave never goes
62
+ // deaf to plain typing on a stale document.
63
+ try { editor.attachEvent("ready", bindDoc); } catch (e) {}
64
+ try { editor.attachEvent("aftersethtml", bindDoc); } catch (e) {}
65
+ setTimeout(startup, 0);
66
+ };
67
+
68
+ var changeHandler = function () { schedule(); };
69
+
70
+ function bindDoc() {
71
+ if (!config || !config.localDraft) return;
72
+ var doc = editor.getDocument();
73
+ if (doc && doc !== boundDoc) {
74
+ try { doc.addEventListener("input", changeHandler, true); } catch (e) {}
75
+ boundDoc = doc;
76
+ }
77
+ }
78
+
79
+ function attach() {
80
+ if (attached) return; // startup can fire from both "ready" and the timeout
81
+ attached = true;
82
+ try { editor.attachEvent("change", changeHandler); } catch (e) {}
83
+ try { editor.attachEvent("keyup", changeHandler); } catch (e) {}
84
+ bindDoc();
85
+ // Last-chance save when the page is hidden / unloaded.
86
+ try {
87
+ window.addEventListener("beforeunload", function () { save(false); }, false);
88
+ window.addEventListener("pagehide", function () { save(false); }, false);
89
+ } catch (e) {}
90
+ }
91
+
92
+ function intervalMs() { var n = opts && +opts.intervalMs; return (n && n > 250) ? n : 2500; }
93
+ function ttlMs() { var n = opts && +opts.ttlMs; return (n && n > 0) ? n : 14 * 24 * 60 * 60 * 1000; }
94
+
95
+ function resolveKey() {
96
+ if (opts && opts.key) return "rte-draft:" + opts.key;
97
+ var id = "";
98
+ try { var ed = editor.getEditable(); id = (ed && (ed.id || (ed.getAttribute && ed.getAttribute("data-rte-id")))) || ""; } catch (e) {}
99
+ if (!id) { try { id = location.pathname; } catch (e) { id = "default"; } }
100
+ return "rte-draft:" + id;
101
+ }
102
+
103
+ function store() { return window.localStorage; }
104
+
105
+ function schedule() {
106
+ if (timer) return;
107
+ timer = setTimeout(function () { timer = null; save(false); }, intervalMs());
108
+ }
109
+
110
+ function safeGetHTML() {
111
+ try { return editor.getHTMLCode() || ""; } catch (e) { return ""; }
112
+ }
113
+
114
+ function save(force) {
115
+ if (!config || !config.localDraft) return false;
116
+ var html = safeGetHTML();
117
+ // Don't persist an empty doc or one identical to the server baseline.
118
+ if (!force && (html === baseline || isBlank(html))) return false;
119
+ var rec = { html: html, t: nowMs() };
120
+ try { store().setItem(resolveKey(), JSON.stringify(rec)); return true; }
121
+ catch (e) { return false; }
122
+ }
123
+
124
+ function read() {
125
+ try {
126
+ var raw = store().getItem(resolveKey());
127
+ if (!raw) return null;
128
+ var rec = JSON.parse(raw);
129
+ if (!rec || typeof rec.html !== "string") return null;
130
+ if (rec.t && (nowMs() - rec.t) > ttlMs()) { try { store().removeItem(resolveKey()); } catch (e) {} return null; }
131
+ return rec;
132
+ } catch (e) { return null; }
133
+ }
134
+
135
+ function isBlank(html) {
136
+ if (!html) return true;
137
+ var t = html.replace(/<[^>]*>/g, "").replace(/&nbsp;/gi, " ").replace(/\s+/g, "");
138
+ return t.length === 0;
139
+ }
140
+
141
+ function nowMs() {
142
+ // Date.now is available in the browser; guard just in case.
143
+ try { return Date.now(); } catch (e) { return 0; }
144
+ }
145
+
146
+ function maybeOfferRestore() {
147
+ if (opts && opts.promptRestore === false) return;
148
+ var d = read();
149
+ if (!d) return;
150
+ if (d.html === baseline || isBlank(d.html)) return;
151
+ showBar(d);
152
+ }
153
+
154
+ // --- Compact restore bar in the host page ---
155
+ function showBar(draft) {
156
+ hideBar();
157
+ var host = hostElement();
158
+ if (!host || !host.parentNode) return;
159
+ var when = friendlyTime(draft.t);
160
+ bar = document.createElement("div");
161
+ bar.className = "rte-localdraft-bar";
162
+ bar.setAttribute("data-rte-localdraft", "1");
163
+ // Announce the recovery offer to assistive tech.
164
+ bar.setAttribute("role", "status");
165
+ bar.setAttribute("aria-live", "polite");
166
+ var msg = document.createElement("span");
167
+ msg.className = "rte-localdraft-message";
168
+ msg.textContent = "An unsaved draft" + (when ? (" from " + when) : "") + " was found.";
169
+ var restore = mkBtn("Restore", "primary");
170
+ var discard = mkBtn("Discard", "secondary");
171
+ restore.onclick = function () { editor.restoreLocalDraft(); };
172
+ discard.onclick = function () { editor.clearLocalDraft(); };
173
+ bar.appendChild(msg); bar.appendChild(restore); bar.appendChild(discard);
174
+ host.parentNode.insertBefore(bar, host);
175
+ }
176
+ function mkBtn(label, tone) {
177
+ var b = document.createElement("button");
178
+ b.type = "button"; b.textContent = label;
179
+ b.className = "rte-localdraft-button is-" + tone;
180
+ return b;
181
+ }
182
+ function hideBar() { if (bar && bar.parentNode) { bar.parentNode.removeChild(bar); } bar = null; }
183
+
184
+ function hostElement() {
185
+ // The editor's top-level wrapper in the host page (the iframe's container).
186
+ try {
187
+ var doc = editor.getDocument();
188
+ var frame = doc && doc.defaultView && doc.defaultView.frameElement;
189
+ if (frame) {
190
+ var n = frame;
191
+ // climb to the editor's outer container if present
192
+ for (var i = 0; i < 4 && n.parentNode; i++) {
193
+ if (n.parentNode && /rte[_-](outer|container|wrapper)/i.test(n.parentNode.className || "")) return n.parentNode;
194
+ n = n.parentNode;
195
+ }
196
+ return frame;
197
+ }
198
+ } catch (e) {}
199
+ try { return editor.getEditable(); } catch (e) { return null; }
200
+ }
201
+
202
+ function friendlyTime(t) {
203
+ if (!t) return "";
204
+ try {
205
+ var d = new Date(t);
206
+ var diff = (nowMs() - t) / 1000;
207
+ if (diff < 60) return "just now";
208
+ if (diff < 3600) return Math.round(diff / 60) + " min ago";
209
+ if (diff < 86400) return Math.round(diff / 3600) + " hr ago";
210
+ return d.toLocaleString();
211
+ } catch (e) { return ""; }
212
+ }
213
+ }
@@ -0,0 +1,110 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-06-06 Markdown export surface. The core already ships an HTML→Markdown
4
+ // engine (editor.toMarkdown()); this plugin makes it discoverable the way Notion
5
+ // "Copy as Markdown" / Obsidian do: a one-click copy-to-clipboard and a
6
+ // download-as-.md, exposed as slash commands + a public API. No content is
7
+ // modified — these are read-only exporters.
8
+ RTE_DefaultConfig.plugin_markdownexport = RTE_Plugin_MarkdownExport;
9
+ if (typeof RTE_DefaultConfig.markdownExportEnabled === "undefined") RTE_DefaultConfig.markdownExportEnabled = true;
10
+
11
+ function RTE_Plugin_MarkdownExport() {
12
+ var obj = this;
13
+ var config, editor;
14
+
15
+ obj.PluginName = "MarkdownExport";
16
+
17
+ obj.InitConfig = function (argconfig) { config = argconfig; };
18
+
19
+ obj.InitEditor = function (argeditor) {
20
+ editor = argeditor;
21
+
22
+ // getMarkdown(): friendly alias for the core toMarkdown() engine.
23
+ editor.getMarkdown = function () {
24
+ if (typeof editor.toMarkdown === "function") return editor.toMarkdown();
25
+ // Fallback: very small HTML→text if the core engine is unavailable.
26
+ var ed = editor.getEditable();
27
+ return ed ? (ed.innerText || ed.textContent || "") : "";
28
+ };
29
+
30
+ // copyAsMarkdown(): write the document's Markdown to the clipboard.
31
+ // Returns a Promise<boolean>. Must run from a user gesture (the slash
32
+ // command click qualifies).
33
+ editor.copyAsMarkdown = function () {
34
+ var md = editor.getMarkdown();
35
+ return writeClipboard(md);
36
+ };
37
+
38
+ // downloadMarkdown(filename): save the Markdown as a .md file.
39
+ editor.downloadMarkdown = function (filename) {
40
+ var md = editor.getMarkdown();
41
+ var base = sanitizeName(filename);
42
+ var name = base ? (base + ".md") : defaultName();
43
+ return triggerDownload(md, name);
44
+ };
45
+ };
46
+
47
+ function defaultName() {
48
+ // Derive from the first heading/line if possible.
49
+ try {
50
+ var ed = editor.getEditable();
51
+ if (ed) {
52
+ var h = ed.querySelector("h1,h2,h3");
53
+ var base = h ? h.textContent : (ed.textContent || "");
54
+ base = (base || "").trim().split("\n")[0].slice(0, 60);
55
+ var clean = sanitizeName(base);
56
+ if (clean) return clean + ".md";
57
+ }
58
+ } catch (e) {}
59
+ return "document.md";
60
+ }
61
+
62
+ function sanitizeName(s) {
63
+ if (!s) return "";
64
+ s = String(s).replace(/\.md$/i, "");
65
+ s = s.replace(/[\\/:*?"<>|\u0000-\u001f]+/g, " ").replace(/\s+/g, " ").trim();
66
+ s = s.replace(/[ .]+$/, "");
67
+ return s;
68
+ }
69
+
70
+ function writeClipboard(text) {
71
+ try {
72
+ if (navigator.clipboard && navigator.clipboard.writeText) {
73
+ return navigator.clipboard.writeText(text).then(function () { return true; }, function () { return legacyCopy(text); });
74
+ }
75
+ } catch (e) {}
76
+ return Promise.resolve(legacyCopy(text));
77
+ }
78
+
79
+ function legacyCopy(text) {
80
+ try {
81
+ var doc = document;
82
+ var ta = doc.createElement("textarea");
83
+ ta.value = text;
84
+ ta.style.position = "fixed"; ta.style.top = "-1000px"; ta.style.opacity = "0";
85
+ doc.body.appendChild(ta);
86
+ ta.focus(); ta.select();
87
+ var ok = false;
88
+ try { ok = doc.execCommand("copy"); } catch (e) { ok = false; }
89
+ doc.body.removeChild(ta);
90
+ return ok;
91
+ } catch (e) { return false; }
92
+ }
93
+
94
+ function triggerDownload(text, filename) {
95
+ try {
96
+ var blob = new Blob([text], { type: "text/markdown;charset=utf-8" });
97
+ var url = (window.URL || window.webkitURL).createObjectURL(blob);
98
+ var a = document.createElement("a");
99
+ a.href = url; a.download = filename;
100
+ a.style.display = "none";
101
+ document.body.appendChild(a);
102
+ a.click();
103
+ setTimeout(function () {
104
+ try { document.body.removeChild(a); } catch (e) {}
105
+ try { (window.URL || window.webkitURL).revokeObjectURL(url); } catch (e) {}
106
+ }, 0);
107
+ return true;
108
+ } catch (e) { return false; }
109
+ }
110
+ }
@@ -0,0 +1,153 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ // 2026-06-04 Equation-editor dialog. Closes the "no equation editor UI" gap
4
+ // vs CKEditor Math / Google Docs equation editor. Builds on the editor's
5
+ // existing inline-math infrastructure (editor.applyMathMarkup / renderMath /
6
+ // the .rte-math-inline markup) — this plugin only adds the authoring UX: a
7
+ // TeX input with a live KaTeX/MathJax preview, plus edit-in-place when the
8
+ // caret is already inside an equation. No renderer is bundled; the host page
9
+ // supplies KaTeX or MathJax exactly as for inline `$...$` rendering.
10
+ RTE_DefaultConfig.plugin_mathdialog = RTE_Plugin_MathDialog;
11
+
12
+ RTE_DefaultConfig.mathDialogSamples = RTE_DefaultConfig.mathDialogSamples || [
13
+ "x^2 + y^2 = z^2",
14
+ "\\frac{a}{b}",
15
+ "\\sqrt{x}",
16
+ "\\sum_{i=1}^{n} i",
17
+ "\\int_0^1 x\\,dx",
18
+ "\\alpha\\beta\\gamma"
19
+ ];
20
+
21
+ function RTE_Plugin_MathDialog() {
22
+ var obj = this;
23
+ var config;
24
+ var editor;
25
+
26
+ obj.PluginName = "MathDialog";
27
+
28
+ obj.InitConfig = function (argconfig) { config = argconfig; };
29
+
30
+ obj.InitEditor = function (argeditor) {
31
+ editor = argeditor;
32
+ editor.attachEvent("exec_command_insertmath", function (state) {
33
+ state.returnValue = true;
34
+ obj.OpenMathDialog();
35
+ });
36
+ };
37
+
38
+ function append(parent, tag, cssText, className) {
39
+ var el = parent.ownerDocument.createElement(tag);
40
+ if (cssText) el.style.cssText = cssText;
41
+ if (className) el.className = className;
42
+ parent.appendChild(el);
43
+ return el;
44
+ }
45
+
46
+ // If the current selection sits inside an existing inline-math span, return
47
+ // it so we can edit in place rather than insert a new equation.
48
+ function findMathSpanAtSelection() {
49
+ try {
50
+ var sel = editor.getSelection();
51
+ if (!sel || sel.rangeCount === 0) return null;
52
+ var node = sel.getRangeAt(0).startContainer;
53
+ var editable = editor.getEditable();
54
+ while (node && node !== editable) {
55
+ if (node.nodeType === 1 && node.classList && node.classList.contains("rte-math-inline")) return node;
56
+ node = node.parentNode;
57
+ }
58
+ } catch (e) { /* ignore */ }
59
+ return null;
60
+ }
61
+
62
+ // Resolve a math renderer from the dialog's (host) window or its parent.
63
+ function getRenderer(win) {
64
+ var katex = win.katex || (win.parent && win.parent.katex);
65
+ if (katex && typeof katex.render === "function") return { kind: "katex", katex: katex };
66
+ var MathJax = win.MathJax || (win.parent && win.parent.MathJax);
67
+ if (MathJax && MathJax.typesetPromise) return { kind: "mathjax", MathJax: MathJax };
68
+ return null;
69
+ }
70
+
71
+ obj.OpenMathDialog = function () {
72
+ var existing = findMathSpanAtSelection();
73
+ var initialTex = existing ? (existing.getAttribute("data-tex") || "") : "";
74
+
75
+ var dlg = editor.createDialog(
76
+ (editor.getLangText && editor.getLangText("insertmathtitle")) || "Insert / edit equation",
77
+ "rte-dialog-math"
78
+ );
79
+ var close = typeof dlg.close === "function" ? function () { dlg.close(); } : function () { editor.closeCurrentPopup(); };
80
+ var win = dlg.ownerDocument.defaultView || window;
81
+
82
+ var wrap = append(dlg, "div", "padding:14px;min-width:420px;font:13px -apple-system,Segoe UI,sans-serif");
83
+
84
+ append(wrap, "div", "font-weight:600;margin-bottom:4px", "").innerText =
85
+ (editor.getLangText && editor.getLangText("insertmathlabel")) || "TeX / LaTeX";
86
+ var ta = append(wrap, "textarea",
87
+ "width:100%;box-sizing:border-box;min-height:72px;padding:8px 10px;border:1px solid #cbd5e1;border-radius:8px;font:13px ui-monospace,Consolas,monospace;resize:vertical");
88
+ ta.value = initialTex;
89
+ ta.placeholder = "e.g. x^2 + y^2 = z^2";
90
+
91
+ // Sample chips
92
+ var samples = append(wrap, "div", "display:flex;flex-wrap:wrap;gap:6px;margin:8px 0");
93
+ (config.mathDialogSamples || []).forEach(function (s) {
94
+ var chip = append(samples, "button", "font:11px ui-monospace,monospace;padding:3px 8px;border:1px solid #e2e8f0;border-radius:6px;background:#f8fafc;cursor:pointer", "");
95
+ chip.type = "button";
96
+ chip.textContent = s;
97
+ chip.onclick = function () { ta.value = s; updatePreview(); ta.focus(); };
98
+ });
99
+
100
+ append(wrap, "div", "font-weight:600;margin:10px 0 4px", "").innerText =
101
+ (editor.getLangText && editor.getLangText("insertmathpreview")) || "Preview";
102
+ var preview = append(wrap, "div",
103
+ "min-height:48px;padding:10px 12px;border:1px solid #e2e8f0;border-radius:8px;background:#fff;overflow:auto;text-align:center");
104
+ var rendererNote = append(wrap, "div", "font-size:11px;color:#94a3b8;margin-top:4px", "");
105
+
106
+ var renderer = getRenderer(win);
107
+ if (!renderer) {
108
+ rendererNote.innerText = "No KaTeX/MathJax detected on the page — the equation is stored as markup and will render wherever a math renderer is loaded.";
109
+ }
110
+
111
+ function updatePreview() {
112
+ var tex = ta.value;
113
+ if (!tex.trim()) { preview.textContent = ""; return; }
114
+ if (renderer && renderer.kind === "katex") {
115
+ try { renderer.katex.render(tex, preview, { throwOnError: false, displayMode: false }); }
116
+ catch (e) { preview.textContent = "⚠ " + (e && e.message ? e.message : "render error"); }
117
+ } else if (renderer && renderer.kind === "mathjax") {
118
+ preview.textContent = "\\(" + tex + "\\)";
119
+ try { renderer.MathJax.typesetPromise([preview]); } catch (e) { /* ignore */ }
120
+ } else {
121
+ preview.textContent = "$" + tex + "$";
122
+ }
123
+ }
124
+ ta.oninput = updatePreview;
125
+ setTimeout(function () { ta.focus(); updatePreview(); }, 0);
126
+
127
+ var footer = append(wrap, "div", "display:flex;justify-content:flex-end;gap:8px;margin-top:14px");
128
+ var cancel = append(footer, "button", "padding:6px 14px;border:1px solid #cbd5e1;border-radius:8px;background:#fff;cursor:pointer", "");
129
+ cancel.type = "button"; cancel.textContent = "Cancel"; cancel.onclick = close;
130
+ var ok = append(footer, "button", "padding:6px 14px;border:1px solid #1d67ba;border-radius:8px;background:#1d67ba;color:#fff;cursor:pointer", "");
131
+ ok.type = "button"; ok.textContent = existing ? "Update" : "Insert";
132
+
133
+ ok.onclick = function () {
134
+ var tex = ta.value.trim();
135
+ if (!tex) { close(); return; }
136
+ if (existing) {
137
+ // Edit in place: update the existing span's data-tex + text, re-render.
138
+ existing.setAttribute("data-tex", tex);
139
+ existing.textContent = "$" + tex + "$";
140
+ close();
141
+ if (editor.renderMath) editor.renderMath();
142
+ editor.focus();
143
+ return;
144
+ }
145
+ var safeTex = String(tex).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
146
+ var safeText = String("$" + tex + "$").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
147
+ editor.insertHTML('<span class="rte-math-inline" data-tex="' + safeTex + '">' + safeText + "</span>​");
148
+ close();
149
+ if (editor.renderMath) editor.renderMath();
150
+ editor.focus();
151
+ };
152
+ };
153
+ }