@richscripts/richtexteditor 2.14.1 → 2.16.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 (244) hide show
  1. package/README.md +182 -10
  2. package/angular/build-manifest.json +23 -0
  3. package/angular/index.d.ts +71 -76
  4. package/angular/index.mjs +2 -0
  5. package/angular/package.json +4 -0
  6. package/headless/index.d.ts +3 -1
  7. package/headless/index.js +72 -16
  8. package/index.d.ts +40 -52
  9. package/integrations/initialization-scope.js +27 -0
  10. package/integrations/native-mount.js +30 -0
  11. package/integrations/shared.d.ts +23 -1
  12. package/integrations/shared.js +68 -22
  13. package/package.json +25 -3
  14. package/react/index.js +118 -37
  15. package/react/index.mjs +29 -0
  16. package/richtexteditor/all_plugins.js +8816 -2373
  17. package/richtexteditor/all_plugins.min.js +77 -59
  18. package/richtexteditor/lang/emoji/LICENSE-CLDR.txt +41 -0
  19. package/richtexteditor/lang/emoji/rte-emoji-de.js +3 -0
  20. package/richtexteditor/lang/emoji/rte-emoji-es.js +3 -0
  21. package/richtexteditor/lang/emoji/rte-emoji-fr.js +3 -0
  22. package/richtexteditor/lang/emoji/rte-emoji-it.js +3 -0
  23. package/richtexteditor/lang/emoji/rte-emoji-ja.js +3 -0
  24. package/richtexteditor/lang/emoji/rte-emoji-ko.js +3 -0
  25. package/richtexteditor/lang/emoji/rte-emoji-nl.js +3 -0
  26. package/richtexteditor/lang/emoji/rte-emoji-pt-pt.js +3 -0
  27. package/richtexteditor/lang/emoji/rte-emoji-pt.js +3 -0
  28. package/richtexteditor/lang/emoji/rte-emoji-ru.js +3 -0
  29. package/richtexteditor/lang/emoji/rte-emoji-zh-Hans.js +3 -0
  30. package/richtexteditor/lang/emoji/rte-emoji-zh-Hant.js +3 -0
  31. package/richtexteditor/lang/rte-lang-af.js +1 -0
  32. package/richtexteditor/lang/rte-lang-ar.js +1 -0
  33. package/richtexteditor/lang/rte-lang-bg.js +1 -0
  34. package/richtexteditor/lang/rte-lang-bn.js +1 -0
  35. package/richtexteditor/lang/rte-lang-bs.js +1 -0
  36. package/richtexteditor/lang/rte-lang-ca.js +1 -0
  37. package/richtexteditor/lang/rte-lang-cs.js +1 -0
  38. package/richtexteditor/lang/rte-lang-cy.js +1 -0
  39. package/richtexteditor/lang/rte-lang-da.js +1 -0
  40. package/richtexteditor/lang/rte-lang-de.js +1050 -2
  41. package/richtexteditor/lang/rte-lang-el.js +1 -0
  42. package/richtexteditor/lang/rte-lang-en.js +1 -0
  43. package/richtexteditor/lang/rte-lang-es.js +1056 -2
  44. package/richtexteditor/lang/rte-lang-et.js +1 -0
  45. package/richtexteditor/lang/rte-lang-fa.js +1 -0
  46. package/richtexteditor/lang/rte-lang-fi.js +1 -0
  47. package/richtexteditor/lang/rte-lang-fil.js +1 -0
  48. package/richtexteditor/lang/rte-lang-fj.js +1 -0
  49. package/richtexteditor/lang/rte-lang-fr.js +1056 -2
  50. package/richtexteditor/lang/rte-lang-ga.js +1 -0
  51. package/richtexteditor/lang/rte-lang-gu.js +1 -0
  52. package/richtexteditor/lang/rte-lang-he.js +1 -0
  53. package/richtexteditor/lang/rte-lang-hi.js +1 -0
  54. package/richtexteditor/lang/rte-lang-hr.js +1 -0
  55. package/richtexteditor/lang/rte-lang-ht.js +1 -0
  56. package/richtexteditor/lang/rte-lang-hu.js +1 -0
  57. package/richtexteditor/lang/rte-lang-id.js +1 -0
  58. package/richtexteditor/lang/rte-lang-is.js +1 -0
  59. package/richtexteditor/lang/rte-lang-it.js +1056 -2
  60. package/richtexteditor/lang/rte-lang-ja.js +1056 -2
  61. package/richtexteditor/lang/rte-lang-kn.js +1 -0
  62. package/richtexteditor/lang/rte-lang-ko.js +1056 -2
  63. package/richtexteditor/lang/rte-lang-lt.js +1 -0
  64. package/richtexteditor/lang/rte-lang-lv.js +1 -0
  65. package/richtexteditor/lang/rte-lang-mg.js +1 -0
  66. package/richtexteditor/lang/rte-lang-mi.js +1 -0
  67. package/richtexteditor/lang/rte-lang-ml.js +1 -0
  68. package/richtexteditor/lang/rte-lang-mr.js +1 -0
  69. package/richtexteditor/lang/rte-lang-ms.js +1 -0
  70. package/richtexteditor/lang/rte-lang-mt.js +1 -0
  71. package/richtexteditor/lang/rte-lang-mww.js +1 -0
  72. package/richtexteditor/lang/rte-lang-nb.js +1 -0
  73. package/richtexteditor/lang/rte-lang-nl.js +1056 -2
  74. package/richtexteditor/lang/rte-lang-otq.js +1 -0
  75. package/richtexteditor/lang/rte-lang-pa.js +1 -0
  76. package/richtexteditor/lang/rte-lang-pl.js +1 -0
  77. package/richtexteditor/lang/rte-lang-pt-pt.js +1056 -2
  78. package/richtexteditor/lang/rte-lang-pt.js +1056 -2
  79. package/richtexteditor/lang/rte-lang-ro.js +1 -0
  80. package/richtexteditor/lang/rte-lang-ru.js +1056 -2
  81. package/richtexteditor/lang/rte-lang-sk.js +1 -0
  82. package/richtexteditor/lang/rte-lang-sl.js +1 -0
  83. package/richtexteditor/lang/rte-lang-sm.js +1 -0
  84. package/richtexteditor/lang/rte-lang-sr-Cyrl.js +1 -0
  85. package/richtexteditor/lang/rte-lang-sr-Latn.js +1 -0
  86. package/richtexteditor/lang/rte-lang-sv.js +1 -0
  87. package/richtexteditor/lang/rte-lang-sw.js +1 -0
  88. package/richtexteditor/lang/rte-lang-ta.js +1 -0
  89. package/richtexteditor/lang/rte-lang-te.js +1 -0
  90. package/richtexteditor/lang/rte-lang-th.js +1 -0
  91. package/richtexteditor/lang/rte-lang-tlh-Latn.js +1 -0
  92. package/richtexteditor/lang/rte-lang-tlh-Piqd.js +1 -0
  93. package/richtexteditor/lang/rte-lang-to.js +1 -0
  94. package/richtexteditor/lang/rte-lang-tr.js +1 -0
  95. package/richtexteditor/lang/rte-lang-ty.js +1 -0
  96. package/richtexteditor/lang/rte-lang-uk.js +1 -0
  97. package/richtexteditor/lang/rte-lang-ur.js +1 -0
  98. package/richtexteditor/lang/rte-lang-vi.js +1 -0
  99. package/richtexteditor/lang/rte-lang-yua.js +1 -0
  100. package/richtexteditor/lang/rte-lang-zh-Hans.js +1050 -2
  101. package/richtexteditor/lang/rte-lang-zh-Hant.js +1050 -2
  102. package/richtexteditor/plugins/a11yenhance.js +4 -1
  103. package/richtexteditor/plugins/a11yenhance.min.js +1 -1
  104. package/richtexteditor/plugins/accessibilitychecker.js +256 -64
  105. package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
  106. package/richtexteditor/plugins/aitoolkit.js +1057 -386
  107. package/richtexteditor/plugins/aitoolkit.min.js +7 -7
  108. package/richtexteditor/plugins/all_plugins.js +8816 -2373
  109. package/richtexteditor/plugins/all_plugins.min.js +77 -59
  110. package/richtexteditor/plugins/autocorrect.js +66 -31
  111. package/richtexteditor/plugins/autocorrect.min.js +1 -1
  112. package/richtexteditor/plugins/autoembed.js +18 -1
  113. package/richtexteditor/plugins/autoembed.min.js +1 -1
  114. package/richtexteditor/plugins/blocktypes.js +18 -9
  115. package/richtexteditor/plugins/blocktypes.min.js +1 -1
  116. package/richtexteditor/plugins/bookmarkcard.js +12 -5
  117. package/richtexteditor/plugins/bookmarkcard.min.js +2 -2
  118. package/richtexteditor/plugins/changecase.js +72 -19
  119. package/richtexteditor/plugins/changecase.min.js +1 -1
  120. package/richtexteditor/plugins/charlimit.js +62 -2
  121. package/richtexteditor/plugins/charlimit.min.js +2 -2
  122. package/richtexteditor/plugins/comments.js +460 -61
  123. package/richtexteditor/plugins/comments.min.js +4 -3
  124. package/richtexteditor/plugins/contentminimap.js +28 -9
  125. package/richtexteditor/plugins/contentminimap.min.js +1 -1
  126. package/richtexteditor/plugins/crdt-engine.js +27 -1
  127. package/richtexteditor/plugins/crossreference.js +1 -1
  128. package/richtexteditor/plugins/crossreference.min.js +1 -1
  129. package/richtexteditor/plugins/dialoga11y.js +26 -4
  130. package/richtexteditor/plugins/dialoga11y.min.js +1 -1
  131. package/richtexteditor/plugins/dictation.js +1 -1
  132. package/richtexteditor/plugins/documentimport.js +250 -59
  133. package/richtexteditor/plugins/documentimport.min.js +4 -4
  134. package/richtexteditor/plugins/documentoutline.js +47 -8
  135. package/richtexteditor/plugins/documentoutline.min.js +1 -1
  136. package/richtexteditor/plugins/docxexport.js +233 -32
  137. package/richtexteditor/plugins/docxexport.min.js +2 -2
  138. package/richtexteditor/plugins/draghandle.js +267 -3
  139. package/richtexteditor/plugins/draghandle.min.js +1 -1
  140. package/richtexteditor/plugins/emailtoolkit.js +28 -14
  141. package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
  142. package/richtexteditor/plugins/emojiautocomplete.js +111 -57
  143. package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
  144. package/richtexteditor/plugins/footnotes.js +65 -11
  145. package/richtexteditor/plugins/footnotes.min.js +1 -1
  146. package/richtexteditor/plugins/formatpainter.js +10 -0
  147. package/richtexteditor/plugins/formatpainter.min.js +1 -1
  148. package/richtexteditor/plugins/formattingmarks.js +6 -6
  149. package/richtexteditor/plugins/formattingmarks.min.js +1 -1
  150. package/richtexteditor/plugins/gapcursor.js +60 -0
  151. package/richtexteditor/plugins/gapcursor.min.js +1 -1
  152. package/richtexteditor/plugins/ghostcomplete.js +1 -1
  153. package/richtexteditor/plugins/html2pdf.js +8 -8
  154. package/richtexteditor/plugins/html2pdf.min.js +1 -1
  155. package/richtexteditor/plugins/imageeditor.js +31 -12
  156. package/richtexteditor/plugins/imageeditor.min.js +1 -1
  157. package/richtexteditor/plugins/importstyles.js +1 -1
  158. package/richtexteditor/plugins/inlinecode.js +25 -0
  159. package/richtexteditor/plugins/inlinecode.min.js +1 -1
  160. package/richtexteditor/plugins/insertcode.js +29 -11
  161. package/richtexteditor/plugins/insertcode.min.js +6 -6
  162. package/richtexteditor/plugins/insertemoji.js +113 -16
  163. package/richtexteditor/plugins/insertemoji.min.js +1 -1
  164. package/richtexteditor/plugins/insertgallery.js +46 -40
  165. package/richtexteditor/plugins/insertgallery.min.js +1 -1
  166. package/richtexteditor/plugins/inserttemplate.js +43 -19
  167. package/richtexteditor/plugins/inserttemplate.min.js +1 -1
  168. package/richtexteditor/plugins/keyboarda11y.js +66 -3
  169. package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
  170. package/richtexteditor/plugins/layouttable.js +20 -0
  171. package/richtexteditor/plugins/layouttable.min.js +1 -1
  172. package/richtexteditor/plugins/linkchecker.js +29 -9
  173. package/richtexteditor/plugins/linkchecker.min.js +1 -1
  174. package/richtexteditor/plugins/localdraft.js +51 -7
  175. package/richtexteditor/plugins/localdraft.min.js +1 -1
  176. package/richtexteditor/plugins/markdownexport.js +159 -0
  177. package/richtexteditor/plugins/markdownexport.min.js +2 -2
  178. package/richtexteditor/plugins/mathdialog.js +11 -5
  179. package/richtexteditor/plugins/mathdialog.min.js +1 -1
  180. package/richtexteditor/plugins/mentionplugin.js +90 -27
  181. package/richtexteditor/plugins/mentionplugin.min.js +1 -1
  182. package/richtexteditor/plugins/mermaiddiagram.js +14 -8
  183. package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
  184. package/richtexteditor/plugins/multilevellist.js +4 -0
  185. package/richtexteditor/plugins/multilevellist.min.js +1 -1
  186. package/richtexteditor/plugins/nodetypes.js +1 -1
  187. package/richtexteditor/plugins/nodetypes.min.js +1 -1
  188. package/richtexteditor/plugins/pagination.js +2 -2
  189. package/richtexteditor/plugins/pagination.min.js +1 -1
  190. package/richtexteditor/plugins/pdfexport.js +1752 -1167
  191. package/richtexteditor/plugins/pdfexport.min.js +28 -13
  192. package/richtexteditor/plugins/permanentpen.js +3 -3
  193. package/richtexteditor/plugins/permanentpen.min.js +1 -1
  194. package/richtexteditor/plugins/readabilitystats.js +25 -11
  195. package/richtexteditor/plugins/readabilitystats.min.js +1 -1
  196. package/richtexteditor/plugins/readaloud.js +30 -16
  197. package/richtexteditor/plugins/readaloud.min.js +1 -1
  198. package/richtexteditor/plugins/restrictedediting.js +166 -0
  199. package/richtexteditor/plugins/restrictedediting.min.js +2 -2
  200. package/richtexteditor/plugins/revisionhistory.js +173 -41
  201. package/richtexteditor/plugins/revisionhistory.min.js +3 -3
  202. package/richtexteditor/plugins/rubyannotation.js +21 -11
  203. package/richtexteditor/plugins/rubyannotation.min.js +1 -1
  204. package/richtexteditor/plugins/sanitizer.js +119 -7
  205. package/richtexteditor/plugins/sanitizer.min.js +1 -1
  206. package/richtexteditor/plugins/slashcommand.js +32 -18
  207. package/richtexteditor/plugins/slashcommand.min.js +2 -2
  208. package/richtexteditor/plugins/smartchips.js +39 -25
  209. package/richtexteditor/plugins/smartchips.min.js +2 -2
  210. package/richtexteditor/plugins/spellcheck.js +20 -11
  211. package/richtexteditor/plugins/spellcheck.min.js +2 -2
  212. package/richtexteditor/plugins/stateinspector.js +21 -11
  213. package/richtexteditor/plugins/stateinspector.min.js +2 -2
  214. package/richtexteditor/plugins/structured-content-bridge.js +90 -32
  215. package/richtexteditor/plugins/structured-content-bridge.min.js +8 -8
  216. package/richtexteditor/plugins/tableofcontents.js +5 -2
  217. package/richtexteditor/plugins/tableofcontents.min.js +1 -1
  218. package/richtexteditor/plugins/tabletools.js +388 -3
  219. package/richtexteditor/plugins/tabletools.min.js +1 -1
  220. package/richtexteditor/plugins/textdirection.js +19 -6
  221. package/richtexteditor/plugins/textdirection.min.js +1 -1
  222. package/richtexteditor/plugins/textpartlanguage.js +161 -32
  223. package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
  224. package/richtexteditor/plugins/todolist.js +90 -6
  225. package/richtexteditor/plugins/todolist.min.js +1 -1
  226. package/richtexteditor/plugins/trackedchanges.js +1812 -25
  227. package/richtexteditor/plugins/trackedchanges.min.js +5 -3
  228. package/richtexteditor/plugins/wordexport.js +2 -2
  229. package/richtexteditor/plugins/yjscollab.js +11 -6
  230. package/richtexteditor/plugins/yjscollab.min.js +1 -1
  231. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
  232. package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
  233. package/richtexteditor/rte-config.js +144 -86
  234. package/richtexteditor/rte-headless.js +19 -4
  235. package/richtexteditor/rte-watchdog.js +3 -1
  236. package/richtexteditor/rte-webcomponent.js +91 -16
  237. package/richtexteditor/rte.js +146 -88
  238. package/richtexteditor/runtime/colorpicker_more_ns.htm +3 -1
  239. package/scripts/build-angular.mjs +97 -0
  240. package/svelte/index.d.ts +2 -1
  241. package/svelte/index.js +80 -9
  242. package/vue/index.js +130 -34
  243. package/angular/component.ts +0 -297
  244. package/angular/index.ts +0 -24
