@richscripts/richtexteditor 2.13.0 → 2.14.1
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/package.json +1 -1
- package/richtexteditor/all_plugins.js +126 -10
- package/richtexteditor/all_plugins.min.js +53 -53
- package/richtexteditor/plugins/a11yenhance.min.js +1 -0
- package/richtexteditor/plugins/accessibilitychecker.min.js +1 -0
- package/richtexteditor/plugins/aitoolkit.min.js +14 -0
- package/richtexteditor/plugins/all_plugins.js +126 -10
- package/richtexteditor/plugins/all_plugins.min.js +53 -53
- package/richtexteditor/plugins/autocorrect.min.js +1 -0
- package/richtexteditor/plugins/autoembed.min.js +1 -0
- package/richtexteditor/plugins/blocktypes.min.js +2 -0
- package/richtexteditor/plugins/bookmarkcard.min.js +2 -0
- package/richtexteditor/plugins/changecase.min.js +1 -0
- package/richtexteditor/plugins/charlimit.min.js +2 -0
- package/richtexteditor/plugins/comments.min.js +3 -0
- package/richtexteditor/plugins/contentminimap.min.js +1 -0
- package/richtexteditor/plugins/crossreference.min.js +1 -0
- package/richtexteditor/plugins/dialoga11y.min.js +1 -0
- package/richtexteditor/plugins/dictation.min.js +1 -0
- package/richtexteditor/plugins/documentimport.min.js +4 -0
- package/richtexteditor/plugins/documentoutline.js +54 -6
- package/richtexteditor/plugins/documentoutline.min.js +1 -0
- package/richtexteditor/plugins/docxexport.min.js +2 -0
- package/richtexteditor/plugins/draghandle.min.js +1 -0
- package/richtexteditor/plugins/emailtoolkit.min.js +1 -0
- package/richtexteditor/plugins/emojiautocomplete.min.js +1 -0
- package/richtexteditor/plugins/foldheadings.min.js +1 -0
- package/richtexteditor/plugins/footnotes.min.js +1 -0
- package/richtexteditor/plugins/formatpainter.min.js +1 -0
- package/richtexteditor/plugins/formattingmarks.js +48 -3
- package/richtexteditor/plugins/formattingmarks.min.js +1 -0
- package/richtexteditor/plugins/gapcursor.min.js +1 -0
- package/richtexteditor/plugins/ghostcomplete.min.js +1 -0
- package/richtexteditor/plugins/html2pdf.min.js +1 -0
- package/richtexteditor/plugins/imageeditor.min.js +1 -0
- package/richtexteditor/plugins/importstyles.min.js +1 -0
- package/richtexteditor/plugins/inlinecode.min.js +1 -0
- package/richtexteditor/plugins/insertcode.min.js +8 -0
- package/richtexteditor/plugins/insertemoji.min.js +1 -0
- package/richtexteditor/plugins/insertgallery.min.js +1 -0
- package/richtexteditor/plugins/inserttemplate.min.js +1 -0
- package/richtexteditor/plugins/keyboarda11y.min.js +1 -0
- package/richtexteditor/plugins/layouttable.min.js +1 -0
- package/richtexteditor/plugins/linenumbers.min.js +1 -0
- package/richtexteditor/plugins/linkchecker.min.js +1 -0
- package/richtexteditor/plugins/localdraft.min.js +1 -0
- package/richtexteditor/plugins/markdownexport.min.js +2 -0
- package/richtexteditor/plugins/mathdialog.min.js +1 -0
- package/richtexteditor/plugins/mentionplugin.min.js +3 -0
- package/richtexteditor/plugins/mergefields.min.js +1 -0
- package/richtexteditor/plugins/mermaiddiagram.min.js +16 -0
- package/richtexteditor/plugins/multilevellist.min.js +1 -0
- package/richtexteditor/plugins/multiroot.min.js +1 -0
- package/richtexteditor/plugins/nodetypes.min.js +1 -0
- package/richtexteditor/plugins/pagination.min.js +1 -0
- package/richtexteditor/plugins/pdfexport.min.js +19 -0
- package/richtexteditor/plugins/permanentpen.min.js +1 -0
- package/richtexteditor/plugins/readabilitystats.min.js +1 -0
- package/richtexteditor/plugins/readaloud.min.js +1 -0
- package/richtexteditor/plugins/restrictedediting.min.js +2 -0
- package/richtexteditor/plugins/revisionhistory.min.js +3 -0
- package/richtexteditor/plugins/rtlui.min.js +2 -0
- package/richtexteditor/plugins/rubyannotation.min.js +1 -0
- package/richtexteditor/plugins/sanitizer.js +24 -1
- package/richtexteditor/plugins/sanitizer.min.js +1 -0
- package/richtexteditor/plugins/shadowdom.min.js +1 -0
- package/richtexteditor/plugins/slashcommand.min.js +2 -0
- package/richtexteditor/plugins/smartchips.min.js +2 -0
- package/richtexteditor/plugins/spellcheck.min.js +2 -0
- package/richtexteditor/plugins/stateinspector.min.js +2 -0
- package/richtexteditor/plugins/structured-content-bridge.js +5178 -0
- package/richtexteditor/plugins/structured-content-bridge.min.js +19 -0
- package/richtexteditor/plugins/tableofcontents.min.js +1 -0
- package/richtexteditor/plugins/tabletools.min.js +1 -0
- package/richtexteditor/plugins/textdirection.min.js +1 -0
- package/richtexteditor/plugins/textpartlanguage.min.js +1 -0
- package/richtexteditor/plugins/todolist.min.js +1 -0
- package/richtexteditor/plugins/trackedchanges.min.js +3 -0
- package/richtexteditor/plugins/typewriter.min.js +1 -0
- package/richtexteditor/plugins/watermark.min.js +1 -0
- package/richtexteditor/plugins/wordexport.min.js +23 -0
- package/richtexteditor/plugins/yjscollab.min.js +2 -0
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -0
- package/richtexteditor/rte-config.js +3 -0
- package/richtexteditor/rte.js +5 -2
- package/richtexteditor/runtime/colorpicker_more_ns.htm +93 -16
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"publishConfig": {
|
|
4
4
|
"access": "public"
|
|
5
5
|
},
|
|
6
|
-
"version": "2.
|
|
6
|
+
"version": "2.14.1",
|
|
7
7
|
"description": "A full-featured JavaScript rich text editor (WYSIWYG) with toolbar, plugins, i18n support, a built-in AI Toolkit (Ask AI, AI Chat, AI Review drawer, BYOK resolver), structured content APIs (JSON/Markdown), and an optional collaboration / review stack: slash commands, @mentions, human-author Track Changes, threaded comments, revision history, and Yjs-backed presence. Ships first-class React, Vue, Angular and Svelte bindings, a headless \"bring your own UI\" core, and server-safe conversion helpers.",
|
|
8
8
|
"main": "./richtexteditor/rte.js",
|
|
9
9
|
"bin": {
|
|
@@ -25516,20 +25516,68 @@ function RTE_Plugin_DocumentOutline() {
|
|
|
25516
25516
|
var minLevel = Math.max(1, Math.min(6, config.documentOutlineMinLevel || 1));
|
|
25517
25517
|
var maxLevel = Math.max(minLevel, Math.min(6, config.documentOutlineMaxLevel || 6));
|
|
25518
25518
|
var headings = editable.querySelectorAll("h1,h2,h3,h4,h5,h6");
|
|
25519
|
-
var counts = {};
|
|
25520
25519
|
var items = [];
|
|
25521
25520
|
var i;
|
|
25521
|
+
// 2026-09-24 Ids must be unique in the SAVED document. This used to keep any id a
|
|
25522
|
+
// heading already had, so copying or cutting a heading and pasting it saved the same
|
|
25523
|
+
// id twice (measured on published 2.6.7: <h1 id="hello-world"> x2), and outline
|
|
25524
|
+
// clicks / anchor links went to the first one. Now the FIRST heading in document
|
|
25525
|
+
// order keeps an id and a later duplicate is renamed, so the original's links stay
|
|
25526
|
+
// valid and the pasted copy becomes "slug-2". Ids on non-heading elements are
|
|
25527
|
+
// reserved too, so a heading never takes one. Every heading is checked, including
|
|
25528
|
+
// levels the outline does not display, since they share one id namespace.
|
|
25529
|
+
//
|
|
25530
|
+
// 2026-09-25 ...but only when the core does not own heading ids. With
|
|
25531
|
+
// autoHeadingAnchors on (the default) the core pass gives every heading a unique id
|
|
25532
|
+
// after each change, with the same first-keeps rule, and marks the ids it generated so
|
|
25533
|
+
// they can follow the text. This block ran 80 ms after each change instead: a heading
|
|
25534
|
+
// a person was typing got the slug of its FIRST letter ("Typed heading" -> id="t"),
|
|
25535
|
+
// unmarked, so the core took it for the author's own id and kept it for good. The
|
|
25536
|
+
// outline needs no id to work (it navigates by element), so it now leaves ids to the
|
|
25537
|
+
// core and keeps this pass for integrations that switched core anchors off.
|
|
25538
|
+
if (config.autoHeadingAnchors === false) {
|
|
25539
|
+
var used = {};
|
|
25540
|
+
var withId = editable.querySelectorAll("[id]");
|
|
25541
|
+
for (i = 0; i < withId.length; i++) {
|
|
25542
|
+
if (!/^H[1-6]$/.test(withId[i].nodeName)) used[withId[i].id] = true;
|
|
25543
|
+
}
|
|
25544
|
+
// Headings with text claim ids first. An EMPTY heading is usually the shell a cut
|
|
25545
|
+
// leaves behind, still carrying the moved heading's id; letting it claim first would
|
|
25546
|
+
// hand the moved heading "-2" and break every link to it. So empty headings go last
|
|
25547
|
+
// and simply drop an id that a real heading already holds.
|
|
25548
|
+
var textOf = function (el) {
|
|
25549
|
+
return (el.innerText || el.textContent || "").replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
|
25550
|
+
};
|
|
25551
|
+
var empties = [];
|
|
25552
|
+
for (i = 0; i < headings.length; i++) {
|
|
25553
|
+
var h = headings[i];
|
|
25554
|
+
var t = textOf(h);
|
|
25555
|
+
if (!t) { empties.push(h); continue; }
|
|
25556
|
+
var base = h.id;
|
|
25557
|
+
if (!base) base = slugifyHeading(t);
|
|
25558
|
+
else if (!used[base]) { used[base] = true; continue; }
|
|
25559
|
+
var unique = base;
|
|
25560
|
+
var n = 2;
|
|
25561
|
+
while (used[unique]) { unique = base + "-" + n; n++; }
|
|
25562
|
+
used[unique] = true;
|
|
25563
|
+
if (h.id !== unique) h.id = unique;
|
|
25564
|
+
}
|
|
25565
|
+
for (i = 0; i < empties.length; i++) {
|
|
25566
|
+
if (!empties[i].id) continue;
|
|
25567
|
+
if (used[empties[i].id]) empties[i].removeAttribute("id");
|
|
25568
|
+
else used[empties[i].id] = true;
|
|
25569
|
+
}
|
|
25570
|
+
}
|
|
25522
25571
|
for (i = 0; i < headings.length; i++) {
|
|
25523
25572
|
var heading = headings[i];
|
|
25524
25573
|
var level = parseInt(heading.nodeName.substring(1), 10);
|
|
25525
25574
|
if (level < minLevel || level > maxLevel) continue;
|
|
25526
25575
|
var text = (heading.innerText || heading.textContent || "").replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
|
25527
|
-
var baseId = heading.id || slugifyHeading(text);
|
|
25528
|
-
counts[baseId] = (counts[baseId] || 0) + 1;
|
|
25529
|
-
var resolvedId = heading.id || (counts[baseId] === 1 ? baseId : (baseId + "-" + counts[baseId]));
|
|
25530
|
-
if (!heading.id) heading.id = resolvedId;
|
|
25531
25576
|
items.push({
|
|
25532
|
-
|
|
25577
|
+
// an empty heading may now have no id; key it internally (never written to
|
|
25578
|
+
// the element) so "" can't match the "no active heading" state and light up
|
|
25579
|
+
// every empty entry at once.
|
|
25580
|
+
id: heading.id || ("__rte-outline-" + items.length),
|
|
25533
25581
|
level: level,
|
|
25534
25582
|
text: text || ("Heading " + (items.length + 1)),
|
|
25535
25583
|
element: heading
|
|
@@ -28440,22 +28488,67 @@ function RTE_Plugin_FormattingMarks() {
|
|
|
28440
28488
|
// may be deliberate.
|
|
28441
28489
|
if (parent.nodeName === "CODE" || parent.nodeName === "PRE") return;
|
|
28442
28490
|
|
|
28491
|
+
// Replacing the text node moves a caret inside it to the node's old place in its
|
|
28492
|
+
// parent - usually the paragraph start. This runs on every keyup, and the first
|
|
28493
|
+
// space typed at a line end is always a non-breaking one, so with marks on every
|
|
28494
|
+
// later character landed at the start, reversed: " edited" became "detide".
|
|
28495
|
+
// Remember where the selection was inside this node and put it back.
|
|
28496
|
+
var sel = doc.getSelection ? doc.getSelection() : null;
|
|
28497
|
+
var range = sel && sel.rangeCount ? sel.getRangeAt(0) : null;
|
|
28498
|
+
var keepStart = range && range.startContainer === node ? range.startOffset : -1;
|
|
28499
|
+
var keepEnd = range && range.endContainer === node ? range.endOffset : -1;
|
|
28500
|
+
|
|
28443
28501
|
var frag = doc.createDocumentFragment();
|
|
28502
|
+
var pieces = []; // in order: { node, marker } - every marker stands for ONE character
|
|
28444
28503
|
var buf = "";
|
|
28445
28504
|
for (var i = 0; i < text.length; i++) {
|
|
28446
28505
|
var def = chars[text[i]];
|
|
28506
|
+
// A space typed at a line end is inserted as a non-breaking placeholder that the
|
|
28507
|
+
// browser turns back into a space when the next character arrives. Wrapped in a
|
|
28508
|
+
// non-editable marker it never could, and saved as a hard . Leave the one
|
|
28509
|
+
// right before the caret alone; it is marked once the caret moves on.
|
|
28510
|
+
if (def && text[i] === " " && i === keepStart - 1 && keepStart === keepEnd) def = null;
|
|
28447
28511
|
if (!def) { buf += text[i]; continue; }
|
|
28448
|
-
if (buf) { frag.appendChild(doc.createTextNode(buf)); buf = ""; }
|
|
28512
|
+
if (buf) { pieces.push({ node: frag.appendChild(doc.createTextNode(buf)), marker: false }); buf = ""; }
|
|
28449
28513
|
var span = doc.createElement("span");
|
|
28450
28514
|
span.setAttribute("data-rte-fm", "1");
|
|
28451
28515
|
span.setAttribute("data-rte-fm-char", text[i]);
|
|
28452
28516
|
span.className = def.cls;
|
|
28453
28517
|
span.setAttribute("contenteditable", "false");
|
|
28454
28518
|
span.textContent = def.label;
|
|
28455
|
-
frag.appendChild(span);
|
|
28519
|
+
pieces.push({ node: frag.appendChild(span), marker: true });
|
|
28456
28520
|
}
|
|
28457
|
-
if (buf) frag.appendChild(doc.createTextNode(buf));
|
|
28521
|
+
if (buf) pieces.push({ node: frag.appendChild(doc.createTextNode(buf)), marker: false });
|
|
28458
28522
|
parent.replaceChild(frag, node);
|
|
28523
|
+
|
|
28524
|
+
if (keepStart < 0 && keepEnd < 0) return;
|
|
28525
|
+
// Offset k in the old text -> a point in the pieces. Inside text where possible;
|
|
28526
|
+
// never inside a marker (it is not editable): just before or after it.
|
|
28527
|
+
function pointAt(k) {
|
|
28528
|
+
var pos = 0;
|
|
28529
|
+
for (var p = 0; p < pieces.length; p++) {
|
|
28530
|
+
var piece = pieces[p];
|
|
28531
|
+
if (piece.marker) {
|
|
28532
|
+
if (k <= pos) return { node: parent, offset: Array.prototype.indexOf.call(parent.childNodes, piece.node) };
|
|
28533
|
+
pos += 1;
|
|
28534
|
+
} else {
|
|
28535
|
+
var len = piece.node.nodeValue.length;
|
|
28536
|
+
if (k <= pos + len) return { node: piece.node, offset: k - pos };
|
|
28537
|
+
pos += len;
|
|
28538
|
+
}
|
|
28539
|
+
}
|
|
28540
|
+
var last = pieces[pieces.length - 1].node;
|
|
28541
|
+
return { node: parent, offset: Array.prototype.indexOf.call(parent.childNodes, last) + 1 };
|
|
28542
|
+
}
|
|
28543
|
+
try {
|
|
28544
|
+
var r = doc.createRange();
|
|
28545
|
+
var a = pointAt(keepStart >= 0 ? keepStart : 0);
|
|
28546
|
+
var b = keepEnd >= 0 ? pointAt(keepEnd) : a;
|
|
28547
|
+
if (keepStart < 0) { r.setStart(range.startContainer, range.startOffset); } else r.setStart(a.node, a.offset);
|
|
28548
|
+
if (keepEnd < 0) { r.setEnd(range.endContainer, range.endOffset); } else r.setEnd(b.node, b.offset);
|
|
28549
|
+
sel.removeAllRanges();
|
|
28550
|
+
sel.addRange(r);
|
|
28551
|
+
} catch (e) { /* a selection we cannot restore is no reason to lose the marks */ }
|
|
28459
28552
|
}
|
|
28460
28553
|
|
|
28461
28554
|
// ---- serialization safety -------------------------------------------
|
|
@@ -40240,6 +40333,7 @@ function RTE_Plugin_Sanitizer() {
|
|
|
40240
40333
|
}
|
|
40241
40334
|
|
|
40242
40335
|
var attrs = [];
|
|
40336
|
+
var strippedSrc = false;
|
|
40243
40337
|
for (var a = 0; a < el.attributes.length; a++) attrs.push(el.attributes[a]);
|
|
40244
40338
|
for (var k = 0; k < attrs.length; k++) {
|
|
40245
40339
|
var name = attrs[k].name.toLowerCase();
|
|
@@ -40257,7 +40351,11 @@ function RTE_Plugin_Sanitizer() {
|
|
|
40257
40351
|
if (name.indexOf("data-") === 0 || name.indexOf("aria-") === 0) continue;
|
|
40258
40352
|
|
|
40259
40353
|
if (URL_ATTRS[name]) {
|
|
40260
|
-
if (!isSafeUrl(value, tag, name)) {
|
|
40354
|
+
if (!isSafeUrl(value, tag, name)) {
|
|
40355
|
+
note(report.removedAttributes, tag + "[" + name + "]");
|
|
40356
|
+
el.removeAttribute(attrs[k].name);
|
|
40357
|
+
if (name === "src") strippedSrc = true;
|
|
40358
|
+
}
|
|
40261
40359
|
continue;
|
|
40262
40360
|
}
|
|
40263
40361
|
if (!ALLOWED_ATTRS[name] && !attrExtra(name)) {
|
|
@@ -40266,6 +40364,18 @@ function RTE_Plugin_Sanitizer() {
|
|
|
40266
40364
|
}
|
|
40267
40365
|
}
|
|
40268
40366
|
|
|
40367
|
+
// An <img> whose only source was just stripped is an empty box that
|
|
40368
|
+
// saves as <img> with no src -- dragging a page image into the editor
|
|
40369
|
+
// from a page opened on disk did exactly that (its file: src is not a
|
|
40370
|
+
// safe scheme). Drop the element when nothing is left to show. Keep it
|
|
40371
|
+
// when srcset or a lazy-load data-*src still carries an image, and never
|
|
40372
|
+
// touch an <img> that arrived without a src (an app placeholder).
|
|
40373
|
+
if (tag === "img" && strippedSrc && !el.hasAttribute("srcset") && !hasDataSource(el)) {
|
|
40374
|
+
note(report.removedTags, "img");
|
|
40375
|
+
el.parentNode.removeChild(el);
|
|
40376
|
+
continue;
|
|
40377
|
+
}
|
|
40378
|
+
|
|
40269
40379
|
// A link that opens a new tab without rel="noopener" hands the
|
|
40270
40380
|
// opener window to the destination.
|
|
40271
40381
|
if (tag === "a" && (el.getAttribute("target") || "").toLowerCase() === "_blank") {
|
|
@@ -40282,6 +40392,12 @@ function RTE_Plugin_Sanitizer() {
|
|
|
40282
40392
|
parent.removeChild(el);
|
|
40283
40393
|
}
|
|
40284
40394
|
function note(list, what) { if (list.indexOf(what) < 0) list.push(what); }
|
|
40395
|
+
function hasDataSource(el) {
|
|
40396
|
+
for (var i = 0; i < el.attributes.length; i++) {
|
|
40397
|
+
if (/^data-(.*-)?(src|srcset|original)$/i.test(el.attributes[i].name)) return true;
|
|
40398
|
+
}
|
|
40399
|
+
return false;
|
|
40400
|
+
}
|
|
40285
40401
|
|
|
40286
40402
|
function sanitizeHtml(html) {
|
|
40287
40403
|
var out = html;
|