@richscripts/richtexteditor 2.14.1 → 2.15.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 (237) hide show
  1. package/README.md +173 -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 +33 -54
  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 +58 -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 +7001 -1078
  17. package/richtexteditor/all_plugins.min.js +62 -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 +1017 -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 +1023 -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 +1023 -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 +1023 -2
  60. package/richtexteditor/lang/rte-lang-ja.js +1023 -2
  61. package/richtexteditor/lang/rte-lang-kn.js +1 -0
  62. package/richtexteditor/lang/rte-lang-ko.js +1023 -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 +1023 -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 +1023 -2
  78. package/richtexteditor/lang/rte-lang-pt.js +1023 -2
  79. package/richtexteditor/lang/rte-lang-ro.js +1 -0
  80. package/richtexteditor/lang/rte-lang-ru.js +1023 -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 +1017 -2
  101. package/richtexteditor/lang/rte-lang-zh-Hant.js +1017 -2
  102. package/richtexteditor/plugins/a11yenhance.js +2 -1
  103. package/richtexteditor/plugins/a11yenhance.min.js +1 -1
  104. package/richtexteditor/plugins/accessibilitychecker.js +252 -60
  105. package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
  106. package/richtexteditor/plugins/aitoolkit.js +966 -303
  107. package/richtexteditor/plugins/aitoolkit.min.js +7 -7
  108. package/richtexteditor/plugins/all_plugins.js +7001 -1078
  109. package/richtexteditor/plugins/all_plugins.min.js +62 -59
  110. package/richtexteditor/plugins/autocorrect.js +66 -31
  111. package/richtexteditor/plugins/autocorrect.min.js +1 -1
  112. package/richtexteditor/plugins/autoembed.js +17 -0
  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 +458 -59
  123. package/richtexteditor/plugins/comments.min.js +4 -3
  124. package/richtexteditor/plugins/contentminimap.js +25 -6
  125. package/richtexteditor/plugins/contentminimap.min.js +1 -1
  126. package/richtexteditor/plugins/crossreference.js +1 -1
  127. package/richtexteditor/plugins/crossreference.min.js +1 -1
  128. package/richtexteditor/plugins/dialoga11y.js +26 -4
  129. package/richtexteditor/plugins/dialoga11y.min.js +1 -1
  130. package/richtexteditor/plugins/dictation.js +1 -1
  131. package/richtexteditor/plugins/documentimport.js +220 -51
  132. package/richtexteditor/plugins/documentimport.min.js +4 -4
  133. package/richtexteditor/plugins/documentoutline.js +47 -8
  134. package/richtexteditor/plugins/documentoutline.min.js +1 -1
  135. package/richtexteditor/plugins/docxexport.js +204 -32
  136. package/richtexteditor/plugins/docxexport.min.js +2 -2
  137. package/richtexteditor/plugins/draghandle.js +267 -3
  138. package/richtexteditor/plugins/draghandle.min.js +1 -1
  139. package/richtexteditor/plugins/emailtoolkit.js +28 -14
  140. package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
  141. package/richtexteditor/plugins/emojiautocomplete.js +110 -56
  142. package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
  143. package/richtexteditor/plugins/footnotes.js +65 -11
  144. package/richtexteditor/plugins/footnotes.min.js +1 -1
  145. package/richtexteditor/plugins/formatpainter.js +10 -0
  146. package/richtexteditor/plugins/formatpainter.min.js +1 -1
  147. package/richtexteditor/plugins/formattingmarks.js +6 -6
  148. package/richtexteditor/plugins/formattingmarks.min.js +1 -1
  149. package/richtexteditor/plugins/gapcursor.js +60 -0
  150. package/richtexteditor/plugins/gapcursor.min.js +1 -1
  151. package/richtexteditor/plugins/ghostcomplete.js +1 -1
  152. package/richtexteditor/plugins/html2pdf.js +1 -1
  153. package/richtexteditor/plugins/html2pdf.min.js +1 -1
  154. package/richtexteditor/plugins/imageeditor.js +12 -6
  155. package/richtexteditor/plugins/imageeditor.min.js +1 -1
  156. package/richtexteditor/plugins/importstyles.js +1 -1
  157. package/richtexteditor/plugins/inlinecode.js +25 -0
  158. package/richtexteditor/plugins/inlinecode.min.js +1 -1
  159. package/richtexteditor/plugins/insertcode.js +21 -3
  160. package/richtexteditor/plugins/insertcode.min.js +4 -4
  161. package/richtexteditor/plugins/insertemoji.js +112 -15
  162. package/richtexteditor/plugins/insertemoji.min.js +1 -1
  163. package/richtexteditor/plugins/insertgallery.js +42 -36
  164. package/richtexteditor/plugins/insertgallery.min.js +1 -1
  165. package/richtexteditor/plugins/inserttemplate.js +33 -17
  166. package/richtexteditor/plugins/inserttemplate.min.js +1 -1
  167. package/richtexteditor/plugins/keyboarda11y.js +66 -3
  168. package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
  169. package/richtexteditor/plugins/layouttable.js +20 -0
  170. package/richtexteditor/plugins/layouttable.min.js +1 -1
  171. package/richtexteditor/plugins/linkchecker.js +29 -9
  172. package/richtexteditor/plugins/linkchecker.min.js +1 -1
  173. package/richtexteditor/plugins/localdraft.js +51 -7
  174. package/richtexteditor/plugins/localdraft.min.js +1 -1
  175. package/richtexteditor/plugins/markdownexport.js +159 -0
  176. package/richtexteditor/plugins/markdownexport.min.js +2 -2
  177. package/richtexteditor/plugins/mathdialog.js +11 -5
  178. package/richtexteditor/plugins/mathdialog.min.js +1 -1
  179. package/richtexteditor/plugins/mentionplugin.js +88 -25
  180. package/richtexteditor/plugins/mentionplugin.min.js +1 -1
  181. package/richtexteditor/plugins/mermaiddiagram.js +9 -3
  182. package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
  183. package/richtexteditor/plugins/multilevellist.js +4 -0
  184. package/richtexteditor/plugins/multilevellist.min.js +1 -1
  185. package/richtexteditor/plugins/pdfexport.js +235 -35
  186. package/richtexteditor/plugins/pdfexport.min.js +12 -12
  187. package/richtexteditor/plugins/permanentpen.js +3 -3
  188. package/richtexteditor/plugins/permanentpen.min.js +1 -1
  189. package/richtexteditor/plugins/readabilitystats.js +25 -11
  190. package/richtexteditor/plugins/readabilitystats.min.js +1 -1
  191. package/richtexteditor/plugins/readaloud.js +29 -15
  192. package/richtexteditor/plugins/readaloud.min.js +1 -1
  193. package/richtexteditor/plugins/restrictedediting.js +166 -0
  194. package/richtexteditor/plugins/restrictedediting.min.js +2 -2
  195. package/richtexteditor/plugins/revisionhistory.js +169 -37
  196. package/richtexteditor/plugins/revisionhistory.min.js +3 -3
  197. package/richtexteditor/plugins/rubyannotation.js +19 -9
  198. package/richtexteditor/plugins/rubyannotation.min.js +1 -1
  199. package/richtexteditor/plugins/sanitizer.js +62 -3
  200. package/richtexteditor/plugins/sanitizer.min.js +1 -1
  201. package/richtexteditor/plugins/slashcommand.js +30 -16
  202. package/richtexteditor/plugins/slashcommand.min.js +2 -2
  203. package/richtexteditor/plugins/smartchips.js +36 -22
  204. package/richtexteditor/plugins/smartchips.min.js +2 -2
  205. package/richtexteditor/plugins/spellcheck.js +17 -8
  206. package/richtexteditor/plugins/spellcheck.min.js +2 -2
  207. package/richtexteditor/plugins/stateinspector.js +19 -9
  208. package/richtexteditor/plugins/stateinspector.min.js +2 -2
  209. package/richtexteditor/plugins/structured-content-bridge.js +56 -24
  210. package/richtexteditor/plugins/structured-content-bridge.min.js +7 -7
  211. package/richtexteditor/plugins/tableofcontents.js +5 -2
  212. package/richtexteditor/plugins/tableofcontents.min.js +1 -1
  213. package/richtexteditor/plugins/tabletools.js +388 -3
  214. package/richtexteditor/plugins/tabletools.min.js +1 -1
  215. package/richtexteditor/plugins/textdirection.js +18 -5
  216. package/richtexteditor/plugins/textdirection.min.js +1 -1
  217. package/richtexteditor/plugins/textpartlanguage.js +161 -32
  218. package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
  219. package/richtexteditor/plugins/todolist.js +90 -6
  220. package/richtexteditor/plugins/todolist.min.js +1 -1
  221. package/richtexteditor/plugins/trackedchanges.js +1812 -25
  222. package/richtexteditor/plugins/trackedchanges.min.js +5 -3
  223. package/richtexteditor/plugins/wordexport.js +2 -2
  224. package/richtexteditor/plugins/yjscollab.js +9 -4
  225. package/richtexteditor/plugins/yjscollab.min.js +1 -1
  226. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
  227. package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
  228. package/richtexteditor/rte-config.js +130 -50
  229. package/richtexteditor/rte-headless.js +19 -4
  230. package/richtexteditor/rte-webcomponent.js +89 -16
  231. package/richtexteditor/rte.js +132 -52
  232. package/scripts/build-angular.mjs +97 -0
  233. package/svelte/index.d.ts +2 -1
  234. package/svelte/index.js +80 -9
  235. package/vue/index.js +130 -34
  236. package/angular/component.ts +0 -297
  237. package/angular/index.ts +0 -24