@@ -226,7 +226,7 @@ RTE_DefaultConfig.insertCellTag = "<td><br/></td>"; // Default cell attributes w
226
226
 
227
227
 
228
228
 
229
- RTE_DefaultConfig.insertOrderedListItems = [["decimal", "1,2,3,4,5"], ["lower-alpha", "a,b,c,d,e"], ["upper-alpha", "A,B,C,D,E"], ["lower-roman", "ⅰ,ⅱ,ⅲ,ⅳ,ⅴ"], ["upper-roman", "Ⅰ,Ⅱ,Ⅲ,Ⅳ,Ⅴ"]];
229
+ RTE_DefaultConfig.insertOrderedListItems = [["decimal", "1,2,3,4,5"], ["lower-alpha", "a,b,c,d,e"], ["upper-alpha", "A,B,C,D,E"], ["lower-roman", "\u2170,\u2171,\u2172,\u2173,\u2174"], ["upper-roman", "\u2160,\u2161,\u2162,\u2163,\u2164"]];
230
230
 
231
231
  RTE_DefaultConfig.insertUnorderedListItems = [["disc", "Disc"], ["circle", "Circle"], ["square", "Square"]];
232
232
 
@@ -258,42 +258,20 @@ RTE_DefaultConfig.backColorItems = ["#000000", "#993300", "#333300", "#003300",
258
258
  "#ff00ff", "#ffcc00", "#ffff00", "#00ff00", "#00ffff", "#00ccff", "#993366", "#c0c0c0",
259
259
  "#ff99cc", "#ffcc99", "#ffff99", "#ccffcc", "#ccffff", "#99ccff", "#cc99ff", "#ffffff"];
