@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.
- package/README.md +360 -0
- package/admin/index.d.ts +100 -0
- package/admin/index.js +94 -0
- package/angular/component.ts +284 -0
- package/angular/index.d.ts +80 -0
- package/angular/index.ts +24 -0
- package/headless/index.d.ts +65 -0
- package/headless/index.js +272 -0
- package/index.d.ts +1412 -0
- package/integrations/shared.d.ts +138 -0
- package/integrations/shared.js +5182 -0
- package/package.json +130 -0
- package/react/index.d.ts +104 -0
- package/react/index.js +310 -0
- package/richtexteditor/CHANGELOG.md +1876 -0
- package/richtexteditor/all_plugins.js +31125 -0
- package/richtexteditor/all_plugins.min.js +78 -0
- package/richtexteditor/lang/rte-lang-af.js +193 -0
- package/richtexteditor/lang/rte-lang-ar.js +193 -0
- package/richtexteditor/lang/rte-lang-bg.js +193 -0
- package/richtexteditor/lang/rte-lang-bn.js +193 -0
- package/richtexteditor/lang/rte-lang-bs.js +193 -0
- package/richtexteditor/lang/rte-lang-ca.js +193 -0
- package/richtexteditor/lang/rte-lang-cs.js +193 -0
- package/richtexteditor/lang/rte-lang-cy.js +193 -0
- package/richtexteditor/lang/rte-lang-da.js +193 -0
- package/richtexteditor/lang/rte-lang-de.js +193 -0
- package/richtexteditor/lang/rte-lang-el.js +193 -0
- package/richtexteditor/lang/rte-lang-en.js +6 -0
- package/richtexteditor/lang/rte-lang-es.js +193 -0
- package/richtexteditor/lang/rte-lang-et.js +193 -0
- package/richtexteditor/lang/rte-lang-fa.js +193 -0
- package/richtexteditor/lang/rte-lang-fi.js +193 -0
- package/richtexteditor/lang/rte-lang-fil.js +193 -0
- package/richtexteditor/lang/rte-lang-fj.js +193 -0
- package/richtexteditor/lang/rte-lang-fr.js +193 -0
- package/richtexteditor/lang/rte-lang-ga.js +193 -0
- package/richtexteditor/lang/rte-lang-gu.js +193 -0
- package/richtexteditor/lang/rte-lang-he.js +193 -0
- package/richtexteditor/lang/rte-lang-hi.js +193 -0
- package/richtexteditor/lang/rte-lang-hr.js +193 -0
- package/richtexteditor/lang/rte-lang-ht.js +193 -0
- package/richtexteditor/lang/rte-lang-hu.js +193 -0
- package/richtexteditor/lang/rte-lang-id.js +193 -0
- package/richtexteditor/lang/rte-lang-is.js +193 -0
- package/richtexteditor/lang/rte-lang-it.js +193 -0
- package/richtexteditor/lang/rte-lang-ja.js +193 -0
- package/richtexteditor/lang/rte-lang-kn.js +193 -0
- package/richtexteditor/lang/rte-lang-ko.js +193 -0
- package/richtexteditor/lang/rte-lang-lt.js +193 -0
- package/richtexteditor/lang/rte-lang-lv.js +193 -0
- package/richtexteditor/lang/rte-lang-mg.js +193 -0
- package/richtexteditor/lang/rte-lang-mi.js +193 -0
- package/richtexteditor/lang/rte-lang-ml.js +193 -0
- package/richtexteditor/lang/rte-lang-mr.js +193 -0
- package/richtexteditor/lang/rte-lang-ms.js +193 -0
- package/richtexteditor/lang/rte-lang-mt.js +193 -0
- package/richtexteditor/lang/rte-lang-mww.js +193 -0
- package/richtexteditor/lang/rte-lang-nb.js +193 -0
- package/richtexteditor/lang/rte-lang-nl.js +193 -0
- package/richtexteditor/lang/rte-lang-otq.js +193 -0
- package/richtexteditor/lang/rte-lang-pa.js +193 -0
- package/richtexteditor/lang/rte-lang-pl.js +193 -0
- package/richtexteditor/lang/rte-lang-pt-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-pt.js +193 -0
- package/richtexteditor/lang/rte-lang-ro.js +193 -0
- package/richtexteditor/lang/rte-lang-ru.js +193 -0
- package/richtexteditor/lang/rte-lang-sk.js +193 -0
- package/richtexteditor/lang/rte-lang-sl.js +193 -0
- package/richtexteditor/lang/rte-lang-sm.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Cyrl.js +193 -0
- package/richtexteditor/lang/rte-lang-sr-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-sv.js +193 -0
- package/richtexteditor/lang/rte-lang-sw.js +193 -0
- package/richtexteditor/lang/rte-lang-ta.js +193 -0
- package/richtexteditor/lang/rte-lang-te.js +193 -0
- package/richtexteditor/lang/rte-lang-th.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Latn.js +193 -0
- package/richtexteditor/lang/rte-lang-tlh-Piqd.js +193 -0
- package/richtexteditor/lang/rte-lang-to.js +193 -0
- package/richtexteditor/lang/rte-lang-tr.js +193 -0
- package/richtexteditor/lang/rte-lang-ty.js +193 -0
- package/richtexteditor/lang/rte-lang-uk.js +193 -0
- package/richtexteditor/lang/rte-lang-ur.js +193 -0
- package/richtexteditor/lang/rte-lang-vi.js +193 -0
- package/richtexteditor/lang/rte-lang-yua.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +193 -0
- package/richtexteditor/lang/rte-lang-zh-Hant.js +193 -0
- package/richtexteditor/lang/rte-lang.js +1 -0
- package/richtexteditor/plugins/-combine-plugin.txt +2 -0
- package/richtexteditor/plugins/-combine-plugins.bat +20 -0
- package/richtexteditor/plugins/a11yenhance.js +90 -0
- package/richtexteditor/plugins/accessibilitychecker.js +579 -0
- package/richtexteditor/plugins/aitoolkit.css +15830 -0
- package/richtexteditor/plugins/aitoolkit.js +19171 -0
- package/richtexteditor/plugins/all_plugins.js +31125 -0
- package/richtexteditor/plugins/all_plugins.min.js +78 -0
- package/richtexteditor/plugins/autoembed.js +174 -0
- package/richtexteditor/plugins/blocktypes.js +175 -0
- package/richtexteditor/plugins/bookmarkcard.js +226 -0
- package/richtexteditor/plugins/charlimit.js +195 -0
- package/richtexteditor/plugins/comments.js +672 -0
- package/richtexteditor/plugins/contentminimap.js +369 -0
- package/richtexteditor/plugins/crdt-engine.js +8905 -0
- package/richtexteditor/plugins/crdt-engine.min.js +5 -0
- package/richtexteditor/plugins/dialoga11y.js +174 -0
- package/richtexteditor/plugins/dictation.js +268 -0
- package/richtexteditor/plugins/documentimport.js +369 -0
- package/richtexteditor/plugins/documentoutline.js +341 -0
- package/richtexteditor/plugins/emailtoolkit.js +165 -0
- package/richtexteditor/plugins/emojiautocomplete.js +241 -0
- package/richtexteditor/plugins/foldheadings.js +181 -0
- package/richtexteditor/plugins/ghostcomplete.js +297 -0
- package/richtexteditor/plugins/html2pdf.js +158 -0
- package/richtexteditor/plugins/imageeditor.js +159 -0
- package/richtexteditor/plugins/insertcode.js +319 -0
- package/richtexteditor/plugins/insertemoji.js +229 -0
- package/richtexteditor/plugins/insertgallery.js +362 -0
- package/richtexteditor/plugins/inserttemplate.js +261 -0
- package/richtexteditor/plugins/localdraft.js +213 -0
- package/richtexteditor/plugins/markdownexport.js +110 -0
- package/richtexteditor/plugins/mathdialog.js +153 -0
- package/richtexteditor/plugins/mentionplugin.js +534 -0
- package/richtexteditor/plugins/mermaiddiagram.js +294 -0
- package/richtexteditor/plugins/readabilitystats.js +148 -0
- package/richtexteditor/plugins/readaloud.js +243 -0
- package/richtexteditor/plugins/restrictedediting.js +208 -0
- package/richtexteditor/plugins/revisionhistory.js +670 -0
- package/richtexteditor/plugins/samples/insertcode_v1.js +76 -0
- package/richtexteditor/plugins/slashcommand.js +705 -0
- package/richtexteditor/plugins/smartchips.js +250 -0
- package/richtexteditor/plugins/spellcheck.js +181 -0
- package/richtexteditor/plugins/todolist.js +137 -0
- package/richtexteditor/plugins/trackedchanges.js +658 -0
- package/richtexteditor/plugins/tui.image-editor/fabric.min.js +8 -0
- package/richtexteditor/plugins/tui.image-editor/note.txt +5 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-a.svg +235 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-b.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-c.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/svg/icon-d.svg +224 -0
- package/richtexteditor/plugins/tui.image-editor/tui-code-snippet.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.css +6 -0
- package/richtexteditor/plugins/tui.image-editor/tui-color-picker.min.js +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.css +7 -0
- package/richtexteditor/plugins/tui.image-editor/tui-image-editor.min.js +15 -0
- package/richtexteditor/plugins/typewriter.js +203 -0
- package/richtexteditor/plugins/wordexport.js +0 -0
- package/richtexteditor/plugins/yjscollab.js +693 -0
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +335 -0
- package/richtexteditor/rte-config.js +933 -0
- package/richtexteditor/rte.js +943 -0
- package/richtexteditor/rte_mobile.css +234 -0
- package/richtexteditor/rte_theme_default.css +4819 -0
- package/richtexteditor/runtime/colorpicker_more_ns.htm +2613 -0
- package/richtexteditor/runtime/help.htm +162 -0
- package/richtexteditor/runtime/images/1x1.gif +0 -0
- package/richtexteditor/runtime/images/cpie_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpie_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpie_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpie_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpie_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_Color.cur +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace1.png +0 -0
- package/richtexteditor/runtime/images/cpns_ColorSpace2.png +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionDark.gif +0 -0
- package/richtexteditor/runtime/images/cpns_GradientPositionLight.gif +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical1.png +0 -0
- package/richtexteditor/runtime/images/cpns_Vertical2.png +0 -0
- package/richtexteditor/runtime/images/cpns_VerticalPosition.gif +0 -0
- package/richtexteditor/runtime/images/cpns_WebSafe.gif +0 -0
- package/richtexteditor/runtime/images/cpns_gradients.png +0 -0
- package/richtexteditor/runtime/images/cpns_hue2.png +0 -0
- package/richtexteditor/runtime/images/formbn.gif +0 -0
- package/richtexteditor/runtime/images/multiclavier.gif +0 -0
- package/richtexteditor/runtime/richtexteditor_content.css +362 -0
- package/richtexteditor/runtime/richtexteditor_preview.css +302 -0
- package/richtexteditor/runtime/richtexteditor_preview.js +1 -0
- package/server/index.d.ts +21 -0
- package/server/index.js +23 -0
- package/svelte/index.d.ts +19 -0
- package/svelte/index.js +59 -0
- package/vue/index.d.ts +94 -0
- package/vue/index.js +291 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
RTE_DefaultConfig.plugin_documentoutline = RTE_Plugin_DocumentOutline;
|
|
4
|
+
|
|
5
|
+
function RTE_Plugin_DocumentOutline() {
|
|
6
|
+
var obj = this;
|
|
7
|
+
var config;
|
|
8
|
+
var editor;
|
|
9
|
+
var shell = null;
|
|
10
|
+
var panel = null;
|
|
11
|
+
var list = null;
|
|
12
|
+
var empty = null;
|
|
13
|
+
var refreshTimer = 0;
|
|
14
|
+
var activeHeadingId = "";
|
|
15
|
+
|
|
16
|
+
obj.PluginName = "DocumentOutline";
|
|
17
|
+
|
|
18
|
+
obj.InitConfig = function (argconfig) {
|
|
19
|
+
config = argconfig;
|
|
20
|
+
if (config.documentOutlineEnabled === false) return;
|
|
21
|
+
|
|
22
|
+
config.documentOutlineTitle = config.documentOutlineTitle || "Outline";
|
|
23
|
+
config.documentOutlineHint = config.documentOutlineHint || "Browse headings and jump through long documents without leaving the editor.";
|
|
24
|
+
config.documentOutlineEmptyText = config.documentOutlineEmptyText || "Add headings to build the document outline.";
|
|
25
|
+
if (typeof config.documentOutlineAutoOpen !== "boolean") config.documentOutlineAutoOpen = false;
|
|
26
|
+
if (typeof config.documentOutlineMinLevel !== "number") config.documentOutlineMinLevel = 1;
|
|
27
|
+
if (typeof config.documentOutlineMaxLevel !== "number") config.documentOutlineMaxLevel = 6;
|
|
28
|
+
|
|
29
|
+
appendToolbarCommand("toolbar_default", "#{documentoutline}");
|
|
30
|
+
appendToolbarCommand("toolbar_full", "#{documentoutline}");
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
obj.InitEditor = function (argeditor) {
|
|
34
|
+
editor = argeditor;
|
|
35
|
+
if (config.documentOutlineEnabled === false) return;
|
|
36
|
+
|
|
37
|
+
editor.documentOutline = {
|
|
38
|
+
close: function () { closePanel(); },
|
|
39
|
+
list: function () { return collectHeadings(); },
|
|
40
|
+
open: function () { openPanel(); },
|
|
41
|
+
refresh: function () { renderOutline(); },
|
|
42
|
+
toggle: function () { togglePanel(); }
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
injectStyles();
|
|
46
|
+
|
|
47
|
+
editor.toolbarFactoryMap = editor.toolbarFactoryMap || {};
|
|
48
|
+
editor.toolbarFactoryMap["documentoutline"] = function (cmd) {
|
|
49
|
+
return editor.createToolbarButton(cmd);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
editor.attachEvent("exec_command_documentoutline", function (state) {
|
|
53
|
+
state.returnValue = true;
|
|
54
|
+
state.stopBubble = true;
|
|
55
|
+
togglePanel();
|
|
56
|
+
});
|
|
57
|
+
editor.attachEvent("change", function () {
|
|
58
|
+
scheduleRefresh();
|
|
59
|
+
});
|
|
60
|
+
editor.attachEvent("selectionchange", function () {
|
|
61
|
+
updateActiveHeading();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
ensureShell();
|
|
65
|
+
renderOutline();
|
|
66
|
+
if (config.documentOutlineAutoOpen) openPanel();
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
function appendToolbarCommand(toolbar, item) {
|
|
70
|
+
if (!config[toolbar]) return;
|
|
71
|
+
if (config[toolbar].indexOf(item) !== -1) return;
|
|
72
|
+
config[toolbar] = config[toolbar] + item;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function injectStyles() {
|
|
76
|
+
var hostDoc = config.container.ownerDocument;
|
|
77
|
+
if (hostDoc.getElementById("rte-document-outline-style")) return;
|
|
78
|
+
var style = hostDoc.createElement("style");
|
|
79
|
+
style.id = "rte-document-outline-style";
|
|
80
|
+
style.innerHTML = [
|
|
81
|
+
".rte-document-outline-shell{display:flex;align-items:stretch;gap:10px;}",
|
|
82
|
+
".rte-document-outline-shell>.rte-document-outline-host{flex:1 1 auto;min-width:0;}",
|
|
83
|
+
".rte-document-outline-panel{display:none;flex:0 0 286px;min-width:248px;max-width:min(330px,30vw);border:1px solid rgba(148,163,184,.22);border-radius:16px;background:linear-gradient(180deg,rgba(255,255,255,.98),rgba(248,251,255,.96));box-shadow:0 18px 42px rgba(29,78,216,.12),0 2px 8px rgba(15,23,42,.06);overflow:hidden;color:#172033;font-family:Aptos,'Segoe UI',sans-serif;}",
|
|
84
|
+
".rte-document-outline-shell.is-open>.rte-document-outline-panel{display:flex;flex-direction:column;}",
|
|
85
|
+
".rte-document-outline-header{padding:12px 12px 9px 14px;border-bottom:1px solid rgba(148,163,184,.16);background:rgba(255,255,255,.72);}",
|
|
86
|
+
".rte-document-outline-kicker{font-size:10px;line-height:1.3;letter-spacing:.08em;text-transform:uppercase;color:#52657e;font-weight:850;}",
|
|
87
|
+
".rte-document-outline-title{margin-top:3px;font-size:15px;line-height:1.2;font-weight:850;color:#172033;}",
|
|
88
|
+
".rte-document-outline-copy{margin-top:5px;font-size:12px;line-height:1.42;color:#52657e;}",
|
|
89
|
+
".rte-document-outline-toolbar{display:flex;align-items:center;justify-content:space-between;padding:8px 10px 8px 14px;border-bottom:1px solid rgba(148,163,184,.16);gap:10px;background:rgba(255,255,255,.52);}",
|
|
90
|
+
".rte-document-outline-count{min-width:0;font-size:12px;font-weight:750;color:#52657e;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}",
|
|
91
|
+
".rte-document-outline-close{appearance:none;border:1px solid rgba(100,116,139,.18);background:#fff;color:#315277;cursor:pointer;font-size:12px;font-weight:750;padding:6px 9px;border-radius:999px;line-height:1;}",
|
|
92
|
+
".rte-document-outline-close:hover,.rte-document-outline-close:focus-visible{background:#eef4ff;color:#0f3f9f;border-color:rgba(37,99,235,.28);}",
|
|
93
|
+
".rte-document-outline-body{padding:6px;overflow:auto;min-height:140px;max-height:520px;scrollbar-width:thin;}",
|
|
94
|
+
".rte-document-outline-list{display:flex;flex-direction:column;gap:5px;}",
|
|
95
|
+
".rte-document-outline-item{appearance:none;width:100%;text-align:left;border:1px solid transparent;background:transparent;color:#172033;cursor:pointer;border-radius:12px;padding:8px 10px 8px calc(10px + (var(--rte-outline-level,1) - 1) * 12px);font-size:13px;line-height:1.35;font-weight:650;}",
|
|
96
|
+
".rte-document-outline-item:hover,.rte-document-outline-item:focus-visible{background:#f8fbff;border-color:rgba(37,99,235,.18);}",
|
|
97
|
+
".rte-document-outline-item.is-active{background:#eef4ff;border-color:rgba(37,99,235,.26);color:#0f3f9f;box-shadow:0 8px 18px rgba(37,99,235,.1);}",
|
|
98
|
+
".rte-document-outline-item-level{display:inline-flex;margin:0 0 3px;padding:2px 6px;border-radius:999px;background:#fff;color:#64748b;font-size:10px;line-height:1.1;letter-spacing:.08em;text-transform:uppercase;font-weight:850;box-shadow:inset 0 0 0 1px rgba(148,163,184,.18);}",
|
|
99
|
+
".rte-document-outline-empty{margin:6px;padding:14px;color:#52657e;font-size:13px;font-weight:700;line-height:1.55;background:#fff;border:1px dashed rgba(148,163,184,.34);border-radius:14px;}",
|
|
100
|
+
"@media (max-width: 1100px){.rte-document-outline-shell{display:block;}.rte-document-outline-panel{margin-top:12px;max-width:none;width:100%;}.rte-document-outline-body{max-height:320px;}}"
|
|
101
|
+
].join("");
|
|
102
|
+
hostDoc.head.appendChild(style);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function ensureShell() {
|
|
106
|
+
if (shell) return shell;
|
|
107
|
+
var container = config.container;
|
|
108
|
+
var hostDoc = container.ownerDocument;
|
|
109
|
+
shell = hostDoc.createElement("div");
|
|
110
|
+
shell.className = "rte-document-outline-shell";
|
|
111
|
+
|
|
112
|
+
var host = hostDoc.createElement("div");
|
|
113
|
+
host.className = "rte-document-outline-host";
|
|
114
|
+
|
|
115
|
+
var parent = container.parentNode;
|
|
116
|
+
parent.insertBefore(shell, container);
|
|
117
|
+
shell.appendChild(host);
|
|
118
|
+
host.appendChild(container);
|
|
119
|
+
|
|
120
|
+
panel = hostDoc.createElement("aside");
|
|
121
|
+
panel.className = "rte-document-outline-panel";
|
|
122
|
+
panel.setAttribute("aria-label", config.documentOutlineTitle);
|
|
123
|
+
panel.setAttribute("role", "complementary");
|
|
124
|
+
|
|
125
|
+
var header = hostDoc.createElement("div");
|
|
126
|
+
header.className = "rte-document-outline-header";
|
|
127
|
+
var kicker = hostDoc.createElement("div");
|
|
128
|
+
kicker.className = "rte-document-outline-kicker";
|
|
129
|
+
kicker.innerText = "Document";
|
|
130
|
+
var title = hostDoc.createElement("div");
|
|
131
|
+
title.className = "rte-document-outline-title";
|
|
132
|
+
title.innerText = config.documentOutlineTitle;
|
|
133
|
+
var copy = hostDoc.createElement("div");
|
|
134
|
+
copy.className = "rte-document-outline-copy";
|
|
135
|
+
copy.innerText = config.documentOutlineHint;
|
|
136
|
+
header.appendChild(kicker);
|
|
137
|
+
header.appendChild(title);
|
|
138
|
+
header.appendChild(copy);
|
|
139
|
+
|
|
140
|
+
var toolbar = hostDoc.createElement("div");
|
|
141
|
+
toolbar.className = "rte-document-outline-toolbar";
|
|
142
|
+
var count = hostDoc.createElement("div");
|
|
143
|
+
count.className = "rte-document-outline-count";
|
|
144
|
+
count.setAttribute("data-rte-outline-count", "1");
|
|
145
|
+
var close = hostDoc.createElement("button");
|
|
146
|
+
close.type = "button";
|
|
147
|
+
close.className = "rte-document-outline-close";
|
|
148
|
+
close.innerText = "Hide";
|
|
149
|
+
close.setAttribute("aria-label", "Hide document outline");
|
|
150
|
+
close.onclick = function () { closePanel(); };
|
|
151
|
+
toolbar.appendChild(count);
|
|
152
|
+
toolbar.appendChild(close);
|
|
153
|
+
|
|
154
|
+
var body = hostDoc.createElement("div");
|
|
155
|
+
body.className = "rte-document-outline-body";
|
|
156
|
+
list = hostDoc.createElement("div");
|
|
157
|
+
list.className = "rte-document-outline-list";
|
|
158
|
+
empty = hostDoc.createElement("div");
|
|
159
|
+
empty.className = "rte-document-outline-empty";
|
|
160
|
+
empty.innerText = config.documentOutlineEmptyText;
|
|
161
|
+
body.appendChild(list);
|
|
162
|
+
body.appendChild(empty);
|
|
163
|
+
|
|
164
|
+
panel.appendChild(header);
|
|
165
|
+
panel.appendChild(toolbar);
|
|
166
|
+
panel.appendChild(body);
|
|
167
|
+
shell.appendChild(panel);
|
|
168
|
+
return shell;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function togglePanel() {
|
|
172
|
+
if (shell && shell.classList.contains("is-open")) closePanel();
|
|
173
|
+
else openPanel();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function openPanel() {
|
|
177
|
+
ensureShell();
|
|
178
|
+
shell.classList.add("is-open");
|
|
179
|
+
renderOutline();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function closePanel() {
|
|
183
|
+
if (!shell) return;
|
|
184
|
+
shell.classList.remove("is-open");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function scheduleRefresh() {
|
|
188
|
+
clearTimeout(refreshTimer);
|
|
189
|
+
refreshTimer = setTimeout(function () {
|
|
190
|
+
renderOutline();
|
|
191
|
+
}, 80);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function slugifyHeading(text) {
|
|
195
|
+
var slug = String(text || "")
|
|
196
|
+
.toLowerCase()
|
|
197
|
+
.replace(/[^a-z0-9\s-]/g, "")
|
|
198
|
+
.replace(/\s+/g, "-")
|
|
199
|
+
.replace(/-+/g, "-")
|
|
200
|
+
.replace(/^-|-$/g, "");
|
|
201
|
+
return slug || "section";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function collectHeadings() {
|
|
205
|
+
var editable = editor.getEditable();
|
|
206
|
+
if (!editable) return [];
|
|
207
|
+
var minLevel = Math.max(1, Math.min(6, config.documentOutlineMinLevel || 1));
|
|
208
|
+
var maxLevel = Math.max(minLevel, Math.min(6, config.documentOutlineMaxLevel || 6));
|
|
209
|
+
var headings = editable.querySelectorAll("h1,h2,h3,h4,h5,h6");
|
|
210
|
+
var counts = {};
|
|
211
|
+
var items = [];
|
|
212
|
+
var i;
|
|
213
|
+
for (i = 0; i < headings.length; i++) {
|
|
214
|
+
var heading = headings[i];
|
|
215
|
+
var level = parseInt(heading.nodeName.substring(1), 10);
|
|
216
|
+
if (level < minLevel || level > maxLevel) continue;
|
|
217
|
+
var text = (heading.innerText || heading.textContent || "").replace(/\s+/g, " ").replace(/^\s+|\s+$/g, "");
|
|
218
|
+
var baseId = heading.id || slugifyHeading(text);
|
|
219
|
+
counts[baseId] = (counts[baseId] || 0) + 1;
|
|
220
|
+
var resolvedId = heading.id || (counts[baseId] === 1 ? baseId : (baseId + "-" + counts[baseId]));
|
|
221
|
+
if (!heading.id) heading.id = resolvedId;
|
|
222
|
+
items.push({
|
|
223
|
+
id: heading.id,
|
|
224
|
+
level: level,
|
|
225
|
+
text: text || ("Heading " + (items.length + 1)),
|
|
226
|
+
element: heading
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return items;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function getCaretRange() {
|
|
233
|
+
var selection = editor.getSelection ? editor.getSelection() : null;
|
|
234
|
+
if (!selection || selection.rangeCount === 0) return null;
|
|
235
|
+
try {
|
|
236
|
+
var range = selection.getRangeAt(0).cloneRange();
|
|
237
|
+
range.collapse(true);
|
|
238
|
+
return range;
|
|
239
|
+
} catch (ignore) {
|
|
240
|
+
return null;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function resolveActiveHeadingId(items) {
|
|
245
|
+
var range = getCaretRange();
|
|
246
|
+
if (!range || !items.length) return "";
|
|
247
|
+
var doc = range.startContainer.ownerDocument || editor.getEditable().ownerDocument;
|
|
248
|
+
var probe = doc.createRange();
|
|
249
|
+
var active = "";
|
|
250
|
+
try {
|
|
251
|
+
probe.setStart(range.startContainer, range.startOffset);
|
|
252
|
+
probe.collapse(true);
|
|
253
|
+
for (var i = 0; i < items.length; i++) {
|
|
254
|
+
var headingRange = doc.createRange();
|
|
255
|
+
headingRange.selectNodeContents(items[i].element);
|
|
256
|
+
headingRange.collapse(true);
|
|
257
|
+
if (headingRange.compareBoundaryPoints(Range.START_TO_START, probe) <= 0) active = items[i].id;
|
|
258
|
+
}
|
|
259
|
+
} catch (ignore) {
|
|
260
|
+
return activeHeadingId;
|
|
261
|
+
}
|
|
262
|
+
return active;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function focusHeading(item) {
|
|
266
|
+
if (!item || !item.element) return;
|
|
267
|
+
item.element.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
268
|
+
try {
|
|
269
|
+
var selection = editor.getSelection ? editor.getSelection() : null;
|
|
270
|
+
var range = item.element.ownerDocument.createRange();
|
|
271
|
+
range.selectNodeContents(item.element);
|
|
272
|
+
range.collapse(true);
|
|
273
|
+
if (selection) {
|
|
274
|
+
selection.removeAllRanges();
|
|
275
|
+
selection.addRange(range);
|
|
276
|
+
}
|
|
277
|
+
} catch (ignore) { }
|
|
278
|
+
editor.focus();
|
|
279
|
+
activeHeadingId = item.id;
|
|
280
|
+
syncActiveButton();
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
function syncActiveButton() {
|
|
284
|
+
if (!list) return;
|
|
285
|
+
var buttons = list.querySelectorAll(".rte-document-outline-item");
|
|
286
|
+
for (var i = 0; i < buttons.length; i++) {
|
|
287
|
+
var button = buttons[i];
|
|
288
|
+
var active = button.getAttribute("data-rte-outline-id") === activeHeadingId;
|
|
289
|
+
button.classList.toggle("is-active", active);
|
|
290
|
+
button.setAttribute("aria-current", active ? "true" : "false");
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
function updateActiveHeading() {
|
|
295
|
+
var items = collectHeadings();
|
|
296
|
+
var nextId = resolveActiveHeadingId(items);
|
|
297
|
+
if (nextId === activeHeadingId && list && list.childNodes.length) {
|
|
298
|
+
syncActiveButton();
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
activeHeadingId = nextId;
|
|
302
|
+
renderOutline(items);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
function renderOutline(cachedItems) {
|
|
306
|
+
ensureShell();
|
|
307
|
+
var items = cachedItems || collectHeadings();
|
|
308
|
+
activeHeadingId = resolveActiveHeadingId(items) || activeHeadingId;
|
|
309
|
+
|
|
310
|
+
while (list.firstChild) list.removeChild(list.firstChild);
|
|
311
|
+
|
|
312
|
+
var count = panel.querySelector("[data-rte-outline-count]");
|
|
313
|
+
if (count) count.innerText = items.length + " heading" + (items.length === 1 ? "" : "s");
|
|
314
|
+
empty.style.display = items.length ? "none" : "block";
|
|
315
|
+
|
|
316
|
+
for (var i = 0; i < items.length; i++) {
|
|
317
|
+
(function (item) {
|
|
318
|
+
var button = panel.ownerDocument.createElement("button");
|
|
319
|
+
button.type = "button";
|
|
320
|
+
button.className = "rte-document-outline-item";
|
|
321
|
+
button.style.setProperty("--rte-outline-level", String(item.level));
|
|
322
|
+
button.setAttribute("data-rte-outline-id", item.id);
|
|
323
|
+
button.setAttribute("aria-current", "false");
|
|
324
|
+
|
|
325
|
+
var level = panel.ownerDocument.createElement("span");
|
|
326
|
+
level.className = "rte-document-outline-item-level";
|
|
327
|
+
level.innerText = "H" + item.level;
|
|
328
|
+
|
|
329
|
+
var text = panel.ownerDocument.createElement("span");
|
|
330
|
+
text.innerText = item.text;
|
|
331
|
+
|
|
332
|
+
button.appendChild(level);
|
|
333
|
+
button.appendChild(text);
|
|
334
|
+
button.onclick = function () { focusHeading(item); };
|
|
335
|
+
list.appendChild(button);
|
|
336
|
+
})(items[i]);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
syncActiveButton();
|
|
340
|
+
}
|
|
341
|
+
}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
2
|
+
|
|
3
|
+
// 2026-06-04 Email toolkit. Closes the CKEditor "email / inline-style export"
|
|
4
|
+
// gap. Email clients (Outlook, Gmail) strip <style> blocks, external CSS, and
|
|
5
|
+
// class attributes — so HTML email must carry style="" inline on every element.
|
|
6
|
+
// editor.getEmailHTML() walks the live editor content, copies a curated set of
|
|
7
|
+
// computed styles onto each element as inline styles, removes class/id, and
|
|
8
|
+
// (optionally) wraps the result in a centered max-width table for client compat.
|
|
9
|
+
RTE_DefaultConfig.plugin_emailtoolkit = RTE_Plugin_EmailToolkit;
|
|
10
|
+
// Curated whitelist of email-relevant CSS properties (kept small to avoid bloat).
|
|
11
|
+
RTE_DefaultConfig.emailInlineProperties = RTE_DefaultConfig.emailInlineProperties || [
|
|
12
|
+
"color", "background-color", "font-family", "font-size", "font-weight",
|
|
13
|
+
"font-style", "text-decoration", "text-align", "line-height",
|
|
14
|
+
"margin-top", "margin-bottom", "margin-left", "margin-right",
|
|
15
|
+
"padding-top", "padding-bottom", "padding-left", "padding-right",
|
|
16
|
+
"border", "border-radius", "vertical-align", "list-style-type", "width"
|
|
17
|
+
];
|
|
18
|
+
RTE_DefaultConfig.emailWrapWidth = RTE_DefaultConfig.emailWrapWidth || 640;
|
|
19
|
+
|
|
20
|
+
function RTE_Plugin_EmailToolkit() {
|
|
21
|
+
var obj = this;
|
|
22
|
+
var config, editor;
|
|
23
|
+
|
|
24
|
+
obj.PluginName = "EmailToolkit";
|
|
25
|
+
|
|
26
|
+
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
27
|
+
|
|
28
|
+
obj.InitEditor = function (argeditor) {
|
|
29
|
+
editor = argeditor;
|
|
30
|
+
// Public API.
|
|
31
|
+
editor.getEmailHTML = function (options) { return obj.BuildEmailHTML(options || {}); };
|
|
32
|
+
editor.attachEvent("exec_command_emailexport", function (state) { state.returnValue = true; obj.OpenEmailExport(); });
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
function append(parent, tag, cssText, className) {
|
|
36
|
+
var el = parent.ownerDocument.createElement(tag);
|
|
37
|
+
if (cssText) el.style.cssText = cssText;
|
|
38
|
+
if (className) el.className = className;
|
|
39
|
+
parent.appendChild(el);
|
|
40
|
+
return el;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Values we treat as "default" and skip to keep the output lean.
|
|
44
|
+
function isDefaultValue(prop, val) {
|
|
45
|
+
if (!val) return true;
|
|
46
|
+
var v = val.trim().toLowerCase();
|
|
47
|
+
if (v === "normal" || v === "auto" || v === "none" || v === "0px" || v === "0") {
|
|
48
|
+
// Keep list-style-type:none (meaningful), drop the rest at default.
|
|
49
|
+
if (prop === "list-style-type" && v === "none") return false;
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
if (prop === "background-color" && (v === "rgba(0, 0, 0, 0)" || v === "transparent")) return true;
|
|
53
|
+
if (prop === "color" && v === "rgb(0, 0, 0)") return false; // keep explicit black text
|
|
54
|
+
if (prop === "text-decoration" && v.indexOf("none") === 0) return true;
|
|
55
|
+
if (prop === "border" && (v.indexOf("0px") === 0 || v.indexOf("none") !== -1 && v.indexOf("0px") !== -1)) return true;
|
|
56
|
+
if (prop === "vertical-align" && v === "baseline") return true;
|
|
57
|
+
if (prop === "font-weight" && v === "400") return true;
|
|
58
|
+
if (prop === "font-style" && v === "normal") return true;
|
|
59
|
+
// start/left are the default flow direction; "start" is poorly supported in Outlook.
|
|
60
|
+
if (prop === "text-align" && (v === "start" || v === "left")) return true;
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Tags where list-style-type is meaningful; emitting "disc" on a <p>/<span> is bloat.
|
|
65
|
+
var LIST_TAGS = { UL: 1, OL: 1, LI: 1 };
|
|
66
|
+
|
|
67
|
+
// Build an inline style string for a live element from its computed style.
|
|
68
|
+
function inlineStyleFor(el, win) {
|
|
69
|
+
var cs = win.getComputedStyle(el);
|
|
70
|
+
var props = config.emailInlineProperties || [];
|
|
71
|
+
var out = [];
|
|
72
|
+
for (var i = 0; i < props.length; i++) {
|
|
73
|
+
var p = props[i];
|
|
74
|
+
var v;
|
|
75
|
+
try { v = cs.getPropertyValue(p); } catch (e) { v = ""; }
|
|
76
|
+
if (v == null || v === "") continue;
|
|
77
|
+
if (isDefaultValue(p, v)) continue;
|
|
78
|
+
// width only on tables/images (avoid forcing widths on every block).
|
|
79
|
+
if (p === "width" && el.nodeName !== "TABLE" && el.nodeName !== "IMG" && el.nodeName !== "TD" && el.nodeName !== "TH") continue;
|
|
80
|
+
// list-style-type only on list elements (else "disc" bloats every <p>/<span>).
|
|
81
|
+
if (p === "list-style-type" && !LIST_TAGS[el.nodeName]) continue;
|
|
82
|
+
out.push(p + ":" + v);
|
|
83
|
+
}
|
|
84
|
+
return out.join(";");
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
var SKIP_TAGS = { SCRIPT: 1, STYLE: 1, LINK: 1, META: 1 };
|
|
88
|
+
|
|
89
|
+
// Recursively clone a live element into an inlined-style clone.
|
|
90
|
+
function cloneInlined(src, win) {
|
|
91
|
+
if (src.nodeType === 3) return src.ownerDocument.createTextNode(src.data); // text
|
|
92
|
+
if (src.nodeType !== 1) return null;
|
|
93
|
+
if (SKIP_TAGS[src.nodeName]) return null;
|
|
94
|
+
var doc = src.ownerDocument;
|
|
95
|
+
var clone = doc.createElement(src.nodeName);
|
|
96
|
+
// Copy semantic attributes (href/src/alt/colspan/etc.) but NOT class/id/contenteditable.
|
|
97
|
+
for (var a = 0; a < src.attributes.length; a++) {
|
|
98
|
+
var at = src.attributes[a];
|
|
99
|
+
var n = at.name.toLowerCase();
|
|
100
|
+
if (n === "class" || n === "id" || n === "contenteditable" || n === "style" || n.indexOf("data-rte-") === 0) continue;
|
|
101
|
+
clone.setAttribute(at.name, at.value);
|
|
102
|
+
}
|
|
103
|
+
var inline = inlineStyleFor(src, win);
|
|
104
|
+
if (inline) clone.setAttribute("style", inline);
|
|
105
|
+
for (var c = 0; c < src.childNodes.length; c++) {
|
|
106
|
+
var child = cloneInlined(src.childNodes[c], win);
|
|
107
|
+
if (child) clone.appendChild(child);
|
|
108
|
+
}
|
|
109
|
+
return clone;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
obj.BuildEmailHTML = function (options) {
|
|
113
|
+
var editable = editor.getEditable();
|
|
114
|
+
var editdoc = editor.getDocument();
|
|
115
|
+
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;
|
|
122
|
+
var wrap = options.wrap !== false;
|
|
123
|
+
if (!wrap) return inner;
|
|
124
|
+
var width = options.width || config.emailWrapWidth || 640;
|
|
125
|
+
// Center the content in an email-safe table.
|
|
126
|
+
return '<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border-collapse:collapse">' +
|
|
127
|
+
'<tr><td align="center" style="padding:0">' +
|
|
128
|
+
'<table role="presentation" width="' + width + '" cellpadding="0" cellspacing="0" style="border-collapse:collapse;max-width:' + width + 'px;width:100%">' +
|
|
129
|
+
'<tr><td style="padding:16px;font-family:Arial,Helvetica,sans-serif">' + inner + '</td></tr></table>' +
|
|
130
|
+
'</td></tr></table>';
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
obj.OpenEmailExport = function () {
|
|
134
|
+
var html = obj.BuildEmailHTML({});
|
|
135
|
+
var dlg = editor.createDialog((editor.getLangText && editor.getLangText("emailexporttitle")) || "Export email HTML", "rte-dialog-email");
|
|
136
|
+
var close = typeof dlg.close === "function" ? function () { dlg.close(); } : function () { editor.closeCurrentPopup(); };
|
|
137
|
+
var wrap = append(dlg, "div", "", "rte-email-export");
|
|
138
|
+
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.";
|
|
140
|
+
var ta = append(wrap, "textarea", "", "rte-email-export-code");
|
|
141
|
+
ta.value = html;
|
|
142
|
+
ta.readOnly = true;
|
|
143
|
+
ta.setAttribute("aria-label", "Inline-styled email HTML");
|
|
144
|
+
var status = append(wrap, "div", "", "rte-email-export-status");
|
|
145
|
+
status.setAttribute("role", "status");
|
|
146
|
+
status.setAttribute("aria-live", "polite");
|
|
147
|
+
status.textContent = "Ready to copy " + html.length + " characters.";
|
|
148
|
+
var foot = append(wrap, "div", "", "rte-email-export-footer");
|
|
149
|
+
var copyBtn = append(foot, "button", "", "rte-email-export-button rte-email-export-button-primary");
|
|
150
|
+
copyBtn.type = "button"; copyBtn.textContent = "Copy";
|
|
151
|
+
copyBtn.onclick = function () {
|
|
152
|
+
ta.select();
|
|
153
|
+
try {
|
|
154
|
+
if (navigator.clipboard && navigator.clipboard.writeText) { navigator.clipboard.writeText(ta.value); }
|
|
155
|
+
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);
|
|
159
|
+
} catch (e) { /* ignore */ }
|
|
160
|
+
};
|
|
161
|
+
var closeBtn = append(foot, "button", "", "rte-email-export-button");
|
|
162
|
+
closeBtn.type = "button"; closeBtn.textContent = "Close"; closeBtn.onclick = close;
|
|
163
|
+
setTimeout(function () { ta.focus(); }, 0);
|
|
164
|
+
};
|
|
165
|
+
}
|