@@ -24,6 +24,13 @@ function RTE_Plugin_EmailToolkit() {
24
24
  obj.PluginName = "EmailToolkit";
25
25
 
26
26
  obj.InitConfig = function (argconfig) { config = argconfig; };
27
+ // 2026-10-18 the words read config.text_ keys (language packs), English as the fallback; {0} / {1} fill a template.
28
+ // tools/i18n/plugin-bars-language.mjs
29
+ function pText(key, fallback) {
30
+ var s = (config && config["text_" + key]) || fallback;
31
+ for (var i = 2; i < arguments.length; i++) s = s.split("{" + (i - 2) + "}").join(String(arguments[i]));
32
+ return s;
33
+ }
27
34
 
28
35
  obj.InitEditor = function (argeditor) {
29
36
  editor = argeditor;
@@ -113,12 +120,19 @@ function RTE_Plugin_EmailToolkit() {
113
120
  var editable = editor.getEditable();
114
121
  var editdoc = editor.getDocument();
115
122
  var win = (editdoc && editdoc.defaultView) || window;
116
- var container = editdoc.createElement("div");
117
- for (var i = 0; i < editable.childNodes.length; i++) {
118
- var c = cloneInlined(editable.childNodes[i], win);
119
- if (c) container.appendChild(c);
120
- }
121
- var inner = container.innerHTML;
123
+ // 2026-10-03 Built inside editor.runInSavedView: the inlining needs the LIVE DOM (computed
124
+ // styles), and the live DOM carries other plugins' on-screen state - with merge-field preview
125
+ // on, the e-mail got the SAMPLE data ("Dear Ada Lovelace") instead of the template's fields.
126
+ // tools/markdown/plugin-chrome-exports.mjs
127
+ var build = function () {
128
+ var container = editdoc.createElement("div");
129
+ for (var i = 0; i < editable.childNodes.length; i++) {
130
+ var c = cloneInlined(editable.childNodes[i], win);
131
+ if (c) container.appendChild(c);
132
+ }
133
+ return container.innerHTML;
134
+ };
135
+ var inner = typeof editor.runInSavedView === "function" ? editor.runInSavedView(build) : build();
122
136
  var wrap = options.wrap !== false;
123
137
  if (!wrap) return inner;
124
138
  var width = options.width || config.emailWrapWidth || 640;
@@ -136,30 +150,30 @@ function RTE_Plugin_EmailToolkit() {
136
150
  var close = typeof dlg.close === "function" ? function () { dlg.close(); } : function () { editor.closeCurrentPopup(); };
137
151
  var wrap = append(dlg, "div", "", "rte-email-export");
138
152
  append(wrap, "div", "", "rte-email-export-copy").innerText =
139
- "Inline-styled HTML for email clients (class/style blocks resolved to inline styles). Copy and paste into your email tool.";
153
+ pText("em_copy", "Inline-styled HTML for email clients (class/style blocks resolved to inline styles). Copy and paste into your email tool.");
140
154
  var ta = append(wrap, "textarea", "", "rte-email-export-code");
141
155
  ta.value = html;
142
156
  ta.readOnly = true;
143
- ta.setAttribute("aria-label", "Inline-styled email HTML");
157
+ ta.setAttribute("aria-label", pText("em_code", "Inline-styled email HTML"));
144
158
  var status = append(wrap, "div", "", "rte-email-export-status");
145
159
  status.setAttribute("role", "status");
146
160
  status.setAttribute("aria-live", "polite");
147
- status.textContent = "Ready to copy " + html.length + " characters.";
161
+ status.textContent = pText("em_ready", "Ready to copy {0} characters.", html.length);
148
162
  var foot = append(wrap, "div", "", "rte-email-export-footer");
149
163
  var copyBtn = append(foot, "button", "", "rte-email-export-button rte-email-export-button-primary");
150
- copyBtn.type = "button"; copyBtn.textContent = "Copy";
164
+ copyBtn.type = "button"; copyBtn.textContent = pText("copy", "Copy");
151
165
  copyBtn.onclick = function () {
152
166
  ta.select();
153
167
  try {
154
168
  if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(ta.value); }
155
169
  else { dlg.ownerDocument.execCommand("copy"); }
156
- copyBtn.textContent = "Copied";
157
- status.textContent = "Copied email HTML to the clipboard.";
158
- setTimeout(function () { copyBtn.textContent = "Copy"; status.textContent = "Ready to copy " + html.length + " characters."; }, 1400);
170
+ copyBtn.textContent = pText("em_copied", "Copied");
171
+ status.textContent = pText("em_copieddone", "Copied email HTML to the clipboard.");
172
+ setTimeout(function () { copyBtn.textContent = pText("copy", "Copy"); status.textContent = pText("em_ready", "Ready to copy {0} characters.", html.length); }, 1400);
159
173
  } catch (e) { /* ignore */ }
160
174
  };
161
175
  var closeBtn = append(foot, "button", "", "rte-email-export-button");
162
- closeBtn.type = "button"; closeBtn.textContent = "Close"; closeBtn.onclick = close;
176
+ closeBtn.type = "button"; closeBtn.textContent = pText("close", "Close"); closeBtn.onclick = close;
163
177
  setTimeout(function () { ta.focus(); }, 0);
164
178
  };
165
179
  }
@@ -1 +1 @@
1
- window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_emailtoolkit=RTE_Plugin_EmailToolkit,RTE_DefaultConfig.emailInlineProperties=RTE_DefaultConfig.emailInlineProperties||["color","background-color","font-family","font-size","font-weight","font-style","text-decoration","text-align","line-height","margin-top","margin-bottom","margin-left","margin-right","padding-top","padding-bottom","padding-left","padding-right","border","border-radius","vertical-align","list-style-type","width"],RTE_DefaultConfig.emailWrapWidth=RTE_DefaultConfig.emailWrapWidth||640;function RTE_Plugin_EmailToolkit(){var f=this,m,d;f.PluginName="EmailToolkit",f.InitConfig=function(e){m=e},f.InitEditor=function(e){d=e,d.getEmailHTML=function(n){return f.BuildEmailHTML(n||{})},d.attachEvent("exec_command_emailexport",function(n){n.returnValue=!0,f.OpenEmailExport()})};function c(e,n,t,a){var i=e.ownerDocument.createElement(n);return t&&(i.style.cssText=t),a&&(i.className=a),e.appendChild(i),i}function g(e,n){if(!n)return!0;var t=n.trim().toLowerCase();return t==="normal"||t==="auto"||t==="none"||t==="0px"||t==="0"?!(e==="list-style-type"&&t==="none"):e==="background-color"&&(t==="rgba(0, 0, 0, 0)"||t==="transparent")?!0:e==="color"&&t==="rgb(0, 0, 0)"?!1:e==="text-decoration"&&t.indexOf("none")===0||e==="border"&&(t.indexOf("0px")===0||t.indexOf("none")!==-1&&t.indexOf("0px")!==-1)||e==="vertical-align"&&t==="baseline"||e==="font-weight"&&t==="400"||e==="font-style"&&t==="normal"||e==="text-align"&&(t==="start"||t==="left")}var v={UL:1,OL:1,LI:1};function x(e,n){for(var t=n.getComputedStyle(e),a=m.emailInlineProperties||[],i=[],o=0;o<a.length;o++){var l=a[o],r;try{r=t.getPropertyValue(l)}catch{r=""}r==null||r===""||g(l,r)||l==="width"&&e.nodeName!=="TABLE"&&e.nodeName!=="IMG"&&e.nodeName!=="TD"&&e.nodeName!=="TH"||l==="list-style-type"&&!v[e.nodeName]||i.push(l+":"+r)}return i.join(";")}var y={SCRIPT:1,STYLE:1,LINK:1,META:1};function p(e,n){if(e.nodeType===3)return e.ownerDocument.createTextNode(e.data);if(e.nodeType!==1||y[e.nodeName])return null;for(var t=e.ownerDocument,a=t.createElement(e.nodeName),i=0;i<e.attributes.length;i++){var o=e.attributes[i],l=o.name.toLowerCase();l==="class"||l==="id"||l==="contenteditable"||l==="style"||l.indexOf("data-rte-")===0||a.setAttribute(o.name,o.value)}var r=x(e,n);r&&a.setAttribute("style",r);for(var u=0;u<e.childNodes.length;u++){var s=p(e.childNodes[u],n);s&&a.appendChild(s)}return a}f.BuildEmailHTML=function(e){for(var n=d.getEditable(),t=d.getDocument(),a=t&&t.defaultView||window,i=t.createElement("div"),o=0;o<n.childNodes.length;o++){var l=p(n.childNodes[o],a);l&&i.appendChild(l)}var r=i.innerHTML,u=e.wrap!==!1;if(!u)return r;var s=e.width||m.emailWrapWidth||640;return'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse"><tr><td align="center" style="padding:0"><table role="presentation" width="'+s+'" cellpadding="0" cellspacing="0" style="border-collapse:collapse;max-width:'+s+'px;width:100%"><tr><td style="padding:16px;font-family:Arial,Helvetica,sans-serif">'+r+"</td></tr></table></td></tr></table>"},f.OpenEmailExport=function(){var e=f.BuildEmailHTML({}),n=d.createDialog(d.getLangText&&d.getLangText("emailexporttitle")||"Export email HTML","rte-dialog-email"),t=typeof n.close=="function"?function(){n.close()}:function(){d.closeCurrentPopup()},a=c(n,"div","","rte-email-export");c(a,"div","","rte-email-export-copy").innerText="Inline-styled HTML for email clients (class/style blocks resolved to inline styles). Copy and paste into your email tool.";var i=c(a,"textarea","","rte-email-export-code");i.value=e,i.readOnly=!0,i.setAttribute("aria-label","Inline-styled email HTML");var o=c(a,"div","","rte-email-export-status");o.setAttribute("role","status"),o.setAttribute("aria-live","polite"),o.textContent="Ready to copy "+e.length+" characters.";var l=c(a,"div","","rte-email-export-footer"),r=c(l,"button","","rte-email-export-button rte-email-export-button-primary");r.type="button",r.textContent="Copy",r.onclick=function(){i.select();try{navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(i.value):n.ownerDocument.execCommand("copy"),r.textContent="Copied",o.textContent="Copied email HTML to the clipboard.",setTimeout(function(){r.textContent="Copy",o.textContent="Ready to copy "+e.length+" characters."},1400)}catch{}};var u=c(l,"button","","rte-email-export-button");u.type="button",u.textContent="Close",u.onclick=t,setTimeout(function(){i.focus()},0)}}
1
+ window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_emailtoolkit=RTE_Plugin_EmailToolkit,RTE_DefaultConfig.emailInlineProperties=RTE_DefaultConfig.emailInlineProperties||["color","background-color","font-family","font-size","font-weight","font-style","text-decoration","text-align","line-height","margin-top","margin-bottom","margin-left","margin-right","padding-top","padding-bottom","padding-left","padding-right","border","border-radius","vertical-align","list-style-type","width"],RTE_DefaultConfig.emailWrapWidth=RTE_DefaultConfig.emailWrapWidth||640;function RTE_Plugin_EmailToolkit(){var f=this,p,u;f.PluginName="EmailToolkit",f.InitConfig=function(e){p=e};function c(e,n){for(var t=p&&p["text_"+e]||n,i=2;i<arguments.length;i++)t=t.split("{"+(i-2)+"}").join(String(arguments[i]));return t}f.InitEditor=function(e){u=e,u.getEmailHTML=function(n){return f.BuildEmailHTML(n||{})},u.attachEvent("exec_command_emailexport",function(n){n.returnValue=!0,f.OpenEmailExport()})};function s(e,n,t,i){var r=e.ownerDocument.createElement(n);return t&&(r.style.cssText=t),i&&(r.className=i),e.appendChild(r),r}function y(e,n){if(!n)return!0;var t=n.trim().toLowerCase();return t==="normal"||t==="auto"||t==="none"||t==="0px"||t==="0"?!(e==="list-style-type"&&t==="none"):e==="background-color"&&(t==="rgba(0, 0, 0, 0)"||t==="transparent")?!0:e==="color"&&t==="rgb(0, 0, 0)"?!1:e==="text-decoration"&&t.indexOf("none")===0||e==="border"&&(t.indexOf("0px")===0||t.indexOf("none")!==-1&&t.indexOf("0px")!==-1)||e==="vertical-align"&&t==="baseline"||e==="font-weight"&&t==="400"||e==="font-style"&&t==="normal"||e==="text-align"&&(t==="start"||t==="left")}var x={UL:1,OL:1,LI:1};function b(e,n){for(var t=n.getComputedStyle(e),i=p.emailInlineProperties||[],r=[],o=0;o<i.length;o++){var l=i[o],a;try{a=t.getPropertyValue(l)}catch{a=""}a==null||a===""||y(l,a)||l==="width"&&e.nodeName!=="TABLE"&&e.nodeName!=="IMG"&&e.nodeName!=="TD"&&e.nodeName!=="TH"||l==="list-style-type"&&!x[e.nodeName]||r.push(l+":"+a)}return r.join(";")}var h={SCRIPT:1,STYLE:1,LINK:1,META:1};function g(e,n){if(e.nodeType===3)return e.ownerDocument.createTextNode(e.data);if(e.nodeType!==1||h[e.nodeName])return null;for(var t=e.ownerDocument,i=t.createElement(e.nodeName),r=0;r<e.attributes.length;r++){var o=e.attributes[r],l=o.name.toLowerCase();l==="class"||l==="id"||l==="contenteditable"||l==="style"||l.indexOf("data-rte-")===0||i.setAttribute(o.name,o.value)}var a=b(e,n);a&&i.setAttribute("style",a);for(var d=0;d<e.childNodes.length;d++){var m=g(e.childNodes[d],n);m&&i.appendChild(m)}return i}f.BuildEmailHTML=function(e){var n=u.getEditable(),t=u.getDocument(),i=t&&t.defaultView||window,r=function(){for(var d=t.createElement("div"),m=0;m<n.childNodes.length;m++){var v=g(n.childNodes[m],i);v&&d.appendChild(v)}return d.innerHTML},o=typeof u.runInSavedView=="function"?u.runInSavedView(r):r(),l=e.wrap!==!1;if(!l)return o;var a=e.width||p.emailWrapWidth||640;return'<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse"><tr><td align="center" style="padding:0"><table role="presentation" width="'+a+'" cellpadding="0" cellspacing="0" style="border-collapse:collapse;max-width:'+a+'px;width:100%"><tr><td style="padding:16px;font-family:Arial,Helvetica,sans-serif">'+o+"</td></tr></table></td></tr></table>"},f.OpenEmailExport=function(){var e=f.BuildEmailHTML({}),n=u.createDialog(u.getLangText&&u.getLangText("emailexporttitle")||"Export email HTML","rte-dialog-email"),t=typeof n.close=="function"?function(){n.close()}:function(){u.closeCurrentPopup()},i=s(n,"div","","rte-email-export");s(i,"div","","rte-email-export-copy").innerText=c("em_copy","Inline-styled HTML for email clients (class/style blocks resolved to inline styles). Copy and paste into your email tool.");var r=s(i,"textarea","","rte-email-export-code");r.value=e,r.readOnly=!0,r.setAttribute("aria-label",c("em_code","Inline-styled email HTML"));var o=s(i,"div","","rte-email-export-status");o.setAttribute("role","status"),o.setAttribute("aria-live","polite"),o.textContent=c("em_ready","Ready to copy {0} characters.",e.length);var l=s(i,"div","","rte-email-export-footer"),a=s(l,"button","","rte-email-export-button rte-email-export-button-primary");a.type="button",a.textContent=c("copy","Copy"),a.onclick=function(){r.select();try{navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(r.value):n.ownerDocument.execCommand("copy"),a.textContent=c("em_copied","Copied"),o.textContent=c("em_copieddone","Copied email HTML to the clipboard."),setTimeout(function(){a.textContent=c("copy","Copy"),o.textContent=c("em_ready","Ready to copy {0} characters.",e.length)},1400)}catch{}};var d=s(l,"button","","rte-email-export-button");d.type="button",d.textContent=c("close","Close"),d.onclick=t,setTimeout(function(){r.focus()},0)}}
@@ -12,61 +12,61 @@ RTE_DefaultConfig.plugin_emojiautocomplete = RTE_Plugin_EmojiAutocomplete;
12
12
  RTE_DefaultConfig.emojiAutocompleteEnabled = (typeof RTE_DefaultConfig.emojiAutocompleteEnabled === "undefined")
13
13
  ? true : RTE_DefaultConfig.emojiAutocompleteEnabled;
14
14
  RTE_DefaultConfig.emojiShortcodes = RTE_DefaultConfig.emojiShortcodes || {
15
- smile: "😄", smiley: "😃", grin: "😁", laughing: "😆", satisfied: "😆", joy: "😂", rofl: "🤣",
16
- sweat_smile: "😅", wink: "😉", blush: "😊", innocent: "😇", heart_eyes: "😍", star_struck: "🤩",
17
- kissing_heart: "😘", yum: "😋", stuck_out_tongue: "😛", zany: "🤪", thinking: "🤔", shush: "🤫",
18
- neutral_face: "😐", expressionless: "😑", no_mouth: "😶", smirk: "😏", unamused: "😒", roll_eyes: "🙄",
19
- grimacing: "😬", relieved: "😌", pensive: "😔", sleepy: "😪", sleeping: "😴", mask: "😷",
20
- face_with_thermometer: "🤒", nauseated_face: "🤢", sneezing_face: "🤧", dizzy_face: "😵",
21
- exploding_head: "🤯", cowboy: "🤠", sunglasses: "😎", nerd: "🤓", monocle: "🧐", confused: "😕",
22
- worried: "😟", slightly_frowning_face: "🙁", open_mouth: "😮", astonished: "😲", flushed: "😳",
23
- fearful: "😨", cold_sweat: "😰", cry: "😢", sob: "😭", scream: "😱", confounded: "😖", persevere: "😣",
24
- disappointed: "😞", sweat: "😓", weary: "😩", tired_face: "😫", triumph: "😤", rage: "😡", angry: "😠",
25
- cursing_face: "🤬", smiling_imp: "😈", imp: "👿", skull: "💀", poop: "💩", hankey: "💩", clown: "🤡",
26
- ghost: "👻", alien: "👽", robot: "🤖", heart_eyes_cat: "😻", see_no_evil: "🙈", hear_no_evil: "🙉",
27
- speak_no_evil: "🙊",
28
- heart: "❤️", orange_heart: "🧡", yellow_heart: "💛", green_heart: "💚", blue_heart: "💙",
29
- purple_heart: "💜", black_heart: "🖤", broken_heart: "💔", two_hearts: "💕", sparkling_heart: "💖",
30
- heartpulse: "💗", cupid: "💘", gift_heart: "💝", revolving_hearts: "💞", kiss: "💋",
31
- "+1": "👍", thumbsup: "👍", "-1": "👎", thumbsdown: "👎", ok_hand: "👌", punch: "👊", fist: "✊",
32
- facepunch: "👊", wave: "👋", raised_hand: "✋", raised_hands: "🙌", pray: "🙏", clap: "👏",
33
- muscle: "💪", point_up: "☝️", point_down: "👇", point_left: "👈", point_right: "👉", v: "✌️",
34
- crossed_fingers: "🤞", handshake: "🤝", writing_hand: "✍️", selfie: "🤳", nail_care: "💅", eyes: "👀",
35
- fire: "🔥", star: "⭐", star2: "🌟", sparkles: "✨", zap: "⚡", boom: "💥", collision: "💥",
36
- dizzy: "💫", sweat_drops: "💦", droplet: "💧", snowflake: "❄️", sunny: "☀️", cloud: "☁️",
37
- rainbow: "🌈", umbrella: "☔", ocean: "🌊", sun_with_face: "🌞", full_moon: "🌕", crescent_moon: "🌙",
38
- tada: "🎉", confetti_ball: "🎊", balloon: "🎈", gift: "🎁", trophy: "🏆", medal: "🏅", crown: "👑",
39
- gem: "💎", rocket: "🚀", airplane: "✈️", car: "🚗", rotating_light: "🚨",
40
- "100": "💯", white_check_mark: "✅", heavy_check_mark: "✔️", ballot_box_with_check: "☑️", x: "❌",
41
- warning: "⚠️", no_entry: "⛔", question: "❓", exclamation: "❗", bulb: "💡", lock: "🔒", key: "🔑",
42
- mag: "🔍", bell: "🔔", recycle: "♻️", heavy_plus_sign: "➕", heavy_minus_sign: "➖",
43
- coffee: "☕", tea: "🍵", beer: "🍺", beers: "🍻", wine_glass: "🍷", cocktail: "🍸", pizza: "🍕",
44
- hamburger: "🍔", fries: "🍟", taco: "🌮", cake: "🎂", birthday: "🎂", cookie: "🍪", doughnut: "🍩",
45
- apple: "🍎", banana: "🍌", strawberry: "🍓", avocado: "🥑", bread: "🍞", cheese: "🧀",
46
- dog: "🐶", cat: "🐱", mouse: "🐭", rabbit: "🐰", bear: "🐻", panda_face: "🐼", koala: "🐨",
47
- tiger: "🐯", lion: "🦁", cow: "🐮", pig: "🐷", frog: "🐸", monkey_face: "🐵", chicken: "🐔",
48
- penguin: "🐧", bird: "🐦", unicorn: "🦄", bee: "🐝", bug: "🐛", butterfly: "🦋", snail: "🐌",
49
- book: "📖", books: "📚", memo: "📝", pencil: "📝", email: "✉️", envelope: "✉️", calendar: "📅",
50
- chart: "📊", bar_chart: "📊", chart_with_upwards_trend: "📈", clipboard: "📋", pushpin: "📌",
51
- paperclip: "📎", scissors: "✂️", hammer: "🔨", wrench: "🔧", gear: "⚙️", nut_and_bolt: "🔩",
52
- bomb: "💣", hourglass: "⌛", watch: "⌚", alarm_clock: "⏰", moneybag: "💰", dollar: "💵",
53
- credit_card: "💳", chart_increasing: "📈", link: "🔗", mag_right: "🔎", telescope: "🔭",
54
- microscope: "🔬", syringe: "💉", pill: "💊", phone: "📞", telephone: "📞", iphone: "📱",
55
- computer: "💻", keyboard: "⌨️", printer: "🖨️", camera: "📷", movie_camera: "🎥", tv: "📺",
56
- seedling: "🌱", evergreen_tree: "🌲", deciduous_tree: "🌳", palm_tree: "🌴", cactus: "🌵",
57
- four_leaf_clover: "🍀", rose: "🌹", sunflower: "🌻", tulip: "🌷", blossom: "🌼", herb: "🌿",
58
- musical_note: "🎵", notes: "🎶", microphone: "🎤", headphones: "🎧", guitar: "🎸", trumpet: "🎺",
59
- soccer: "⚽", basketball: "🏀", football: "🏈", baseball: "⚾", tennis: "🎾", "8ball": "🎱",
60
- dart: "🎯", video_game: "🎮", game_die: "🎲", trophy2: "🏆",
61
- sos: "🆘", new: "🆕", ok: "🆗", up: "🆙", cool: "🆒", free: "🆓",
62
- arrow_up: "⬆️", arrow_down: "⬇️", arrow_left: "⬅️", arrow_right: "➡️", arrow_forward: "▶️",
63
- bangbang: "‼️", interrobang: "⁉️", grey_question: "❔", grey_exclamation: "❕"
15
+ smile: "\ud83d\ude04", smiley: "\ud83d\ude03", grin: "\ud83d\ude01", laughing: "\ud83d\ude06", satisfied: "\ud83d\ude06", joy: "\ud83d\ude02", rofl: "\ud83e\udd23",
16
+ sweat_smile: "\ud83d\ude05", wink: "\ud83d\ude09", blush: "\ud83d\ude0a", innocent: "\ud83d\ude07", heart_eyes: "\ud83d\ude0d", star_struck: "\ud83e\udd29",
17
+ kissing_heart: "\ud83d\ude18", yum: "\ud83d\ude0b", stuck_out_tongue: "\ud83d\ude1b", zany: "\ud83e\udd2a", thinking: "\ud83e\udd14", shush: "\ud83e\udd2b",
18
+ neutral_face: "\ud83d\ude10", expressionless: "\ud83d\ude11", no_mouth: "\ud83d\ude36", smirk: "\ud83d\ude0f", unamused: "\ud83d\ude12", roll_eyes: "\ud83d\ude44",
19
+ grimacing: "\ud83d\ude2c", relieved: "\ud83d\ude0c", pensive: "\ud83d\ude14", sleepy: "\ud83d\ude2a", sleeping: "\ud83d\ude34", mask: "\ud83d\ude37",
20
+ face_with_thermometer: "\ud83e\udd12", nauseated_face: "\ud83e\udd22", sneezing_face: "\ud83e\udd27", dizzy_face: "\ud83d\ude35",
21
+ exploding_head: "\ud83e\udd2f", cowboy: "\ud83e\udd20", sunglasses: "\ud83d\ude0e", nerd: "\ud83e\udd13", monocle: "\ud83e\uddd0", confused: "\ud83d\ude15",
22
+ worried: "\ud83d\ude1f", slightly_frowning_face: "\ud83d\ude41", open_mouth: "\ud83d\ude2e", astonished: "\ud83d\ude32", flushed: "\ud83d\ude33",
23
+ fearful: "\ud83d\ude28", cold_sweat: "\ud83d\ude30", cry: "\ud83d\ude22", sob: "\ud83d\ude2d", scream: "\ud83d\ude31", confounded: "\ud83d\ude16", persevere: "\ud83d\ude23",
24
+ disappointed: "\ud83d\ude1e", sweat: "\ud83d\ude13", weary: "\ud83d\ude29", tired_face: "\ud83d\ude2b", triumph: "\ud83d\ude24", rage: "\ud83d\ude21", angry: "\ud83d\ude20",
25
+ cursing_face: "\ud83e\udd2c", smiling_imp: "\ud83d\ude08", imp: "\ud83d\udc7f", skull: "\ud83d\udc80", poop: "\ud83d\udca9", hankey: "\ud83d\udca9", clown: "\ud83e\udd21",
26
+ ghost: "\ud83d\udc7b", alien: "\ud83d\udc7d", robot: "\ud83e\udd16", heart_eyes_cat: "\ud83d\ude3b", see_no_evil: "\ud83d\ude48", hear_no_evil: "\ud83d\ude49",
27
+ speak_no_evil: "\ud83d\ude4a",
28
+ heart: "\u2764\ufe0f", orange_heart: "\ud83e\udde1", yellow_heart: "\ud83d\udc9b", green_heart: "\ud83d\udc9a", blue_heart: "\ud83d\udc99",
29
+ purple_heart: "\ud83d\udc9c", black_heart: "\ud83d\udda4", broken_heart: "\ud83d\udc94", two_hearts: "\ud83d\udc95", sparkling_heart: "\ud83d\udc96",
30
+ heartpulse: "\ud83d\udc97", cupid: "\ud83d\udc98", gift_heart: "\ud83d\udc9d", revolving_hearts: "\ud83d\udc9e", kiss: "\ud83d\udc8b",
31
+ "+1": "\ud83d\udc4d", thumbsup: "\ud83d\udc4d", "-1": "\ud83d\udc4e", thumbsdown: "\ud83d\udc4e", ok_hand: "\ud83d\udc4c", punch: "\ud83d\udc4a", fist: "\u270a",
32
+ facepunch: "\ud83d\udc4a", wave: "\ud83d\udc4b", raised_hand: "\u270b", raised_hands: "\ud83d\ude4c", pray: "\ud83d\ude4f", clap: "\ud83d\udc4f",
33
+ muscle: "\ud83d\udcaa", point_up: "\u261d\ufe0f", point_down: "\ud83d\udc47", point_left: "\ud83d\udc48", point_right: "\ud83d\udc49", v: "\u270c\ufe0f",
34
+ crossed_fingers: "\ud83e\udd1e", handshake: "\ud83e\udd1d", writing_hand: "\u270d\ufe0f", selfie: "\ud83e\udd33", nail_care: "\ud83d\udc85", eyes: "\ud83d\udc40",
35
+ fire: "\ud83d\udd25", star: "\u2b50", star2: "\ud83c\udf1f", sparkles: "\u2728", zap: "\u26a1", boom: "\ud83d\udca5", collision: "\ud83d\udca5",
36
+ dizzy: "\ud83d\udcab", sweat_drops: "\ud83d\udca6", droplet: "\ud83d\udca7", snowflake: "\u2744\ufe0f", sunny: "\u2600\ufe0f", cloud: "\u2601\ufe0f",
37
+ rainbow: "\ud83c\udf08", umbrella: "\u2614", ocean: "\ud83c\udf0a", sun_with_face: "\ud83c\udf1e", full_moon: "\ud83c\udf15", crescent_moon: "\ud83c\udf19",
38
+ tada: "\ud83c\udf89", confetti_ball: "\ud83c\udf8a", balloon: "\ud83c\udf88", gift: "\ud83c\udf81", trophy: "\ud83c\udfc6", medal: "\ud83c\udfc5", crown: "\ud83d\udc51",
39
+ gem: "\ud83d\udc8e", rocket: "\ud83d\ude80", airplane: "\u2708\ufe0f", car: "\ud83d\ude97", rotating_light: "\ud83d\udea8",
40
+ "100": "\ud83d\udcaf", white_check_mark: "\u2705", heavy_check_mark: "\u2714\ufe0f", ballot_box_with_check: "\u2611\ufe0f", x: "\u274c",
41
+ warning: "\u26a0\ufe0f", no_entry: "\u26d4", question: "\u2753", exclamation: "\u2757", bulb: "\ud83d\udca1", lock: "\ud83d\udd12", key: "\ud83d\udd11",
42
+ mag: "\ud83d\udd0d", bell: "\ud83d\udd14", recycle: "\u267b\ufe0f", heavy_plus_sign: "\u2795", heavy_minus_sign: "\u2796",
43
+ coffee: "\u2615", tea: "\ud83c\udf75", beer: "\ud83c\udf7a", beers: "\ud83c\udf7b", wine_glass: "\ud83c\udf77", cocktail: "\ud83c\udf78", pizza: "\ud83c\udf55",
44
+ hamburger: "\ud83c\udf54", fries: "\ud83c\udf5f", taco: "\ud83c\udf2e", cake: "\ud83c\udf82", birthday: "\ud83c\udf82", cookie: "\ud83c\udf6a", doughnut: "\ud83c\udf69",
45
+ apple: "\ud83c\udf4e", banana: "\ud83c\udf4c", strawberry: "\ud83c\udf53", avocado: "\ud83e\udd51", bread: "\ud83c\udf5e", cheese: "\ud83e\uddc0",
46
+ dog: "\ud83d\udc36", cat: "\ud83d\udc31", mouse: "\ud83d\udc2d", rabbit: "\ud83d\udc30", bear: "\ud83d\udc3b", panda_face: "\ud83d\udc3c", koala: "\ud83d\udc28",
47
+ tiger: "\ud83d\udc2f", lion: "\ud83e\udd81", cow: "\ud83d\udc2e", pig: "\ud83d\udc37", frog: "\ud83d\udc38", monkey_face: "\ud83d\udc35", chicken: "\ud83d\udc14",
48
+ penguin: "\ud83d\udc27", bird: "\ud83d\udc26", unicorn: "\ud83e\udd84", bee: "\ud83d\udc1d", bug: "\ud83d\udc1b", butterfly: "\ud83e\udd8b", snail: "\ud83d\udc0c",
49
+ book: "\ud83d\udcd6", books: "\ud83d\udcda", memo: "\ud83d\udcdd", pencil: "\ud83d\udcdd", email: "\u2709\ufe0f", envelope: "\u2709\ufe0f", calendar: "\ud83d\udcc5",
50
+ chart: "\ud83d\udcca", bar_chart: "\ud83d\udcca", chart_with_upwards_trend: "\ud83d\udcc8", clipboard: "\ud83d\udccb", pushpin: "\ud83d\udccc",
51
+ paperclip: "\ud83d\udcce", scissors: "\u2702\ufe0f", hammer: "\ud83d\udd28", wrench: "\ud83d\udd27", gear: "\u2699\ufe0f", nut_and_bolt: "\ud83d\udd29",
52
+ bomb: "\ud83d\udca3", hourglass: "\u231b", watch: "\u231a", alarm_clock: "\u23f0", moneybag: "\ud83d\udcb0", dollar: "\ud83d\udcb5",
53
+ credit_card: "\ud83d\udcb3", chart_increasing: "\ud83d\udcc8", link: "\ud83d\udd17", mag_right: "\ud83d\udd0e", telescope: "\ud83d\udd2d",
54
+ microscope: "\ud83d\udd2c", syringe: "\ud83d\udc89", pill: "\ud83d\udc8a", phone: "\ud83d\udcde", telephone: "\ud83d\udcde", iphone: "\ud83d\udcf1",
55
+ computer: "\ud83d\udcbb", keyboard: "\u2328\ufe0f", printer: "\ud83d\udda8\ufe0f", camera: "\ud83d\udcf7", movie_camera: "\ud83c\udfa5", tv: "\ud83d\udcfa",
56
+ seedling: "\ud83c\udf31", evergreen_tree: "\ud83c\udf32", deciduous_tree: "\ud83c\udf33", palm_tree: "\ud83c\udf34", cactus: "\ud83c\udf35",
57
+ four_leaf_clover: "\ud83c\udf40", rose: "\ud83c\udf39", sunflower: "\ud83c\udf3b", tulip: "\ud83c\udf37", blossom: "\ud83c\udf3c", herb: "\ud83c\udf3f",
58
+ musical_note: "\ud83c\udfb5", notes: "\ud83c\udfb6", microphone: "\ud83c\udfa4", headphones: "\ud83c\udfa7", guitar: "\ud83c\udfb8", trumpet: "\ud83c\udfba",
59
+ soccer: "\u26bd", basketball: "\ud83c\udfc0", football: "\ud83c\udfc8", baseball: "\u26be", tennis: "\ud83c\udfbe", "8ball": "\ud83c\udfb1",
60
+ dart: "\ud83c\udfaf", video_game: "\ud83c\udfae", game_die: "\ud83c\udfb2", trophy2: "\ud83c\udfc6",
61
+ sos: "\ud83c\udd98", new: "\ud83c\udd95", ok: "\ud83c\udd97", up: "\ud83c\udd99", cool: "\ud83c\udd92", free: "\ud83c\udd93",
62
+ arrow_up: "\u2b06\ufe0f", arrow_down: "\u2b07\ufe0f", arrow_left: "\u2b05\ufe0f", arrow_right: "\u27a1\ufe0f", arrow_forward: "\u25b6\ufe0f",
63
+ bangbang: "\u203c\ufe0f", interrobang: "\u2049\ufe0f", grey_question: "\u2754", grey_exclamation: "\u2755"
64
64
  };
65
65
 
66
66
  function RTE_Plugin_EmojiAutocomplete() {
67
67
  var obj = this;
68
68
  var config, editor, editable, editdoc;
69
- var pop = null, items = [], sel = 0, queryStart = -1, queryNode = null;
69
+ var pop = null, items = [], sel = 0, queryStart = -1, queryNode = null, lastFix = null;
70
70
 
71
71
  obj.PluginName = "EmojiAutocomplete";
72
72
 
@@ -77,10 +77,19 @@ function RTE_Plugin_EmojiAutocomplete() {
77
77
  editable = editor.getEditable();
78
78
  editdoc = editor.getDocument();
79
79
  if (!editable || !editdoc) return;
80
- editdoc.addEventListener("input", onInput, false);
80
+ // 2026-10-03 Bound through editor.bindDocumentEvent, so the listeners follow the live document:
81
+ // shell plugins move the iframe during the init loop, and listeners added to editdoc here stayed
82
+ // on the dead document (this feature never fired). tools/keyboard/plugin-listeners.mjs
83
+ var on = function (type, fn, capture) {
84
+ if (editor.bindDocumentEvent) editor.bindDocumentEvent(type, fn, capture);
85
+ else editor.getDocument().addEventListener(type, fn, capture);
86
+ };
87
+ on("input", onInput, false);
81
88
  // keydown in capture so we intercept nav keys before the editor does.
82
- editdoc.addEventListener("keydown", onKeyDown, true);
83
- editdoc.addEventListener("blur", function () { setTimeout(closePopup, 150); }, true);
89
+ on("keydown", onKeyDown, true);
90
+ on("blur", function () { setTimeout(closePopup, 150); }, true);
91
+ // the references held above go stale with the document
92
+ editor.attachEvent("documentreplaced", function () { editable = editor.getEditable(); editdoc = editor.getDocument(); });
84
93
  };
85
94
 
86
95
  function map() { return config.emojiShortcodes || {}; }
@@ -150,10 +159,18 @@ function RTE_Plugin_EmojiAutocomplete() {
150
159
  pop = hostDoc.createElement("div");
151
160
  pop.className = "rte-emoji-pop";
152
161
  pop.setAttribute("role", "listbox");
153
- pop.setAttribute("aria-label", "Emoji suggestions");
162
+ pop.setAttribute("aria-label", ((typeof config !== "undefined" && config && config.text_emoji_suggest) || "Emoji suggestions"));
154
163
  pop.style.cssText = "position:fixed;z-index:2147483600;";
155
164
  hostDoc.body.appendChild(pop);
156
165
  }
166
+ // 2026-10-17 appended to the page body, outside the editor, so rte-dark could not reach it: mirrored (and
167
+ // rte-auto-dark under a dark system setting). tools/emoji/emoji-autocomplete-dark.mjs
168
+ var popDark = false;
169
+ try {
170
+ var hk = editor.container && editor.container.classList;
171
+ popDark = !!hk && (hk.contains("rte-dark") || (hk.contains("rte-auto-dark") && !hk.contains("rte-light") && !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches)));
172
+ } catch (x) { popDark = false; }
173
+ pop.className = "rte-emoji-pop" + (popDark ? " rte-emoji-pop-dark" : "");
157
174
  pop.innerHTML = "";
158
175
  items.forEach(function (it, i) {
159
176
  var row = hostDoc.createElement("div");
@@ -175,8 +192,12 @@ function RTE_Plugin_EmojiAutocomplete() {
175
192
  // position near the caret (iframe rect + caret rect)
176
193
  try {
177
194
  var cr = range.getBoundingClientRect();
178
- var fr = (editdoc.defaultView && editdoc.defaultView.frameElement) ? editdoc.defaultView.frameElement.getBoundingClientRect() : { left: 0, top: 0 };
179
- var x = fr.left + cr.left, y = fr.top + cr.bottom + 4;
195
+ var fe = editdoc.defaultView && editdoc.defaultView.frameElement;
196
+ var fr = fe ? fe.getBoundingClientRect() : { left: 0, top: 0, width: 0 };
197
+ // the caret rect is in iframe pixels, which are scaled on screen inside a
198
+ // transform:scale() ancestor; the popup lives in the host body (viewport pixels)
199
+ var k = fe && fe.offsetWidth && fr.width > 0 ? fr.width / fe.offsetWidth : 1;
200
+ var x = fr.left + cr.left * k, y = fr.top + cr.bottom * k + 4;
180
201
  var vw = hostDoc.documentElement.clientWidth, vh = hostDoc.documentElement.clientHeight;
181
202
  pop.style.left = Math.max(4, Math.min(x, vw - 290)) + "px";
182
203
  pop.style.top = Math.max(4, Math.min(y, vh - 20)) + "px";
@@ -194,6 +215,15 @@ function RTE_Plugin_EmojiAutocomplete() {
194
215
  }
195
216
 
196
217
  function onKeyDown(e) {
218
+ // 2026-10-03 Ctrl/Cmd+Z straight after a conversion gives the typed shortcode back (":smile:"),
219
+ // as autocorrect does for its corrections - the editor's own undo would also take back the
220
+ // typing it was grouped with. Any other key forgets the conversion.
221
+ // tools/keyboard/emoji-autocomplete.mjs
222
+ if (lastFix) {
223
+ var isUndo = (e.ctrlKey || e.metaKey) && !e.shiftKey && !e.altKey && (e.code === "KeyZ" || e.key === "z" || e.key === "Z");
224
+ if (isUndo && undoLastFix()) { e.preventDefault(); e.stopPropagation(); return; }
225
+ if (!/^(Control|Meta|Shift|Alt|CapsLock)$/.test(e.key)) lastFix = null;
226
+ }
197
227
  if (!pop || pop.style.display === "none" || !items.length) return;
198
228
  switch (e.key) {
199
229
  case "ArrowDown": e.preventDefault(); e.stopPropagation(); sel = (sel + 1) % items.length; highlightRows(); break;
@@ -204,6 +234,29 @@ function RTE_Plugin_EmojiAutocomplete() {
204
234
  }
205
235
  }
206
236
 
237
+ // Put the typed shortcode back over the emoji the last conversion wrote - only while the caret is
238
+ // still right behind that emoji and the emoji is untouched.
239
+ function undoLastFix() {
240
+ var f = lastFix;
241
+ lastFix = null;
242
+ if (!f || !f.node.parentNode || f.node.data !== f.emo) return false;
243
+ var s = editor.getSelection();
244
+ if (!s || !s.rangeCount || !s.isCollapsed) return false;
245
+ var r = s.getRangeAt(0), here = r.startContainer, off = r.startOffset;
246
+ var behind = (here === f.node && off === f.node.data.length) ||
247
+ (here.nodeType === 3 && off === 0 && here.previousSibling === f.node) ||
248
+ (here === f.node.parentNode && here.childNodes[off - 1] === f.node);
249
+ if (!behind) return false;
250
+ f.node.data = f.typed;
251
+ var c = editor.getDocument().createRange();
252
+ c.setStart(f.node, f.typed.length);
253
+ c.collapse(true);
254
+ s.removeAllRanges();
255
+ s.addRange(c);
256
+ if (typeof editor.fireChange === "function") { try { editor.fireChange(); } catch (e) { /* ignore */ } }
257
+ return true;
258
+ }
259
+
207
260
  function commit() {
208
261
  if (!items.length || !queryNode || queryStart < 0) { closePopup(); return; }
209
262
  var it = items[sel] || items[0];
@@ -225,6 +278,7 @@ function RTE_Plugin_EmojiAutocomplete() {
225
278
  var tailNode = tail ? editdoc.createTextNode(tail) : null;
226
279
  if (tailNode) frag.appendChild(tailNode);
227
280
  parent.replaceChild(frag, node);
281
+ lastFix = { node: emoNode, emo: text, typed: data.slice(start, end) };
228
282
  var rr = editdoc.createRange();
229
283
  if (tailNode) rr.setStart(tailNode, 0); else rr.setStartAfter(emoNode);
230
284
  rr.collapse(true);
@@ -1 +1 @@
1
- window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_emojiautocomplete=RTE_Plugin_EmojiAutocomplete,RTE_DefaultConfig.emojiAutocompleteEnabled=typeof RTE_DefaultConfig.emojiAutocompleteEnabled>"u"?!0:RTE_DefaultConfig.emojiAutocompleteEnabled,RTE_DefaultConfig.emojiShortcodes=RTE_DefaultConfig.emojiShortcodes||{smile:"\u{1F604}",smiley:"\u{1F603}",grin:"\u{1F601}",laughing:"\u{1F606}",satisfied:"\u{1F606}",joy:"\u{1F602}",rofl:"\u{1F923}",sweat_smile:"\u{1F605}",wink:"\u{1F609}",blush:"\u{1F60A}",innocent:"\u{1F607}",heart_eyes:"\u{1F60D}",star_struck:"\u{1F929}",kissing_heart:"\u{1F618}",yum:"\u{1F60B}",stuck_out_tongue:"\u{1F61B}",zany:"\u{1F92A}",thinking:"\u{1F914}",shush:"\u{1F92B}",neutral_face:"\u{1F610}",expressionless:"\u{1F611}",no_mouth:"\u{1F636}",smirk:"\u{1F60F}",unamused:"\u{1F612}",roll_eyes:"\u{1F644}",grimacing:"\u{1F62C}",relieved:"\u{1F60C}",pensive:"\u{1F614}",sleepy:"\u{1F62A}",sleeping:"\u{1F634}",mask:"\u{1F637}",face_with_thermometer:"\u{1F912}",nauseated_face:"\u{1F922}",sneezing_face:"\u{1F927}",dizzy_face:"\u{1F635}",exploding_head:"\u{1F92F}",cowboy:"\u{1F920}",sunglasses:"\u{1F60E}",nerd:"\u{1F913}",monocle:"\u{1F9D0}",confused:"\u{1F615}",worried:"\u{1F61F}",slightly_frowning_face:"\u{1F641}",open_mouth:"\u{1F62E}",astonished:"\u{1F632}",flushed:"\u{1F633}",fearful:"\u{1F628}",cold_sweat:"\u{1F630}",cry:"\u{1F622}",sob:"\u{1F62D}",scream:"\u{1F631}",confounded:"\u{1F616}",persevere:"\u{1F623}",disappointed:"\u{1F61E}",sweat:"\u{1F613}",weary:"\u{1F629}",tired_face:"\u{1F62B}",triumph:"\u{1F624}",rage:"\u{1F621}",angry:"\u{1F620}",cursing_face:"\u{1F92C}",smiling_imp:"\u{1F608}",imp:"\u{1F47F}",skull:"\u{1F480}",poop:"\u{1F4A9}",hankey:"\u{1F4A9}",clown:"\u{1F921}",ghost:"\u{1F47B}",alien:"\u{1F47D}",robot:"\u{1F916}",heart_eyes_cat:"\u{1F63B}",see_no_evil:"\u{1F648}",hear_no_evil:"\u{1F649}",speak_no_evil:"\u{1F64A}",heart:"\u2764\uFE0F",orange_heart:"\u{1F9E1}",yellow_heart:"\u{1F49B}",green_heart:"\u{1F49A}",blue_heart:"\u{1F499}",purple_heart:"\u{1F49C}",black_heart:"\u{1F5A4}",broken_heart:"\u{1F494}",two_hearts:"\u{1F495}",sparkling_heart:"\u{1F496}",heartpulse:"\u{1F497}",cupid:"\u{1F498}",gift_heart:"\u{1F49D}",revolving_hearts:"\u{1F49E}",kiss:"\u{1F48B}","+1":"\u{1F44D}",thumbsup:"\u{1F44D}","-1":"\u{1F44E}",thumbsdown:"\u{1F44E}",ok_hand:"\u{1F44C}",punch:"\u{1F44A}",fist:"\u270A",facepunch:"\u{1F44A}",wave:"\u{1F44B}",raised_hand:"\u270B",raised_hands:"\u{1F64C}",pray:"\u{1F64F}",clap:"\u{1F44F}",muscle:"\u{1F4AA}",point_up:"\u261D\uFE0F",point_down:"\u{1F447}",point_left:"\u{1F448}",point_right:"\u{1F449}",v:"\u270C\uFE0F",crossed_fingers:"\u{1F91E}",handshake:"\u{1F91D}",writing_hand:"\u270D\uFE0F",selfie:"\u{1F933}",nail_care:"\u{1F485}",eyes:"\u{1F440}",fire:"\u{1F525}",star:"\u2B50",star2:"\u{1F31F}",sparkles:"\u2728",zap:"\u26A1",boom:"\u{1F4A5}",collision:"\u{1F4A5}",dizzy:"\u{1F4AB}",sweat_drops:"\u{1F4A6}",droplet:"\u{1F4A7}",snowflake:"\u2744\uFE0F",sunny:"\u2600\uFE0F",cloud:"\u2601\uFE0F",rainbow:"\u{1F308}",umbrella:"\u2614",ocean:"\u{1F30A}",sun_with_face:"\u{1F31E}",full_moon:"\u{1F315}",crescent_moon:"\u{1F319}",tada:"\u{1F389}",confetti_ball:"\u{1F38A}",balloon:"\u{1F388}",gift:"\u{1F381}",trophy:"\u{1F3C6}",medal:"\u{1F3C5}",crown:"\u{1F451}",gem:"\u{1F48E}",rocket:"\u{1F680}",airplane:"\u2708\uFE0F",car:"\u{1F697}",rotating_light:"\u{1F6A8}",100:"\u{1F4AF}",white_check_mark:"\u2705",heavy_check_mark:"\u2714\uFE0F",ballot_box_with_check:"\u2611\uFE0F",x:"\u274C",warning:"\u26A0\uFE0F",no_entry:"\u26D4",question:"\u2753",exclamation:"\u2757",bulb:"\u{1F4A1}",lock:"\u{1F512}",key:"\u{1F511}",mag:"\u{1F50D}",bell:"\u{1F514}",recycle:"\u267B\uFE0F",heavy_plus_sign:"\u2795",heavy_minus_sign:"\u2796",coffee:"\u2615",tea:"\u{1F375}",beer:"\u{1F37A}",beers:"\u{1F37B}",wine_glass:"\u{1F377}",cocktail:"\u{1F378}",pizza:"\u{1F355}",hamburger:"\u{1F354}",fries:"\u{1F35F}",taco:"\u{1F32E}",cake:"\u{1F382}",birthday:"\u{1F382}",cookie:"\u{1F36A}",doughnut:"\u{1F369}",apple:"\u{1F34E}",banana:"\u{1F34C}",strawberry:"\u{1F353}",avocado:"\u{1F951}",bread:"\u{1F35E}",cheese:"\u{1F9C0}",dog:"\u{1F436}",cat:"\u{1F431}",mouse:"\u{1F42D}",rabbit:"\u{1F430}",bear:"\u{1F43B}",panda_face:"\u{1F43C}",koala:"\u{1F428}",tiger:"\u{1F42F}",lion:"\u{1F981}",cow:"\u{1F42E}",pig:"\u{1F437}",frog:"\u{1F438}",monkey_face:"\u{1F435}",chicken:"\u{1F414}",penguin:"\u{1F427}",bird:"\u{1F426}",unicorn:"\u{1F984}",bee:"\u{1F41D}",bug:"\u{1F41B}",butterfly:"\u{1F98B}",snail:"\u{1F40C}",book:"\u{1F4D6}",books:"\u{1F4DA}",memo:"\u{1F4DD}",pencil:"\u{1F4DD}",email:"\u2709\uFE0F",envelope:"\u2709\uFE0F",calendar:"\u{1F4C5}",chart:"\u{1F4CA}",bar_chart:"\u{1F4CA}",chart_with_upwards_trend:"\u{1F4C8}",clipboard:"\u{1F4CB}",pushpin:"\u{1F4CC}",paperclip:"\u{1F4CE}",scissors:"\u2702\uFE0F",hammer:"\u{1F528}",wrench:"\u{1F527}",gear:"\u2699\uFE0F",nut_and_bolt:"\u{1F529}",bomb:"\u{1F4A3}",hourglass:"\u231B",watch:"\u231A",alarm_clock:"\u23F0",moneybag:"\u{1F4B0}",dollar:"\u{1F4B5}",credit_card:"\u{1F4B3}",chart_increasing:"\u{1F4C8}",link:"\u{1F517}",mag_right:"\u{1F50E}",telescope:"\u{1F52D}",microscope:"\u{1F52C}",syringe:"\u{1F489}",pill:"\u{1F48A}",phone:"\u{1F4DE}",telephone:"\u{1F4DE}",iphone:"\u{1F4F1}",computer:"\u{1F4BB}",keyboard:"\u2328\uFE0F",printer:"\u{1F5A8}\uFE0F",camera:"\u{1F4F7}",movie_camera:"\u{1F3A5}",tv:"\u{1F4FA}",seedling:"\u{1F331}",evergreen_tree:"\u{1F332}",deciduous_tree:"\u{1F333}",palm_tree:"\u{1F334}",cactus:"\u{1F335}",four_leaf_clover:"\u{1F340}",rose:"\u{1F339}",sunflower:"\u{1F33B}",tulip:"\u{1F337}",blossom:"\u{1F33C}",herb:"\u{1F33F}",musical_note:"\u{1F3B5}",notes:"\u{1F3B6}",microphone:"\u{1F3A4}",headphones:"\u{1F3A7}",guitar:"\u{1F3B8}",trumpet:"\u{1F3BA}",soccer:"\u26BD",basketball:"\u{1F3C0}",football:"\u{1F3C8}",baseball:"\u26BE",tennis:"\u{1F3BE}","8ball":"\u{1F3B1}",dart:"\u{1F3AF}",video_game:"\u{1F3AE}",game_die:"\u{1F3B2}",trophy2:"\u{1F3C6}",sos:"\u{1F198}",new:"\u{1F195}",ok:"\u{1F197}",up:"\u{1F199}",cool:"\u{1F192}",free:"\u{1F193}",arrow_up:"\u2B06\uFE0F",arrow_down:"\u2B07\uFE0F",arrow_left:"\u2B05\uFE0F",arrow_right:"\u27A1\uFE0F",arrow_forward:"\u25B6\uFE0F",bangbang:"\u203C\uFE0F",interrobang:"\u2049\uFE0F",grey_question:"\u2754",grey_exclamation:"\u2755"};function RTE_Plugin_EmojiAutocomplete(){var y=this,k,b,E,i,n=null,f=[],l=0,w=-1,v=null;y.PluginName="EmojiAutocomplete",y.InitConfig=function(e){k=e},y.InitEditor=function(e){b=e,E=b.getEditable(),i=b.getDocument(),!(!E||!i)&&(i.addEventListener("input",j,!1),i.addEventListener("keydown",P,!0),i.addEventListener("blur",function(){setTimeout(m,150)},!0))};function D(){return k.emojiShortcodes||{}}function R(){var e=b.getSelection();if(!e||e.rangeCount===0||!e.isCollapsed)return null;var t=e.getRangeAt(0);if(t.startContainer.nodeType!==3)return null;for(var a=t.startContainer.parentNode;a&&a!==E;){if(a.nodeName==="CODE"||a.nodeName==="PRE")return null;a=a.parentNode}return t}function j(){if(k.emojiAutocompleteEnabled){var e=R();if(!e){m();return}var t=e.startContainer,a=e.startOffset,h=t.data.slice(0,a),u=/(^|[^a-z0-9_])(:[a-z0-9_+\-]{1,30}:)$/i.exec(h);if(u){var _=u[2].slice(1,-1).toLowerCase(),p=D()[_];if(p){A(t,a-u[2].length,a,p),m();return}}var c=/(^|[^a-z0-9_])(:)([a-z0-9_+\-]{1,30})$/i.exec(h);if(c){var o=c[3].toLowerCase();v=t,w=a-o.length-1,z(o,e)}else m()}}function z(e,t){for(var a=D(),h=Object.keys(a),u=[],_=[],p=0;p<h.length;p++){var c=h[p];c.indexOf(e)===0?u.push(c):c.indexOf(e)!==-1&&_.push(c)}for(var o=u.concat(_),g={},r=[],s=0;s<o.length&&r.length<8;s++){var d=a[o[s]];g[d]||(g[d]=1,r.push({code:o[s],emo:d}))}if(!r.length){m();return}f=r,l=0,N(t)}function N(e){var t=document;n||(n=t.createElement("div"),n.className="rte-emoji-pop",n.setAttribute("role","listbox"),n.setAttribute("aria-label","Emoji suggestions"),n.style.cssText="position:fixed;z-index:2147483600;",t.body.appendChild(n)),n.innerHTML="",f.forEach(function(o,g){var r=t.createElement("div");r.className="rte-emoji-pop-row"+(g===l?" is-active":""),r.setAttribute("role","option"),r.setAttribute("aria-selected",g===l?"true":"false");var s=t.createElement("span");s.className="rte-emoji-pop-glyph",s.textContent=o.emo;var d=t.createElement("span");d.className="rte-emoji-pop-code",d.textContent=":"+o.code+":",r.appendChild(s),r.appendChild(d),r.onmousedown=function(x){x.preventDefault(),l=g,T()},r.onmouseenter=function(){l=g,C()},n.appendChild(r)});try{var a=e.getBoundingClientRect(),h=i.defaultView&&i.defaultView.frameElement?i.defaultView.frameElement.getBoundingClientRect():{left:0,top:0},u=h.left+a.left,_=h.top+a.bottom+4,p=t.documentElement.clientWidth,c=t.documentElement.clientHeight;n.style.left=Math.max(4,Math.min(u,p-290))+"px",n.style.top=Math.max(4,Math.min(_,c-20))+"px",n.style.display="block"}catch{}}function C(){if(n)for(var e=n.childNodes,t=0;t<e.length;t++)e[t].classList.toggle("is-active",t===l),e[t].setAttribute("aria-selected",t===l?"true":"false")}function P(e){if(!(!n||n.style.display==="none"||!f.length))switch(e.key){case"ArrowDown":e.preventDefault(),e.stopPropagation(),l=(l+1)%f.length,C();break;case"ArrowUp":e.preventDefault(),e.stopPropagation(),l=(l-1+f.length)%f.length,C();break;case"Enter":case"Tab":e.preventDefault(),e.stopPropagation(),T();break;case"Escape":e.preventDefault(),e.stopPropagation(),m();break}}function T(){if(!f.length||!v||w<0){m();return}var e=f[l]||f[0],t=R();if(!t||t.startContainer!==v){m();return}A(v,w,t.startOffset,e.emo),m()}function A(e,t,a,h){try{var u=e.data,_=u.slice(0,t),p=u.slice(a),c=e.parentNode;if(!c)return;var o=i.createDocumentFragment();_&&o.appendChild(i.createTextNode(_));var g=i.createTextNode(h);o.appendChild(g);var r=p?i.createTextNode(p):null;r&&o.appendChild(r),c.replaceChild(o,e);var s=i.createRange();r?s.setStart(r,0):s.setStartAfter(g),s.collapse(!0);var d=b.getSelection();if(d&&(d.removeAllRanges(),d.addRange(s)),typeof b.fireChange=="function")try{b.fireChange()}catch{}}catch{}}function m(){n&&(n.style.display="none"),f=[],l=0,w=-1,v=null}}
1
+ window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_emojiautocomplete=RTE_Plugin_EmojiAutocomplete,RTE_DefaultConfig.emojiAutocompleteEnabled=typeof RTE_DefaultConfig.emojiAutocompleteEnabled>"u"?!0:RTE_DefaultConfig.emojiAutocompleteEnabled,RTE_DefaultConfig.emojiShortcodes=RTE_DefaultConfig.emojiShortcodes||{smile:"\u{1F604}",smiley:"\u{1F603}",grin:"\u{1F601}",laughing:"\u{1F606}",satisfied:"\u{1F606}",joy:"\u{1F602}",rofl:"\u{1F923}",sweat_smile:"\u{1F605}",wink:"\u{1F609}",blush:"\u{1F60A}",innocent:"\u{1F607}",heart_eyes:"\u{1F60D}",star_struck:"\u{1F929}",kissing_heart:"\u{1F618}",yum:"\u{1F60B}",stuck_out_tongue:"\u{1F61B}",zany:"\u{1F92A}",thinking:"\u{1F914}",shush:"\u{1F92B}",neutral_face:"\u{1F610}",expressionless:"\u{1F611}",no_mouth:"\u{1F636}",smirk:"\u{1F60F}",unamused:"\u{1F612}",roll_eyes:"\u{1F644}",grimacing:"\u{1F62C}",relieved:"\u{1F60C}",pensive:"\u{1F614}",sleepy:"\u{1F62A}",sleeping:"\u{1F634}",mask:"\u{1F637}",face_with_thermometer:"\u{1F912}",nauseated_face:"\u{1F922}",sneezing_face:"\u{1F927}",dizzy_face:"\u{1F635}",exploding_head:"\u{1F92F}",cowboy:"\u{1F920}",sunglasses:"\u{1F60E}",nerd:"\u{1F913}",monocle:"\u{1F9D0}",confused:"\u{1F615}",worried:"\u{1F61F}",slightly_frowning_face:"\u{1F641}",open_mouth:"\u{1F62E}",astonished:"\u{1F632}",flushed:"\u{1F633}",fearful:"\u{1F628}",cold_sweat:"\u{1F630}",cry:"\u{1F622}",sob:"\u{1F62D}",scream:"\u{1F631}",confounded:"\u{1F616}",persevere:"\u{1F623}",disappointed:"\u{1F61E}",sweat:"\u{1F613}",weary:"\u{1F629}",tired_face:"\u{1F62B}",triumph:"\u{1F624}",rage:"\u{1F621}",angry:"\u{1F620}",cursing_face:"\u{1F92C}",smiling_imp:"\u{1F608}",imp:"\u{1F47F}",skull:"\u{1F480}",poop:"\u{1F4A9}",hankey:"\u{1F4A9}",clown:"\u{1F921}",ghost:"\u{1F47B}",alien:"\u{1F47D}",robot:"\u{1F916}",heart_eyes_cat:"\u{1F63B}",see_no_evil:"\u{1F648}",hear_no_evil:"\u{1F649}",speak_no_evil:"\u{1F64A}",heart:"\u2764\uFE0F",orange_heart:"\u{1F9E1}",yellow_heart:"\u{1F49B}",green_heart:"\u{1F49A}",blue_heart:"\u{1F499}",purple_heart:"\u{1F49C}",black_heart:"\u{1F5A4}",broken_heart:"\u{1F494}",two_hearts:"\u{1F495}",sparkling_heart:"\u{1F496}",heartpulse:"\u{1F497}",cupid:"\u{1F498}",gift_heart:"\u{1F49D}",revolving_hearts:"\u{1F49E}",kiss:"\u{1F48B}","+1":"\u{1F44D}",thumbsup:"\u{1F44D}","-1":"\u{1F44E}",thumbsdown:"\u{1F44E}",ok_hand:"\u{1F44C}",punch:"\u{1F44A}",fist:"\u270A",facepunch:"\u{1F44A}",wave:"\u{1F44B}",raised_hand:"\u270B",raised_hands:"\u{1F64C}",pray:"\u{1F64F}",clap:"\u{1F44F}",muscle:"\u{1F4AA}",point_up:"\u261D\uFE0F",point_down:"\u{1F447}",point_left:"\u{1F448}",point_right:"\u{1F449}",v:"\u270C\uFE0F",crossed_fingers:"\u{1F91E}",handshake:"\u{1F91D}",writing_hand:"\u270D\uFE0F",selfie:"\u{1F933}",nail_care:"\u{1F485}",eyes:"\u{1F440}",fire:"\u{1F525}",star:"\u2B50",star2:"\u{1F31F}",sparkles:"\u2728",zap:"\u26A1",boom:"\u{1F4A5}",collision:"\u{1F4A5}",dizzy:"\u{1F4AB}",sweat_drops:"\u{1F4A6}",droplet:"\u{1F4A7}",snowflake:"\u2744\uFE0F",sunny:"\u2600\uFE0F",cloud:"\u2601\uFE0F",rainbow:"\u{1F308}",umbrella:"\u2614",ocean:"\u{1F30A}",sun_with_face:"\u{1F31E}",full_moon:"\u{1F315}",crescent_moon:"\u{1F319}",tada:"\u{1F389}",confetti_ball:"\u{1F38A}",balloon:"\u{1F388}",gift:"\u{1F381}",trophy:"\u{1F3C6}",medal:"\u{1F3C5}",crown:"\u{1F451}",gem:"\u{1F48E}",rocket:"\u{1F680}",airplane:"\u2708\uFE0F",car:"\u{1F697}",rotating_light:"\u{1F6A8}",100:"\u{1F4AF}",white_check_mark:"\u2705",heavy_check_mark:"\u2714\uFE0F",ballot_box_with_check:"\u2611\uFE0F",x:"\u274C",warning:"\u26A0\uFE0F",no_entry:"\u26D4",question:"\u2753",exclamation:"\u2757",bulb:"\u{1F4A1}",lock:"\u{1F512}",key:"\u{1F511}",mag:"\u{1F50D}",bell:"\u{1F514}",recycle:"\u267B\uFE0F",heavy_plus_sign:"\u2795",heavy_minus_sign:"\u2796",coffee:"\u2615",tea:"\u{1F375}",beer:"\u{1F37A}",beers:"\u{1F37B}",wine_glass:"\u{1F377}",cocktail:"\u{1F378}",pizza:"\u{1F355}",hamburger:"\u{1F354}",fries:"\u{1F35F}",taco:"\u{1F32E}",cake:"\u{1F382}",birthday:"\u{1F382}",cookie:"\u{1F36A}",doughnut:"\u{1F369}",apple:"\u{1F34E}",banana:"\u{1F34C}",strawberry:"\u{1F353}",avocado:"\u{1F951}",bread:"\u{1F35E}",cheese:"\u{1F9C0}",dog:"\u{1F436}",cat:"\u{1F431}",mouse:"\u{1F42D}",rabbit:"\u{1F430}",bear:"\u{1F43B}",panda_face:"\u{1F43C}",koala:"\u{1F428}",tiger:"\u{1F42F}",lion:"\u{1F981}",cow:"\u{1F42E}",pig:"\u{1F437}",frog:"\u{1F438}",monkey_face:"\u{1F435}",chicken:"\u{1F414}",penguin:"\u{1F427}",bird:"\u{1F426}",unicorn:"\u{1F984}",bee:"\u{1F41D}",bug:"\u{1F41B}",butterfly:"\u{1F98B}",snail:"\u{1F40C}",book:"\u{1F4D6}",books:"\u{1F4DA}",memo:"\u{1F4DD}",pencil:"\u{1F4DD}",email:"\u2709\uFE0F",envelope:"\u2709\uFE0F",calendar:"\u{1F4C5}",chart:"\u{1F4CA}",bar_chart:"\u{1F4CA}",chart_with_upwards_trend:"\u{1F4C8}",clipboard:"\u{1F4CB}",pushpin:"\u{1F4CC}",paperclip:"\u{1F4CE}",scissors:"\u2702\uFE0F",hammer:"\u{1F528}",wrench:"\u{1F527}",gear:"\u2699\uFE0F",nut_and_bolt:"\u{1F529}",bomb:"\u{1F4A3}",hourglass:"\u231B",watch:"\u231A",alarm_clock:"\u23F0",moneybag:"\u{1F4B0}",dollar:"\u{1F4B5}",credit_card:"\u{1F4B3}",chart_increasing:"\u{1F4C8}",link:"\u{1F517}",mag_right:"\u{1F50E}",telescope:"\u{1F52D}",microscope:"\u{1F52C}",syringe:"\u{1F489}",pill:"\u{1F48A}",phone:"\u{1F4DE}",telephone:"\u{1F4DE}",iphone:"\u{1F4F1}",computer:"\u{1F4BB}",keyboard:"\u2328\uFE0F",printer:"\u{1F5A8}\uFE0F",camera:"\u{1F4F7}",movie_camera:"\u{1F3A5}",tv:"\u{1F4FA}",seedling:"\u{1F331}",evergreen_tree:"\u{1F332}",deciduous_tree:"\u{1F333}",palm_tree:"\u{1F334}",cactus:"\u{1F335}",four_leaf_clover:"\u{1F340}",rose:"\u{1F339}",sunflower:"\u{1F33B}",tulip:"\u{1F337}",blossom:"\u{1F33C}",herb:"\u{1F33F}",musical_note:"\u{1F3B5}",notes:"\u{1F3B6}",microphone:"\u{1F3A4}",headphones:"\u{1F3A7}",guitar:"\u{1F3B8}",trumpet:"\u{1F3BA}",soccer:"\u26BD",basketball:"\u{1F3C0}",football:"\u{1F3C8}",baseball:"\u26BE",tennis:"\u{1F3BE}","8ball":"\u{1F3B1}",dart:"\u{1F3AF}",video_game:"\u{1F3AE}",game_die:"\u{1F3B2}",trophy2:"\u{1F3C6}",sos:"\u{1F198}",new:"\u{1F195}",ok:"\u{1F197}",up:"\u{1F199}",cool:"\u{1F192}",free:"\u{1F193}",arrow_up:"\u2B06\uFE0F",arrow_down:"\u2B07\uFE0F",arrow_left:"\u2B05\uFE0F",arrow_right:"\u27A1\uFE0F",arrow_forward:"\u25B6\uFE0F",bangbang:"\u203C\uFE0F",interrobang:"\u2049\uFE0F",grey_question:"\u2754",grey_exclamation:"\u2755"};function RTE_Plugin_EmojiAutocomplete(){var D=this,y,c,E,g,n=null,m=[],f=0,x=-1,k=null,C=null;D.PluginName="EmojiAutocomplete",D.InitConfig=function(d){y=d},D.InitEditor=function(d){if(c=d,E=c.getEditable(),g=c.getDocument(),!(!E||!g)){var e=function(u,t,a){c.bindDocumentEvent?c.bindDocumentEvent(u,t,a):c.getDocument().addEventListener(u,t,a)};e("input",P,!1),e("keydown",O,!0),e("blur",function(){setTimeout(b,150)},!0),c.attachEvent("documentreplaced",function(){E=c.getEditable(),g=c.getDocument()})}};function T(){return y.emojiShortcodes||{}}function z(){var d=c.getSelection();if(!d||d.rangeCount===0||!d.isCollapsed)return null;var e=d.getRangeAt(0);if(e.startContainer.nodeType!==3)return null;for(var u=e.startContainer.parentNode;u&&u!==E;){if(u.nodeName==="CODE"||u.nodeName==="PRE")return null;u=u.parentNode}return e}function P(){if(y.emojiAutocompleteEnabled){var d=z();if(!d){b();return}var e=d.startContainer,u=d.startOffset,t=e.data.slice(0,u),a=/(^|[^a-z0-9_])(:[a-z0-9_+\-]{1,30}:)$/i.exec(t);if(a){var r=a[2].slice(1,-1).toLowerCase(),o=T()[r];if(o){S(e,u-a[2].length,u,o),b();return}}var i=/(^|[^a-z0-9_])(:)([a-z0-9_+\-]{1,30})$/i.exec(t);if(i){var l=i[3].toLowerCase();k=e,x=u-l.length-1,M(l,d)}else b()}}function M(d,e){for(var u=T(),t=Object.keys(u),a=[],r=[],o=0;o<t.length;o++){var i=t[o];i.indexOf(d)===0?a.push(i):i.indexOf(d)!==-1&&r.push(i)}for(var l=a.concat(r),_={},p=[],h=0;h<l.length&&p.length<8;h++){var s=u[l[h]];_[s]||(_[s]=1,p.push({code:l[h],emo:s}))}if(!p.length){b();return}m=p,f=0,L(e)}function L(d){var e=document;n||(n=e.createElement("div"),n.className="rte-emoji-pop",n.setAttribute("role","listbox"),n.setAttribute("aria-label",typeof y<"u"&&y&&y.text_emoji_suggest||"Emoji suggestions"),n.style.cssText="position:fixed;z-index:2147483600;",e.body.appendChild(n));var u=!1;try{var t=c.container&&c.container.classList;u=!!t&&(t.contains("rte-dark")||t.contains("rte-auto-dark")&&!t.contains("rte-light")&&!!(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches))}catch{u=!1}n.className="rte-emoji-pop"+(u?" rte-emoji-pop-dark":""),n.innerHTML="",m.forEach(function(s,w){var v=e.createElement("div");v.className="rte-emoji-pop-row"+(w===f?" is-active":""),v.setAttribute("role","option"),v.setAttribute("aria-selected",w===f?"true":"false");var A=e.createElement("span");A.className="rte-emoji-pop-glyph",A.textContent=s.emo;var j=e.createElement("span");j.className="rte-emoji-pop-code",j.textContent=":"+s.code+":",v.appendChild(A),v.appendChild(j),v.onmousedown=function(F){F.preventDefault(),f=w,N()},v.onmouseenter=function(){f=w,R()},n.appendChild(v)});try{var a=d.getBoundingClientRect(),r=g.defaultView&&g.defaultView.frameElement,o=r?r.getBoundingClientRect():{left:0,top:0,width:0},i=r&&r.offsetWidth&&o.width>0?o.width/r.offsetWidth:1,l=o.left+a.left*i,_=o.top+a.bottom*i+4,p=e.documentElement.clientWidth,h=e.documentElement.clientHeight;n.style.left=Math.max(4,Math.min(l,p-290))+"px",n.style.top=Math.max(4,Math.min(_,h-20))+"px",n.style.display="block"}catch{}}function R(){if(n)for(var d=n.childNodes,e=0;e<d.length;e++)d[e].classList.toggle("is-active",e===f),d[e].setAttribute("aria-selected",e===f?"true":"false")}function O(d){if(C){var e=(d.ctrlKey||d.metaKey)&&!d.shiftKey&&!d.altKey&&(d.code==="KeyZ"||d.key==="z"||d.key==="Z");if(e&&K()){d.preventDefault(),d.stopPropagation();return}/^(Control|Meta|Shift|Alt|CapsLock)$/.test(d.key)||(C=null)}if(!(!n||n.style.display==="none"||!m.length))switch(d.key){case"ArrowDown":d.preventDefault(),d.stopPropagation(),f=(f+1)%m.length,R();break;case"ArrowUp":d.preventDefault(),d.stopPropagation(),f=(f-1+m.length)%m.length,R();break;case"Enter":case"Tab":d.preventDefault(),d.stopPropagation(),N();break;case"Escape":d.preventDefault(),d.stopPropagation(),b();break}}function K(){var d=C;if(C=null,!d||!d.node.parentNode||d.node.data!==d.emo)return!1;var e=c.getSelection();if(!e||!e.rangeCount||!e.isCollapsed)return!1;var u=e.getRangeAt(0),t=u.startContainer,a=u.startOffset,r=t===d.node&&a===d.node.data.length||t.nodeType===3&&a===0&&t.previousSibling===d.node||t===d.node.parentNode&&t.childNodes[a-1]===d.node;if(!r)return!1;d.node.data=d.typed;var o=c.getDocument().createRange();if(o.setStart(d.node,d.typed.length),o.collapse(!0),e.removeAllRanges(),e.addRange(o),typeof c.fireChange=="function")try{c.fireChange()}catch{}return!0}function N(){if(!m.length||!k||x<0){b();return}var d=m[f]||m[0],e=z();if(!e||e.startContainer!==k){b();return}S(k,x,e.startOffset,d.emo),b()}function S(d,e,u,t){try{var a=d.data,r=a.slice(0,e),o=a.slice(u),i=d.parentNode;if(!i)return;var l=g.createDocumentFragment();r&&l.appendChild(g.createTextNode(r));var _=g.createTextNode(t);l.appendChild(_);var p=o?g.createTextNode(o):null;p&&l.appendChild(p),i.replaceChild(l,d),C={node:_,emo:t,typed:a.slice(e,u)};var h=g.createRange();p?h.setStart(p,0):h.setStartAfter(_),h.collapse(!0);var s=c.getSelection();if(s&&(s.removeAllRanges(),s.addRange(h)),typeof c.fireChange=="function")try{c.fireChange()}catch{}}catch{}}function b(){n&&(n.style.display="none"),m=[],f=0,x=-1,k=null}}
@@ -91,6 +91,16 @@ function RTE_Plugin_Footnotes() {
91
91
  editable.addEventListener("keyup", queueSync);
92
92
  editable.addEventListener("cut", queueSync);
93
93
  editable.addEventListener("paste", queueSync);
94
+ // 2026-10-13 A marker can arrive after the events above ran: an asynchronous paste or drop inserts it on a later
95
+ // tick, after the sync those events queued had already run, and a fast or synthetic paste had no keystroke after
96
+ // it. The observer resyncs after any change to the document outside the notes section. tools/footnotes/footnote-marker-arrives.mjs
97
+ try {
98
+ if (syncObserver) syncObserver.disconnect();
99
+ syncObserver = new MutationObserver(function () {
100
+ if (!syncRunning) queueSync();
101
+ });
102
+ syncObserver.observe(editable, { childList: true, subtree: true, characterData: true });
103
+ } catch (e) { /* no observer: the events above still resync */ }
94
104
  editable.addEventListener("drop", queueSync);
95
105
  // Clicking a marker jumps to its note, and clicking the backlink returns.
96
106
  editable.addEventListener("click", function (e) {
@@ -109,10 +119,21 @@ function RTE_Plugin_Footnotes() {
109
119
  // Coalesce bursts of edits into one renumber. setTimeout, not rAF: the
110
120
  // editor may live in a background tab or a hidden iframe, where rAF never
111
121
  // fires and the numbering would silently stop updating.
122
+ // 2026-10-13 runSync is the only place the queued renumber runs. The observer's own records from the sync are
123
+ // dropped, so the renumber does not queue itself again.
124
+ var syncObserver = null, syncRunning = false;
125
+ function runSync() {
126
+ syncRunning = true;
127
+ try { syncFootnotes(); }
128
+ finally {
129
+ if (syncObserver) { try { syncObserver.takeRecords(); } catch (e) { } }
130
+ syncRunning = false;
131
+ }
132
+ }
112
133
  function queueSync() {
113
134
  if (syncQueued) return;
114
135
  syncQueued = true;
115
- setTimeout(function () { syncQueued = false; syncFootnotes(); }, 0);
136
+ setTimeout(function () { syncQueued = false; runSync(); }, 0);
116
137
  }
117
138
 
118
139
  function closestClass(node, cls, root) {
@@ -210,7 +231,7 @@ function RTE_Plugin_Footnotes() {
210
231
  back.setAttribute("data-fn-id", id);
211
232
  back.setAttribute("href", "#fnref-" + id);
212
233
  back.setAttribute("contenteditable", "false");
213
- back.setAttribute("title", "Back to reference");
234
+ back.setAttribute("title", ((typeof config !== "undefined" && config && config.text_fn_backref) || "Back to reference"));
214
235
  back.textContent = "\u21a9";
215
236
  li.appendChild(back);
216
237
  note.appendChild(li);
@@ -265,6 +286,7 @@ function RTE_Plugin_Footnotes() {
265
286
  if (!list.length) {
266
287
  // Last marker went away: drop the section rather than leaving an
267
288
  // empty "Footnotes" heading in the saved HTML.
289
+ if (sec) keepNotes(sec.querySelectorAll("li.rte-fn-note"));
268
290
  if (sec && sec.parentNode) sec.parentNode.removeChild(sec);
269
291
  return [];
270
292
  }
@@ -280,7 +302,9 @@ function RTE_Plugin_Footnotes() {
280
302
  var id = ref.getAttribute("data-fn-id");
281
303
  // A copy/paste of a marker duplicates its id; re-key the duplicate so
282
304
  // the two markers do not fight over one note.
305
+ var dupOf = null;
283
306
  if (seen[id]) {
307
+ dupOf = id;
284
308
  var fresh = newId();
285
309
  ref.setAttribute("data-fn-id", fresh);
286
310
  ref.setAttribute("id", "fnref-" + fresh);
@@ -296,14 +320,7 @@ function RTE_Plugin_Footnotes() {
296
320
  if (ref.getAttribute("id") !== "fnref-" + id) ref.setAttribute("id", "fnref-" + id);
297
321
 
298
322
  var note = ol.querySelector('li.rte-fn-note[data-fn-id="' + cssEscape(id) + '"]');
299
- if (!note) {
300
- // Marker with no note — a paste from elsewhere, or a note the
301
- // user deleted by hand. Give it an empty note to write into.
302
- note = doc.createElement("li");
303
- note.className = "rte-fn-note";
304
- note.setAttribute("data-fn-id", id);
305
- note.setAttribute("id", "fn-" + id);
306
- }
323
+ if (!note) note = noteFor(doc, ol, id, dupOf);
307
324
  note.setAttribute("data-fn-number", num);
308
325
  // Append in marker order — this both inserts new notes and re-sorts
309
326
  // existing ones to match a reordered document.
@@ -314,7 +331,7 @@ function RTE_Plugin_Footnotes() {
314
331
  // Drop notes whose marker is gone.
315
332
  var notes = Array.prototype.slice.call(ol.querySelectorAll("li.rte-fn-note"));
316
333
  for (var j = 0; j < notes.length; j++) {
317
- if (!seen[notes[j].getAttribute("data-fn-id")]) ol.removeChild(notes[j]);
334
+ if (!seen[notes[j].getAttribute("data-fn-id")]) { keepNotes([notes[j]]); ol.removeChild(notes[j]); }
318
335
  }
319
336
 
320
337
  if (ol.getAttribute("data-numbering") !== config.footnotesNumbering) {
@@ -323,6 +340,43 @@ function RTE_Plugin_Footnotes() {
323
340
  return out;
324
341
  }
325
342
 
343
+ // 2026-10-03 A note is KEPT when its marker goes, so a marker that comes back - cut + paste, a
344
+ // drag - gets its text back; and a copied marker gets a copy of its note, as in Word. The pass
345
+ // deleted the note and gave the returning marker an EMPTY one: cutting "Beta.[2]" and pasting
346
+ // it earlier renumbered correctly but lost "note B" for good. Kept for the editor's lifetime,
347
+ // keyed by footnote id. tools/footnotes/footnotes-move.mjs
348
+ var keptNotes = {};
349
+ function keepNotes(list) {
350
+ for (var i = 0; i < list.length; i++) {
351
+ var id = list[i].getAttribute("data-fn-id");
352
+ if (id) keptNotes[id] = list[i];
353
+ }
354
+ }
355
+ function noteFor(doc, ol, id, dupOf) {
356
+ var note = keptNotes[id];
357
+ if (note) {
358
+ delete keptNotes[id];
359
+ return note;
360
+ }
361
+ var src = dupOf ? (ol.querySelector('li.rte-fn-note[data-fn-id="' + cssEscape(dupOf) + '"]') || keptNotes[dupOf]) : null;
362
+ if (src) {
363
+ note = src.cloneNode(true);
364
+ var backs = note.querySelectorAll("a.rte-fn-back");
365
+ for (var i = 0; i < backs.length; i++) {
366
+ backs[i].setAttribute("data-fn-id", id);
367
+ backs[i].setAttribute("href", "#fnref-" + id);
368
+ }
369
+ } else {
370
+ // Marker with no note anywhere - a paste from another document, or a note the user
371
+ // deleted by hand. Give it an empty note to write into.
372
+ note = doc.createElement("li");
373
+ note.className = "rte-fn-note";
374
+ }
375
+ note.setAttribute("data-fn-id", id);
376
+ note.setAttribute("id", "fn-" + id);
377
+ return note;
378
+ }
379
+
326
380
  // Read the WHOLE <li> minus the backlink rather than one designated child.
327
381
  // The user can type anywhere in the note, and documents saved by an earlier
328
382
  // revision still carry a .rte-fn-text wrapper, so anything that trusts a