260
260
 
261
- RTE_DefaultConfig.linkItems = [
262
- "https://www.intel.com"
263
- ,
264
- "https://www.ibm.com"
265
- ,
266
- "https://www.microsoft.com"
267
- ,
268
- "https://www.google.com"
269
- ,
270
- "https://www.apple.com"
271
- ] // A predefined set of links.
272
-
273
- RTE_DefaultConfig.imageItems = [
274
- "https://richtexteditor.com/uploads/1.jpg",
275
- "https://richtexteditor.com/uploads/2.jpg",
276
- "https://richtexteditor.com/uploads/3.jpg",
277
- "https://richtexteditor.com/uploads/4.jpg",
278
- "https://richtexteditor.com/uploads/5.jpg",
279
- "https://richtexteditor.com/uploads/6.jpg"
280
- ] // For insert image by URL
281
-
282
-
283
- RTE_DefaultConfig.galleryImages = [
284
- "https://richtexteditor.com/uploads/1.jpg",
285
- "https://richtexteditor.com/uploads/2.jpg",
286
- "https://richtexteditor.com/uploads/3.jpg",
287
- "https://richtexteditor.com/uploads/4.jpg",
288
- "https://richtexteditor.com/uploads/5.jpg",
289
- "https://richtexteditor.com/uploads/6.jpg"
290
- ]; // Default images for gallery Images dialog.
291
-
292
- RTE_DefaultConfig.htmlTemplates = [
293
- ["My Doc 1", "<h2>MyTitleMyTitleMyTitleMyTitleMyTitleMyTitleMyTitle</h2><p>Paragraph 1 </p><p>Paragraph 2 </p><p>Paragraph 3 </p><p>Paragraph 4 </p><p>Paragraph 5 </p>"]
294
- ,
295
- ["My Doc 1", "<h2>MyTitleMyTitleMyTitle</h2><p>Paragraph 1 </p><p>Paragraph 2 </p><p>Paragraph 3 </p><p>Paragraph 4 </p><p>Paragraph 5 </p>"]
296
- ];// Default html Templates for html Templates dialog.
261
+ // 2026-10-26 these three shipped DEMO data: every customer's link dialog suggested five big-company sites, and
262
+ // opening the gallery fetched six images from the vendor's server, on a product that makes no outbound calls.
263
+ // They are empty now; set your own, e.g. linkItems = ["/about", "/contact"], galleryImages = ["/img/a.jpg"].
264
+ // An empty list hides the URL field's suggestion arrow. tools/security/no-default-outbound.mjs
265
+ RTE_DefaultConfig.linkItems = [] // Addresses the Insert Link dialog suggests.
266
+
267
+ RTE_DefaultConfig.imageItems = [] // Addresses the Insert Image (by URL) dialog suggests.
268
+
269
+ RTE_DefaultConfig.galleryImages = []; // Preset images for the gallery dialog: "url", [url, text] or { url, name, alt }.
270
+
271
+ // 2026-10-26 shipped two demo templates, both named "My Doc 1" ("MyTitleMyTitle..."), in every Insert Template
272
+ // dialog. Empty now; set your own: htmlTemplates = [["Letter", "<p>Dear ...</p>"]] or { title, html, description }.
273
+ // tools/templates/template-defaults.mjs
274
+ RTE_DefaultConfig.htmlTemplates = [];// Templates the Insert Template dialog offers.
297
275
 
