@richscripts/richtexteditor 2.14.0 → 2.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +173 -10
- package/angular/build-manifest.json +23 -0
- package/angular/index.d.ts +71 -76
- package/angular/index.mjs +2 -0
- package/angular/package.json +4 -0
- package/headless/index.d.ts +3 -1
- package/headless/index.js +72 -16
- package/index.d.ts +33 -54
- package/integrations/initialization-scope.js +27 -0
- package/integrations/native-mount.js +30 -0
- package/integrations/shared.d.ts +23 -1
- package/integrations/shared.js +58 -22
- package/package.json +25 -3
- package/react/index.js +118 -37
- package/react/index.mjs +29 -0
- package/richtexteditor/all_plugins.js +7113 -1111
- package/richtexteditor/all_plugins.min.js +62 -59
- package/richtexteditor/lang/emoji/LICENSE-CLDR.txt +41 -0
- package/richtexteditor/lang/emoji/rte-emoji-de.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-es.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-fr.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-it.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ja.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ko.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-nl.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-pt-pt.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-pt.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-ru.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-zh-Hans.js +3 -0
- package/richtexteditor/lang/emoji/rte-emoji-zh-Hant.js +3 -0
- package/richtexteditor/lang/rte-lang-af.js +1 -0
- package/richtexteditor/lang/rte-lang-ar.js +1 -0
- package/richtexteditor/lang/rte-lang-bg.js +1 -0
- package/richtexteditor/lang/rte-lang-bn.js +1 -0
- package/richtexteditor/lang/rte-lang-bs.js +1 -0
- package/richtexteditor/lang/rte-lang-ca.js +1 -0
- package/richtexteditor/lang/rte-lang-cs.js +1 -0
- package/richtexteditor/lang/rte-lang-cy.js +1 -0
- package/richtexteditor/lang/rte-lang-da.js +1 -0
- package/richtexteditor/lang/rte-lang-de.js +1017 -2
- package/richtexteditor/lang/rte-lang-el.js +1 -0
- package/richtexteditor/lang/rte-lang-en.js +1 -0
- package/richtexteditor/lang/rte-lang-es.js +1023 -2
- package/richtexteditor/lang/rte-lang-et.js +1 -0
- package/richtexteditor/lang/rte-lang-fa.js +1 -0
- package/richtexteditor/lang/rte-lang-fi.js +1 -0
- package/richtexteditor/lang/rte-lang-fil.js +1 -0
- package/richtexteditor/lang/rte-lang-fj.js +1 -0
- package/richtexteditor/lang/rte-lang-fr.js +1023 -2
- package/richtexteditor/lang/rte-lang-ga.js +1 -0
- package/richtexteditor/lang/rte-lang-gu.js +1 -0
- package/richtexteditor/lang/rte-lang-he.js +1 -0
- package/richtexteditor/lang/rte-lang-hi.js +1 -0
- package/richtexteditor/lang/rte-lang-hr.js +1 -0
- package/richtexteditor/lang/rte-lang-ht.js +1 -0
- package/richtexteditor/lang/rte-lang-hu.js +1 -0
- package/richtexteditor/lang/rte-lang-id.js +1 -0
- package/richtexteditor/lang/rte-lang-is.js +1 -0
- package/richtexteditor/lang/rte-lang-it.js +1023 -2
- package/richtexteditor/lang/rte-lang-ja.js +1023 -2
- package/richtexteditor/lang/rte-lang-kn.js +1 -0
- package/richtexteditor/lang/rte-lang-ko.js +1023 -2
- package/richtexteditor/lang/rte-lang-lt.js +1 -0
- package/richtexteditor/lang/rte-lang-lv.js +1 -0
- package/richtexteditor/lang/rte-lang-mg.js +1 -0
- package/richtexteditor/lang/rte-lang-mi.js +1 -0
- package/richtexteditor/lang/rte-lang-ml.js +1 -0
- package/richtexteditor/lang/rte-lang-mr.js +1 -0
- package/richtexteditor/lang/rte-lang-ms.js +1 -0
- package/richtexteditor/lang/rte-lang-mt.js +1 -0
- package/richtexteditor/lang/rte-lang-mww.js +1 -0
- package/richtexteditor/lang/rte-lang-nb.js +1 -0
- package/richtexteditor/lang/rte-lang-nl.js +1023 -2
- package/richtexteditor/lang/rte-lang-otq.js +1 -0
- package/richtexteditor/lang/rte-lang-pa.js +1 -0
- package/richtexteditor/lang/rte-lang-pl.js +1 -0
- package/richtexteditor/lang/rte-lang-pt-pt.js +1023 -2
- package/richtexteditor/lang/rte-lang-pt.js +1023 -2
- package/richtexteditor/lang/rte-lang-ro.js +1 -0
- package/richtexteditor/lang/rte-lang-ru.js +1023 -2
- package/richtexteditor/lang/rte-lang-sk.js +1 -0
- package/richtexteditor/lang/rte-lang-sl.js +1 -0
- package/richtexteditor/lang/rte-lang-sm.js +1 -0
- package/richtexteditor/lang/rte-lang-sr-Cyrl.js +1 -0
- package/richtexteditor/lang/rte-lang-sr-Latn.js +1 -0
- package/richtexteditor/lang/rte-lang-sv.js +1 -0
- package/richtexteditor/lang/rte-lang-sw.js +1 -0
- package/richtexteditor/lang/rte-lang-ta.js +1 -0
- package/richtexteditor/lang/rte-lang-te.js +1 -0
- package/richtexteditor/lang/rte-lang-th.js +1 -0
- package/richtexteditor/lang/rte-lang-tlh-Latn.js +1 -0
- package/richtexteditor/lang/rte-lang-tlh-Piqd.js +1 -0
- package/richtexteditor/lang/rte-lang-to.js +1 -0
- package/richtexteditor/lang/rte-lang-tr.js +1 -0
- package/richtexteditor/lang/rte-lang-ty.js +1 -0
- package/richtexteditor/lang/rte-lang-uk.js +1 -0
- package/richtexteditor/lang/rte-lang-ur.js +1 -0
- package/richtexteditor/lang/rte-lang-vi.js +1 -0
- package/richtexteditor/lang/rte-lang-yua.js +1 -0
- package/richtexteditor/lang/rte-lang-zh-Hans.js +1017 -2
- package/richtexteditor/lang/rte-lang-zh-Hant.js +1017 -2
- package/richtexteditor/plugins/a11yenhance.js +2 -1
- package/richtexteditor/plugins/a11yenhance.min.js +1 -1
- package/richtexteditor/plugins/accessibilitychecker.js +252 -60
- package/richtexteditor/plugins/accessibilitychecker.min.js +1 -1
- package/richtexteditor/plugins/aitoolkit.js +966 -303
- package/richtexteditor/plugins/aitoolkit.min.js +7 -7
- package/richtexteditor/plugins/all_plugins.js +7113 -1111
- package/richtexteditor/plugins/all_plugins.min.js +62 -59
- package/richtexteditor/plugins/autocorrect.js +66 -31
- package/richtexteditor/plugins/autocorrect.min.js +1 -1
- package/richtexteditor/plugins/autoembed.js +17 -0
- package/richtexteditor/plugins/autoembed.min.js +1 -1
- package/richtexteditor/plugins/blocktypes.js +18 -9
- package/richtexteditor/plugins/blocktypes.min.js +1 -1
- package/richtexteditor/plugins/bookmarkcard.js +12 -5
- package/richtexteditor/plugins/bookmarkcard.min.js +2 -2
- package/richtexteditor/plugins/changecase.js +72 -19
- package/richtexteditor/plugins/changecase.min.js +1 -1
- package/richtexteditor/plugins/charlimit.js +62 -2
- package/richtexteditor/plugins/charlimit.min.js +2 -2
- package/richtexteditor/plugins/comments.js +458 -59
- package/richtexteditor/plugins/comments.min.js +4 -3
- package/richtexteditor/plugins/contentminimap.js +25 -6
- package/richtexteditor/plugins/contentminimap.min.js +1 -1
- package/richtexteditor/plugins/crossreference.js +1 -1
- package/richtexteditor/plugins/crossreference.min.js +1 -1
- package/richtexteditor/plugins/dialoga11y.js +26 -4
- package/richtexteditor/plugins/dialoga11y.min.js +1 -1
- package/richtexteditor/plugins/dictation.js +1 -1
- package/richtexteditor/plugins/documentimport.js +220 -51
- package/richtexteditor/plugins/documentimport.min.js +4 -4
- package/richtexteditor/plugins/documentoutline.js +88 -38
- package/richtexteditor/plugins/documentoutline.min.js +1 -1
- package/richtexteditor/plugins/docxexport.js +204 -32
- package/richtexteditor/plugins/docxexport.min.js +2 -2
- package/richtexteditor/plugins/draghandle.js +267 -3
- package/richtexteditor/plugins/draghandle.min.js +1 -1
- package/richtexteditor/plugins/emailtoolkit.js +28 -14
- package/richtexteditor/plugins/emailtoolkit.min.js +1 -1
- package/richtexteditor/plugins/emojiautocomplete.js +110 -56
- package/richtexteditor/plugins/emojiautocomplete.min.js +1 -1
- package/richtexteditor/plugins/footnotes.js +65 -11
- package/richtexteditor/plugins/footnotes.min.js +1 -1
- package/richtexteditor/plugins/formatpainter.js +10 -0
- package/richtexteditor/plugins/formatpainter.min.js +1 -1
- package/richtexteditor/plugins/formattingmarks.js +53 -8
- package/richtexteditor/plugins/formattingmarks.min.js +1 -1
- package/richtexteditor/plugins/gapcursor.js +60 -0
- package/richtexteditor/plugins/gapcursor.min.js +1 -1
- package/richtexteditor/plugins/ghostcomplete.js +1 -1
- package/richtexteditor/plugins/html2pdf.js +1 -1
- package/richtexteditor/plugins/html2pdf.min.js +1 -1
- package/richtexteditor/plugins/imageeditor.js +12 -6
- package/richtexteditor/plugins/imageeditor.min.js +1 -1
- package/richtexteditor/plugins/importstyles.js +1 -1
- package/richtexteditor/plugins/inlinecode.js +25 -0
- package/richtexteditor/plugins/inlinecode.min.js +1 -1
- package/richtexteditor/plugins/insertcode.js +21 -3
- package/richtexteditor/plugins/insertcode.min.js +4 -4
- package/richtexteditor/plugins/insertemoji.js +112 -15
- package/richtexteditor/plugins/insertemoji.min.js +1 -1
- package/richtexteditor/plugins/insertgallery.js +42 -36
- package/richtexteditor/plugins/insertgallery.min.js +1 -1
- package/richtexteditor/plugins/inserttemplate.js +33 -17
- package/richtexteditor/plugins/inserttemplate.min.js +1 -1
- package/richtexteditor/plugins/keyboarda11y.js +66 -3
- package/richtexteditor/plugins/keyboarda11y.min.js +1 -1
- package/richtexteditor/plugins/layouttable.js +20 -0
- package/richtexteditor/plugins/layouttable.min.js +1 -1
- package/richtexteditor/plugins/linkchecker.js +29 -9
- package/richtexteditor/plugins/linkchecker.min.js +1 -1
- package/richtexteditor/plugins/localdraft.js +51 -7
- package/richtexteditor/plugins/localdraft.min.js +1 -1
- package/richtexteditor/plugins/markdownexport.js +159 -0
- package/richtexteditor/plugins/markdownexport.min.js +2 -2
- package/richtexteditor/plugins/mathdialog.js +11 -5
- package/richtexteditor/plugins/mathdialog.min.js +1 -1
- package/richtexteditor/plugins/mentionplugin.js +88 -25
- package/richtexteditor/plugins/mentionplugin.min.js +1 -1
- package/richtexteditor/plugins/mermaiddiagram.js +9 -3
- package/richtexteditor/plugins/mermaiddiagram.min.js +2 -2
- package/richtexteditor/plugins/multilevellist.js +4 -0
- package/richtexteditor/plugins/multilevellist.min.js +1 -1
- package/richtexteditor/plugins/pdfexport.js +235 -35
- package/richtexteditor/plugins/pdfexport.min.js +12 -12
- package/richtexteditor/plugins/permanentpen.js +3 -3
- package/richtexteditor/plugins/permanentpen.min.js +1 -1
- package/richtexteditor/plugins/readabilitystats.js +25 -11
- package/richtexteditor/plugins/readabilitystats.min.js +1 -1
- package/richtexteditor/plugins/readaloud.js +29 -15
- package/richtexteditor/plugins/readaloud.min.js +1 -1
- package/richtexteditor/plugins/restrictedediting.js +166 -0
- package/richtexteditor/plugins/restrictedediting.min.js +2 -2
- package/richtexteditor/plugins/revisionhistory.js +169 -37
- package/richtexteditor/plugins/revisionhistory.min.js +3 -3
- package/richtexteditor/plugins/rubyannotation.js +19 -9
- package/richtexteditor/plugins/rubyannotation.min.js +1 -1
- package/richtexteditor/plugins/sanitizer.js +86 -4
- package/richtexteditor/plugins/sanitizer.min.js +1 -1
- package/richtexteditor/plugins/slashcommand.js +30 -16
- package/richtexteditor/plugins/slashcommand.min.js +2 -2
- package/richtexteditor/plugins/smartchips.js +36 -22
- package/richtexteditor/plugins/smartchips.min.js +2 -2
- package/richtexteditor/plugins/spellcheck.js +17 -8
- package/richtexteditor/plugins/spellcheck.min.js +2 -2
- package/richtexteditor/plugins/stateinspector.js +19 -9
- package/richtexteditor/plugins/stateinspector.min.js +2 -2
- package/richtexteditor/plugins/structured-content-bridge.js +56 -24
- package/richtexteditor/plugins/structured-content-bridge.min.js +7 -7
- package/richtexteditor/plugins/tableofcontents.js +5 -2
- package/richtexteditor/plugins/tableofcontents.min.js +1 -1
- package/richtexteditor/plugins/tabletools.js +388 -3
- package/richtexteditor/plugins/tabletools.min.js +1 -1
- package/richtexteditor/plugins/textdirection.js +18 -5
- package/richtexteditor/plugins/textdirection.min.js +1 -1
- package/richtexteditor/plugins/textpartlanguage.js +161 -32
- package/richtexteditor/plugins/textpartlanguage.min.js +1 -1
- package/richtexteditor/plugins/todolist.js +90 -6
- package/richtexteditor/plugins/todolist.min.js +1 -1
- package/richtexteditor/plugins/trackedchanges.js +1812 -25
- package/richtexteditor/plugins/trackedchanges.min.js +5 -3
- package/richtexteditor/plugins/wordexport.js +2 -2
- package/richtexteditor/plugins/yjscollab.js +9 -4
- package/richtexteditor/plugins/yjscollab.min.js +1 -1
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.js +22 -13
- package/richtexteditor/plugins/zz_richtextbox_dialog_style.min.js +1 -1
- package/richtexteditor/rte-config.js +131 -50
- package/richtexteditor/rte-headless.js +19 -4
- package/richtexteditor/rte-webcomponent.js +89 -16
- package/richtexteditor/rte.js +133 -52
- package/richtexteditor/runtime/colorpicker_more_ns.htm +93 -16
- package/scripts/build-angular.mjs +97 -0
- package/svelte/index.d.ts +2 -1
- package/svelte/index.js +80 -9
- package/vue/index.js +130 -34
- package/angular/component.ts +0 -297
- package/angular/index.ts +0 -24
|
@@ -39,34 +39,34 @@ if (!window.RTE_DefaultConfig) window.RTE_DefaultConfig = {};
|
|
|
39
39
|
RTE_DefaultConfig.plugin_textpartlanguage = RTE_Plugin_TextPartLanguage;
|
|
40
40
|
|
|
41
41
|
// The dropdown list. `dir` is only needed for right-to-left languages.
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
42
|
+
// Kept by name so the menu can tell a built-in entry (named in the page's language) from the application's own label.
|
|
43
|
+
var RTE_TextPartLanguageDefaults = [
|
|
44
|
+
{ code: "ar", label: "Arabic", dir: "rtl" },
|
|
45
|
+
{ code: "zh-Hans", label: "Chinese (Simplified)" },
|
|
46
|
+
{ code: "zh-Hant", label: "Chinese (Traditional)" },
|
|
47
|
+
{ code: "cs", label: "Czech" },
|
|
48
|
+
{ code: "nl", label: "Dutch" },
|
|
49
|
+
{ code: "en", label: "English" },
|
|
50
|
+
{ code: "fr", label: "French" },
|
|
51
|
+
{ code: "de", label: "German" },
|
|
52
|
+
{ code: "el", label: "Greek" },
|
|
53
|
+
{ code: "he", label: "Hebrew", dir: "rtl" },
|
|
54
|
+
{ code: "hi", label: "Hindi" },
|
|
55
|
+
{ code: "it", label: "Italian" },
|
|
56
|
+
{ code: "ja", label: "Japanese" },
|
|
57
|
+
{ code: "ko", label: "Korean" },
|
|
58
|
+
{ code: "fa", label: "Persian", dir: "rtl" },
|
|
59
|
+
{ code: "pl", label: "Polish" },
|
|
60
|
+
{ code: "pt", label: "Portuguese" },
|
|
61
|
+
{ code: "ru", label: "Russian" },
|
|
62
|
+
{ code: "es", label: "Spanish" },
|
|
63
|
+
{ code: "sv", label: "Swedish" },
|
|
64
|
+
{ code: "tr", label: "Turkish" },
|
|
65
|
+
{ code: "uk", label: "Ukrainian" },
|
|
66
|
+
{ code: "ur", label: "Urdu", dir: "rtl" },
|
|
67
|
+
{ code: "vi", label: "Vietnamese" }
|
|
68
|
+
];
|
|
69
|
+
if (typeof RTE_DefaultConfig.textPartLanguages === "undefined") RTE_DefaultConfig.textPartLanguages = RTE_TextPartLanguageDefaults.slice();
|
|
70
70
|
|
|
71
71
|
// Show marked phrases with a dotted underline while editing. Editor-only: the
|
|
72
72
|
// rule lives in the editing document, never in the saved HTML.
|
|
@@ -81,6 +81,14 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
81
81
|
|
|
82
82
|
obj.InitConfig = function (argconfig) { config = argconfig; };
|
|
83
83
|
|
|
84
|
+
// record the change as its own undo step: seal the step before, run, save the snapshot, seal it
|
|
85
|
+
function undoStep(run) {
|
|
86
|
+
try { if (editor.commitBookmark) editor.commitBookmark(); } catch (er) { /* ignore */ }
|
|
87
|
+
var result = run();
|
|
88
|
+
try { if (editor.recordUndo) editor.recordUndo(); else if (editor.notifySelectionChange) editor.notifySelectionChange(); if (editor.commitBookmark) editor.commitBookmark(); } catch (er) { /* ignore */ }
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
|
|
84
92
|
obj.InitEditor = function (argeditor) {
|
|
85
93
|
editor = argeditor;
|
|
86
94
|
|
|
@@ -103,8 +111,9 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
103
111
|
setTimeout(setup, 0);
|
|
104
112
|
|
|
105
113
|
// Public API.
|
|
106
|
-
editor.setTextLanguage = function (code) { return obj.Apply(code); };
|
|
107
|
-
|
|
114
|
+
editor.setTextLanguage = function (code) { return undoStep(function () { return obj.Apply(code); }); };
|
|
115
|
+
// 2026-10-04 one undo step when called directly (it could not be undone). tools/api/plugin-api-undo.mjs
|
|
116
|
+
editor.removeTextLanguage = function () { return undoStep(function () { return obj.Remove(); }); };
|
|
108
117
|
editor.getTextLanguage = function () { return obj.Current(); };
|
|
109
118
|
editor.listTextLanguages = function () { return languages().slice(); };
|
|
110
119
|
editor.getMarkedLanguages = function () { return obj.List(); };
|
|
@@ -124,6 +133,30 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
124
133
|
return { code: code, label: code };
|
|
125
134
|
}
|
|
126
135
|
|
|
136
|
+
// 2026-10-18 The menu names languages in the page's language: a built-in entry is named by Intl.DisplayNames in
|
|
137
|
+
// the language pack's locale (text_uilocale); a label the application set is kept as written; an entry with no
|
|
138
|
+
// label (it showed "undefined") gets the English name, else its code. tools/i18n/text-language-names.mjs
|
|
139
|
+
var displayNames = {};
|
|
140
|
+
function nameIn(locale, code) {
|
|
141
|
+
try {
|
|
142
|
+
if (typeof Intl === "undefined" || !Intl.DisplayNames) return "";
|
|
143
|
+
if (!displayNames[locale]) displayNames[locale] = new Intl.DisplayNames([locale], { type: "language" });
|
|
144
|
+
var n = displayNames[locale].of(code);
|
|
145
|
+
return n && n !== code ? n : "";
|
|
146
|
+
} catch (e) { return ""; }
|
|
147
|
+
}
|
|
148
|
+
function displayLabel(item) {
|
|
149
|
+
var code = item && item.code;
|
|
150
|
+
if (!code) return "";
|
|
151
|
+
var builtIn = null;
|
|
152
|
+
var defs = typeof RTE_TextPartLanguageDefaults !== "undefined" ? RTE_TextPartLanguageDefaults : [];
|
|
153
|
+
for (var i = 0; i < defs.length; i++) if (defs[i].code === code) builtIn = defs[i].label;
|
|
154
|
+
if (item.label && item.label !== builtIn) return item.label;
|
|
155
|
+
var locale = config && config.text_uilocale;
|
|
156
|
+
if (locale && !/^en(-|$)/i.test(locale)) { var local = nameIn(locale, code); if (local) return local; }
|
|
157
|
+
return item.label || nameIn("en", code) || code;
|
|
158
|
+
}
|
|
159
|
+
|
|
127
160
|
// ---- state -----------------------------------------------------------
|
|
128
161
|
|
|
129
162
|
// The nearest ancestor carrying a lang, bounded by the editable so the host
|
|
@@ -182,6 +215,8 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
182
215
|
var doc = getDoc();
|
|
183
216
|
var r = selectionRange();
|
|
184
217
|
if (!doc || !r) return false;
|
|
218
|
+
// 2026-10-14 Suggesting mode: a language change is not tracked yet, so the track hook refuses it. tools/review/suggesting-structure-audit.mjs
|
|
219
|
+
if (editor.__trackHook && editor.__trackHook("refuse", { message: "Suggesting is on, so the language was not changed. Turn suggesting off to change it." }) === true) return false;
|
|
185
220
|
|
|
186
221
|
// Re-picking the current language toggles it off.
|
|
187
222
|
if (obj.Current() === code) return obj.Remove();
|
|
@@ -195,6 +230,43 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
195
230
|
return code;
|
|
196
231
|
}
|
|
197
232
|
|
|
233
|
+
// 2026-10-02 A selection that crosses blocks marks the selected text of EACH block. One
|
|
234
|
+
// span went AROUND the blocks: two paragraphs became four
|
|
235
|
+
// (<p>al</p><span lang="fr"><p>pha</p><p>be</p></span><p>ta</p>), list items and table cells
|
|
236
|
+
// got a span straight inside <ul> / <tr> and were split, a heading was split in two (with its
|
|
237
|
+
// id twice) - and removing the language left them split. CKEditor marks each block's text.
|
|
238
|
+
// tools/format/text-part-language.mjs
|
|
239
|
+
var runs = crossBlockRuns(doc, r);
|
|
240
|
+
if (runs) {
|
|
241
|
+
var spans = [];
|
|
242
|
+
// from the last run back: wrapping one never moves the text of a run still to come
|
|
243
|
+
for (var ri = runs.length - 1; ri >= 0; ri--) {
|
|
244
|
+
var sub = doc.createRange();
|
|
245
|
+
sub.setStart(runs[ri].sn, runs[ri].so);
|
|
246
|
+
sub.setEnd(runs[ri].en, runs[ri].eo);
|
|
247
|
+
widenToInlineEdges(sub, runs[ri].block);
|
|
248
|
+
var part = doc.createElement("span");
|
|
249
|
+
stamp(part, code);
|
|
250
|
+
part.appendChild(sub.extractContents());
|
|
251
|
+
sub.insertNode(part);
|
|
252
|
+
unwrapInner(part);
|
|
253
|
+
pruneEmptyText(part);
|
|
254
|
+
if (!part.firstChild) { part.parentNode.removeChild(part); continue; }
|
|
255
|
+
spans.unshift(mergeSiblings(part));
|
|
256
|
+
}
|
|
257
|
+
if (!spans.length) return false;
|
|
258
|
+
try {
|
|
259
|
+
var all = doc.createRange();
|
|
260
|
+
all.setStart(spans[0], 0);
|
|
261
|
+
all.setEnd(spans[spans.length - 1], spans[spans.length - 1].childNodes.length);
|
|
262
|
+
var sel = editor.getSelection();
|
|
263
|
+
sel.removeAllRanges();
|
|
264
|
+
sel.addRange(all);
|
|
265
|
+
} catch (e) {}
|
|
266
|
+
fireChange();
|
|
267
|
+
return code;
|
|
268
|
+
}
|
|
269
|
+
|
|
198
270
|
var span = doc.createElement("span");
|
|
199
271
|
stamp(span, code);
|
|
200
272
|
|
|
@@ -222,6 +294,8 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
222
294
|
};
|
|
223
295
|
|
|
224
296
|
obj.Remove = function () {
|
|
297
|
+
// 2026-10-14 Suggesting mode: removing this is not tracked yet, so the track hook refuses it. tools/review/suggesting-structure-audit.mjs
|
|
298
|
+
if (editor.__trackHook && editor.__trackHook("remove", {}) === true) return false;
|
|
225
299
|
var r = selectionRange();
|
|
226
300
|
if (!r) return false;
|
|
227
301
|
var ed = getEditable();
|
|
@@ -256,6 +330,59 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
256
330
|
} catch (e) { return false; }
|
|
257
331
|
}
|
|
258
332
|
|
|
333
|
+
// The runs of selected text that share a block, when the selection crosses blocks; null when it
|
|
334
|
+
// starts and ends in one block (that keeps the single-span path). Whitespace-only runs (the line
|
|
335
|
+
// break between two blocks) are dropped; text inside a non-editable element moves with its run.
|
|
336
|
+
var BLOCK_RE = /^(P|DIV|LI|UL|OL|DL|DT|DD|TABLE|TBODY|THEAD|TFOOT|TR|TD|TH|CAPTION|H[1-6]|PRE|BLOCKQUOTE|ADDRESS|FIGURE|FIGCAPTION|SECTION|ARTICLE|ASIDE|HEADER|FOOTER|NAV|MAIN|DETAILS|SUMMARY|FORM|FIELDSET|LEGEND|HR|BODY)$/;
|
|
337
|
+
function blockOf(node) {
|
|
338
|
+
for (var n = node; n; n = n.parentNode)
|
|
339
|
+
if (n.nodeType === 1 && BLOCK_RE.test(n.nodeName)) return n;
|
|
340
|
+
return null;
|
|
341
|
+
}
|
|
342
|
+
function crossBlockRuns(doc, range) {
|
|
343
|
+
if (range.collapsed || blockOf(range.startContainer) === blockOf(range.endContainer)) return null;
|
|
344
|
+
var root = range.commonAncestorContainer;
|
|
345
|
+
if (root.nodeType !== 1) root = root.parentNode;
|
|
346
|
+
var runs = [], cur = null;
|
|
347
|
+
var w = doc.createTreeWalker(root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT, {
|
|
348
|
+
acceptNode: function (n) { return n.nodeType === 1 && n.getAttribute("contenteditable") === "false" ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT; }
|
|
349
|
+
});
|
|
350
|
+
for (var n = w.nextNode(); n; n = w.nextNode()) {
|
|
351
|
+
if (n.nodeType === 1) {
|
|
352
|
+
if (BLOCK_RE.test(n.nodeName)) cur = null;
|
|
353
|
+
continue;
|
|
354
|
+
}
|
|
355
|
+
if (!range.intersectsNode(n)) continue;
|
|
356
|
+
var a = n === range.startContainer ? range.startOffset : 0, b = n === range.endContainer ? range.endOffset : n.data.length;
|
|
357
|
+
if (b <= a) continue;
|
|
358
|
+
var blk = blockOf(n);
|
|
359
|
+
if (!cur || cur.block !== blk) {
|
|
360
|
+
cur = { block: blk, sn: n, so: a, text: "" };
|
|
361
|
+
runs.push(cur);
|
|
362
|
+
}
|
|
363
|
+
cur.en = n;
|
|
364
|
+
cur.eo = b;
|
|
365
|
+
cur.text += n.data.substring(a, b);
|
|
366
|
+
}
|
|
367
|
+
var keep = [];
|
|
368
|
+
for (var i = 0; i < runs.length; i++) if (/\S/.test(runs[i].text)) keep.push(runs[i]);
|
|
369
|
+
return keep;
|
|
370
|
+
}
|
|
371
|
+
// A run starting or ending at the edge of an inline element (<b>) takes the whole element, so
|
|
372
|
+
// no empty <b></b> is left behind by extractContents.
|
|
373
|
+
function widenToInlineEdges(range, block) {
|
|
374
|
+
var n = range.startContainer;
|
|
375
|
+
if (n.nodeType === 3 && range.startOffset === 0) {
|
|
376
|
+
while (n.parentNode && n.parentNode !== block && !n.previousSibling && !BLOCK_RE.test(n.parentNode.nodeName)) n = n.parentNode;
|
|
377
|
+
range.setStartBefore(n);
|
|
378
|
+
}
|
|
379
|
+
n = range.endContainer;
|
|
380
|
+
if (n.nodeType === 3 && range.endOffset === n.data.length) {
|
|
381
|
+
while (n.parentNode && n.parentNode !== block && !n.nextSibling && !BLOCK_RE.test(n.parentNode.nodeName)) n = n.parentNode;
|
|
382
|
+
range.setEndAfter(n);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
259
386
|
function stamp(el, code) {
|
|
260
387
|
el.setAttribute("lang", code);
|
|
261
388
|
var dir = entry(code).dir;
|
|
@@ -350,7 +477,7 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
350
477
|
|
|
351
478
|
for (var i = 0; i < list.length; i++) {
|
|
352
479
|
(function (item) {
|
|
353
|
-
addRow(panel, item
|
|
480
|
+
addRow(panel, displayLabel(item), item.code, current === item.code, function () { obj.Apply(item.code); });
|
|
354
481
|
})(list[i]);
|
|
355
482
|
}
|
|
356
483
|
|
|
@@ -397,7 +524,9 @@ function RTE_Plugin_TextPartLanguage() {
|
|
|
397
524
|
|
|
398
525
|
if (code) {
|
|
399
526
|
var tag = doc.createElement("span");
|
|
400
|
-
|
|
527
|
+
// 2026-10-15 opacity .55 left the code at 4.06:1 in dark mode and 3.23:1 in light; .75 clears 4.5:1 in both.
|
|
528
|
+
// tools/a11y/dark-mode-contrast.mjs
|
|
529
|
+
tag.style.cssText = "opacity:.75;font-size:11px;";
|
|
401
530
|
tag.appendChild(doc.createTextNode(code));
|
|
402
531
|
row.appendChild(tag);
|
|
403
532
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_textpartlanguage=RTE_Plugin_TextPartLanguage,typeof RTE_DefaultConfig.textPartLanguages>"u"&&(RTE_DefaultConfig.textPartLanguages=[{code:"ar",label:"Arabic",dir:"rtl"},{code:"zh-Hans",label:"Chinese (Simplified)"},{code:"zh-Hant",label:"Chinese (Traditional)"},{code:"cs",label:"Czech"},{code:"nl",label:"Dutch"},{code:"en",label:"English"},{code:"fr",label:"French"},{code:"de",label:"German"},{code:"el",label:"Greek"},{code:"he",label:"Hebrew",dir:"rtl"},{code:"hi",label:"Hindi"},{code:"it",label:"Italian"},{code:"ja",label:"Japanese"},{code:"ko",label:"Korean"},{code:"fa",label:"Persian",dir:"rtl"},{code:"pl",label:"Polish"},{code:"pt",label:"Portuguese"},{code:"ru",label:"Russian"},{code:"es",label:"Spanish"},{code:"sv",label:"Swedish"},{code:"tr",label:"Turkish"},{code:"uk",label:"Ukrainian"},{code:"ur",label:"Urdu",dir:"rtl"},{code:"vi",label:"Vietnamese"}]),typeof RTE_DefaultConfig.textPartLanguageHighlight>"u"&&(RTE_DefaultConfig.textPartLanguageHighlight=!0);function RTE_Plugin_TextPartLanguage(){var u=this,g,a,T=null;u.PluginName="TextPartLanguage",u.InitConfig=function(e){g=e},u.InitEditor=function(e){a=e,a.attachEvent("exec_command_textpartlanguage",function(r){r.returnValue=!0;var t=r&&r.value;!t||t==="none"||t==="remove"?u.Remove():u.Apply(t)}),a.toolbarFactoryMap&&(a.toolbarFactoryMap.textpartlanguage=function(r){return a.createToolbarItemDropDownPanel(r,q)}),h();try{a.attachEvent("ready",h)}catch{}try{a.attachEvent("aftersethtml",h)}catch{}setTimeout(h,0),a.setTextLanguage=function(r){return u.Apply(r)},a.removeTextLanguage=function(){return u.Remove()},a.getTextLanguage=function(){return u.Current()},a.listTextLanguages=function(){return y().slice()},a.getMarkedLanguages=function(){return u.List()}};function w(){try{return a.getDocument()}catch{return null}}function b(){try{return a.getEditable()}catch{return null}}function y(){var e=g&&g.textPartLanguages;return e&&e.length?e:[]}function E(e){for(var r=y(),t=0;t<r.length;t++)if(r[t].code===e)return r[t];return{code:e,label:e}}function s(e){for(var r=b(),t=e&&e.nodeType===3?e.parentNode:e;t&&t!==r&&t.nodeType===1;){if(t.hasAttribute&&t.hasAttribute("lang"))return t;t=t.parentNode}return null}function m(){try{var e=a.getSelection();return!e||e.rangeCount===0?null:e.getRangeAt(0)}catch{return null}}u.Current=function(){var e=m();if(!e)return null;var r=s(e.startContainer);return r&&!e.collapsed&&s(e.endContainer)!==r?null:r?r.getAttribute("lang"):null},u.List=function(){var e=b(),r=[];if(!e)return r;for(var t={},n=e.querySelectorAll("[lang]"),i=0;i<n.length;i++){var o=n[i].getAttribute("lang");!o||t[o]||(t[o]=!0,r.push({code:o,label:E(o).label}))}return r},u.Apply=function(e){if(e=String(e??"").trim(),!e)return!1;var r=w(),t=m();if(!r||!t)return!1;if(u.Current()===e)return u.Remove();if(t.collapsed){var n=s(t.startContainer);return n?(N(n,e),x(),e):!1}var i=r.createElement("span");N(i,e);try{t.surroundContents(i)}catch{var o=t.extractContents();i.appendChild(o),t.insertNode(i)}return L(i),k(i),i.firstChild?(_(i),H(i),x(),e):(i.parentNode&&i.parentNode.removeChild(i),!1)},u.Remove=function(){var e=m();if(!e)return!1;var r=b();if(!r)return!1;var t=0,n=s(e.startContainer);if(n&&(C(n),t++),!e.collapsed)for(var i=r.querySelectorAll("[lang]"),o=i.length-1;o>=0;o--){var l=i[o];l!==n&&S(e,l)&&(C(l),t++)}return t&&x(),t>0};function S(e,r){try{var t=r.ownerDocument.createRange();return t.selectNodeContents(r),e.compareBoundaryPoints(Range.END_TO_START,t)<0&&e.compareBoundaryPoints(Range.START_TO_END,t)>0}catch{return!1}}function N(e,r){e.setAttribute("lang",r);var t=E(r).dir;t==="rtl"||t==="ltr"?e.setAttribute("dir",t):e.removeAttribute("dir")}function L(e){for(var r=e.querySelectorAll("[lang]"),t=r.length-1;t>=0;t--)C(r[t])}function C(e){if(!(!e||!e.parentNode))if(e.nodeName==="SPAN"&&e.attributes.length<=2&&!e.getAttribute("class")&&!e.getAttribute("style")&&!e.getAttribute("id")){for(var r=e.parentNode;e.firstChild;)r.insertBefore(e.firstChild,e);r.removeChild(e);try{r.normalize()}catch{}}else e.removeAttribute("lang"),e.removeAttribute("dir")}function k(e){for(var r=e.childNodes.length-1;r>=0;r--){var t=e.childNodes[r];t.nodeType===3&&t.nodeValue===""&&e.removeChild(t)}}function _(e){var r=e.getAttribute("lang"),t=e.previousSibling;if(t&&t.nodeType===1&&t.nodeName==="SPAN"&&t.getAttribute("lang")===r){for(;e.firstChild;)t.appendChild(e.firstChild);e.parentNode.removeChild(e),e=t}var n=e.nextSibling;if(n&&n.nodeType===1&&n.nodeName==="SPAN"&&n.getAttribute("lang")===r){for(;n.firstChild;)e.appendChild(n.firstChild);n.parentNode.removeChild(n)}try{e.normalize()}catch{}return e}function H(e){try{var r=a.getSelection(),t=e.ownerDocument.createRange();t.selectNodeContents(e),r.removeAllRanges(),r.addRange(t)}catch{}}function x(){try{typeof a.updateDesign=="function"&&a.updateDesign()}catch{}try{typeof a.fireChange=="function"&&a.fireChange()}catch{}}function q(e){var r=u.Current();e.style.width="220px",e.style.maxHeight="340px",e.style.overflowY="auto",e.style.padding="4px 0";var t=y();D(e,a.getLangText&&a.getLangText("removelanguage")||"Remove language",null,!r,function(){u.Remove()});var n=e.ownerDocument.createElement("div");n.style.cssText="height:1px;margin:4px 6px;background:var(--rte-border-color,#e0e0e0);",e.appendChild(n);for(var i=0;i<t.length;i++)(function(o){D(e,o.label,o.code,r===o.code,function(){u.Apply(o.code)})})(t[i]);setTimeout(function(){var o=e.querySelectorAll("rte-toolbar-dropdown-item");if(o.length){for(var l=o[0],d=0;d<o.length;d++)if(o[d].getAttribute("aria-checked")==="true"){l=o[d];break}try{l.focus()}catch{}}},0)}function D(e,r,t,n,i){var o=e.ownerDocument,l=o.createElement("rte-toolbar-dropdown-item");e.appendChild(l),l.setAttribute("role","menuitemradio"),l.setAttribute("aria-checked",n?"true":"false"),l.setAttribute("tabindex","0"),l.style.cssText="padding:5px 12px;cursor:pointer;white-space:nowrap;display:flex;justify-content:space-between;gap:12px;color:var(--rte-text-color,inherit);"+(n?"font-weight:600;":"");var d=o.createElement("span");if(d.appendChild(o.createTextNode(r)),l.appendChild(d),t){var p=o.createElement("span");p.style.cssText="opacity:.55;font-size:11px;",p.appendChild(o.createTextNode(t)),l.appendChild(p)}l.onmouseover=function(){l.style.backgroundColor="var(--rte-hover-bg,#e6e6e6)"},l.onmouseout=function(){l.style.backgroundColor=""},l.onclick=function(){try{a.closeCurrentPopup()}catch{}try{a.focus()}catch{}i()},l.onkeydown=function(c){var f=c.key;if(f==="Enter"||f===" "||f==="Spacebar"){c.preventDefault(),c.stopPropagation(),l.onclick();return}if(f==="ArrowDown"||f==="ArrowUp"){c.preventDefault(),c.stopPropagation();var B=e.querySelectorAll("rte-toolbar-dropdown-item"),v=Array.prototype.slice.call(B),j=v.indexOf(l),R=v[(j+(f==="ArrowDown"?1:-1)+v.length)%v.length];R&&R.focus();return}if(f==="Home"||f==="End"){c.preventDefault(),c.stopPropagation();var A=e.querySelectorAll("rte-toolbar-dropdown-item"),P=f==="Home"?A[0]:A[A.length-1];P&&P.focus();return}if(f==="Escape"){c.preventDefault(),c.stopPropagation();try{a.closeCurrentPopup()}catch{}}}}function h(){var e=w();!e||e===T||(T=e,I(e))}function z(){return g&&g.textPartLanguageHighlight===!1?"":"[lang]:not(html):not(body){border-bottom:1px dotted rgba(120,120,120,.75);}[lang]:not(html):not(body):hover{background:rgba(120,120,120,.10);}"}function I(e){if(e){var r=z(),t=e.getElementById("rte-textpartlanguage-styles");if(t){if(t.getAttribute("data-css")===r)return;t.parentNode&&t.parentNode.removeChild(t)}if(r){var n=e.createElement("style");n.id="rte-textpartlanguage-styles",n.setAttribute("data-css",r),n.appendChild(e.createTextNode(r)),(e.head||e.getElementsByTagName("head")[0]||e.documentElement).appendChild(n),a&&typeof a.ensureStyleSheetLive=="function"&&a.ensureStyleSheetLive(e,n,"rte-textpartlanguage-styles",n.textContent)}}}}
|
|
1
|
+
window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_textpartlanguage=RTE_Plugin_TextPartLanguage;var RTE_TextPartLanguageDefaults=[{code:"ar",label:"Arabic",dir:"rtl"},{code:"zh-Hans",label:"Chinese (Simplified)"},{code:"zh-Hant",label:"Chinese (Traditional)"},{code:"cs",label:"Czech"},{code:"nl",label:"Dutch"},{code:"en",label:"English"},{code:"fr",label:"French"},{code:"de",label:"German"},{code:"el",label:"Greek"},{code:"he",label:"Hebrew",dir:"rtl"},{code:"hi",label:"Hindi"},{code:"it",label:"Italian"},{code:"ja",label:"Japanese"},{code:"ko",label:"Korean"},{code:"fa",label:"Persian",dir:"rtl"},{code:"pl",label:"Polish"},{code:"pt",label:"Portuguese"},{code:"ru",label:"Russian"},{code:"es",label:"Spanish"},{code:"sv",label:"Swedish"},{code:"tr",label:"Turkish"},{code:"uk",label:"Ukrainian"},{code:"ur",label:"Urdu",dir:"rtl"},{code:"vi",label:"Vietnamese"}];typeof RTE_DefaultConfig.textPartLanguages>"u"&&(RTE_DefaultConfig.textPartLanguages=RTE_TextPartLanguageDefaults.slice()),typeof RTE_DefaultConfig.textPartLanguageHighlight>"u"&&(RTE_DefaultConfig.textPartLanguageHighlight=!0);function RTE_Plugin_TextPartLanguage(){var c=this,h,o,w=null;c.PluginName="TextPartLanguage",c.InitConfig=function(e){h=e};function k(e){try{o.commitBookmark&&o.commitBookmark()}catch{}var r=e();try{o.recordUndo?o.recordUndo():o.notifySelectionChange&&o.notifySelectionChange(),o.commitBookmark&&o.commitBookmark()}catch{}return r}c.InitEditor=function(e){o=e,o.attachEvent("exec_command_textpartlanguage",function(r){r.returnValue=!0;var t=r&&r.value;!t||t==="none"||t==="remove"?c.Remove():c.Apply(t)}),o.toolbarFactoryMap&&(o.toolbarFactoryMap.textpartlanguage=function(r){return o.createToolbarItemDropDownPanel(r,j)}),b();try{o.attachEvent("ready",b)}catch{}try{o.attachEvent("aftersethtml",b)}catch{}setTimeout(b,0),o.setTextLanguage=function(r){return k(function(){return c.Apply(r)})},o.removeTextLanguage=function(){return k(function(){return c.Remove()})},o.getTextLanguage=function(){return c.Current()},o.listTextLanguages=function(){return T().slice()},o.getMarkedLanguages=function(){return c.List()}};function L(){try{return o.getDocument()}catch{return null}}function C(){try{return o.getEditable()}catch{return null}}function T(){var e=h&&h.textPartLanguages;return e&&e.length?e:[]}function P(e){for(var r=T(),t=0;t<r.length;t++)if(r[t].code===e)return r[t];return{code:e,label:e}}var E={};function _(e,r){try{if(typeof Intl>"u"||!Intl.DisplayNames)return"";E[e]||(E[e]=new Intl.DisplayNames([e],{type:"language"}));var t=E[e].of(r);return t&&t!==r?t:""}catch{return""}}function M(e){var r=e&&e.code;if(!r)return"";for(var t=null,a=typeof RTE_TextPartLanguageDefaults<"u"?RTE_TextPartLanguageDefaults:[],l=0;l<a.length;l++)a[l].code===r&&(t=a[l].label);if(e.label&&e.label!==t)return e.label;var i=h&&h.text_uilocale;if(i&&!/^en(-|$)/i.test(i)){var n=_(i,r);if(n)return n}return e.label||_("en",r)||r}function p(e){for(var r=C(),t=e&&e.nodeType===3?e.parentNode:e;t&&t!==r&&t.nodeType===1;){if(t.hasAttribute&&t.hasAttribute("lang"))return t;t=t.parentNode}return null}function N(){try{var e=o.getSelection();return!e||e.rangeCount===0?null:e.getRangeAt(0)}catch{return null}}c.Current=function(){var e=N();if(!e)return null;var r=p(e.startContainer);return r&&!e.collapsed&&p(e.endContainer)!==r?null:r?r.getAttribute("lang"):null},c.List=function(){var e=C(),r=[];if(!e)return r;for(var t={},a=e.querySelectorAll("[lang]"),l=0;l<a.length;l++){var i=a[l].getAttribute("lang");!i||t[i]||(t[i]=!0,r.push({code:i,label:P(i).label}))}return r},c.Apply=function(e){if(e=String(e??"").trim(),!e)return!1;var r=L(),t=N();if(!r||!t||o.__trackHook&&o.__trackHook("refuse",{message:"Suggesting is on, so the language was not changed. Turn suggesting off to change it."})===!0)return!1;if(c.Current()===e)return c.Remove();if(t.collapsed){var a=p(t.startContainer);return a?(x(a,e),y(),e):!1}var l=z(r,t);if(l){for(var i=[],n=l.length-1;n>=0;n--){var s=r.createRange();s.setStart(l[n].sn,l[n].so),s.setEnd(l[n].en,l[n].eo),G(s,l[n].block);var f=r.createElement("span");if(x(f,e),f.appendChild(s.extractContents()),s.insertNode(f),I(f),O(f),!f.firstChild){f.parentNode.removeChild(f);continue}i.unshift(H(f))}if(!i.length)return!1;try{var d=r.createRange();d.setStart(i[0],0),d.setEnd(i[i.length-1],i[i.length-1].childNodes.length);var g=o.getSelection();g.removeAllRanges(),g.addRange(d)}catch{}return y(),e}var u=r.createElement("span");x(u,e);try{t.surroundContents(u)}catch{var v=t.extractContents();u.appendChild(v),t.insertNode(u)}return I(u),O(u),u.firstChild?(H(u),V(u),y(),e):(u.parentNode&&u.parentNode.removeChild(u),!1)},c.Remove=function(){if(o.__trackHook&&o.__trackHook("remove",{})===!0)return!1;var e=N();if(!e)return!1;var r=C();if(!r)return!1;var t=0,a=p(e.startContainer);if(a&&(R(a),t++),!e.collapsed)for(var l=r.querySelectorAll("[lang]"),i=l.length-1;i>=0;i--){var n=l[i];n!==a&&q(e,n)&&(R(n),t++)}return t&&y(),t>0};function q(e,r){try{var t=r.ownerDocument.createRange();return t.selectNodeContents(r),e.compareBoundaryPoints(Range.END_TO_START,t)<0&&e.compareBoundaryPoints(Range.START_TO_END,t)>0}catch{return!1}}var m=/^(P|DIV|LI|UL|OL|DL|DT|DD|TABLE|TBODY|THEAD|TFOOT|TR|TD|TH|CAPTION|H[1-6]|PRE|BLOCKQUOTE|ADDRESS|FIGURE|FIGCAPTION|SECTION|ARTICLE|ASIDE|HEADER|FOOTER|NAV|MAIN|DETAILS|SUMMARY|FORM|FIELDSET|LEGEND|HR|BODY)$/;function A(e){for(var r=e;r;r=r.parentNode)if(r.nodeType===1&&m.test(r.nodeName))return r;return null}function z(e,r){if(r.collapsed||A(r.startContainer)===A(r.endContainer))return null;var t=r.commonAncestorContainer;t.nodeType!==1&&(t=t.parentNode);for(var a=[],l=null,i=e.createTreeWalker(t,NodeFilter.SHOW_ELEMENT|NodeFilter.SHOW_TEXT,{acceptNode:function(v){return v.nodeType===1&&v.getAttribute("contenteditable")==="false"?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_ACCEPT}}),n=i.nextNode();n;n=i.nextNode()){if(n.nodeType===1){m.test(n.nodeName)&&(l=null);continue}if(r.intersectsNode(n)){var s=n===r.startContainer?r.startOffset:0,f=n===r.endContainer?r.endOffset:n.data.length;if(!(f<=s)){var d=A(n);(!l||l.block!==d)&&(l={block:d,sn:n,so:s,text:""},a.push(l)),l.en=n,l.eo=f,l.text+=n.data.substring(s,f)}}}for(var g=[],u=0;u<a.length;u++)/\S/.test(a[u].text)&&g.push(a[u]);return g}function G(e,r){var t=e.startContainer;if(t.nodeType===3&&e.startOffset===0){for(;t.parentNode&&t.parentNode!==r&&!t.previousSibling&&!m.test(t.parentNode.nodeName);)t=t.parentNode;e.setStartBefore(t)}if(t=e.endContainer,t.nodeType===3&&e.endOffset===t.data.length){for(;t.parentNode&&t.parentNode!==r&&!t.nextSibling&&!m.test(t.parentNode.nodeName);)t=t.parentNode;e.setEndAfter(t)}}function x(e,r){e.setAttribute("lang",r);var t=P(r).dir;t==="rtl"||t==="ltr"?e.setAttribute("dir",t):e.removeAttribute("dir")}function I(e){for(var r=e.querySelectorAll("[lang]"),t=r.length-1;t>=0;t--)R(r[t])}function R(e){if(!(!e||!e.parentNode))if(e.nodeName==="SPAN"&&e.attributes.length<=2&&!e.getAttribute("class")&&!e.getAttribute("style")&&!e.getAttribute("id")){for(var r=e.parentNode;e.firstChild;)r.insertBefore(e.firstChild,e);r.removeChild(e);try{r.normalize()}catch{}}else e.removeAttribute("lang"),e.removeAttribute("dir")}function O(e){for(var r=e.childNodes.length-1;r>=0;r--){var t=e.childNodes[r];t.nodeType===3&&t.nodeValue===""&&e.removeChild(t)}}function H(e){var r=e.getAttribute("lang"),t=e.previousSibling;if(t&&t.nodeType===1&&t.nodeName==="SPAN"&&t.getAttribute("lang")===r){for(;e.firstChild;)t.appendChild(e.firstChild);e.parentNode.removeChild(e),e=t}var a=e.nextSibling;if(a&&a.nodeType===1&&a.nodeName==="SPAN"&&a.getAttribute("lang")===r){for(;a.firstChild;)e.appendChild(a.firstChild);a.parentNode.removeChild(a)}try{e.normalize()}catch{}return e}function V(e){try{var r=o.getSelection(),t=e.ownerDocument.createRange();t.selectNodeContents(e),r.removeAllRanges(),r.addRange(t)}catch{}}function y(){try{typeof o.updateDesign=="function"&&o.updateDesign()}catch{}try{typeof o.fireChange=="function"&&o.fireChange()}catch{}}function j(e){var r=c.Current();e.style.width="220px",e.style.maxHeight="340px",e.style.overflowY="auto",e.style.padding="4px 0";var t=T();B(e,o.getLangText&&o.getLangText("removelanguage")||"Remove language",null,!r,function(){c.Remove()});var a=e.ownerDocument.createElement("div");a.style.cssText="height:1px;margin:4px 6px;background:var(--rte-border-color,#e0e0e0);",e.appendChild(a);for(var l=0;l<t.length;l++)(function(i){B(e,M(i),i.code,r===i.code,function(){c.Apply(i.code)})})(t[l]);setTimeout(function(){var i=e.querySelectorAll("rte-toolbar-dropdown-item");if(i.length){for(var n=i[0],s=0;s<i.length;s++)if(i[s].getAttribute("aria-checked")==="true"){n=i[s];break}try{n.focus()}catch{}}},0)}function B(e,r,t,a,l){var i=e.ownerDocument,n=i.createElement("rte-toolbar-dropdown-item");e.appendChild(n),n.setAttribute("role","menuitemradio"),n.setAttribute("aria-checked",a?"true":"false"),n.setAttribute("tabindex","0"),n.style.cssText="padding:5px 12px;cursor:pointer;white-space:nowrap;display:flex;justify-content:space-between;gap:12px;color:var(--rte-text-color,inherit);"+(a?"font-weight:600;":"");var s=i.createElement("span");if(s.appendChild(i.createTextNode(r)),n.appendChild(s),t){var f=i.createElement("span");f.style.cssText="opacity:.75;font-size:11px;",f.appendChild(i.createTextNode(t)),n.appendChild(f)}n.onmouseover=function(){n.style.backgroundColor="var(--rte-hover-bg,#e6e6e6)"},n.onmouseout=function(){n.style.backgroundColor=""},n.onclick=function(){try{o.closeCurrentPopup()}catch{}try{o.focus()}catch{}l()},n.onkeydown=function(d){var g=d.key;if(g==="Enter"||g===" "||g==="Spacebar"){d.preventDefault(),d.stopPropagation(),n.onclick();return}if(g==="ArrowDown"||g==="ArrowUp"){d.preventDefault(),d.stopPropagation();var u=e.querySelectorAll("rte-toolbar-dropdown-item"),v=Array.prototype.slice.call(u),D=v.indexOf(n),F=v[(D+(g==="ArrowDown"?1:-1)+v.length)%v.length];F&&F.focus();return}if(g==="Home"||g==="End"){d.preventDefault(),d.stopPropagation();var S=e.querySelectorAll("rte-toolbar-dropdown-item"),U=g==="Home"?S[0]:S[S.length-1];U&&U.focus();return}if(g==="Escape"){d.preventDefault(),d.stopPropagation();try{o.closeCurrentPopup()}catch{}}}}function b(){var e=L();!e||e===w||(w=e,K(e))}function Y(){return h&&h.textPartLanguageHighlight===!1?"":"[lang]:not(html):not(body){border-bottom:1px dotted rgba(120,120,120,.75);}[lang]:not(html):not(body):hover{background:rgba(120,120,120,.10);}"}function K(e){if(e){var r=Y(),t=e.getElementById("rte-textpartlanguage-styles");if(t){if(t.getAttribute("data-css")===r)return;t.parentNode&&t.parentNode.removeChild(t)}if(r){var a=e.createElement("style");a.id="rte-textpartlanguage-styles",a.setAttribute("data-css",r),a.appendChild(e.createTextNode(r)),(e.head||e.getElementsByTagName("head")[0]||e.documentElement).appendChild(a),o&&typeof o.ensureStyleSheetLive=="function"&&o.ensureStyleSheetLive(e,a,"rte-textpartlanguage-styles",a.textContent)}}}}
|
|
@@ -37,13 +37,18 @@ function RTE_Plugin_TodoList() {
|
|
|
37
37
|
e.preventDefault();
|
|
38
38
|
fireChange();
|
|
39
39
|
});
|
|
40
|
-
//
|
|
40
|
+
// 2026-10-02 Enter in a task item is handled here. The browser's own Enter made a bare <li>
|
|
41
|
+
// and the keyup below then put the checkbox in FRONT of the caret: in Chromium and Firefox
|
|
42
|
+
// every item after the first saved as <li>two<span class="rte-task-checkbox"></span></li>,
|
|
43
|
+
// Firefox sometimes dropped the typed text altogether, and WebKit put the caret back at
|
|
44
|
+
// the end of the previous item. tools/keyboard/todolist-enter.mjs
|
|
45
|
+
editable.addEventListener("keydown", function (e) {
|
|
46
|
+
if (e.key !== "Enter" || e.shiftKey || e.ctrlKey || e.metaKey || e.altKey || e.isComposing || e.keyCode === 229) return;
|
|
47
|
+
try { if (splitTaskItem()) e.preventDefault(); } catch (er) { /* the browser's own Enter */ }
|
|
48
|
+
}, true);
|
|
49
|
+
// Keep items made any other way well-formed: a checkbox + a done state.
|
|
41
50
|
editable.addEventListener("keyup", function (e) {
|
|
42
|
-
if (e.key === "Enter" && !e.shiftKey)
|
|
43
|
-
obj.Normalize();
|
|
44
|
-
var li = currentTaskItem();
|
|
45
|
-
if (li) li.setAttribute("data-checked", "false"); // a freshly split/created item starts open
|
|
46
|
-
}
|
|
51
|
+
if (e.key === "Enter" && !e.shiftKey) obj.Normalize();
|
|
47
52
|
});
|
|
48
53
|
// Inject CSS once the iframe head exists.
|
|
49
54
|
setTimeout(function () { injectStyles(editdoc); }, 0);
|
|
@@ -99,6 +104,85 @@ function RTE_Plugin_TodoList() {
|
|
|
99
104
|
return cb;
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
function isEmptyItem(li) {
|
|
108
|
+
return !/[^\s\u200b]/.test(li.textContent || "") && !li.querySelector("img,iframe,video,audio,svg,object,embed,table");
|
|
109
|
+
}
|
|
110
|
+
// The caret's place in an item that holds only its checkbox: a zero-width text node after
|
|
111
|
+
// it (as InsertTaskList does) - next to a contenteditable="false" span alone, WebKit moves
|
|
112
|
+
// the caret back into the previous item.
|
|
113
|
+
function caretAfterCheckbox(li) {
|
|
114
|
+
var zw = editdoc.createTextNode("\u200b");
|
|
115
|
+
var cb = li.firstChild && li.firstChild.nodeType === 1 && li.firstChild.classList && li.firstChild.classList.contains("rte-task-checkbox") ? li.firstChild : null;
|
|
116
|
+
li.insertBefore(zw, cb ? cb.nextSibling : li.firstChild);
|
|
117
|
+
return zw;
|
|
118
|
+
}
|
|
119
|
+
function placeCaret(node, offset) {
|
|
120
|
+
var r = editdoc.createRange();
|
|
121
|
+
r.setStart(node, offset);
|
|
122
|
+
r.collapse(true);
|
|
123
|
+
var s = editor.getSelection();
|
|
124
|
+
s.removeAllRanges();
|
|
125
|
+
s.addRange(r);
|
|
126
|
+
}
|
|
127
|
+
function newItem() {
|
|
128
|
+
var li = editdoc.createElement("li");
|
|
129
|
+
li.className = "rte-task-item";
|
|
130
|
+
li.setAttribute("data-checked", "false"); // a new item starts open
|
|
131
|
+
li.appendChild(makeCheckbox());
|
|
132
|
+
return li;
|
|
133
|
+
}
|
|
134
|
+
// Enter in a task item: an empty item leaves the list (a paragraph takes its place, the list
|
|
135
|
+
// splits around it); at the start of an item a new open item goes above; otherwise the text
|
|
136
|
+
// after the caret moves into a new open item below, checkbox first, caret before that text.
|
|
137
|
+
function splitTaskItem() {
|
|
138
|
+
var s = editor.getSelection();
|
|
139
|
+
if (!s || s.rangeCount === 0) return false;
|
|
140
|
+
var r = s.getRangeAt(0);
|
|
141
|
+
var li = closestClass(r.startContainer, "rte-task-item", editable);
|
|
142
|
+
if (!li || li !== closestClass(r.endContainer, "rte-task-item", editable) || li.parentNode.nodeName !== "UL") return false;
|
|
143
|
+
if (typeof editor.commitBookmark === "function") { try { editor.commitBookmark(); } catch (er) { /* ignore */ } }
|
|
144
|
+
if (!r.collapsed) r.deleteContents();
|
|
145
|
+
var ul = li.parentNode;
|
|
146
|
+
if (isEmptyItem(li)) {
|
|
147
|
+
var tag = String((config && config.enterKeyTag) || "p").toLowerCase();
|
|
148
|
+
if (tag === "br") tag = "div";
|
|
149
|
+
var p = editdoc.createElement(tag);
|
|
150
|
+
p.appendChild(editdoc.createElement("br"));
|
|
151
|
+
if (li.nextSibling) {
|
|
152
|
+
var rest = ul.cloneNode(false);
|
|
153
|
+
while (li.nextSibling) rest.appendChild(li.nextSibling);
|
|
154
|
+
ul.parentNode.insertBefore(rest, ul.nextSibling);
|
|
155
|
+
}
|
|
156
|
+
ul.parentNode.insertBefore(p, ul.nextSibling);
|
|
157
|
+
ul.removeChild(li);
|
|
158
|
+
if (!ul.querySelector("li")) ul.parentNode.removeChild(ul);
|
|
159
|
+
placeCaret(p, 0);
|
|
160
|
+
fireChange();
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
var head = editdoc.createRange();
|
|
164
|
+
head.setStart(li, 0);
|
|
165
|
+
head.setEnd(r.startContainer, r.startOffset);
|
|
166
|
+
if (!/[^\s\u200b]/.test(head.toString()) && !head.cloneContents().querySelector("img,iframe,video,audio,svg,object,embed")) {
|
|
167
|
+
var above = newItem();
|
|
168
|
+
caretAfterCheckbox(above);
|
|
169
|
+
ul.insertBefore(above, li);
|
|
170
|
+
fireChange();
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
var tail = editdoc.createRange();
|
|
174
|
+
tail.setStart(r.startContainer, r.startOffset);
|
|
175
|
+
tail.setEnd(li, li.childNodes.length);
|
|
176
|
+
var moved = tail.extractContents();
|
|
177
|
+
var below = newItem();
|
|
178
|
+
below.appendChild(moved);
|
|
179
|
+
var zw = caretAfterCheckbox(below);
|
|
180
|
+
ul.insertBefore(below, li.nextSibling);
|
|
181
|
+
placeCaret(zw, 1);
|
|
182
|
+
fireChange();
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
|
|
102
186
|
// Ensure every <li> in a task list has a checkbox span + a data-checked value.
|
|
103
187
|
obj.Normalize = function () {
|
|
104
188
|
if (!editable) return;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_todolist=RTE_Plugin_TodoList;function RTE_Plugin_TodoList(){var c=this,h,
|
|
1
|
+
window.RTE_DefaultConfig||(window.RTE_DefaultConfig={}),RTE_DefaultConfig.plugin_todolist=RTE_Plugin_TodoList;function RTE_Plugin_TodoList(){var c=this,h,i,l,s;c.PluginName="TodoList",c.InitConfig=function(e){h=e},c.InitEditor=function(e){i=e,l=i.getEditable(),s=i.getDocument(),i.attachEvent("exec_command_inserttodolist",function(r){r.returnValue=!0,c.InsertTaskList()}),l&&(l.addEventListener("click",function(r){var t=u(r.target,"rte-task-checkbox",l);if(t){var a=t.parentNode;if(!(!a||!a.classList||!a.classList.contains("rte-task-item"))){var n=a.getAttribute("data-checked")==="true";a.setAttribute("data-checked",n?"false":"true"),r.preventDefault(),d()}}}),l.addEventListener("keydown",function(r){if(!(r.key!=="Enter"||r.shiftKey||r.ctrlKey||r.metaKey||r.altKey||r.isComposing||r.keyCode===229))try{E()&&r.preventDefault()}catch{}},!0),l.addEventListener("keyup",function(r){r.key==="Enter"&&!r.shiftKey&&c.Normalize()}),setTimeout(function(){b(s)},0))};function u(e,r,t){for(;e&&e!==t;){if(e.nodeType===1&&e.classList&&e.classList.contains(r))return e;e=e.parentNode}return null}function d(){if(typeof i.fireChange=="function")try{i.fireChange()}catch{}}function N(){try{var e=i.getSelection();if(!e||e.rangeCount===0)return null;var r=e.getRangeAt(0).startContainer;return u(r,"rte-task-item",l)}catch{return null}}function b(e){if(!(!e||e.getElementById("rte-todolist-styles"))){var r="ul.rte-task-list{list-style:none;margin:.4em 0;padding-left:0;}ul.rte-task-list li.rte-task-item{position:relative;padding-left:28px;margin:3px 0;min-height:20px;}li.rte-task-item>.rte-task-checkbox{position:absolute;left:0;top:1px;width:18px;height:18px;border:2px solid #94a3b8;border-radius:5px;box-sizing:border-box;cursor:pointer;user-select:none;transition:background .12s,border-color .12s;}li.rte-task-item>.rte-task-checkbox:hover{border-color:#1d67ba;}li.rte-task-item[data-checked='true']>.rte-task-checkbox{background:#1d67ba;border-color:#1d67ba;}li.rte-task-item[data-checked='true']>.rte-task-checkbox::after{content:'';position:absolute;left:4px;top:0px;width:6px;height:11px;border:solid #fff;border-width:0 2px 2px 0;transform:rotate(45deg);}li.rte-task-item[data-checked='true']{color:#94a3b8;text-decoration:line-through;}",t=e.createElement("style");t.id="rte-todolist-styles",t.appendChild(e.createTextNode(r)),(e.head||e.getElementsByTagName("head")[0]||e.documentElement).appendChild(t),i&&typeof i.ensureStyleSheetLive=="function"&&i.ensureStyleSheetLive(e,t,"rte-todolist-styles",t.textContent)}}function p(){var e=s.createElement("span");return e.className="rte-task-checkbox",e.setAttribute("contenteditable","false"),e}function S(e){return!/[^\s\u200b]/.test(e.textContent||"")&&!e.querySelector("img,iframe,video,audio,svg,object,embed,table")}function v(e){var r=s.createTextNode("\u200B"),t=e.firstChild&&e.firstChild.nodeType===1&&e.firstChild.classList&&e.firstChild.classList.contains("rte-task-checkbox")?e.firstChild:null;return e.insertBefore(r,t?t.nextSibling:e.firstChild),r}function C(e,r){var t=s.createRange();t.setStart(e,r),t.collapse(!0);var a=i.getSelection();a.removeAllRanges(),a.addRange(t)}function y(){var e=s.createElement("li");return e.className="rte-task-item",e.setAttribute("data-checked","false"),e.appendChild(p()),e}function E(){var e=i.getSelection();if(!e||e.rangeCount===0)return!1;var r=e.getRangeAt(0),t=u(r.startContainer,"rte-task-item",l);if(!t||t!==u(r.endContainer,"rte-task-item",l)||t.parentNode.nodeName!=="UL")return!1;if(typeof i.commitBookmark=="function")try{i.commitBookmark()}catch{}r.collapsed||r.deleteContents();var a=t.parentNode;if(S(t)){var n=String(h&&h.enterKeyTag||"p").toLowerCase();n==="br"&&(n="div");var o=s.createElement(n);if(o.appendChild(s.createElement("br")),t.nextSibling){for(var g=a.cloneNode(!1);t.nextSibling;)g.appendChild(t.nextSibling);a.parentNode.insertBefore(g,a.nextSibling)}return a.parentNode.insertBefore(o,a.nextSibling),a.removeChild(t),a.querySelector("li")||a.parentNode.removeChild(a),C(o,0),d(),!0}var f=s.createRange();if(f.setStart(t,0),f.setEnd(r.startContainer,r.startOffset),!/[^\s\u200b]/.test(f.toString())&&!f.cloneContents().querySelector("img,iframe,video,audio,svg,object,embed")){var x=y();return v(x),a.insertBefore(x,t),d(),!0}var m=s.createRange();m.setStart(r.startContainer,r.startOffset),m.setEnd(t,t.childNodes.length);var L=m.extractContents(),k=y();k.appendChild(L);var T=v(k);return a.insertBefore(k,t.nextSibling),C(T,1),d(),!0}c.Normalize=function(){if(l)for(var e=l.querySelectorAll("ul.rte-task-list"),r=0;r<e.length;r++)for(var t=e[r].children,a=0;a<t.length;a++){var n=t[a];if(n.nodeName==="LI"){n.classList.add("rte-task-item"),n.getAttribute("data-checked")!=="true"&&n.getAttribute("data-checked")!=="false"&&n.setAttribute("data-checked","false");var o=n.firstChild;o&&o.nodeType===1&&o.classList&&o.classList.contains("rte-task-checkbox")||n.insertBefore(p(),n.firstChild)}}},c.InsertTaskList=function(){b(s);var e="rte-todo-pending",r='<ul class="rte-task-list"><li class="rte-task-item" data-checked="false" id="'+e+'"><span class="rte-task-checkbox" contenteditable="false"></span>​</li></ul>';i.insertHTML(r);try{var t=s.getElementById(e);if(t){t.removeAttribute("id");var a=s.createRange(),n=t.lastChild;n&&n.nodeType===3?a.setStart(n,n.length):a.setStart(t,t.childNodes.length),a.collapse(!0);var o=i.getSelection();o&&(o.removeAllRanges(),o.addRange(a))}}catch{}i.focus(),d()}}
|