@richscripts/richtexteditor 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. package/README.md +360 -0
  2. package/admin/index.d.ts +100 -0
  3. package/admin/index.js +94 -0
  4. package/angular/component.ts +284 -0
  5. package/angular/index.d.ts +80 -0
  6. package/angular/index.ts +24 -0
  7. package/headless/index.d.ts +65 -0
  8. package/headless/index.js +272 -0
  9. package/index.d.ts +1412 -0
  10. package/integrations/shared.d.ts +138 -0
  11. package/integrations/shared.js +5182 -0
  12. package/package.json +130 -0
  13. package/react/index.d.ts +104 -0
  14. package/react/index.js +310 -0
  15. package/richtexteditor/CHANGELOG.md +1876 -0
  16. package/richtexteditor/all_plugins.js +31125 -0
  17. package/richtexteditor/all_plugins.min.js +78 -0
  18. package/richtexteditor/lang/rte-lang-af.js +193 -0
  19. package/richtexteditor/lang/rte-lang-ar.js +193 -0
  20. package/richtexteditor/lang/rte-lang-bg.js +193 -0
  21. package/richtexteditor/lang/rte-lang-bn.js +193 -0
  22. package/richtexteditor/lang/rte-lang-bs.js +193 -0
  23. package/richtexteditor/lang/rte-lang-ca.js +193 -0
  24. package/richtexteditor/lang/rte-lang-cs.js +193 -0
  25. package/richtexteditor/lang/rte-lang-cy.js +193 -0
  26. package/richtexteditor/lang/rte-lang-da.js +193 -0
  27. package/richtexteditor/lang/rte-lang-de.js +193 -0
  28. package/richtexteditor/lang/rte-lang-el.js +193 -0
  29. package/richtexteditor/lang/rte-lang-en.js +6 -0
  30. package/richtexteditor/lang/rte-lang-es.js +193 -0
  31. package/richtexteditor/lang/rte-lang-et.js +193 -0
  32. package/richtexteditor/lang/rte-lang-fa.js +193 -0
  33. package/richtexteditor/lang/rte-lang-fi.js +193 -0
  34. package/richtexteditor/lang/rte-lang-fil.js +193 -0
  35. package/richtexteditor/lang/rte-lang-fj.js +193 -0
  36. package/richtexteditor/lang/rte-lang-fr.js +193 -0
  37. package/richtexteditor/lang/rte-lang-ga.js +193 -0
  38. package/richtexteditor/lang/rte-lang-gu.js +193 -0
  39. package/richtexteditor/lang/rte-lang-he.js +193 -0
  40. package/richtexteditor/lang/rte-lang-hi.js +193 -0
  41. package/richtexteditor/lang/rte-lang-hr.js +193 -0
  42. package/richtexteditor/lang/rte-lang-ht.js +193 -0
  43. package/richtexteditor/lang/rte-lang-hu.js +193 -0
  44. package/richtexteditor/lang/rte-lang-id.js +193 -0
  45. package/richtexteditor/lang/rte-lang-is.js +193 -0
  46. package/richtexteditor/lang/rte-lang-it.js +193 -0
  47. package/richtexteditor/lang/rte-lang-ja.js +193 -0
  48. package/richtexteditor/lang/rte-lang-kn.js +193 -0
  49. package/richtexteditor/lang/rte-lang-ko.js +193 -0
  50. package/richtexteditor/lang/rte-lang-lt.js +193 -0
  51. package/richtexteditor/lang/rte-lang-lv.js +193 -0
  52. package/richtexteditor/lang/rte-lang-mg.js +193 -0
  53. package/richtexteditor/lang/rte-lang-mi.js +193 -0
  54. package/richtexteditor/lang/rte-lang-ml.js +193 -0
  55. package/richtexteditor/lang/rte-lang-mr.js +193 -0
  56. package/richtexteditor/lang/rte-lang-ms.js +193 -0
  57. package/richtexteditor/lang/rte-lang-mt.js +193 -0
  58. package/richtexteditor/lang/rte-lang-mww.js +193 -0
  59. package/richtexteditor/lang/rte-lang-nb.js +193 -0
  60. package/richtexteditor/lang/rte-lang-nl.js +193 -0
  61. package/richtexteditor/lang/rte-lang-otq.js +193 -0
  62. package/richtexteditor/lang/rte-lang-pa.js +193 -0
  63. package/richtexteditor/lang/rte-lang-pl.js +193 -0
  64. package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
  65. package/richtexteditor/lang/rte-lang-pt.js +193 -0
  66. package/richtexteditor/lang/rte-lang-ro.js +193 -0
  67. package/richtexteditor/lang/rte-lang-ru.js +193 -0
  68. package/richtexteditor/lang/rte-lang-sk.js +193 -0
  69. package/richtexteditor/lang/rte-lang-sl.js +193 -0
  70. package/richtexteditor/lang/rte-lang-sm.js +193 -0
  71. package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
  72. package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
  73. package/richtexteditor/lang/rte-lang-sv.js +193 -0
  74. package/richtexteditor/lang/rte-lang-sw.js +193 -0
  75. package/richtexteditor/lang/rte-lang-ta.js +193 -0
  76. package/richtexteditor/lang/rte-lang-te.js +193 -0
  77. package/richtexteditor/lang/rte-lang-th.js +193 -0
  78. package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
  79. package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
  80. package/richtexteditor/lang/rte-lang-to.js +193 -0
  81. package/richtexteditor/lang/rte-lang-tr.js +193 -0
  82. package/richtexteditor/lang/rte-lang-ty.js +193 -0
  83. package/richtexteditor/lang/rte-lang-uk.js +193 -0
  84. package/richtexteditor/lang/rte-lang-ur.js +193 -0
  85. package/richtexteditor/lang/rte-lang-vi.js +193 -0
  86. package/richtexteditor/lang/rte-lang-yua.js +193 -0
  87. package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
  88. package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
  89. package/richtexteditor/lang/rte-lang.js +1 -0
  90. package/richtexteditor/plugins/-combine-plugin.txt +2 -0
  91. package/richtexteditor/plugins/-combine-plugins.bat +20 -0
  92. package/richtexteditor/plugins/a11yenhance.js +90 -0
  93. package/richtexteditor/plugins/accessibilitychecker.js +579 -0
  94. package/richtexteditor/plugins/aitoolkit.css +15830 -0
  95. package/richtexteditor/plugins/aitoolkit.js +19171 -0
  96. package/richtexteditor/plugins/all_plugins.js +31125 -0
  97. package/richtexteditor/plugins/all_plugins.min.js +78 -0
  98. package/richtexteditor/plugins/autoembed.js +174 -0
  99. package/richtexteditor/plugins/blocktypes.js +175 -0
  100. package/richtexteditor/plugins/bookmarkcard.js +226 -0
  101. package/richtexteditor/plugins/charlimit.js +195 -0
  102. package/richtexteditor/plugins/comments.js +672 -0
  103. package/richtexteditor/plugins/contentminimap.js +369 -0
  104. package/richtexteditor/plugins/crdt-engine.js +8905 -0
  105. package/richtexteditor/plugins/crdt-engine.min.js +5 -0
  106. package/richtexteditor/plugins/dialoga11y.js +174 -0
  107. package/richtexteditor/plugins/dictation.js +268 -0
  108. package/richtexteditor/plugins/documentimport.js +369 -0
  109. package/richtexteditor/plugins/documentoutline.js +341 -0
  110. package/richtexteditor/plugins/emailtoolkit.js +165 -0
  111. package/richtexteditor/plugins/emojiautocomplete.js +241 -0
  112. package/richtexteditor/plugins/foldheadings.js +181 -0
  113. package/richtexteditor/plugins/ghostcomplete.js +297 -0
  114. package/richtexteditor/plugins/html2pdf.js +158 -0
  115. package/richtexteditor/plugins/imageeditor.js +159 -0
  116. package/richtexteditor/plugins/insertcode.js +319 -0
  117. package/richtexteditor/plugins/insertemoji.js +229 -0
  118. package/richtexteditor/plugins/insertgallery.js +362 -0
  119. package/richtexteditor/plugins/inserttemplate.js +261 -0
  120. package/richtexteditor/plugins/localdraft.js +213 -0
  121. package/richtexteditor/plugins/markdownexport.js +110 -0
  122. package/richtexteditor/plugins/mathdialog.js +153 -0
  123. package/richtexteditor/plugins/mentionplugin.js +534 -0
  124. package/richtexteditor/plugins/mermaiddiagram.js +294 -0
  125. package/richtexteditor/plugins/readabilitystats.js +148 -0
  126. package/richtexteditor/plugins/readaloud.js +243 -0
  127. package/richtexteditor/plugins/restrictedediting.js +208 -0
  128. package/richtexteditor/plugins/revisionhistory.js +670 -0
  129. package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
  130. package/richtexteditor/plugins/slashcommand.js +705 -0
  131. package/richtexteditor/plugins/smartchips.js +250 -0
  132. package/richtexteditor/plugins/spellcheck.js +181 -0
  133. package/richtexteditor/plugins/todolist.js +137 -0
  134. package/richtexteditor/plugins/trackedchanges.js +658 -0
  135. package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
  136. package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
  137. package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
  138. package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
  139. package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
  140. package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
  141. package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
  142. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
  143. package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
  144. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
  145. package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
  146. package/richtexteditor/plugins/typewriter.js +203 -0
  147. package/richtexteditor/plugins/wordexport.js +0 -0
  148. package/richtexteditor/plugins/yjscollab.js +693 -0
  149. package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
  150. package/richtexteditor/rte-config.js +933 -0
  151. package/richtexteditor/rte.js +943 -0
  152. package/richtexteditor/rte_mobile.css +234 -0
  153. package/richtexteditor/rte_theme_default.css +4819 -0
  154. package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
  155. package/richtexteditor/runtime/help.htm +162 -0
  156. package/richtexteditor/runtime/images/1x1.gif +0 -0
  157. package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
  158. package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
  159. package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
  160. package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
  161. package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
  162. package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
  163. package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
  164. package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
  165. package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
  166. package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
  167. package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
  168. package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
  169. package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
  170. package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
  171. package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
  172. package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
  173. package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
  174. package/richtexteditor/runtime/images/formbn.gif +0 -0
  175. package/richtexteditor/runtime/images/multiclavier.gif +0 -0
  176. package/richtexteditor/runtime/richtexteditor_content.css +362 -0
  177. package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
  178. package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
  179. package/server/index.d.ts +21 -0
  180. package/server/index.js +23 -0
  181. package/svelte/index.d.ts +19 -0
  182. package/svelte/index.js +59 -0
  183. package/vue/index.d.ts +94 -0
  184. package/vue/index.js +291 -0