298
276
  // Slash command menu — type "/" at the start of a line or after whitespace to open an inline command picker.
299
277
  // Set slashCommandEnabled to false to disable. Set slashCommandIncludeAi to false to hide AI actions from the menu.
@@ -377,7 +355,7 @@ RTE_DefaultConfig.stylesMerge = false; // true: the Quick Styles lists below are
377
355
  // { title: "Callouts", items: [ ... ] } a labelled GROUP; items are indented under a heading
378
356
  // Groups render as a heading plus indented items, not as a flyout submenu.
379
357
  RTE_DefaultConfig.inlineStyles = [["Red", "color:red", "color:red"], ["Bold", "font-weight:bold", "font-weight:bold"], ["Mark", "my-cls-mark"], ["Warning", "my-cls-warning"]]; // Default CSS styles for inline styles dropdown.
380
- RTE_DefaultConfig.paragraphStyles = [["Red", "color:red", "color:red"], ["Bold", "font-weight:bold", "font-weight:bold"], ["Quote", "my-cls-quote"], ["LargeCenter", "my-cls-largecenter"]]; // Default CSS styles for paragraph styles dropdown.
358
+ RTE_DefaultConfig.paragraphStyles = [["Red", "color:red", "color:red"], ["Bold", "font-weight:bold", "font-weight:bold"], ["Quote", "my-cls-quote"], ["Large Center", "my-cls-largecenter"]]; // Default CSS styles for paragraph styles dropdown.
381
359
  RTE_DefaultConfig.imageStyles = [["Border", "border: 1px solid #ddd; border-radius: 4px; padding: 5px;"], ["grayscale", "filter: grayscale(100%);"], ["Shadow", "box-shadow:0 0 8px gray"], ["Margin10", "margin:10px"], ["Padding:10", "padding:10px"]
382
360
  , ["Rounded Corners", "border-radius: 10px;"], ["Rounded Images", "border-radius: 50%;"], ["Thumbnail Image", "border: 1px solid #ddd; border-radius: 4px; padding: 5px;width:150px"]// Default CSS styles for image Styles dropdown.
383
361
  ];
@@ -420,13 +398,15 @@ RTE_DefaultConfig.subtoolbar_more_full = "{strike,ucase,lcase,titlecase,insertho
420
398
  RTE_DefaultConfig.subtoolbar_more_mobile = "{save} #{newdoc,help}"; // A set of buttons that appears in the subtoolbar of mobile toolbar set.
421
399
  RTE_DefaultConfig.subtoolbar_paste = "pasteauto,pastetext,pasteword"; // A set of buttons that appears in the rich text editor's paste subtoolbar.
422
400
  RTE_DefaultConfig.subtoolbar_paragraphop = "{justifyleft,justifycenter,justifyright,insertorderedlist,insertunorderedlist,indent,outdent,insertblockquote}" // A set of buttons that appears in the rich text editor's paragraph subtoolbar.
423
- RTE_DefaultConfig.subtoolbar_table = "controlsizeauto,controlsize100,controlsize75,controlsize50,tabledistributecolumns,tabledistributerows,tableproperties,tabledelete"; // A set of buttons that appears in the table subtoolbar.
401
+ RTE_DefaultConfig.subtoolbar_table = "controlsizeauto,controlsize100,controlsize75,controlsize50,tabledistributecolumns,tabledistributerows,tablecaption,tableproperties,tabledelete"; // A set of buttons that appears in the table subtoolbar.
424
402
  RTE_DefaultConfig.subtoolbar_tablerow = "tablerowinsertabove,tablerowinsertbelow,tablerowdelete"; // A set of buttons that appears in the tablerow subtoolbar.
425
403
  RTE_DefaultConfig.subtoolbar_tablecell = "tablecellmerge,tablecellsplitver,tablecellsplithor,tablecellforecolor,tablecellbackcolor,tablecelltype,tablecellproperties"; // A set of buttons that appears in the tablecell subtoolbar.
426
404
 
427
405
  // Icons + tooltips for the table/cell commands added 2026-06-18 (Lucide-style line icons).
