@richscripts/richtexteditor 2.4.0 → 2.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/migration/README.md +100 -0
- package/migration/cli.js +294 -0
- package/migration/index.d.ts +18 -0
- package/migration/index.js +242 -0
- package/migration/report-schema-v2.json +127 -0
- package/package.json +14 -2
- package/richtexteditor/CHANGELOG.md +326 -1
- package/richtexteditor/all_plugins.js +14982 -4831
- package/richtexteditor/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/accessibilitychecker.js +8 -7
- package/richtexteditor/plugins/aitoolkit.css +17 -8
- package/richtexteditor/plugins/aitoolkit.js +8 -8
- package/richtexteditor/plugins/all_plugins.js +14982 -4831
- package/richtexteditor/plugins/all_plugins.min.js +62 -42
- package/richtexteditor/plugins/autocorrect.js +353 -0
- package/richtexteditor/plugins/bookmarkcard.js +12 -1
- package/richtexteditor/plugins/changecase.js +238 -0
- package/richtexteditor/plugins/comments.js +15 -28
- package/richtexteditor/plugins/contentminimap.js +6 -5
- package/richtexteditor/plugins/crdt-engine.js +2201 -198
- package/richtexteditor/plugins/crdt-engine.min.js +5 -5
- package/richtexteditor/plugins/crossreference.js +408 -0
- package/richtexteditor/plugins/documentimport.js +915 -21
- package/richtexteditor/plugins/documentoutline.js +5 -4
- package/richtexteditor/plugins/docxexport.js +960 -0
- package/richtexteditor/plugins/draghandle.js +309 -0
- package/richtexteditor/plugins/footnotes.js +398 -0
- package/richtexteditor/plugins/formatpainter.js +298 -0
- package/richtexteditor/plugins/formattingmarks.js +246 -0
- package/richtexteditor/plugins/html2pdf.js +108 -4
- package/richtexteditor/plugins/imageeditor.js +37 -7
- package/richtexteditor/plugins/insertgallery.js +553 -170
- package/richtexteditor/plugins/keyboarda11y.js +316 -0
- package/richtexteditor/plugins/linenumbers.js +352 -0
- package/richtexteditor/plugins/linkchecker.js +396 -0
- package/richtexteditor/plugins/mentionplugin.js +4 -3
- package/richtexteditor/plugins/mergefields.js +328 -0
- package/richtexteditor/plugins/multilevellist.js +137 -0
- package/richtexteditor/plugins/pagination.js +505 -0
- package/richtexteditor/plugins/pdfexport.js +1137 -0
- package/richtexteditor/plugins/permanentpen.js +211 -0
- package/richtexteditor/plugins/revisionhistory.js +47 -34
- package/richtexteditor/plugins/rtlui.js +150 -0
- package/richtexteditor/plugins/sanitizer.js +510 -0
- package/richtexteditor/plugins/slashcommand.js +142 -17
- package/richtexteditor/plugins/spellcheck.js +6 -3
- package/richtexteditor/plugins/tableofcontents.js +353 -0
- package/richtexteditor/plugins/tabletools.js +342 -0
- package/richtexteditor/plugins/textdirection.js +313 -0
- package/richtexteditor/plugins/trackedchanges.js +37 -6
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +1 -1
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +1 -1
- package/richtexteditor/plugins/vendor/html2pdf.bundle.min.js +3 -0
- package/richtexteditor/plugins/watermark.js +214 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +49 -11
- package/richtexteditor/rte-config.js +44 -44
- package/richtexteditor/rte.js +91 -63
- package/richtexteditor/rte_theme_default.css +483 -384
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-07-27 Link checker. Finds links that are broken, unsafe, unreachable by
|
|
4
|
+
// assistive technology, or actively misleading.
|
|
5
|
+
//
|
|
6
|
+
// TinyMCE's Link Checker is premium AND infrastructural: "This plugin is only
|
|
7
|
+
// available for paid TinyMCE subscriptions", and self-hosted customers "will
|
|
8
|
+
// need to provide a URL to their deployment of the link checking service via
|
|
9
|
+
// the linkchecker_service_url parameter". So the feature costs a subscription
|
|
10
|
+
// and a service to run.
|
|
11
|
+
//
|
|
12
|
+
// This one inverts that. The whole offline half needs NO network and NO service:
|
|
13
|
+
// broken in-document anchors, unsafe schemes, mixed content, unlabelled links
|
|
14
|
+
// and deceptive link text are all decidable from the document itself, and that
|
|
15
|
+
// is where most real link defects actually live. Checking whether a remote URL
|
|
16
|
+
// still resolves is the only part that needs the network, and that is delegated
|
|
17
|
+
// to a host-supplied resolver — the same BYOK shape as the AI, spell, bookmark
|
|
18
|
+
// and chip resolvers elsewhere in this editor.
|
|
19
|
+
//
|
|
20
|
+
// This matters beyond convenience: this editor's position is that it makes no
|
|
21
|
+
// outbound calls of its own. A built-in URL prober would quietly break that, and
|
|
22
|
+
// would send every URL a customer edits to somebody's server.
|
|
23
|
+
//
|
|
24
|
+
// Design note: the highlight is CHROME, not content. It is stripped around every
|
|
25
|
+
// serialize (the pagination.js contract), so marking up problems never changes
|
|
26
|
+
// the HTML you save.
|
|
27
|
+
RTE_DefaultConfig.plugin_linkchecker = RTE_Plugin_LinkChecker;
|
|
28
|
+
|
|
29
|
+
// Optional. function (urls) -> Promise<{ "<url>": {ok:true|false, status:404} }>
|
|
30
|
+
// (written without a literal URL on purpose: this file is concatenated into
|
|
31
|
+
// all_plugins.js, and customers run static scanners over that)
|
|
32
|
+
// Supply one to have external URLs checked; omit it and only offline checks run.
|
|
33
|
+
if (typeof RTE_DefaultConfig.linkCheckResolver === "undefined") RTE_DefaultConfig.linkCheckResolver = null;
|
|
34
|
+
// Schemes rejected outright.
|
|
35
|
+
if (typeof RTE_DefaultConfig.linkCheckUnsafeSchemes === "undefined") {
|
|
36
|
+
RTE_DefaultConfig.linkCheckUnsafeSchemes = ["javascript:", "data:", "vbscript:", "file:"];
|
|
37
|
+
}
|
|
38
|
+
// Warn when an https page links to http.
|
|
39
|
+
if (typeof RTE_DefaultConfig.linkCheckMixedContent === "undefined") RTE_DefaultConfig.linkCheckMixedContent = true;
|
|
40
|
+
|
|
41
|
+
function RTE_Plugin_LinkChecker() {
|
|
42
|
+
var obj = this;
|
|
43
|
+
var config, editor;
|
|
44
|
+
var boundDoc = null;
|
|
45
|
+
var wrapped = false;
|
|
46
|
+
var lastIssues = [];
|
|
47
|
+
var highlighting = false;
|
|
48
|
+
|
|
49
|
+
obj.PluginName = "LinkChecker";
|
|
50
|
+
|
|
51
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
52
|
+
|
|
53
|
+
obj.InitEditor = function (argeditor) {
|
|
54
|
+
editor = argeditor;
|
|
55
|
+
|
|
56
|
+
editor.attachEvent("exec_command_checklinks", function (state) {
|
|
57
|
+
state.returnValue = true;
|
|
58
|
+
obj.Check();
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
setup();
|
|
62
|
+
try { editor.attachEvent("ready", setup); } catch (e) {}
|
|
63
|
+
try { editor.attachEvent("aftersethtml", setup); } catch (e) {}
|
|
64
|
+
setTimeout(setup, 0);
|
|
65
|
+
|
|
66
|
+
// Public API.
|
|
67
|
+
editor.checkLinks = function () { return obj.Check(); };
|
|
68
|
+
editor.getLinkIssues = function () { return lastIssues.slice(); };
|
|
69
|
+
editor.highlightLinkIssues = function (on) { return applyHighlight(on !== false); };
|
|
70
|
+
editor.clearLinkHighlights = function () { return applyHighlight(false); };
|
|
71
|
+
editor.getLinkCheckerCss = function () { return css(); };
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
function setup() {
|
|
75
|
+
var doc = getDoc();
|
|
76
|
+
if (!doc) return;
|
|
77
|
+
injectStyles(doc);
|
|
78
|
+
wrapSerializers();
|
|
79
|
+
boundDoc = doc;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function getDoc() { try { return editor.getDocument(); } catch (e) { return null; } }
|
|
83
|
+
function getEditable() { try { return editor.getEditable(); } catch (e) { return null; } }
|
|
84
|
+
|
|
85
|
+
// ---- the checks ------------------------------------------------------
|
|
86
|
+
|
|
87
|
+
// Every <a> that is meant to be a LINK — deliberately not "a[href]".
|
|
88
|
+
// rte.js sanitises a javascript: href by removing the attribute outright, so
|
|
89
|
+
// the most dangerous input in the document arrives as an <a> with no href at
|
|
90
|
+
// all. Selecting on a[href] would make exactly that case invisible: the user
|
|
91
|
+
// sees a link that goes nowhere and the checker reports nothing.
|
|
92
|
+
// A bare <a name="x"> / <a id="x"> with no href is an anchor TARGET, not a
|
|
93
|
+
// link, and is excluded.
|
|
94
|
+
function links() {
|
|
95
|
+
var editable = getEditable();
|
|
96
|
+
if (!editable) return [];
|
|
97
|
+
var all = editable.querySelectorAll("a");
|
|
98
|
+
var out = [];
|
|
99
|
+
for (var i = 0; i < all.length; i++) {
|
|
100
|
+
var a = all[i];
|
|
101
|
+
if (a.hasAttribute("href")) { out.push(a); continue; }
|
|
102
|
+
if (a.getAttribute("name") || a.getAttribute("id")) continue; // anchor target
|
|
103
|
+
out.push(a);
|
|
104
|
+
}
|
|
105
|
+
return out;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Everything decidable without the network.
|
|
109
|
+
function offlineIssues() {
|
|
110
|
+
var editable = getEditable();
|
|
111
|
+
var out = [];
|
|
112
|
+
if (!editable) return out;
|
|
113
|
+
var list = links();
|
|
114
|
+
var unsafe = config.linkCheckUnsafeSchemes || [];
|
|
115
|
+
var pageIsHttps = false;
|
|
116
|
+
try { pageIsHttps = (window.location.protocol === "https:"); } catch (e) {}
|
|
117
|
+
|
|
118
|
+
// Link text -> hrefs, for the "same words, different destinations" check.
|
|
119
|
+
var byText = {};
|
|
120
|
+
|
|
121
|
+
for (var i = 0; i < list.length; i++) {
|
|
122
|
+
var a = list[i];
|
|
123
|
+
var href = (a.getAttribute("href") || "").trim();
|
|
124
|
+
var text = (a.textContent || "").trim();
|
|
125
|
+
var add = function (severity, code, message) {
|
|
126
|
+
out.push({ element: a, href: href, text: text, severity: severity, code: code, message: message });
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
if (!a.hasAttribute("href")) {
|
|
130
|
+
add("error", "empty-href",
|
|
131
|
+
"Link has no destination — its address may have been removed for being unsafe.");
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (!href || href === "#") {
|
|
135
|
+
add("error", "empty-href", "Link has no destination.");
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
var lower = href.toLowerCase();
|
|
140
|
+
var bad = null;
|
|
141
|
+
for (var u = 0; u < unsafe.length; u++) {
|
|
142
|
+
if (lower.indexOf(unsafe[u]) === 0) { bad = unsafe[u]; break; }
|
|
143
|
+
}
|
|
144
|
+
if (bad) {
|
|
145
|
+
add("error", "unsafe-scheme", "Link uses the " + bad + " scheme, which is unsafe in published content.");
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// In-document anchor: decidable with certainty, and a very common
|
|
150
|
+
// breakage because headings get retitled and ids change.
|
|
151
|
+
if (href.charAt(0) === "#") {
|
|
152
|
+
var id = href.substring(1);
|
|
153
|
+
var target = null;
|
|
154
|
+
try {
|
|
155
|
+
target = editable.querySelector("#" + id.replace(/["\\\]\[]/g, "\\$&")) ||
|
|
156
|
+
editable.querySelector('[name="' + id.replace(/"/g, '\\"') + '"]');
|
|
157
|
+
} catch (e) { target = null; }
|
|
158
|
+
if (!target) add("error", "broken-anchor", "Links to #" + id + ", which does not exist in this document.");
|
|
159
|
+
} else if (/^[a-z][a-z0-9+.-]*:/i.test(href) || href.indexOf("//") === 0) {
|
|
160
|
+
// Absolute URL: parse it to catch malformed ones.
|
|
161
|
+
var parsed = parseUrl(href);
|
|
162
|
+
if (!parsed) {
|
|
163
|
+
add("error", "malformed", "Link address cannot be parsed as a URL.");
|
|
164
|
+
} else {
|
|
165
|
+
if (config.linkCheckMixedContent !== false && pageIsHttps && parsed.protocol === "http:") {
|
|
166
|
+
add("warning", "mixed-content", "Insecure http:// link on a secure page; browsers may block or warn.");
|
|
167
|
+
}
|
|
168
|
+
// Text that looks like a URL but points somewhere else is the
|
|
169
|
+
// classic phishing shape, and it also happens innocently when
|
|
170
|
+
// someone edits the visible text but not the href.
|
|
171
|
+
if (/^(https?:\/\/|www\.)/i.test(text)) {
|
|
172
|
+
var shown = parseUrl(/^www\./i.test(text) ? "http://" + text : text);
|
|
173
|
+
if (shown && shown.hostname && parsed.hostname &&
|
|
174
|
+
shown.hostname.toLowerCase() !== parsed.hostname.toLowerCase()) {
|
|
175
|
+
add("warning", "deceptive-text",
|
|
176
|
+
"Link text shows " + shown.hostname + " but the link goes to " + parsed.hostname + ".");
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Accessibility: a link with no accessible name is unusable by screen
|
|
183
|
+
// reader users, who navigate by pulling up a list of link names.
|
|
184
|
+
if (!text) {
|
|
185
|
+
var hasImgAlt = false;
|
|
186
|
+
var imgs = a.querySelectorAll ? a.querySelectorAll("img[alt]") : [];
|
|
187
|
+
for (var m = 0; m < imgs.length; m++) if ((imgs[m].getAttribute("alt") || "").trim()) hasImgAlt = true;
|
|
188
|
+
if (!hasImgAlt && !(a.getAttribute("aria-label") || "").trim() && !(a.getAttribute("title") || "").trim()) {
|
|
189
|
+
add("error", "empty-text", "Link has no text, image alt, aria-label or title, so it has no accessible name.");
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
var key = text.toLowerCase();
|
|
193
|
+
(byText[key] = byText[key] || []).push({ a: a, href: href });
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Same visible words pointing at different destinations reads as one
|
|
198
|
+
// repeated link to anyone navigating by link name.
|
|
199
|
+
for (var k in byText) {
|
|
200
|
+
if (!byText.hasOwnProperty(k)) continue;
|
|
201
|
+
var group = byText[k];
|
|
202
|
+
if (group.length < 2) continue;
|
|
203
|
+
var distinct = {};
|
|
204
|
+
for (var g = 0; g < group.length; g++) distinct[group[g].href] = true;
|
|
205
|
+
if (Object.keys(distinct).length < 2) continue;
|
|
206
|
+
for (var g2 = 0; g2 < group.length; g2++) {
|
|
207
|
+
out.push({
|
|
208
|
+
element: group[g2].a, href: group[g2].href, text: group[g2].a.textContent.trim(),
|
|
209
|
+
severity: "warning", code: "ambiguous-text",
|
|
210
|
+
message: "Several links share the text “" + group[g2].a.textContent.trim() +
|
|
211
|
+
"” but point to different destinations."
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return out;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function parseUrl(href) {
|
|
219
|
+
try {
|
|
220
|
+
var doc = getDoc() || document;
|
|
221
|
+
var a = doc.createElement("a");
|
|
222
|
+
a.href = href;
|
|
223
|
+
if (!a.protocol || a.protocol === ":") return null;
|
|
224
|
+
return { protocol: a.protocol, hostname: a.hostname, pathname: a.pathname };
|
|
225
|
+
} catch (e) { return null; }
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// ---- run -------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
obj.Check = function () {
|
|
231
|
+
var issues = offlineIssues();
|
|
232
|
+
var resolver = config.linkCheckResolver;
|
|
233
|
+
|
|
234
|
+
if (typeof resolver !== "function") {
|
|
235
|
+
lastIssues = issues;
|
|
236
|
+
finish(issues);
|
|
237
|
+
// Always a promise, so callers do not branch on whether a resolver
|
|
238
|
+
// happens to be configured.
|
|
239
|
+
return typeof Promise === "function" ? Promise.resolve(issues.slice()) : issues.slice();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Only URLs that survived the offline checks are worth a round trip.
|
|
243
|
+
var flagged = {};
|
|
244
|
+
for (var i = 0; i < issues.length; i++) flagged[issues[i].href] = true;
|
|
245
|
+
var urls = [], seen = {};
|
|
246
|
+
var list = links();
|
|
247
|
+
for (var j = 0; j < list.length; j++) {
|
|
248
|
+
var href = (list[j].getAttribute("href") || "").trim();
|
|
249
|
+
if (!href || href.charAt(0) === "#" || flagged[href] || seen[href]) continue;
|
|
250
|
+
if (!/^https?:/i.test(href)) continue; // only real web URLs
|
|
251
|
+
seen[href] = true;
|
|
252
|
+
urls.push(href);
|
|
253
|
+
}
|
|
254
|
+
if (!urls.length) {
|
|
255
|
+
lastIssues = issues; finish(issues);
|
|
256
|
+
return typeof Promise === "function" ? Promise.resolve(issues.slice()) : issues.slice();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return Promise.resolve()
|
|
260
|
+
.then(function () { return resolver(urls.slice()); })
|
|
261
|
+
.then(function (result) {
|
|
262
|
+
result = result || {};
|
|
263
|
+
var byHref = {};
|
|
264
|
+
for (var n = 0; n < list.length; n++) {
|
|
265
|
+
var h = (list[n].getAttribute("href") || "").trim();
|
|
266
|
+
(byHref[h] = byHref[h] || []).push(list[n]);
|
|
267
|
+
}
|
|
268
|
+
for (var u in result) {
|
|
269
|
+
if (!result.hasOwnProperty(u)) continue;
|
|
270
|
+
var r = result[u];
|
|
271
|
+
if (!r || r.ok !== false) continue;
|
|
272
|
+
var els = byHref[u] || [];
|
|
273
|
+
for (var e = 0; e < els.length; e++) {
|
|
274
|
+
issues.push({
|
|
275
|
+
element: els[e], href: u, text: (els[e].textContent || "").trim(),
|
|
276
|
+
severity: "error", code: "unreachable",
|
|
277
|
+
message: "Link did not resolve" + (r.status ? " (HTTP " + r.status + ")" : "") + "."
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
lastIssues = issues;
|
|
282
|
+
finish(issues);
|
|
283
|
+
return issues.slice();
|
|
284
|
+
})
|
|
285
|
+
.catch(function () {
|
|
286
|
+
// A resolver that fails must not lose the offline findings.
|
|
287
|
+
lastIssues = issues;
|
|
288
|
+
finish(issues);
|
|
289
|
+
return issues.slice();
|
|
290
|
+
});
|
|
291
|
+
};
|
|
292
|
+
|
|
293
|
+
function finish(issues) {
|
|
294
|
+
if (highlighting) paint(issues);
|
|
295
|
+
try { editor.fireEvent && editor.fireEvent("linkcheck", issues); } catch (e) {}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// ---- highlighting (chrome, never content) ----------------------------
|
|
299
|
+
|
|
300
|
+
function applyHighlight(on) {
|
|
301
|
+
highlighting = !!on;
|
|
302
|
+
if (highlighting) paint(lastIssues);
|
|
303
|
+
else clear();
|
|
304
|
+
return highlighting;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function clear() {
|
|
308
|
+
var editable = getEditable();
|
|
309
|
+
if (!editable) return;
|
|
310
|
+
var marked = editable.querySelectorAll("a[data-rte-link-issue]");
|
|
311
|
+
for (var i = 0; i < marked.length; i++) {
|
|
312
|
+
marked[i].removeAttribute("data-rte-link-issue");
|
|
313
|
+
marked[i].removeAttribute("data-rte-link-issue-message");
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
function paint(issues) {
|
|
318
|
+
clear();
|
|
319
|
+
for (var i = 0; i < issues.length; i++) {
|
|
320
|
+
var el = issues[i].element;
|
|
321
|
+
if (!el || !el.setAttribute) continue;
|
|
322
|
+
// An error already recorded outranks a later warning on the same link.
|
|
323
|
+
if (el.getAttribute("data-rte-link-issue") === "error") continue;
|
|
324
|
+
el.setAttribute("data-rte-link-issue", issues[i].severity);
|
|
325
|
+
el.setAttribute("data-rte-link-issue-message", issues[i].message);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function stripFor() {
|
|
330
|
+
var editable = getEditable();
|
|
331
|
+
if (!editable) return function () {};
|
|
332
|
+
var marked = editable.querySelectorAll("a[data-rte-link-issue]");
|
|
333
|
+
var parked = [];
|
|
334
|
+
for (var i = 0; i < marked.length; i++) {
|
|
335
|
+
parked.push({
|
|
336
|
+
el: marked[i],
|
|
337
|
+
sev: marked[i].getAttribute("data-rte-link-issue"),
|
|
338
|
+
msg: marked[i].getAttribute("data-rte-link-issue-message")
|
|
339
|
+
});
|
|
340
|
+
marked[i].removeAttribute("data-rte-link-issue");
|
|
341
|
+
marked[i].removeAttribute("data-rte-link-issue-message");
|
|
342
|
+
}
|
|
343
|
+
return function restore() {
|
|
344
|
+
for (var j = 0; j < parked.length; j++) {
|
|
345
|
+
parked[j].el.setAttribute("data-rte-link-issue", parked[j].sev);
|
|
346
|
+
if (parked[j].msg) parked[j].el.setAttribute("data-rte-link-issue-message", parked[j].msg);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function wrapSerializers() {
|
|
352
|
+
if (wrapped) return;
|
|
353
|
+
var names = ["getHTMLCode", "getJSON", "getHTMLContent", "getText"];
|
|
354
|
+
var did = false;
|
|
355
|
+
for (var i = 0; i < names.length; i++) {
|
|
356
|
+
(function (name) {
|
|
357
|
+
var orig = editor[name];
|
|
358
|
+
if (typeof orig !== "function" || orig.__rteLcWrapped) return;
|
|
359
|
+
var w = function () {
|
|
360
|
+
var restore = stripFor();
|
|
361
|
+
try { return orig.apply(editor, arguments); } finally { restore(); }
|
|
362
|
+
};
|
|
363
|
+
w.__rteLcWrapped = true;
|
|
364
|
+
editor[name] = w;
|
|
365
|
+
did = true;
|
|
366
|
+
})(names[i]);
|
|
367
|
+
}
|
|
368
|
+
if (did) wrapped = true;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// ---- styles ----------------------------------------------------------
|
|
372
|
+
|
|
373
|
+
function css() {
|
|
374
|
+
return (
|
|
375
|
+
"a[data-rte-link-issue='error']{background:rgba(220,38,38,.12);" +
|
|
376
|
+
"box-shadow:inset 0 -2px 0 rgba(220,38,38,.65);border-radius:2px;}" +
|
|
377
|
+
"a[data-rte-link-issue='warning']{background:rgba(217,119,6,.12);" +
|
|
378
|
+
"box-shadow:inset 0 -2px 0 rgba(217,119,6,.6);border-radius:2px;}"
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function injectStyles(doc) {
|
|
383
|
+
if (!doc) return;
|
|
384
|
+
var existing = doc.getElementById("rte-link-checker-styles");
|
|
385
|
+
var text = css();
|
|
386
|
+
if (existing) {
|
|
387
|
+
if (existing.getAttribute("data-css") === text) return;
|
|
388
|
+
existing.parentNode && existing.parentNode.removeChild(existing);
|
|
389
|
+
}
|
|
390
|
+
var st = doc.createElement("style");
|
|
391
|
+
st.id = "rte-link-checker-styles";
|
|
392
|
+
st.setAttribute("data-css", text);
|
|
393
|
+
st.appendChild(doc.createTextNode(text));
|
|
394
|
+
(doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement).appendChild(st);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
@@ -503,10 +503,11 @@ function RTE_Plugin_Mention() {
|
|
|
503
503
|
var style = host.createElement("style");
|
|
504
504
|
style.setAttribute("data-rte-mention", "1");
|
|
505
505
|
style.textContent = [
|
|
506
|
-
"
|
|
507
|
-
".rte-mention-
|
|
506
|
+
"/* 2026-07-12 mention picker precision */",
|
|
507
|
+
".rte-mention-popup{position:absolute;z-index:2147483000;min-width:240px;max-width:min(340px,calc(100vw - 24px));max-height:min(320px,60vh);overflow-y:auto;padding:5px;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));border:1px solid rgba(148,163,184,.22);box-shadow:0 14px 32px rgba(29,78,216,.12),0 2px 8px rgba(15,23,42,.08);border-radius:8px;font-family:Aptos,'Segoe UI',sans-serif;font-size:13px;color:#172033;scrollbar-width:thin}",
|
|
508
|
+
".rte-mention-item{display:flex;align-items:center;gap:9px;width:100%;min-height:40px;padding:6px 8px;border:0;background:transparent;text-align:left;cursor:pointer;color:inherit;font:inherit;border-radius:7px}",
|
|
508
509
|
".rte-mention-item:hover,.rte-mention-item-active{background:#eef4ff;color:#0f3f9f}",
|
|
509
|
-
".rte-mention-avatar{flex:0 0 30px;width:30px;height:30px;border-radius:
|
|
510
|
+
".rte-mention-avatar{flex:0 0 30px;width:30px;height:30px;border-radius:8px;overflow:hidden;display:inline-flex;align-items:center;justify-content:center;background:#e7eefb;color:#315277;font-size:11px;font-weight:800;letter-spacing:.02em;box-shadow:inset 0 0 0 1px rgba(148,163,184,.18)}",
|
|
510
511
|
".rte-mention-avatar img{width:100%;height:100%;object-fit:cover}",
|
|
511
512
|
".rte-mention-item-active .rte-mention-avatar{background:#ffffff;color:#0f3f9f}",
|
|
512
513
|
".rte-mention-body{display:flex;flex-direction:column;min-width:0;flex:1}",
|