@@ -0,0 +1,672 @@
1
+ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
2
+
3
+ if (!RTE_DefaultConfig.svgCode_commentadd) {
4
+ RTE_DefaultConfig.svgCode_commentadd = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 5h14a2 2 0 012 2v8a2 2 0 01-2 2h-7l-4 4v-4H4a2 2 0 01-2-2V7a2 2 0 012-2z"/><path d="M8 9h8"/><path d="M8 12h5"/></svg>';
5
+ }
6
+
7
+ RTE_DefaultConfig.plugin_comments = RTE_Plugin_Comments;
8
+
9
+ function RTE_Plugin_Comments() {
10
+ var obj = this;
11
+ var config;
12
+ var editor;
13
+ var sidebar = null;
14
+ var composer = null;
15
+
16
+ obj.PluginName = "Comments";
17
+
18
+ obj.InitConfig = function (argconfig) {
19
+ config = argconfig;
20
+ if (config.commentsEnabled === false) return;
21
+
22
+ config.text_commentadd = config.text_commentadd || "Comment";
23
+ config.commentHighlightBg = config.commentHighlightBg || "#fff9c4";
24
+ config.commentHighlightBorder = config.commentHighlightBorder || "#f9a825";
25
+
26
+ appendToolbarCommand("toolbar_default", "#{commentadd}");
27
+ appendToolbarCommand("toolbar_full", "#{commentadd}");
28
+ appendToolbarCommand("toolbar_mobile", "#{commentadd}");
29
+ if ((config.controltoolbar_TEXT || "").indexOf("commentadd") === -1) {
30
+ config.controltoolbar_TEXT = (config.controltoolbar_TEXT || "") + "|{commentadd}";
31
+ }
32
+ };
33
+
34
+ obj.InitEditor = function (argeditor) {
35
+ editor = argeditor;
36
+ if (config.commentsEnabled === false) return;
37
+
38
+ editor.comments = {
39
+ add: function (options) { return addComment(options); },
40
+ reply: function (commentId, text) { return replyToComment(commentId, text); },
41
+ resolve: function (commentId) { return resolveComment(commentId); },
42
+ delete: function (commentId) { return deleteComment(commentId); },
43
+ list: function (filter) { return listComments(filter); },
44
+ openSidebar: function () { openSidebar(); },
45
+ closeSidebar: function () { closeSidebar(); },
46
+ toggleSidebar: function () { if (sidebar && sidebar.isConnected) closeSidebar(); else openSidebar(); },
47
+ focusComment: function (id) { focusComment(id); }
48
+ };
49
+
50
+ injectStyles();
51
+
52
+ editor.toolbarFactoryMap = editor.toolbarFactoryMap || {};
53
+ editor.toolbarFactoryMap["commentadd"] = function (cmd) {
54
+ return editor.createToolbarButton(cmd);
55
+ };
56
+
57
+ editor.attachEvent("exec_command_commentadd", function (state) {
58
+ state.returnValue = true;
59
+ state.stopBubble = true;
60
+ openComposerForSelection();
61
+ });
62
+
63
+ editor.getEditable().addEventListener("click", function (e) {
64
+ var span = e.target && e.target.closest ? e.target.closest(".rte-comment") : null;
65
+ if (!span) return;
66
+ var id = span.getAttribute("data-comment-id");
67
+ if (id) focusComment(id);
68
+ });
69
+
70
+ // Restore existing comments on load so their spans re-hydrate from the ledger.
71
+ // (Span markup is persisted in the HTML itself, but we also make sure no
72
+ // orphan ledger entries point at missing spans.)
73
+ pruneOrphanComments();
74
+ };
75
+
76
+ function appendToolbarCommand(toolbar, item) {
77
+ if (!config[toolbar]) return;
78
+ if (config[toolbar].indexOf(item) !== -1) return;
79
+ config[toolbar] = config[toolbar] + item;
80
+ }
81
+
82
+ function getCurrentUser() {
83
+ if (config.currentUser && config.currentUser.id) return config.currentUser;
84
+ return { id: "user", name: "User", color: "#2563eb" };
85
+ }
86
+
87
+ // The sidebar/composer are body-appended, outside the editor container, so
88
+ // the container's rte-dark class can't reach them via CSS. Mirror forced
89
+ // dark mode by adding rte-comment-dark; automatic dark uses a media query.
90
+ function isEditorDark() {
91
+ try {
92
+ var host = editor.container || (editor.getEditable && editor.getEditable().closest && editor.getEditable().closest(".richtexteditor"));
93
+ return !!(host && host.classList && host.classList.contains("rte-dark"));
94
+ } catch (e) { return false; }
95
+ }
96
+
97
+ function listComments(filter) {
98
+ if (!editor.reviewLedger) return [];
99
+ return editor.reviewLedger.list().filter(function (e) {
100
+ if (e.changeType !== "comment") return false;
101
+ if (filter && filter.status && e.status !== filter.status) return false;
102
+ if (filter && filter.author && e.author.id !== filter.author) return false;
103
+ return true;
104
+ });
105
+ }
106
+
107
+ function pruneOrphanComments() {
108
+ var editable = editor.getEditable();
109
+ var entries = listComments();
110
+ for (var i = 0; i < entries.length; i++) {
111
+ var id = entries[i].id;
112
+ if (!editable.querySelector('[data-comment-id="' + cssEscape(id) + '"]')) {
113
+ // Don't actually delete — just mark stale.
114
+ if (editor.reviewLedger && entries[i].status === "pending") {
115
+ editor.reviewLedger.update(id, { status: "stale" });
116
+ }
117
+ }
118
+ }
119
+ }
120
+
121
+ // --- creating comments ---
122
+
123
+ function openComposerForSelection() {
124
+ var sel = editor.getSelection();
125
+ if (!sel || sel.rangeCount === 0 || sel.isCollapsed) {
126
+ // No selection — open composer anchored to caret, with empty snapshot.
127
+ var rangeInfo = sel && sel.rangeCount ? snapshotRange(sel.getRangeAt(0)) : null;
128
+ showComposer({ anchor: rangeInfo, allowEmpty: true });
129
+ return;
130
+ }
131
+ var range = sel.getRangeAt(0);
132
+ showComposer({ anchor: snapshotRange(range), range: range });
133
+ }
134
+
135
+ function snapshotRange(range) {
136
+ if (!range) return null;
137
+ return {
138
+ startContainer: range.startContainer,
139
+ startOffset: range.startOffset,
140
+ endContainer: range.endContainer,
141
+ endOffset: range.endOffset,
142
+ text: range.toString()
143
+ };
144
+ }
145
+
146
+ function showComposer(options) {
147
+ closeComposer();
148
+ options = options || {};
149
+ var host = editor.iframe.ownerDocument;
150
+ composer = host.createElement("div");
151
+ composer.className = "rte-comment-composer" + (isEditorDark() ? " rte-comment-dark" : "");
152
+ composer.setAttribute("role", "dialog");
153
+ composer.setAttribute("aria-label", "Add comment");
154
+
155
+ var header = host.createElement("div");
156
+ header.className = "rte-comment-composer-header";
157
+ header.textContent = "Add comment";
158
+ composer.appendChild(header);
159
+
160
+ var textarea = host.createElement("textarea");
161
+ textarea.className = "rte-comment-composer-textarea";
162
+ textarea.placeholder = "Type your comment...";
163
+ textarea.rows = 3;
164
+ composer.appendChild(textarea);
165
+
166
+ var actions = host.createElement("div");
167
+ actions.className = "rte-comment-composer-actions";
168
+ var cancel = host.createElement("button");
169
+ cancel.type = "button";
170
+ cancel.className = "rte-comment-btn rte-comment-btn-ghost";
171
+ cancel.textContent = "Cancel";
172
+ cancel.addEventListener("mousedown", function (e) { e.preventDefault(); closeComposer(); });
173
+ var submit = host.createElement("button");
174
+ submit.type = "button";
175
+ submit.className = "rte-comment-btn rte-comment-btn-primary";
176
+ submit.textContent = "Comment";
177
+ submit.addEventListener("mousedown", function (e) {
178
+ e.preventDefault();
179
+ var text = textarea.value.replace(/^\s+|\s+$/g, "");
180
+ if (!text) return;
181
+ addComment({ anchor: options.anchor, text: text });
182
+ closeComposer();
183
+ });
184
+ actions.appendChild(cancel);
185
+ actions.appendChild(submit);
186
+ composer.appendChild(actions);
187
+
188
+ host.body.appendChild(composer);
189
+ positionComposer(options.anchor);
190
+ setTimeout(function () { textarea.focus(); }, 0);
191
+
192
+ var escListener = function (e) {
193
+ if (e.key === "Escape" || e.key === "Esc") closeComposer();
194
+ };
195
+ composer.addEventListener("keydown", escListener);
196
+ }
197
+
198
+ function positionComposer(anchor) {
199
+ if (!composer) return;
200
+ composer.style.position = "absolute";
201
+ var iframe = editor.iframe;
202
+ var ir = iframe.getBoundingClientRect();
203
+ var left = ir.left + window.pageXOffset + 12;
204
+ var top = ir.top + window.pageYOffset + 12;
205
+ if (anchor && anchor.startContainer) {
206
+ try {
207
+ var r = editor.getDocument().createRange();
208
+ r.setStart(anchor.startContainer, anchor.startOffset);
209
+ r.setEnd(anchor.endContainer, anchor.endOffset);
210
+ var rects = r.getClientRects();
211
+ var rect = rects && rects.length ? rects[rects.length - 1] : r.getBoundingClientRect();
212
+ left = ir.left + (rect.left || 0) + window.pageXOffset;
213
+ top = ir.top + (rect.bottom || rect.top || 0) + window.pageYOffset + 6;
214
+ } catch (ignore) { }
215
+ }
216
+ composer.style.left = left + "px";
217
+ composer.style.top = top + "px";
218
+ }
219
+
220
+ function closeComposer() {
221
+ if (composer && composer.parentNode) composer.parentNode.removeChild(composer);
222
+ composer = null;
223
+ }
224
+
225
+ function addComment(options) {
226
+ options = options || {};
227
+ if (!editor.reviewLedger) return null;
228
+ var text = (options.text || "").replace(/^\s+|\s+$/g, "");
229
+ if (!text) return null;
230
+
231
+ var id = "cmt-" + Date.now() + "-" + Math.floor(Math.random() * 10000);
232
+ var user = getCurrentUser();
233
+ var anchor = options.anchor;
234
+ var wrappedText = "";
235
+
236
+ if (anchor && anchor.startContainer && anchor.endContainer && anchor.text) {
237
+ var editdoc = editor.getDocument();
238
+ try {
239
+ var wrapRange = editdoc.createRange();
240
+ wrapRange.setStart(anchor.startContainer, anchor.startOffset);
241
+ wrapRange.setEnd(anchor.endContainer, anchor.endOffset);
242
+ var span = editdoc.createElement("span");
243
+ span.className = "rte-comment";
244
+ span.setAttribute("data-comment-id", id);
245
+ span.setAttribute("data-comment-author", user.id);
246
+ var fragment = wrapRange.extractContents();
247
+ span.appendChild(fragment);
248
+ wrapRange.insertNode(span);
249
+ wrappedText = span.textContent;
250
+ // Collapse caret after the span.
251
+ var after = editdoc.createRange();
252
+ after.setStartAfter(span);
253
+ after.collapse(true);
254
+ var sel = editor.getSelection();
255
+ sel.removeAllRanges();
256
+ sel.addRange(after);
257
+ } catch (err) {
258
+ console.warn("comments: range wrap failed:", err);
259
+ }
260
+ }
261
+
262
+ editor.reviewLedger.add({
263
+ id: id,
264
+ changeType: "comment",
265
+ author: user,
266
+ text: text,
267
+ originalText: wrappedText || (anchor && anchor.text) || "",
268
+ status: "pending",
269
+ sourceLabel: "Comment",
270
+ createdAt: Date.now(),
271
+ replies: []
272
+ });
273
+
274
+ if (sidebar && sidebar.isConnected) renderSidebar();
275
+ else openSidebar();
276
+ focusComment(id);
277
+ try { if (typeof editor.fireEvent === "function") editor.fireEvent("comment_added", { id: id, text: text, author: user }); } catch (e) { }
278
+ return id;
279
+ }
280
+
281
+ function replyToComment(commentId, text) {
282
+ if (!editor.reviewLedger) return false;
283
+ var entry = editor.reviewLedger.get(commentId);
284
+ if (!entry) return false;
285
+ var replies = Array.isArray(entry.replies) ? entry.replies.slice() : [];
286
+ replies.push({
287
+ id: "rpl-" + Date.now() + "-" + Math.floor(Math.random() * 10000),
288
+ author: getCurrentUser(),
289
+ text: text,
290
+ createdAt: Date.now()
291
+ });
292
+ editor.reviewLedger.update(commentId, { replies: replies });
293
+ if (sidebar && sidebar.isConnected) renderSidebar();
294
+ return true;
295
+ }
296
+
297
+ function resolveComment(commentId) {
298
+ if (!editor.reviewLedger) return false;
299
+ var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
300
+ if (span) unwrapKeepChildren(span);
301
+ editor.reviewLedger.update(commentId, { status: "resolved", decidedAt: Date.now() });
302
+ if (sidebar && sidebar.isConnected) renderSidebar();
303
+ try { if (typeof editor.fireEvent === "function") editor.fireEvent("comment_resolved", { id: commentId }); } catch (e) { }
304
+ return true;
305
+ }
306
+
307
+ function deleteComment(commentId) {
308
+ if (!editor.reviewLedger) return false;
309
+ var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
310
+ if (span) unwrapKeepChildren(span);
311
+ editor.reviewLedger.remove(commentId);
312
+ if (sidebar && sidebar.isConnected) renderSidebar();
313
+ return true;
314
+ }
315
+
316
+ function focusComment(commentId) {
317
+ if (!sidebar || !sidebar.isConnected) openSidebar();
318
+ setTimeout(function () {
319
+ if (!sidebar) return;
320
+ var item = sidebar.querySelector('[data-comment-ref="' + cssEscape(commentId) + '"]');
321
+ if (item) {
322
+ item.scrollIntoView({ block: "nearest" });
323
+ item.classList.add("rte-comment-item-active");
324
+ setTimeout(function () { item.classList.remove("rte-comment-item-active"); }, 1400);
325
+ }
326
+ }, 30);
327
+ }
328
+
329
+ function unwrapKeepChildren(el) {
330
+ var parent = el.parentNode;
331
+ if (!parent) return;
332
+ while (el.firstChild) parent.insertBefore(el.firstChild, el);
333
+ parent.removeChild(el);
334
+ }
335
+
336
+ // 2026-05-28 Prefer native CSS.escape when available — handles every code
337
+ // point that would break an attribute selector (], ', U+0000, U+0001..0x1F,
338
+ // leading digits, etc.). Comment IDs are auto-generated today so the legacy
339
+ // single-char shim worked, but defense-in-depth pays off if a customer
340
+ // ever wires reviewLedger.add() with custom IDs containing punctuation.
341
+ function cssEscape(s) {
342
+ var str = String(s);
343
+ if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
344
+ try { return CSS.escape(str); } catch (e) { /* fall through */ }
345
+ }
346
+ // Fallback: escape every char that could break an attribute selector.
347
+ return str.replace(/["\\\]\[\(\)\{\}'#:;,<>=`~!@$%^&*+/?\s\x00-\x1F]/g, function (c) {
348
+ return "\\" + c;
349
+ });
350
+ }
351
+
352
+ // --- sidebar ---
353
+
354
+ function openSidebar() {
355
+ if (sidebar && sidebar.isConnected) { renderSidebar(); return; }
356
+ var host = editor.iframe.ownerDocument;
357
+ sidebar = host.createElement("div");
358
+ sidebar.className = "rte-comment-sidebar" + (isEditorDark() ? " rte-comment-dark" : "");
359
+ sidebar.setAttribute("role", "complementary");
360
+ sidebar.setAttribute("aria-label", "Comments");
361
+ var shell = getEditorShell();
362
+ if (shell) {
363
+ shell.parentNode.insertBefore(sidebar, shell.nextSibling);
364
+ shell.classList.add("rte-comment-sidebar-host");
365
+ } else {
366
+ host.body.appendChild(sidebar);
367
+ }
368
+ renderSidebar();
369
+ }
370
+
371
+ function closeSidebar() {
372
+ if (sidebar && sidebar.parentNode) sidebar.parentNode.removeChild(sidebar);
373
+ sidebar = null;
374
+ var shell = getEditorShell();
375
+ if (shell) shell.classList.remove("rte-comment-sidebar-host");
376
+ }
377
+
378
+ function getEditorShell() {
379
+ var el = editor.iframe;
380
+ while (el && el !== document.body) {
381
+ if (el.classList && el.classList.contains("richtexteditor")) return el;
382
+ el = el.parentNode;
383
+ }
384
+ return null;
385
+ }
386
+
387
+ function renderSidebar() {
388
+ if (!sidebar) return;
389
+ var host = sidebar.ownerDocument;
390
+ sidebar.innerHTML = "";
391
+
392
+ var header = host.createElement("div");
393
+ header.className = "rte-comment-sidebar-header";
394
+ var title = host.createElement("div");
395
+ title.className = "rte-comment-sidebar-title";
396
+ title.textContent = "Comments";
397
+ header.appendChild(title);
398
+ var close = host.createElement("button");
399
+ close.type = "button";
400
+ close.className = "rte-comment-sidebar-close";
401
+ close.setAttribute("aria-label", "Close");
402
+ close.innerHTML = '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 6l12 12"/><path d="M18 6L6 18"/></svg>';
403
+ close.addEventListener("mousedown", function (e) { e.preventDefault(); closeSidebar(); });
404
+ header.appendChild(close);
405
+ sidebar.appendChild(header);
406
+
407
+ var list = host.createElement("div");
408
+ list.className = "rte-comment-list";
409
+ sidebar.appendChild(list);
410
+
411
+ var entries = listComments({ status: "pending" });
412
+ if (!entries.length) {
413
+ var empty = host.createElement("div");
414
+ empty.className = "rte-comment-empty";
415
+ empty.textContent = "No open comments. Select text and click the Comment button to start.";
416
+ list.appendChild(empty);
417
+ return;
418
+ }
419
+
420
+ entries.sort(function (a, b) { return (b.createdAt || 0) - (a.createdAt || 0); });
421
+
422
+ for (var i = 0; i < entries.length; i++) {
423
+ list.appendChild(renderEntry(entries[i], host));
424
+ }
425
+ }
426
+
427
+ function renderEntry(entry, host) {
428
+ var wrap = host.createElement("div");
429
+ wrap.className = "rte-comment-item";
430
+ wrap.setAttribute("data-comment-ref", entry.id);
431
+ wrap.setAttribute("role", "article");
432
+
433
+ var topRow = host.createElement("div");
434
+ topRow.className = "rte-comment-item-top";
435
+ var avatar = host.createElement("span");
436
+ avatar.className = "rte-comment-avatar";
437
+ avatar.style.background = entry.author.color || "#2563eb";
438
+ avatar.textContent = initialsOf(entry.author.name || entry.author.id || "?");
439
+ var nameWrap = host.createElement("div");
440
+ nameWrap.className = "rte-comment-namewrap";
441
+ var name = host.createElement("div");
442
+ name.className = "rte-comment-name";
443
+ name.textContent = entry.author.name || entry.author.id || "";
444
+ var when = host.createElement("div");
445
+ when.className = "rte-comment-when";
446
+ when.textContent = relativeTime(entry.createdAt);
447
+ nameWrap.appendChild(name);
448
+ nameWrap.appendChild(when);
449
+ topRow.appendChild(avatar);
450
+ topRow.appendChild(nameWrap);
451
+
452
+ var actions = host.createElement("div");
453
+ actions.className = "rte-comment-actions";
454
+ var resolveBtn = host.createElement("button");
455
+ resolveBtn.type = "button";
456
+ resolveBtn.className = "rte-comment-action rte-comment-resolve";
457
+ resolveBtn.textContent = "Resolve";
458
+ resolveBtn.setAttribute("aria-label", "Resolve comment");
459
+ resolveBtn.addEventListener("mousedown", function (e) { e.preventDefault(); resolveComment(entry.id); });
460
+ var delBtn = host.createElement("button");
461
+ delBtn.type = "button";
462
+ delBtn.className = "rte-comment-action rte-comment-delete";
463
+ delBtn.textContent = "Delete";
464
+ delBtn.setAttribute("aria-label", "Delete comment");
465
+ delBtn.addEventListener("mousedown", function (e) { e.preventDefault(); deleteComment(entry.id); });
466
+ actions.appendChild(resolveBtn);
467
+ actions.appendChild(delBtn);
468
+ topRow.appendChild(actions);
469
+
470
+ wrap.appendChild(topRow);
471
+
472
+ if (entry.originalText) {
473
+ var quote = host.createElement("div");
474
+ quote.className = "rte-comment-quote";
475
+ quote.textContent = truncate(entry.originalText, 140);
476
+ quote.addEventListener("click", function () { scrollToSpan(entry.id); });
477
+ wrap.appendChild(quote);
478
+ }
479
+
480
+ var body = host.createElement("div");
481
+ body.className = "rte-comment-body";
482
+ body.textContent = entry.text;
483
+ wrap.appendChild(body);
484
+
485
+ var replies = Array.isArray(entry.replies) ? entry.replies : [];
486
+ for (var i = 0; i < replies.length; i++) {
487
+ var rep = replies[i];
488
+ var repEl = host.createElement("div");
489
+ repEl.className = "rte-comment-reply";
490
+ var repAv = host.createElement("span");
491
+ repAv.className = "rte-comment-reply-avatar";
492
+ repAv.style.background = (rep.author && rep.author.color) || "#64748b";
493
+ repAv.textContent = initialsOf((rep.author && rep.author.name) || "?");
494
+ var repBody = host.createElement("div");
495
+ repBody.className = "rte-comment-reply-body";
496
+ var repName = host.createElement("div");
497
+ repName.className = "rte-comment-reply-name";
498
+ repName.textContent = ((rep.author && rep.author.name) || "User") + " \u00B7 " + relativeTime(rep.createdAt);
499
+ var repText = host.createElement("div");
500
+ repText.className = "rte-comment-reply-text";
501
+ repText.textContent = rep.text;
502
+ repBody.appendChild(repName);
503
+ repBody.appendChild(repText);
504
+ repEl.appendChild(repAv);
505
+ repEl.appendChild(repBody);
506
+ wrap.appendChild(repEl);
507
+ }
508
+
509
+ // Reply composer
510
+ var replyRow = host.createElement("div");
511
+ replyRow.className = "rte-comment-reply-composer";
512
+ var replyInput = host.createElement("input");
513
+ replyInput.type = "text";
514
+ replyInput.className = "rte-comment-reply-input";
515
+ replyInput.placeholder = "Reply...";
516
+ replyInput.setAttribute("aria-label", "Reply to comment");
517
+ replyInput.addEventListener("keydown", function (e) {
518
+ if (e.key === "Enter" && !e.shiftKey) {
519
+ e.preventDefault();
520
+ var t = replyInput.value.replace(/^\s+|\s+$/g, "");
521
+ if (t) replyToComment(entry.id, t);
522
+ replyInput.value = "";
523
+ }
524
+ });
525
+ replyRow.appendChild(replyInput);
526
+ wrap.appendChild(replyRow);
527
+
528
+ return wrap;
529
+ }
530
+
531
+ function scrollToSpan(commentId) {
532
+ var span = editor.getEditable().querySelector('[data-comment-id="' + cssEscape(commentId) + '"]');
533
+ if (!span) return;
534
+ span.scrollIntoView({ block: "center", behavior: "smooth" });
535
+ span.classList.add("rte-comment-flash");
536
+ setTimeout(function () { span.classList.remove("rte-comment-flash"); }, 1200);
537
+ }
538
+
539
+ function initialsOf(label) {
540
+ var parts = String(label).trim().split(/\s+/);
541
+ if (!parts[0]) return "?";
542
+ if (parts.length === 1) return parts[0].charAt(0).toUpperCase();
543
+ return (parts[0].charAt(0) + parts[parts.length - 1].charAt(0)).toUpperCase();
544
+ }
545
+
546
+ function relativeTime(ts) {
547
+ if (!ts) return "";
548
+ var diff = Math.floor((Date.now() - ts) / 1000);
549
+ if (diff < 60) return "just now";
550
+ if (diff < 3600) return Math.floor(diff / 60) + "m ago";
551
+ if (diff < 86400) return Math.floor(diff / 3600) + "h ago";
552
+ return Math.floor(diff / 86400) + "d ago";
553
+ }
554
+
555
+ function truncate(s, n) {
556
+ if (s.length <= n) return s;
557
+ return s.slice(0, n - 1) + "\u2026";
558
+ }
559
+
560
+ function injectStyles() {
561
+ var host = (editor && editor.iframe && editor.iframe.ownerDocument) || document;
562
+ if (!host.querySelector("style[data-rte-comments]")) {
563
+ var style = host.createElement("style");
564
+ style.setAttribute("data-rte-comments", "1");
565
+ style.textContent = [
566
+ ".rte-comment-sidebar-host{display:flex!important;align-items:stretch}",
567
+ ".rte-comment-sidebar{width:310px;min-width:270px;max-width:min(360px,32vw);margin-left:12px;padding:0;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));border:1px solid rgba(148,163,184,.22);border-radius:16px;font-family:Aptos,'Segoe UI',sans-serif;font-size:13px;color:#172033;display:flex;flex-direction:column;max-height:calc(100vh - 120px);overflow:hidden;box-shadow:0 18px 42px rgba(29,78,216,.12),0 2px 8px rgba(15,23,42,.06)}",
568
+ ".rte-comment-sidebar-header{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:10px 10px 9px 14px;border-bottom:1px solid rgba(148,163,184,.16);font-weight:750;background:rgba(255,255,255,.72)}",
569
+ ".rte-comment-sidebar-title{font-size:13px;font-weight:850;letter-spacing:.02em}",
570
+ ".rte-comment-sidebar-close{width:28px;height:28px;border:1px solid rgba(100,116,139,.18);background:#fff;cursor:pointer;color:#64748b;padding:0;border-radius:999px;display:inline-flex;align-items:center;justify-content:center}",
571
+ ".rte-comment-sidebar-close:hover,.rte-comment-sidebar-close:focus-visible{background:#eef4ff;color:#0f3f9f;border-color:rgba(37,99,235,.28)}",
572
+ ".rte-comment-list{flex:1;min-height:0;overflow-y:auto;padding:6px;scrollbar-width:thin}",
573
+ ".rte-comment-empty{margin:6px;padding:14px;color:#52657e;font-size:12px;font-weight:700;line-height:1.45;text-align:center;background:#fff;border:1px dashed rgba(148,163,184,.28);border-radius:13px}",
574
+ ".rte-comment-item{padding:10px;border:1px solid rgba(148,163,184,.14);border-radius:14px;background:rgba(255,255,255,.82);box-shadow:0 8px 18px rgba(15,23,42,.04);transition:background 200ms ease,box-shadow 200ms ease,transform 200ms ease}",
575
+ ".rte-comment-item+.rte-comment-item{margin-top:7px}",
576
+ ".rte-comment-item-active{background:#eef4ff;box-shadow:0 12px 28px rgba(37,99,235,.14);transform:translateY(-1px)}",
577
+ ".rte-comment-item-top{display:grid;grid-template-columns:30px minmax(0,1fr) auto;align-items:center;gap:8px}",
578
+ ".rte-comment-avatar{width:30px;height:30px;border-radius:11px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:11px;font-weight:800;flex:0 0 30px;box-shadow:inset 0 0 0 1px rgba(255,255,255,.28)}",
579
+ ".rte-comment-namewrap{flex:1;min-width:0}",
580
+ ".rte-comment-name{font-weight:800;font-size:12px;line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}",
581
+ ".rte-comment-when{font-size:11px;color:#64748b}",
582
+ ".rte-comment-actions{display:flex;gap:4px;align-items:center}",
583
+ ".rte-comment-action{font-size:11px;font-weight:750;padding:4px 8px;border-radius:999px;border:1px solid rgba(100,116,139,.16);background:#fff;cursor:pointer}",
584
+ ".rte-comment-action:hover,.rte-comment-action:focus-visible{transform:translateY(-1px)}",
585
+ ".rte-comment-resolve{color:#0f3f9f;border-color:rgba(37,99,235,.2);background:#eef4ff}",
586
+ ".rte-comment-delete{color:#9f1239;border-color:rgba(159,18,57,.18);background:#fff1f2}",
587
+ ".rte-comment-quote{margin:8px 0 0;padding:7px 9px;background:#fff8e8;border:1px solid rgba(245,158,11,.18);border-left:3px solid #f59e0b;border-radius:11px;font-size:11px;color:#67430a;cursor:pointer;line-height:1.35}",
588
+ ".rte-comment-quote:hover{background:#fff3cf}",
589
+ ".rte-comment-body{margin-top:8px;font-size:13px;line-height:1.45;color:#172033;white-space:pre-wrap}",
590
+ ".rte-comment-reply{display:flex;gap:8px;margin-top:10px;padding-top:9px;border-top:1px dashed rgba(148,163,184,.22)}",
591
+ ".rte-comment-reply-avatar{width:24px;height:24px;border-radius:9px;display:inline-flex;align-items:center;justify-content:center;color:#fff;font-size:10px;font-weight:800;flex:0 0 24px}",
592
+ ".rte-comment-reply-body{flex:1;min-width:0}",
593
+ ".rte-comment-reply-name{font-size:11px;color:#64748b}",
594
+ ".rte-comment-reply-text{font-size:12px;color:#172033;line-height:1.45;white-space:pre-wrap;margin-top:2px}",
595
+ ".rte-comment-reply-composer{margin-top:8px;display:flex}",
596
+ ".rte-comment-reply-input{flex:1;min-width:0;padding:7px 10px;border-radius:999px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:12px;background:#fff}",
597
+ ".rte-comment-reply-input:focus,.rte-comment-composer-textarea:focus{outline:2px solid rgba(37,99,235,.18);border-color:rgba(37,99,235,.38)}",
598
+ ".rte-comment-composer{position:absolute;z-index:2147483000;width:min(320px,calc(100vw - 24px));background:linear-gradient(180deg,#fff,#f8fbff);border:1px solid rgba(148,163,184,.22);box-shadow:0 18px 42px rgba(29,78,216,.16),0 2px 8px rgba(15,23,42,.08);border-radius:16px;padding:12px;font-family:Aptos,'Segoe UI',sans-serif;color:#172033}",
599
+ ".rte-comment-composer-header{font-weight:850;font-size:13px;margin-bottom:8px;color:#172033}",
600
+ ".rte-comment-composer-textarea{width:100%;box-sizing:border-box;padding:9px 10px;border-radius:12px;border:1px solid rgba(148,163,184,.24);font:inherit;font-size:13px;resize:vertical;min-height:70px;background:#fff}",
601
+ ".rte-comment-composer-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:9px}",
602
+ ".rte-comment-btn{min-height:30px;padding:0 13px;border-radius:999px;border:1px solid rgba(100,116,139,.18);font:750 12px/1 Aptos,'Segoe UI',sans-serif;cursor:pointer}",
603
+ ".rte-comment-btn-ghost{background:#fff;color:#52657e}",
604
+ ".rte-comment-btn-primary{background:#1d67ba;color:#fff;border-color:#1d67ba;box-shadow:0 8px 16px rgba(29,103,186,.18)}",
605
+ // Dark mode — the sidebar/composer are body-appended (outside the
606
+ // editor container) so the .rte-dark class can't reach them. Apply
607
+ // automatically via prefers-color-scheme, and via an explicit
608
+ // .rte-comment-dark class set when the editor is forced dark.
609
+ "@media (prefers-color-scheme:dark){",
610
+ " .rte-comment-sidebar,.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
611
+ " .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
612
+ " .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
613
+ " .rte-comment-item-active{background:#1e3a8a}",
614
+ " .rte-comment-empty{background:#0f172a;color:#94a3b8;border-color:#334155}",
615
+ " .rte-comment-when,.rte-comment-reply-name{color:#94a3b8}",
616
+ " .rte-comment-body,.rte-comment-reply-text,.rte-comment-composer-header{color:#e2e8f0}",
617
+ " .rte-comment-reply-input,.rte-comment-composer-textarea{background:#0f172a;color:#e2e8f0;border-color:#334155}",
618
+ " .rte-comment-action,.rte-comment-btn-ghost,.rte-comment-sidebar-close{background:#0f172a;color:#cbd5e1;border-color:#334155}",
619
+ " .rte-comment-quote{background:rgba(245,158,11,.12);color:#fcd34d}",
620
+ "}",
621
+ ".rte-comment-dark.rte-comment-sidebar,.rte-comment-dark.rte-comment-composer{background:linear-gradient(180deg,#1e293b,#0f172a);border-color:#334155;color:#e2e8f0}",
622
+ ".rte-comment-dark .rte-comment-sidebar-header{background:rgba(30,41,59,.72);border-color:#334155}",
623
+ ".rte-comment-dark .rte-comment-item{background:rgba(30,41,59,.85);border-color:#334155}",
624
+ ".rte-comment-dark .rte-comment-item-active{background:#1e3a8a}",
625
+ ".rte-comment-dark .rte-comment-empty{background:#0f172a;color:#94a3b8;border-color:#334155}",
626
+ ".rte-comment-dark .rte-comment-when,.rte-comment-dark .rte-comment-reply-name{color:#94a3b8}",
627
+ ".rte-comment-dark .rte-comment-body,.rte-comment-dark .rte-comment-reply-text,.rte-comment-dark .rte-comment-composer-header{color:#e2e8f0}",
628
+ ".rte-comment-dark .rte-comment-reply-input,.rte-comment-dark .rte-comment-composer-textarea{background:#0f172a;color:#e2e8f0;border-color:#334155}",
629
+ ".rte-comment-dark .rte-comment-action,.rte-comment-dark .rte-comment-btn-ghost,.rte-comment-dark .rte-comment-sidebar-close{background:#0f172a;color:#cbd5e1;border-color:#334155}",
630
+ ".rte-comment-dark .rte-comment-quote{background:rgba(245,158,11,.12);color:#fcd34d}"
631
+ ].join("\n");
632
+ host.head.appendChild(style);
633
+ }
634
+
635
+ var editdoc = editor.getDocument();
636
+ if (editdoc && editdoc.head && !editdoc.querySelector("style[data-rte-comments-inline]")) {
637
+ var iStyle = editdoc.createElement("style");
638
+ iStyle.setAttribute("data-rte-comments-inline", "1");
639
+ iStyle.textContent = [
640
+ ".rte-comment{background:" + config.commentHighlightBg + ";border-bottom:2px solid " + config.commentHighlightBorder + ";cursor:pointer;padding:0 1px;border-radius:2px}",
641
+ ".rte-comment:hover{background:rgba(253,230,138,.6)}",
642
+ ".rte-comment-flash{animation:rte-comment-flash 1.2s ease}",
643
+ "@keyframes rte-comment-flash{0%{background:rgba(251,191,36,.9)}100%{background:" + config.commentHighlightBg + "}}"
644
+ ].join("\n");
645
+ editdoc.head.appendChild(iStyle);
646
+ }
647
+ }
648
+
649
+ // Block-anchored comments: img, table, figure, video, audio, iframe, embed, object, hr, svg.
650
+ function resolveCommentTarget(range, editdoc) {
651
+ if (!range) return null;
652
+ var container = range.commonAncestorContainer;
653
+ var el = container && container.nodeType === 1 ? container : (container && container.parentNode);
654
+ if (!el) return null;
655
+ // If selection collapses on/inside an embedded block, anchor to that element instead of a text range.
656
+ var block = el.closest && el.closest("img,table,figure,video,audio,iframe,embed,object,hr,svg");
657
+ if (!block && el.querySelector) {
658
+ block = el.querySelector("img,table,figure,video,audio,iframe,embed,object,hr,svg");
659
+ }
660
+ if (block) {
661
+ block.setAttribute("data-rte-comment-anchor", "element");
662
+ return { kind: "element", element: block };
663
+ }
664
+ return { kind: "range", range: range };
665
+ }
666
+
667
+ function isElementAnchoredComment(el) {
668
+ if (!el || el.nodeType !== 1 || !el.getAttribute) return false;
669
+ if (el.getAttribute("data-rte-comment-anchor") === "element") return true;
670
+ return false;
671
+ }
672
+ }