428
406
  RTE_DefaultConfig.text_tablecellproperties = "Cell Properties";
429
407
  RTE_DefaultConfig.text_tableproperties = "Table Properties";
408
+ RTE_DefaultConfig.text_tablecaption = "Table Caption";
409
+ RTE_DefaultConfig.svgCode_tablecaption = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="4" x2="18" y2="4"/><rect x="3" y="8" width="18" height="13" rx="1"/><line x1="3" y1="14.5" x2="21" y2="14.5"/><line x1="12" y1="8" x2="12" y2="21"/></svg>';
430
410
  RTE_DefaultConfig.text_tablecelltype = "Toggle Header Cell";
431
411
  RTE_DefaultConfig.text_tabledistributecolumns = "Distribute Columns";
432
412
  RTE_DefaultConfig.text_tabledistributerows = "Distribute Rows";
@@ -649,22 +629,22 @@ RTE_DefaultConfig.text_ok = "OK";
649
629
  RTE_DefaultConfig.text_cancel = "Cancel";
650
630
 
651
631
  RTE_DefaultConfig.text_normal = "Normal"
652
- RTE_DefaultConfig.text_h1 = "Headline 1"
653
- RTE_DefaultConfig.text_h2 = "Headline 2"
654
- RTE_DefaultConfig.text_h3 = "Headline 3"
655
- RTE_DefaultConfig.text_h4 = "Headline 4"
656
- RTE_DefaultConfig.text_h5 = "Headline 5"
657
- RTE_DefaultConfig.text_h6 = "Headline 6"
658
- RTE_DefaultConfig.text_h7 = "Headline 7"
632
+ RTE_DefaultConfig.text_h1 = "Heading 1"
633
+ RTE_DefaultConfig.text_h2 = "Heading 2"
634
+ RTE_DefaultConfig.text_h3 = "Heading 3"
635
+ RTE_DefaultConfig.text_h4 = "Heading 4"
636
+ RTE_DefaultConfig.text_h5 = "Heading 5"
637
+ RTE_DefaultConfig.text_h6 = "Heading 6"
638
+ RTE_DefaultConfig.text_h7 = "Heading 7"
659
639
 
660
640
  RTE_DefaultConfig.text_close = "Close";
661
641
 
662
642
  RTE_DefaultConfig.text_bold = "Bold";
663
643
  RTE_DefaultConfig.text_italic = "Italic";
664
644
  RTE_DefaultConfig.text_underline = "Underline";
665
- RTE_DefaultConfig.text_strike = "Strike Line";
645
+ RTE_DefaultConfig.text_strike = "Strikethrough";
666
646
  RTE_DefaultConfig.text_superscript = "Superscript";
667
- RTE_DefaultConfig.text_subscript = "Subcript";
647
+ RTE_DefaultConfig.text_subscript = "Subscript";
668
648
  RTE_DefaultConfig.text_ucase = "Upper Case";
669
649
  RTE_DefaultConfig.text_lcase = "Lower Case";
670
650
  RTE_DefaultConfig.text_titlecase = "Title Case";
@@ -675,17 +655,17 @@ RTE_DefaultConfig.text_titlecase = "Title Case";
675
655
  // translate without forking the plugin source.
676
656
  RTE_DefaultConfig.svgCode_documentoutline = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="6" y1="6" x2="20" y2="6"/><line x1="9" y1="11" x2="20" y2="11"/><line x1="12" y1="16" x2="20" y2="16"/><line x1="3" y1="6" x2="3.5" y2="6"/><line x1="6" y1="11" x2="6.5" y2="11"/><line x1="9" y1="16" x2="9.5" y2="16"/></svg>';
677
657
  RTE_DefaultConfig.text_documentoutline = "Document outline";
678
- RTE_DefaultConfig.text_documentoutlinehint = "Live table of contents — click any heading to jump to it.";
658
+ RTE_DefaultConfig.text_documentoutlinehint = "Live table of contents \u2014 click any heading to jump to it.";
679
659
 
680
660
  RTE_DefaultConfig.svgCode_contentminimap = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><line x1="6" y1="7" x2="14" y2="7"/><line x1="6" y1="11" x2="18" y2="11"/><line x1="6" y1="15" x2="12" y2="15"/><line x1="6" y1="19" x2="16" y2="19"/></svg>';
681
661
  RTE_DefaultConfig.text_contentminimap = "Content minimap";
682
- RTE_DefaultConfig.text_contentminimaphint = "Bird's-eye view of the document — drag the highlight box to scroll.";
662
+ RTE_DefaultConfig.text_contentminimaphint = "Bird's-eye view of the document \u2014 drag the highlight box to scroll.";
683
663
 
684
664
  RTE_DefaultConfig.svgCode_accessibilitychecker = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="7" r="1.4" fill="currentColor"/><path d="M9 11.5h6"/><path d="M12 11.5v8"/><path d="M9 19.5l3-4 3 4"/></svg>';
685
665
  RTE_DefaultConfig.text_accessibilitychecker = "Accessibility checker";
686
666
  RTE_DefaultConfig.text_accessibilitycheckerhint = "Scan the document for missing alt text, empty headings, low contrast, and other a11y issues.";
687
667
 
688
- RTE_DefaultConfig.text_removeformat = "Remove Format";
668
+ RTE_DefaultConfig.text_removeformat = "Clear Formatting";
689
669
 
690
670
  RTE_DefaultConfig.text_insertlink = "Insert Link";
691
671
  RTE_DefaultConfig.text_openlink = "Open Link";
@@ -704,8 +684,8 @@ RTE_DefaultConfig.text_outdent = "Outdent";
704
684
 
705
685
  RTE_DefaultConfig.text_insertblockquote = "Block Quote";
706
686
 
707
- RTE_DefaultConfig.text_insertorderedlist = "Ordered List";
708
- RTE_DefaultConfig.text_insertunorderedlist = "Unordered List";
687
+ RTE_DefaultConfig.text_insertorderedlist = "Numbered List";
688
+ RTE_DefaultConfig.text_insertunorderedlist = "Bulleted List";
709
689
 
710
690
  RTE_DefaultConfig.text_inserthorizontalrule = "Insert Horizontal Rule";
711
691
  RTE_DefaultConfig.text_insertdate = "Insert Date";
@@ -745,13 +725,13 @@ RTE_DefaultConfig.text_inlinestyles = "Text styles";// Quick Styles applied to t
745
725
  RTE_DefaultConfig.text_fontname = "Font";
746
726
  RTE_DefaultConfig.text_fontsize = "Size";
747
727
  RTE_DefaultConfig.text_forecolor = "Text Color";
748
- RTE_DefaultConfig.text_backcolor = "Back Color";
728
+ RTE_DefaultConfig.text_backcolor = "Background Color";
749
729
 
750
730
  RTE_DefaultConfig.text_justify = "Justify"
751
- RTE_DefaultConfig.text_justifyleft = "Justify Left"
752
- RTE_DefaultConfig.text_justifyright = "Justify Right"
753
- RTE_DefaultConfig.text_justifycenter = "Justify Center"
754
- RTE_DefaultConfig.text_justifyfull = "Justify Full"
731
+ RTE_DefaultConfig.text_justifyleft = "Align Left"
732
+ RTE_DefaultConfig.text_justifyright = "Align Right"
733
+ RTE_DefaultConfig.text_justifycenter = "Align Center"
734
+ RTE_DefaultConfig.text_justifyfull = "Justify"
755
735
  RTE_DefaultConfig.text_justifynone = "Justify None"
756
736
 
757
737
  RTE_DefaultConfig.text_delete = "Delete";
@@ -764,11 +744,11 @@ RTE_DefaultConfig.text_preview = "Preview";
764
744
  RTE_DefaultConfig.text_print = "Print";
765
745
  RTE_DefaultConfig.text_undo = "Undo";
766
746
  RTE_DefaultConfig.text_redo = "Redo";
767
- RTE_DefaultConfig.text_more = "More...";
747
+ RTE_DefaultConfig.text_more = "More\u2026";
768
748
  RTE_DefaultConfig.text_newdoc = "New Doc";
769
749
  RTE_DefaultConfig.text_help = "Help";
770
750
 
771
- RTE_DefaultConfig.text_fullscreenenter = "Fit to Window";
751
+ RTE_DefaultConfig.text_fullscreenenter = "Full Screen";
772
752
  RTE_DefaultConfig.text_fullscreenexit = "Exit Full Screen";
773
753
  RTE_DefaultConfig.text_fullscreen = "@text_fullscreenenter";
774
754
 
@@ -782,9 +762,9 @@ RTE_DefaultConfig.text_paragraphstyle = "Paragraph Styles";
782
762
  RTE_DefaultConfig.text_linkstyle = "Link Styles";
783
763
  RTE_DefaultConfig.text_link = "Link";
784
764
  RTE_DefaultConfig.text_style = "Styles";
785
- RTE_DefaultConfig.text_cssclass = "Css Classes";
786
- RTE_DefaultConfig.text_url = "Url";
787
- RTE_DefaultConfig.text_byurl = "By Url";
765
+ RTE_DefaultConfig.text_cssclass = "CSS Classes";
766
+ RTE_DefaultConfig.text_url = "URL";
767
+ RTE_DefaultConfig.text_byurl = "By URL";
788
768
  RTE_DefaultConfig.text_upload = "Upload";
789
769
  RTE_DefaultConfig.text_size = "Size";
790
770
 
@@ -795,7 +775,7 @@ RTE_DefaultConfig.text_opennewwin = "Open in new tab";
795
775
  RTE_DefaultConfig.text_insert = "Insert";
796
776
  RTE_DefaultConfig.text_update = "Update";
797
777
 
798
- RTE_DefaultConfig.text_find = "Find&Replace";
778
+ RTE_DefaultConfig.text_find = "Find and Replace";
799
779
  RTE_DefaultConfig.text_findwhat = "Find";
800
780
  RTE_DefaultConfig.text_replacewith = "Replace";
801
781
 
@@ -854,9 +834,9 @@ RTE_DefaultConfig.text_pmoveup = "Move Up";
854
834
  RTE_DefaultConfig.text_pmovedown = "Move Down";
855
835
  RTE_DefaultConfig.text_pduplicate = "Duplicate";
856
836
  RTE_DefaultConfig.text_pdelete = "Delete";
857
- RTE_DefaultConfig.text_pmore = "More..";
837
+ RTE_DefaultConfig.text_pmore = "More\u2026";
858
838
 
859
- RTE_DefaultConfig.text_togglemore = "More..";
839
+ RTE_DefaultConfig.text_togglemore = "More\u2026";
860
840
  RTE_DefaultConfig.text_toggleborder = "Toggle Border";
861
841
 
862
842
  RTE_DefaultConfig.text_cut = "Cut";
@@ -877,7 +857,7 @@ RTE_DefaultConfig.text_insertbookmarktitle = "Insert bookmark";
877
857
  RTE_DefaultConfig.text_insertbookmarklabel = "Link URL";
878
858
  RTE_DefaultConfig.text_spellcheck = "Spell Check";
879
859
  RTE_DefaultConfig.text_spellchecktitle = "Spelling";
880
- RTE_DefaultConfig.text_spellchecknative = "Browser spell-check is on — misspellings are underlined as you type.";
860
+ RTE_DefaultConfig.text_spellchecknative = "Browser spell-check is on \u2014 misspellings are underlined as you type.";
881
861
  RTE_DefaultConfig.text_spellcheckclean = "No spelling issues found.";
882
862
  RTE_DefaultConfig.text_insertdatechip = "Date Chip";
883
863
  RTE_DefaultConfig.text_insertchip = "Smart Chip";
@@ -954,14 +934,80 @@ RTE_DefaultConfig.translation = RTE_DefaultConfig.translation || {
954
934
  RTE_DefaultConfig.plugin_insertcomment = function () {
955
935
  var obj = this;
956
936
  obj.PluginName = "InsertComment";
937
+ // Wraps each block's part of range in its own make() element, and returns them. The selection is
938
+ // cut into runs of text that share a block; a run starting or ending at the edge of an inline
939
+ // element (<b>) takes the whole element, so no empty <b></b> is left behind; text inside a
940
+ // non-editable element (a chip) moves with its run; a whitespace-only run (the line break between
941
+ // two blocks) gets nothing.
942
+ function markEachBlock(doc, range, make) {
943
+ var BLOCK = /^(P|DIV|LI|UL|OL|DL|DT|DD|TABLE|TBODY|THEAD|TFOOT|TR|TD|TH|CAPTION|H[1-6]|PRE|BLOCKQUOTE|ADDRESS|FIGURE|FIGCAPTION|SECTION|ARTICLE|ASIDE|HEADER|FOOTER|NAV|MAIN|DETAILS|SUMMARY|FORM|FIELDSET|LEGEND|HR|BODY)$/;
944
+ function blockOf(node) {
945
+ for (var n = node; n; n = n.parentNode)
946
+ if (n.nodeType === 1 && BLOCK.test(n.nodeName)) return n;
947
+ return null;
948
+ }
949
+ var root = range.commonAncestorContainer;
950
+ if (root.nodeType !== 1) root = root.parentNode;
951
+ var runs = [], cur = null;
952
+ var w = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, {
953
+ acceptNode: function (n) { return n.nodeType === 1 && n.getAttribute("contenteditable") === "false" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT; }
954
+ });
955
+ for (var n = w.nextNode(); n; n = w.nextNode()) {
956
+ if (n.nodeType === 1) {
957
+ if (BLOCK.test(n.nodeName)) cur = null;
958
+ continue;
959
+ }
960
+ if (!range.intersectsNode(n)) continue;
961
+ var a = n === range.startContainer ? range.startOffset : 0, b = n === range.endContainer ? range.endOffset : n.data.length;
962
+ if (b <= a) continue;
963
+ var blk = blockOf(n);
964
+ if (!cur || cur.block !== blk) {
965
+ cur = { block: blk, sn: n, so: a, text: "" };
966
+ runs.push(cur);
967
+ }
968
+ cur.en = n;
969
+ cur.eo = b;
970
+ cur.text += n.data.substring(a, b);
971
+ }
972
+ var out = [];
973
+ // from the last run back: wrapping one never moves the text of a run still to come
974
+ for (var i = runs.length - 1; i >= 0; i--) {
975
+ var run = runs[i];
976
+ if (!/\S/.test(run.text)) continue;
977
+ var sub = doc.createRange();
978
+ sub.setStart(run.sn, run.so);
979
+ sub.setEnd(run.en, run.eo);
980
+ var x = sub.startContainer;
981
+ if (x.nodeType === 3 && sub.startOffset === 0) {
982
+ while (x.parentNode && x.parentNode !== run.block && !x.previousSibling && !BLOCK.test(x.parentNode.nodeName)) x = x.parentNode;
983
+ sub.setStartBefore(x);
984
+ }
985
+ x = sub.endContainer;
986
+ if (x.nodeType === 3 && sub.endOffset === x.data.length) {
987
+ while (x.parentNode && x.parentNode !== run.block && !x.nextSibling && !BLOCK.test(x.parentNode.nodeName)) x = x.parentNode;
988
+ sub.setEndAfter(x);
989
+ }
990
+ var el = make();
991
+ el.appendChild(sub.extractContents());
992
+ sub.insertNode(el);
993
+ out.unshift(el);
994
+ }
995
+ return out;
996
+ }
957
997
  obj.InitConfig = function () {};
958
998
  obj.InitEditor = function (editor) {
959
999
  editor.attachEvent("exec_command_insertcomment", function (state) {
960
1000
  var edDoc = editor.document;
961
1001
  var sel = edDoc.defaultView.getSelection();
962
1002
  var selectedText = sel && !sel.isCollapsed ? sel.toString() : "";
963
-
964
- var dialoginner = editor.createDialog("Add Comment", "rte-dialog-insertcomment");
1003
+ // 2026-10-02 The range is kept from the moment the dialog opens: read on submit, after
1004
+ // focus had moved into the dialog, it could be collapsed (an EMPTY mark landed at the
1005
+ // start of the document in Firefox). tools/comments/comment-dialog.mjs
1006
+ var savedRange = selectedText ? sel.getRangeAt(0).cloneRange() : null;
1007
+
1008
+ // 2026-10-18 the dialog's words read config.text_ keys (English as the fallback).
1009
+ // tools/i18n/core-dialogs-language.mjs
1010
+ var dialoginner = editor.createDialog((editor.config || {}).text_commentapply || "Add Comment", "rte-dialog-insertcomment");
965
1011
  dialoginner.style.padding = "14px";
966
1012
  if (selectedText) {
967
1013
  var preview = document.createElement("div");
@@ -970,30 +1016,39 @@ RTE_DefaultConfig.plugin_insertcomment = function () {
970
1016
  dialoginner.appendChild(preview);
971
1017
  }
972
1018
  var label = document.createElement("label");
973
- label.innerText = "Comment:";
1019
+ label.innerText = (editor.config || {}).text_dlg_commentlabel || "Comment:";
974
1020
  label.className = "rte-comment-dialog-label";
975
1021
  dialoginner.appendChild(label);
976
1022
  var textarea = document.createElement("textarea");
977
1023
  textarea.id = "rte-comment-input-" + new Date().getTime();
978
1024
  label.htmlFor = textarea.id;
979
- textarea.placeholder = "Type your comment...";
1025
+ textarea.placeholder = (editor.config || {}).text_commentplaceholder || "Type your comment...";
980
1026
  textarea.className = "rte-comment-dialog-input";
981
1027
  dialoginner.appendChild(textarea);
982
1028
  var btnRow = document.createElement("div");
983
1029
  btnRow.className = "rte-comment-dialog-actions";
984
1030
  var insertBtn = document.createElement("button");
985
- insertBtn.innerText = "Add Comment";
1031
+ insertBtn.innerText = (editor.config || {}).text_commentapply || "Add Comment";
986
1032
  insertBtn.type = "button";
987
1033
  insertBtn.className = "rte-comment-dialog-submit";
1034
+ // 2026-10-15 unstyled, the buttons were 21px tall: WCAG 2.5.8 asks for 24px targets. tools/a11y/dialog-axe.mjs
1035
+ insertBtn.style.cssText = "min-height:28px;padding:4px 14px;margin-right:6px;cursor:pointer;";
988
1036
  insertBtn.onclick = function () {
989
1037
  var comment = textarea.value.replace(/^\s+|\s+$/g, "");
990
- if (comment && selectedText) {
991
- var range = sel.getRangeAt(0);
992
- var mark = edDoc.createElement("mark");
993
- mark.style.cssText = "background:#fff9c4;border-bottom:2px solid #f9a825;cursor:pointer;position:relative;";
994
- mark.setAttribute("title", comment);
995
- mark.setAttribute("data-comment", comment);
996
- try { range.surroundContents(mark); } catch (e) {}
1038
+ if (comment && savedRange) {
1039
+ // 2026-10-02 range.surroundContents THREW whenever the selection crossed an
1040
+ // element edge - starting inside a bold word, two paragraphs, two list items - and
1041
+ // the throw was swallowed: the dialog closed and the typed comment was gone. Each
1042
+ // block's part of the selection now gets its own mark (a mark around blocks would
1043
+ // split them, as links and the comments plugin did).
1044
+ if (editor.commitBookmark) editor.commitBookmark();
1045
+ markEachBlock(edDoc, savedRange, function () {
1046
+ var mark = edDoc.createElement("mark");
1047
+ mark.style.cssText = "background:#fff9c4;border-bottom:2px solid #f9a825;cursor:pointer;position:relative;";
1048
+ mark.setAttribute("title", comment);
1049
+ mark.setAttribute("data-comment", comment);
1050
+ return mark;
1051
+ });
997
1052
  } else if (comment) {
998
1053
  var safeComment = comment.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
999
1054
  editor.insertHTML('<span class="rte-comment-marker" contenteditable="false" style="background:#fff9c4;border:1px solid #f9a825;border-radius:3px;padding:1px 6px;font-size:11px;color:#f57f17;cursor:pointer;" title="' + safeComment + '">&#128172; Comment</span>');
@@ -1002,9 +1057,10 @@ RTE_DefaultConfig.plugin_insertcomment = function () {
1002
1057
  editor.focus();
1003
1058
  };
1004
1059
  var cancelBtn = document.createElement("button");
1005
- cancelBtn.innerText = "Cancel";
1060
+ cancelBtn.innerText = (editor.config || {}).text_cancel || "Cancel";
1006
1061
  cancelBtn.type = "button";
1007
1062
  cancelBtn.className = "rte-comment-dialog-cancel";
1063
+ cancelBtn.style.cssText = "min-height:28px;padding:4px 14px;cursor:pointer;";
1008
1064
  cancelBtn.onclick = function () {
1009
1065
  dialoginner.close();
1010
1066
  editor.focus();
@@ -1030,16 +1086,18 @@ RTE_DefaultConfig.plugin_insertcomment = function () {
1030
1086
  };
1031
1087
  };
1032
1088
 
1033
- // === Apply keyboard shortcut hints to tooltips (must run AFTER text_* defaults). ===
1089
+ // === Keyboard shortcut hints for the toolbar tooltips (config.keyboardShortcutHints). ===
1034
1090
  (function () {
1035
1091
  if (!RTE_DefaultConfig.showKeyboardShortcutsInTooltips) return;
1036
1092
  var isMac = typeof navigator !== "undefined" && /Mac|iPhone|iPad|iPod/i.test(navigator.platform || navigator.userAgent || "");
1037
- var mod = isMac ? "⌘" : "Ctrl";
1038
- var shift = isMac ? "⇧" : "Shift";
1093
+ var mod = isMac ? "\u2318" : "Ctrl";
1094
+ var shift = isMac ? "\u21e7" : "Shift";
1095
+ // 2026-10-18 a map read by rte.js for the toolbar TOOLTIPS; the label strings are no longer rewritten, so a
1096
+ // dialog title or a button's accessible name never carries " (Ctrl+K)" and a language pack keeps the hints.
1097
+ // tools/i18n/shortcut-hints.mjs
1098
+ var hints = RTE_DefaultConfig.keyboardShortcutHints = RTE_DefaultConfig.keyboardShortcutHints || {};
1039
1099
  function add(cmd, key) {
1040
- var base = RTE_DefaultConfig["text_" + cmd];
1041
- if (!base || / \(.+\)$/.test(base)) return;
1042
- RTE_DefaultConfig["text_" + cmd] = base + " (" + key + ")";
1100
+ if (!hints[cmd]) hints[cmd] = key;
1043
1101
  }
1044
1102
  add("bold", mod + "+B");
1045
1103
  add("italic", mod + "+I");
@@ -116,15 +116,18 @@
116
116
  }, 16);
117
117
  }
118
118
  this._emitUpdate = emitUpdate;
119
+ this._editorListeners = [];
119
120
 
120
121
  ["selectionchange", "change", "focus", "blur"].forEach(function (evt) {
121
122
  try {
122
- self.editor.attachEvent(evt, function (a, b) {
123
+ var listener = function (a, b) {
123
124
  self._emit(evt, a, b);
124
125
  if (evt === "selectionchange" || evt === "change") {
125
126
  emitUpdate();
126
127
  }
127
- });
128
+ };
129
+ self.editor.attachEvent(evt, listener);
130
+ self._editorListeners.push([evt, listener]);
128
131
  } catch (e) { /* event surface differs by build; ignore */ }
129
132
  });
130
133
 
@@ -150,6 +153,7 @@
150
153
  };
151
154
 
152
155
  HeadlessController.prototype.on = function (name, cb) {
156
+ if (this._destroyed) return this;
153
157
  if (typeof cb === "function") {
154
158
  (this._handlers[name] = this._handlers[name] || []).push(cb);
155
159
  }
@@ -165,12 +169,13 @@
165
169
  };
166
170
 
167
171
  HeadlessController.prototype._emit = function (name) {
172
+ if (this._destroyed) return;
168
173
  var list = this._handlers[name];
169
174
  if (!list || !list.length) {
170
175
  return;
171
176
  }
172
177
  var args = Array.prototype.slice.call(arguments, 1);
173
- for (var i = 0; i < list.length; i++) {
178
+ for (var i = 0; i < list.length && !this._destroyed; i++) {
174
179
  try {
175
180
  list[i].apply(this, args);
176
181
  } catch (e) {
@@ -303,8 +308,15 @@
303
308
  };
304
309
 
305
310
  HeadlessController.prototype.destroy = function () {
311
+ if (this._destroyed) return this;
306
312
  this._destroyed = true;
307
313
  this._handlers = {};
314
+ if (this._editorListeners) {
315
+ for (var ei = 0; ei < this._editorListeners.length; ei++) {
316
+ try { this.editor.detachEvent(this._editorListeners[ei][0], this._editorListeners[ei][1]); } catch (e) {}
317
+ }
318
+ this._editorListeners = null;
319
+ }
308
320
  // Drop format subscriptions here too: _destroyed alone only silences the
309
321
  // callback, it does not stop the core from evaluating the commands on
310
322
  // every toolbar refresh for a controller nobody is using any more.
@@ -321,7 +333,10 @@
321
333
  }
322
334
  this._docListeners = null;
323
335
  this._boundDoc = null;
324
- try { if (this.editor.prepareDestroy) { this.editor.prepareDestroy(); } } catch (e) { /* ignore */ }
336
+ try {
337
+ if (typeof this.editor.destroy === "function") this.editor.destroy();
338
+ else if (typeof this.editor.prepareDestroy === "function") this.editor.prepareDestroy();
339
+ } catch (e) { /* ignore */ }
325
340
  return this;
326
341
  };
327
342
 
@@ -279,7 +279,9 @@
279
279
 
280
280
  // The old instance leaves its chrome in the host element; a fresh editor
281
281
  // built on top of that would stack two toolbars.
282
- try { if (this._host) this._host.innerHTML = ""; } catch (e) {}
282
+ // textContent, not innerHTML: under Trusted Types even innerHTML = "" is refused (a CSP violation the catch
283
+ // hid, so the host was never cleared here). tools/csp/trusted-types-companions.mjs
284
+ try { if (this._host) this._host.textContent = ""; } catch (e) {}
283
285
 
284
286
  try {
285
287
  this._start();