@richscripts/richtexteditor 2.14.1 → 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 +7001 -1078
- 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 +7001 -1078
- 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 +47 -8
- 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 +6 -6
- 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 +62 -3
- 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 +130 -50
- package/richtexteditor/rte-headless.js +19 -4
- package/richtexteditor/rte-webcomponent.js +89 -16
- package/richtexteditor/rte.js +132 -52
- 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
|
@@ -140,7 +140,9 @@ function RTE_Plugin_DocumentImport() {
|
|
|
140
140
|
if (mode === "insert") {
|
|
141
141
|
if (typeof editor.insertHTML === "function") { editor.insertHTML(html); return true; }
|
|
142
142
|
}
|
|
143
|
-
|
|
143
|
+
// 2026-10-03 undoable: Ctrl+Z after an import gives back exactly the document it replaced
|
|
144
|
+
// (tools/import/import-undo.mjs)
|
|
145
|
+
if (typeof editor.setHTMLCode === "function") { editor.setHTMLCode(html, { undoable: true }); return true; }
|
|
144
146
|
return false;
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -168,14 +170,14 @@ function RTE_Plugin_DocumentImport() {
|
|
|
168
170
|
// Library-free .docx: unzip word/document.xml (native
|
|
169
171
|
// DecompressionStream) + transform WordprocessingML -> HTML.
|
|
170
172
|
if (!docxSupported()) {
|
|
171
|
-
notify("This browser can't unpack .docx (no DecompressionStream). Save as .doc/.html/.md, or wire config.documentImportResolver.");
|
|
173
|
+
notify((config && config.text_imp_nounpack) || "This browser can't unpack .docx (no DecompressionStream). Save as .doc/.html/.md, or wire config.documentImportResolver.");
|
|
172
174
|
resolve(false);
|
|
173
175
|
return;
|
|
174
176
|
}
|
|
175
177
|
readDocx(file).then(function (html) {
|
|
176
178
|
if (html != null) resolve(applyHtml(html, mode));
|
|
177
|
-
else { notify("Could not read this .docx file."); resolve(false); }
|
|
178
|
-
}, function () { notify("Could not read this .docx file."); resolve(false); });
|
|
179
|
+
else { notify((config && config.text_imp_badfile) || "Could not read this .docx file."); resolve(false); }
|
|
180
|
+
}, function () { notify((config && config.text_imp_badfile) || "Could not read this .docx file."); resolve(false); });
|
|
179
181
|
return;
|
|
180
182
|
}
|
|
181
183
|
if (typeof FileReader === "undefined") { resolve(false); return; }
|
|
@@ -345,6 +347,13 @@ function RTE_Plugin_DocumentImport() {
|
|
|
345
347
|
ctx.rels = parseRels(utf8(b));
|
|
346
348
|
}));
|
|
347
349
|
}
|
|
350
|
+
// 2026-10-15 the document's default language, so it is not repeated as lang= on every run. tools/docx/run-props.mjs
|
|
351
|
+
if (byName["word/styles.xml"]) {
|
|
352
|
+
jobs.push(entryBytes(byName["word/styles.xml"]).then(function (b) {
|
|
353
|
+
var mm = /<w:docDefaults>[\s\S]*?<w:lang[^>]*w:val="([^"]+)"/.exec(utf8(b));
|
|
354
|
+
if (mm) ctx.defaultLang = mm[1].toLowerCase();
|
|
355
|
+
}));
|
|
356
|
+
}
|
|
348
357
|
if (byName["word/numbering.xml"]) {
|
|
349
358
|
jobs.push(entryBytes(byName["word/numbering.xml"]).then(function (b) {
|
|
350
359
|
ctx.numbering = parseNumbering(utf8(b));
|
|
@@ -517,6 +526,9 @@ function RTE_Plugin_DocumentImport() {
|
|
|
517
526
|
var ilvl = lvls[j].getAttributeNS(W, "ilvl") || lvls[j].getAttribute("w:ilvl") || "0";
|
|
518
527
|
var fmt = val(lvls[j].getElementsByTagNameNS(W, "numFmt")[0]);
|
|
519
528
|
levels[ilvl] = fmt || "decimal";
|
|
529
|
+
// 2026-10-04 the level's start number ("s" + ilvl), read by the list builder
|
|
530
|
+
var stEl = lvls[j].getElementsByTagNameNS(W, "start")[0];
|
|
531
|
+
if (stEl) levels["s" + ilvl] = parseInt(val(stEl), 10) || 1;
|
|
520
532
|
}
|
|
521
533
|
if (aid != null) abstracts[aid] = levels;
|
|
522
534
|
}
|
|
@@ -526,7 +538,18 @@ function RTE_Plugin_DocumentImport() {
|
|
|
526
538
|
var nid = nums[k].getAttributeNS(W, "numId") || nums[k].getAttribute("w:numId");
|
|
527
539
|
var ref = nums[k].getElementsByTagNameNS(W, "abstractNumId")[0];
|
|
528
540
|
var aref = val(ref);
|
|
529
|
-
if (nid != null && abstracts[aref])
|
|
541
|
+
if (nid != null && abstracts[aref]) {
|
|
542
|
+
// a copy per num: its lvlOverride/startOverride (how Word restarts a list) is its own
|
|
543
|
+
var own = {};
|
|
544
|
+
for (var key in abstracts[aref]) own[key] = abstracts[aref][key];
|
|
545
|
+
var ovs = nums[k].getElementsByTagNameNS(W, "lvlOverride");
|
|
546
|
+
for (var oi = 0; oi < ovs.length; oi++) {
|
|
547
|
+
var oIl = ovs[oi].getAttributeNS(W, "ilvl") || ovs[oi].getAttribute("w:ilvl") || "0";
|
|
548
|
+
var so = ovs[oi].getElementsByTagNameNS(W, "startOverride")[0];
|
|
549
|
+
if (so) own["s" + oIl] = parseInt(val(so), 10) || 1;
|
|
550
|
+
}
|
|
551
|
+
out[nid] = own;
|
|
552
|
+
}
|
|
530
553
|
}
|
|
531
554
|
} catch (e) {}
|
|
532
555
|
return out;
|
|
@@ -555,34 +578,34 @@ function RTE_Plugin_DocumentImport() {
|
|
|
555
578
|
// Passing "∑" through untouched produces a glyph that KaTeX cannot size or
|
|
556
579
|
// place limits on.
|
|
557
580
|
var MATH_SYMBOLS = {
|
|
558
|
-
"
|
|
559
|
-
"
|
|
560
|
-
"
|
|
561
|
-
"
|
|
562
|
-
"
|
|
563
|
-
"
|
|
564
|
-
"
|
|
565
|
-
"
|
|
566
|
-
"
|
|
567
|
-
"
|
|
568
|
-
"
|
|
569
|
-
"
|
|
570
|
-
"
|
|
571
|
-
"
|
|
572
|
-
"
|
|
573
|
-
"
|
|
574
|
-
"
|
|
575
|
-
"
|
|
576
|
-
"
|
|
577
|
-
"
|
|
578
|
-
"
|
|
579
|
-
"
|
|
581
|
+
"\u2211": "\\sum", "\u220f": "\\prod", "\u2210": "\\coprod",
|
|
582
|
+
"\u222b": "\\int", "\u222c": "\\iint", "\u222d": "\\iiint", "\u222e": "\\oint",
|
|
583
|
+
"\u22c3": "\\bigcup", "\u22c2": "\\bigcap",
|
|
584
|
+
"\xb1": "\\pm", "\u2213": "\\mp", "\xd7": "\\times", "\xf7": "\\div",
|
|
585
|
+
"\u2212": "-", "\u2260": "\\neq", "\u2264": "\\leq", "\u2265": "\\geq",
|
|
586
|
+
"\u2248": "\\approx", "\u2261": "\\equiv", "\u221d": "\\propto",
|
|
587
|
+
"\u221e": "\\infty", "\u2202": "\\partial", "\u2207": "\\nabla",
|
|
588
|
+
"\u221a": "\\sqrt", "\u2220": "\\angle", "\u22c5": "\\cdot", "\u2026": "\\dots",
|
|
589
|
+
"\u2192": "\\to", "\u2190": "\\leftarrow", "\u21d2": "\\Rightarrow", "\u21d4": "\\Leftrightarrow",
|
|
590
|
+
"\u2208": "\\in", "\u2209": "\\notin", "\u2282": "\\subset", "\u2286": "\\subseteq",
|
|
591
|
+
"\u222a": "\\cup", "\u2229": "\\cap", "\u2205": "\\emptyset",
|
|
592
|
+
"\u2200": "\\forall", "\u2203": "\\exists", "\xac": "\\neg",
|
|
593
|
+
"\u2227": "\\land", "\u2228": "\\lor", "\u2234": "\\therefore",
|
|
594
|
+
"\u03b1": "\\alpha", "\u03b2": "\\beta", "\u03b3": "\\gamma", "\u03b4": "\\delta",
|
|
595
|
+
"\u03b5": "\\epsilon", "\u03b6": "\\zeta", "\u03b7": "\\eta", "\u03b8": "\\theta",
|
|
596
|
+
"\u03b9": "\\iota", "\u03ba": "\\kappa", "\u03bb": "\\lambda", "\u03bc": "\\mu",
|
|
597
|
+
"\u03bd": "\\nu", "\u03be": "\\xi", "\u03c0": "\\pi", "\u03c1": "\\rho",
|
|
598
|
+
"\u03c3": "\\sigma", "\u03c4": "\\tau", "\u03c5": "\\upsilon", "\u03c6": "\\varphi",
|
|
599
|
+
"\u03c7": "\\chi", "\u03c8": "\\psi", "\u03c9": "\\omega",
|
|
600
|
+
"\u0393": "\\Gamma", "\u0394": "\\Delta", "\u0398": "\\Theta", "\u039b": "\\Lambda",
|
|
601
|
+
"\u039e": "\\Xi", "\u03a0": "\\Pi", "\u03a3": "\\Sigma", "\u03a6": "\\Phi",
|
|
602
|
+
"\u03a8": "\\Psi", "\u03a9": "\\Omega"
|
|
580
603
|
};
|
|
581
604
|
// Accent character -> TeX accent command (m:acc carries the mark as a chr).
|
|
582
605
|
var MATH_ACCENTS = {
|
|
583
|
-
"
|
|
584
|
-
"
|
|
585
|
-
"
|
|
606
|
+
"\u0302": "\\hat", "\u0303": "\\tilde", "\u0304": "\\bar", "\u0305": "\\overline",
|
|
607
|
+
"\u0306": "\\breve", "\u0307": "\\dot", "\u0308": "\\ddot", "\u030a": "\\mathring",
|
|
608
|
+
"\u030c": "\\check", "\u2192": "\\vec", "\u20d7": "\\vec", "\u0301": "\\acute", "\u0300": "\\grave"
|
|
586
609
|
};
|
|
587
610
|
|
|
588
611
|
function ommlToTex(node) {
|
|
@@ -702,7 +725,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
702
725
|
case "nary": {
|
|
703
726
|
// The operator itself is an ATTRIBUTE (m:chr), not text. Miss
|
|
704
727
|
// it and every summation imports as a bare limit pair.
|
|
705
|
-
var chr = propOf(el, "naryPr", "chr") || "
|
|
728
|
+
var chr = propOf(el, "naryPr", "chr") || "\u222b";
|
|
706
729
|
var op = MATH_SYMBOLS[chr] || mapText(chr);
|
|
707
730
|
var subHide = propOf(el, "naryPr", "subHide");
|
|
708
731
|
var supHide = propOf(el, "naryPr", "supHide");
|
|
@@ -725,7 +748,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
725
748
|
if (!ch) return side === "l" ? "\\left." : "\\right.";
|
|
726
749
|
if (ch === "{" || ch === "}") return "\\" + ch;
|
|
727
750
|
if (ch === "|") return "|";
|
|
728
|
-
if (ch === "
|
|
751
|
+
if (ch === "\u2016") return "\\|";
|
|
729
752
|
// THREE different codepoints render as an angle
|
|
730
753
|
// bracket and are indistinguishable on screen: the
|
|
731
754
|
// mathematical pair (U+27E8/9), the CJK pair
|
|
@@ -736,10 +759,10 @@ function RTE_Plugin_DocumentImport() {
|
|
|
736
759
|
var cp = ch.charCodeAt(0);
|
|
737
760
|
if (cp === 0x27E8 || cp === 0x3008 || cp === 0x2329) return "\\langle";
|
|
738
761
|
if (cp === 0x27E9 || cp === 0x3009 || cp === 0x232A) return "\\rangle";
|
|
739
|
-
if (ch === "
|
|
740
|
-
if (ch === "
|
|
741
|
-
if (ch === "
|
|
742
|
-
if (ch === "
|
|
762
|
+
if (ch === "\u230a") return "\\lfloor";
|
|
763
|
+
if (ch === "\u230b") return "\\rfloor";
|
|
764
|
+
if (ch === "\u2308") return "\\lceil";
|
|
765
|
+
if (ch === "\u2309") return "\\rceil";
|
|
743
766
|
return ch;
|
|
744
767
|
}
|
|
745
768
|
var parts = children(el, "e");
|
|
@@ -763,7 +786,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
763
786
|
case "limUpp":
|
|
764
787
|
return "\\overset" + arg(child(el, "lim")) + arg(child(el, "e"));
|
|
765
788
|
case "acc": {
|
|
766
|
-
var mark = propOf(el, "accPr", "chr") || "
|
|
789
|
+
var mark = propOf(el, "accPr", "chr") || "\u0302";
|
|
767
790
|
var cmd = MATH_ACCENTS[mark] || "\\hat";
|
|
768
791
|
return cmd + arg(child(el, "e"));
|
|
769
792
|
}
|
|
@@ -771,7 +794,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
771
794
|
return (propOf(el, "barPr", "pos") === "bot" ? "\\underline" : "\\overline") + arg(child(el, "e"));
|
|
772
795
|
case "groupChr": {
|
|
773
796
|
var g = propOf(el, "groupChrPr", "chr");
|
|
774
|
-
if (g === "
|
|
797
|
+
if (g === "\u23df") return "\\underbrace" + arg(child(el, "e"));
|
|
775
798
|
return "\\overbrace" + arg(child(el, "e"));
|
|
776
799
|
}
|
|
777
800
|
case "borderBox":
|
|
@@ -820,6 +843,23 @@ function RTE_Plugin_DocumentImport() {
|
|
|
820
843
|
}
|
|
821
844
|
|
|
822
845
|
// <w:drawing>/<w:pict> -> <img> from word/media, inlined as a data URI.
|
|
846
|
+
// 2026-10-15 with its alt text (<wp:docPr descr>, else title) and size (<wp:extent>, EMU; or a VML shape's
|
|
847
|
+
// width/height in pt): every picture came back alt="" at natural size. tools/docx/image-alt-size.mjs
|
|
848
|
+
function imageAttrs(node) {
|
|
849
|
+
var all = node.getElementsByTagName("*"), alt = "", w = 0, h = 0;
|
|
850
|
+
for (var i = 0; i < all.length; i++) {
|
|
851
|
+
var e = all[i], ln = e.localName;
|
|
852
|
+
if (ln === "docPr") alt = e.getAttribute("descr") || e.getAttribute("title") || "";
|
|
853
|
+
else if (ln === "extent" && !w) { w = Math.round((parseInt(e.getAttribute("cx"), 10) || 0) * 96 / 914400); h = Math.round((parseInt(e.getAttribute("cy"), 10) || 0) * 96 / 914400); }
|
|
854
|
+
else if (ln === "shape" && !w) {
|
|
855
|
+
var st = e.getAttribute("style") || "", mw = /width:\s*([\d.]+)pt/.exec(st), mh = /height:\s*([\d.]+)pt/.exec(st);
|
|
856
|
+
if (mw && mh) { w = Math.round(parseFloat(mw[1]) * 96 / 72); h = Math.round(parseFloat(mh[1]) * 96 / 72); }
|
|
857
|
+
if (!alt) alt = e.getAttribute("alt") || "";
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
// esc() leaves quotes alone; this is an attribute value
|
|
861
|
+
return ' alt="' + esc(alt).replace(/"/g, """) + '"' + (w > 0 && h > 0 ? ' width="' + w + '" height="' + h + '"' : "");
|
|
862
|
+
}
|
|
823
863
|
function imageHtml(node) {
|
|
824
864
|
var blips = node.getElementsByTagName("*");
|
|
825
865
|
for (var i = 0; i < blips.length; i++) {
|
|
@@ -830,8 +870,8 @@ function RTE_Plugin_DocumentImport() {
|
|
|
830
870
|
var path = rel.target.replace(/^\.\//, "");
|
|
831
871
|
var key = path.indexOf("word/") === 0 ? path : "word/" + path;
|
|
832
872
|
var uri = ctx.media[key];
|
|
833
|
-
if (uri) return '<img src="' + uri + '"
|
|
834
|
-
if (rel.external) return '<img src="' + esc(rel.target) + '"
|
|
873
|
+
if (uri) return '<img src="' + uri + '"' + imageAttrs(node) + '>';
|
|
874
|
+
if (rel.external) return '<img src="' + esc(rel.target) + '"' + imageAttrs(node) + '>';
|
|
835
875
|
// A vector metafile (WMF/EMF) has no browser-renderable form, so
|
|
836
876
|
// there is no data URI for it. Dropping it silently leaves the
|
|
837
877
|
// reader with no idea a figure was ever there -- and no filename
|
|
@@ -840,7 +880,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
840
880
|
var fileName = path.split("/").pop();
|
|
841
881
|
queueBlock('<p class="rte-imported-unsupported" data-source="' + esc(fileName) +
|
|
842
882
|
'"><em>[Unsupported image: ' + esc(fileName) +
|
|
843
|
-
"
|
|
883
|
+
" \u2014 Windows metafiles have no browser-renderable form]</em></p>");
|
|
844
884
|
return "";
|
|
845
885
|
}
|
|
846
886
|
}
|
|
@@ -885,6 +925,9 @@ function RTE_Plugin_DocumentImport() {
|
|
|
885
925
|
// and every tracked deletion imports as an empty run.
|
|
886
926
|
if (c.localName === "t" || c.localName === "delText") t += c.textContent || "";
|
|
887
927
|
else if (c.localName === "tab") t += "\t";
|
|
928
|
+
// 2026-10-15 a PAGE break is not a line break: mark it so the paragraph can be split around the
|
|
929
|
+
// editor's page-break marker. tools/docx/page-breaks.mjs
|
|
930
|
+
else if (c.localName === "br" && (c.getAttributeNS(W, "type") || c.getAttribute("w:type")) === "page") t += "\f";
|
|
888
931
|
else if (c.localName === "br" || c.localName === "cr") t += "\n";
|
|
889
932
|
}
|
|
890
933
|
return t;
|
|
@@ -975,7 +1018,20 @@ function RTE_Plugin_DocumentImport() {
|
|
|
975
1018
|
var sv = parseInt(sz.getAttributeNS(W, "val") || sz.getAttribute("w:val") || "", 10);
|
|
976
1019
|
if (sv > 0) css += "font-size:" + (sv / 2) + "pt;";
|
|
977
1020
|
}
|
|
978
|
-
|
|
1021
|
+
// 2026-10-15 small caps, caps, letter spacing and the phrase's language. tools/docx/run-props.mjs
|
|
1022
|
+
var sc = firstChildEl(rpr, "smallCaps"), cp = firstChildEl(rpr, "caps");
|
|
1023
|
+
function on(el) { var v = el.getAttributeNS(W, "val") || el.getAttribute("w:val") || ""; return !/^(0|false|off)$/.test(v); }
|
|
1024
|
+
if (sc && on(sc)) css += "font-variant:small-caps;";
|
|
1025
|
+
if (cp && on(cp)) css += "text-transform:uppercase;";
|
|
1026
|
+
var spc = firstChildEl(rpr, "spacing");
|
|
1027
|
+
if (spc) { var spv = parseInt(spc.getAttributeNS(W, "val") || spc.getAttribute("w:val") || "", 10); if (spv) css += "letter-spacing:" + (Math.round(spv / 15 * 100) / 100) + "px;"; }
|
|
1028
|
+
var lg = firstChildEl(rpr, "lang"), lang = "";
|
|
1029
|
+
if (lg) {
|
|
1030
|
+
lang = (lg.getAttributeNS(W, "val") || lg.getAttribute("w:val") || "").replace(/[^A-Za-z0-9-]/g, "");
|
|
1031
|
+
// a run in the document's own language says nothing: Word stamps it on nearly every run
|
|
1032
|
+
if (ctx.defaultLang ? lang.toLowerCase() === ctx.defaultLang : /^en(-|$)/i.test(lang)) lang = "";
|
|
1033
|
+
}
|
|
1034
|
+
if (css || lang) txt = "<span" + (lang ? ' lang="' + lang + '"' : "") + (css ? ' style="' + css + '"' : "") + ">" + txt + "</span>";
|
|
979
1035
|
}
|
|
980
1036
|
return pics + txt;
|
|
981
1037
|
}
|
|
@@ -1051,7 +1107,7 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1051
1107
|
items += '<li class="rte-fn-note" data-fn-id="' + f.id + '" data-fn-number="' + (i + 1) +
|
|
1052
1108
|
'" id="fn-' + f.id + '">' + esc(f.text) +
|
|
1053
1109
|
'<a class="rte-fn-back" data-fn-id="' + f.id + '" href="#fnref-' + f.id +
|
|
1054
|
-
'" contenteditable="false" title="Back to reference"
|
|
1110
|
+
'" contenteditable="false" title="Back to reference">\u21a9</a></li>';
|
|
1055
1111
|
}
|
|
1056
1112
|
return '<section class="rte-footnotes"><h2 class="rte-fn-title">Footnotes</h2>' +
|
|
1057
1113
|
'<ol class="rte-fn-list" data-numbering="decimal">' + items + "</ol></section>";
|
|
@@ -1165,6 +1221,10 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1165
1221
|
// ordered list. Defaulting to bullet when numbering.xml is absent
|
|
1166
1222
|
// matches the old behaviour rather than inventing <ol>s.
|
|
1167
1223
|
info.ordered = !!fmt && fmt !== "bullet" && fmt !== "none";
|
|
1224
|
+
info.numId = nid;
|
|
1225
|
+
info.start = (levels && levels["s" + info.level]) || 1;
|
|
1226
|
+
// 2026-10-04 Word's lettered / Roman formats come back as list-style-type (they became 1. 2. 3.)
|
|
1227
|
+
info.listStyle = { lowerLetter: "lower-alpha", upperLetter: "upper-alpha", lowerRoman: "lower-roman", upperRoman: "upper-roman" }[fmt] || "";
|
|
1168
1228
|
}
|
|
1169
1229
|
// Word paragraph indentation. Nothing read this before 2026-09-04,
|
|
1170
1230
|
// so every indented paragraph in every imported .docx arrived
|
|
@@ -1178,7 +1238,23 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1178
1238
|
// indent, already carried by the blockquote element itself.
|
|
1179
1239
|
if (tw > 0 && !(info.quote && tw === 720))
|
|
1180
1240
|
info.indentPx = Math.round(tw / 15);
|
|
1241
|
+
// 2026-10-15 first-line / hanging indent, line spacing and direction were not read.
|
|
1242
|
+
// tools/docx/paragraph-props.mjs
|
|
1243
|
+
var fl = parseInt(ind.getAttributeNS(W, "firstLine") || ind.getAttribute("w:firstLine") || "", 10);
|
|
1244
|
+
var hg = parseInt(ind.getAttributeNS(W, "hanging") || ind.getAttribute("w:hanging") || "", 10);
|
|
1245
|
+
if (hg > 0) info.textIndentPx = -Math.round(hg / 15);
|
|
1246
|
+
else if (fl > 0) info.textIndentPx = Math.round(fl / 15);
|
|
1247
|
+
}
|
|
1248
|
+
var sp = firstChildEl(ppr, "spacing");
|
|
1249
|
+
if (sp) {
|
|
1250
|
+
var ln = parseInt(sp.getAttributeNS(W, "line") || sp.getAttribute("w:line") || "", 10);
|
|
1251
|
+
var rule = sp.getAttributeNS(W, "lineRule") || sp.getAttribute("w:lineRule") || "auto";
|
|
1252
|
+
// Word's default single spacing (240 auto) is not worth a style
|
|
1253
|
+
if (ln > 0 && !(rule === "auto" && ln === 240)) info.lineHeight = rule === "auto" ? String(Math.round(ln / 240 * 100) / 100) : Math.round(ln / 15) + "px";
|
|
1181
1254
|
}
|
|
1255
|
+
var bidi = firstChildEl(ppr, "bidi");
|
|
1256
|
+
if (bidi && !/^(0|false|off)$/.test(bidi.getAttributeNS(W, "val") || bidi.getAttribute("w:val") || "")) info.rtl = true;
|
|
1257
|
+
if (firstChildEl(ppr, "pageBreakBefore")) info.pageBreakBefore = true;
|
|
1182
1258
|
var jc = firstChildEl(ppr, "jc");
|
|
1183
1259
|
if (jc) {
|
|
1184
1260
|
var a = jc.getAttributeNS(W, "val") || jc.getAttribute("w:val") || "";
|
|
@@ -1198,6 +1274,10 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1198
1274
|
// markup out of an importer undermines the whole clean-output promise.
|
|
1199
1275
|
var listStack = [];
|
|
1200
1276
|
var liOpen = [];
|
|
1277
|
+
// 2026-10-04 items already numbered per numId and level: Word continues a list across
|
|
1278
|
+
// other paragraphs (same numId), so a list reopened here numbers on, and a list that does not
|
|
1279
|
+
// start at 1 keeps its start. Both restarted at 1. tools/docx/import-list-start.mjs
|
|
1280
|
+
var numCount = {};
|
|
1201
1281
|
function closeListsTo(depth) {
|
|
1202
1282
|
while (listStack.length > depth) {
|
|
1203
1283
|
var d = listStack.length - 1;
|
|
@@ -1223,6 +1303,32 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1223
1303
|
if (node.localName === "p") {
|
|
1224
1304
|
var st = paraStyle(node);
|
|
1225
1305
|
var inner = paraInner(node);
|
|
1306
|
+
// 2026-10-15 page breaks come back as the editor's page-break marker (they were dropped):
|
|
1307
|
+
// <w:pageBreakBefore/> before the paragraph, <w:br w:type="page"/> splitting it. tools/docx/page-breaks.mjs
|
|
1308
|
+
if (st.pageBreakBefore) { closeListsTo(0); out.push(pageBreakHtml()); }
|
|
1309
|
+
if (inner && inner.indexOf("\f") !== -1) {
|
|
1310
|
+
if (!st.list && !st.heading && !st.pre && !st.quote) {
|
|
1311
|
+
var pbParts = inner.split("\f");
|
|
1312
|
+
inner = pbParts.pop();
|
|
1313
|
+
closeListsTo(0);
|
|
1314
|
+
for (var pbi = 0; pbi < pbParts.length; pbi++) {
|
|
1315
|
+
if (pbParts[pbi].replace(/<[^>]*>/g, "").trim()) out.push("<p>" + pbParts[pbi] + "</p>");
|
|
1316
|
+
out.push(pageBreakHtml());
|
|
1317
|
+
}
|
|
1318
|
+
} else inner = inner.split("\f").join("");
|
|
1319
|
+
}
|
|
1320
|
+
// 2026-10-15 a paragraph that starts with a ballot box (open U+2610, ticked U+2611 / U+2612) is a task
|
|
1321
|
+
// item: that is how our export and Google Docs write a checklist, and how one is typed in Word. Each
|
|
1322
|
+
// becomes a one-item task list; adjacent ones are joined at the end. tools/docx/task-list.mjs
|
|
1323
|
+
var taskM = !st.list && !st.heading && inner && /^((?:<[^>]+>)*)\s*([\u2610\u2611\u2612])[\s\u00a0]?/.exec(inner);
|
|
1324
|
+
if (taskM) {
|
|
1325
|
+
closeListsTo(0);
|
|
1326
|
+
var taskBody = taskM[1] + inner.slice(taskM[0].length);
|
|
1327
|
+
out.push('<ul class="rte-task-list"><li class="rte-task-item" data-checked="' + (taskM[2] === "\u2610" ? "false" : "true") + '"><span class="rte-task-checkbox" contenteditable="false"></span>' + (taskBody || "<br>") + "</li></ul>");
|
|
1328
|
+
var tboxes = flushBlocks();
|
|
1329
|
+
if (tboxes) out.push(tboxes);
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1226
1332
|
if (st.list) {
|
|
1227
1333
|
var want = (st.level || 0) + 1;
|
|
1228
1334
|
var tag = st.ordered ? "ol" : "ul";
|
|
@@ -1235,11 +1341,24 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1235
1341
|
// Going deeper leaves the parent <li> open so the sublist
|
|
1236
1342
|
// nests inside it.
|
|
1237
1343
|
while (listStack.length < want) {
|
|
1238
|
-
|
|
1344
|
+
var olStart = (tag === "ol" && listStack.length === want - 1) ? (st.start || 1) + (numCount[st.numId + ":" + st.level] || 0) : 1;
|
|
1345
|
+
var olAttrs = (olStart !== 1 ? " start=\"" + olStart + "\"" : "") +
|
|
1346
|
+
(tag === "ol" && listStack.length === want - 1 && st.listStyle ? " style=\"list-style-type: " + st.listStyle + "\"" : "");
|
|
1347
|
+
out.push("<" + tag + olAttrs + ">");
|
|
1239
1348
|
listStack.push(tag);
|
|
1240
1349
|
liOpen.push(false);
|
|
1241
1350
|
}
|
|
1242
1351
|
if (liOpen[want - 1]) { out.push("</li>"); liOpen[want - 1] = false; }
|
|
1352
|
+
if (st.ordered) {
|
|
1353
|
+
var ck = st.numId + ":" + st.level;
|
|
1354
|
+
numCount[ck] = (numCount[ck] || 0) + 1;
|
|
1355
|
+
}
|
|
1356
|
+
// any higher-level item - a BULLET too (2026-10-05 review) - restarts the numbered
|
|
1357
|
+
// levels below it (Word's default lvlRestart)
|
|
1358
|
+
for (var cKey in numCount) {
|
|
1359
|
+
var cp = cKey.split(":");
|
|
1360
|
+
if (cp[0] === String(st.numId) && +cp[1] > st.level) numCount[cKey] = 0;
|
|
1361
|
+
}
|
|
1243
1362
|
out.push("<li>" + (inner || ""));
|
|
1244
1363
|
liOpen[want - 1] = true;
|
|
1245
1364
|
continue;
|
|
@@ -1264,7 +1383,9 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1264
1383
|
// indentUseMargin defaults to margin, and margin is what
|
|
1265
1384
|
// w:ind means, so import writes margin-left to match.
|
|
1266
1385
|
if (st.indentPx) decl.push("margin-left:" + st.indentPx + "px");
|
|
1267
|
-
|
|
1386
|
+
if (st.textIndentPx) decl.push("text-indent:" + st.textIndentPx + "px");
|
|
1387
|
+
if (st.lineHeight) decl.push("line-height:" + st.lineHeight);
|
|
1388
|
+
var style = (st.rtl ? ' dir="rtl"' : "") + (decl.length ? ' style="' + decl.join(";") + '"' : "");
|
|
1268
1389
|
// A paragraph that contained ONLY a text box has no text of
|
|
1269
1390
|
// its own; emitting an empty <p> before the box adds a blank
|
|
1270
1391
|
// line that was never in the document.
|
|
@@ -1285,10 +1406,23 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1285
1406
|
// The notes section goes last, after every marker has been collected.
|
|
1286
1407
|
var notes = footnotesSection();
|
|
1287
1408
|
if (notes) out.push(notes);
|
|
1288
|
-
|
|
1409
|
+
// one task list per run of task items
|
|
1410
|
+
return out.join("\n").replace(/<\/ul>\n<ul class="rte-task-list">/g, "");
|
|
1289
1411
|
|
|
1412
|
+
// the editor's own page-break marker (rte.js __UI_InsertPageBreak), so it looks and behaves the same
|
|
1413
|
+
function pageBreakHtml() {
|
|
1414
|
+
var lbl = editor && editor.getLangText ? editor.getLangText("insertpagebreak") : "";
|
|
1415
|
+
if (!lbl || lbl === "insertpagebreak") lbl = "Page break";
|
|
1416
|
+
return '<div data-rte-page-break="true" contenteditable="false" style="position:relative;text-align:center;border-top:2px dashed #94a3b8;margin:18px 0 14px;padding:0;height:0;line-height:0;user-select:none;">' +
|
|
1417
|
+
'<span style="position:relative;top:-0.75em;display:inline-block;padding:0 10px;background:#fff;color:#64748b;font-size:12px;font-weight:600;letter-spacing:0.08em;text-transform:uppercase;line-height:1.6;">' + esc(lbl) + "</span></div>";
|
|
1418
|
+
}
|
|
1290
1419
|
function tableHtml(tbl) {
|
|
1291
1420
|
var rows = [], kids = tbl.childNodes;
|
|
1421
|
+
// 2026-10-15 merged cells: <w:gridSpan> is colspan; <w:vMerge w:val="restart"/> starts a rowspan and each
|
|
1422
|
+
// bare <w:vMerge/> below it, at the same grid column, extends it instead of becoming a cell. Both were
|
|
1423
|
+
// ignored, so merged cells came back split. tools/docx/merged-cells.mjs
|
|
1424
|
+
var openMerge = {};
|
|
1425
|
+
function wv(el) { return el ? (el.getAttributeNS(W, "val") || el.getAttribute("w:val") || "") : ""; }
|
|
1292
1426
|
for (var i = 0; i < kids.length; i++) {
|
|
1293
1427
|
var tr = kids[i];
|
|
1294
1428
|
if (tr.nodeType !== 1 || tr.localName !== "tr") continue;
|
|
@@ -1299,26 +1433,61 @@ function RTE_Plugin_DocumentImport() {
|
|
|
1299
1433
|
var trPr = firstChildEl(tr, "trPr");
|
|
1300
1434
|
var isHeaderRow = !!(trPr && firstChildEl(trPr, "tblHeader"));
|
|
1301
1435
|
var cellTag = isHeaderRow ? "th" : "td";
|
|
1302
|
-
var cells = [];
|
|
1436
|
+
var cells = [], gcol = 0;
|
|
1303
1437
|
for (var j = 0; j < tr.childNodes.length; j++) {
|
|
1304
1438
|
var tc = tr.childNodes[j];
|
|
1305
1439
|
if (tc.nodeType !== 1 || tc.localName !== "tc") continue;
|
|
1440
|
+
var tcPr = firstChildEl(tc, "tcPr");
|
|
1441
|
+
var span = parseInt(wv(tcPr && firstChildEl(tcPr, "gridSpan")), 10) || 1;
|
|
1442
|
+
var vm = tcPr && firstChildEl(tcPr, "vMerge");
|
|
1443
|
+
if (vm && wv(vm) !== "restart" && openMerge[gcol]) { openMerge[gcol].rowspan++; gcol += span; continue; }
|
|
1306
1444
|
var cellHtml = "";
|
|
1307
1445
|
for (var k = 0; k < tc.childNodes.length; k++) {
|
|
1308
1446
|
if (tc.childNodes[k].localName === "p") cellHtml += "<p>" + (paraInner(tc.childNodes[k]) || "<br>") + "</p>";
|
|
1309
1447
|
}
|
|
1310
|
-
|
|
1448
|
+
var cellObj = { tag: cellTag, html: cellHtml, colspan: span, rowspan: 1 };
|
|
1449
|
+
cells.push(cellObj);
|
|
1450
|
+
for (var sc = gcol; sc < gcol + span; sc++) delete openMerge[sc];
|
|
1451
|
+
if (vm && wv(vm) === "restart") openMerge[gcol] = cellObj;
|
|
1452
|
+
gcol += span;
|
|
1453
|
+
}
|
|
1454
|
+
rows.push(cells);
|
|
1455
|
+
}
|
|
1456
|
+
// 2026-10-15 the grid's column widths (as percentages, so the ratio holds at any page width) and a
|
|
1457
|
+
// percentage table width. tools/docx/table-widths.mjs
|
|
1458
|
+
var head = "<table", colgroup = "";
|
|
1459
|
+
var tPr = firstChildEl(tbl, "tblPr"), tW = tPr && firstChildEl(tPr, "tblW");
|
|
1460
|
+
if (tW && (tW.getAttributeNS(W, "type") || tW.getAttribute("w:type")) === "pct") {
|
|
1461
|
+
var pv = String(tW.getAttributeNS(W, "w") || tW.getAttribute("w:w") || "");
|
|
1462
|
+
var pn = /%$/.test(pv) ? parseFloat(pv) : parseFloat(pv) / 50;
|
|
1463
|
+
if (pn > 0) head += ' style="width:' + (Math.round(pn * 10) / 10) + '%"';
|
|
1464
|
+
}
|
|
1465
|
+
var grid = firstChildEl(tbl, "tblGrid");
|
|
1466
|
+
if (grid) {
|
|
1467
|
+
var gws = [], gsum = 0;
|
|
1468
|
+
for (var gi = 0; gi < grid.childNodes.length; gi++) {
|
|
1469
|
+
var gc = grid.childNodes[gi];
|
|
1470
|
+
if (gc.nodeType !== 1 || gc.localName !== "gridCol") continue;
|
|
1471
|
+
var gw = parseInt(gc.getAttributeNS(W, "w") || gc.getAttribute("w:w") || "", 10) || 0;
|
|
1472
|
+
gws.push(gw); gsum += gw;
|
|
1311
1473
|
}
|
|
1312
|
-
|
|
1474
|
+
if (gws.length > 1 && gsum > 0 && gws.every(function (g) { return g > 0; }))
|
|
1475
|
+
colgroup = "<colgroup>" + gws.map(function (g) { return '<col style="width:' + (Math.round(g / gsum * 1000) / 10) + '%">'; }).join("") + "</colgroup>";
|
|
1313
1476
|
}
|
|
1314
|
-
return "
|
|
1477
|
+
return head + ">" + colgroup + rows.map(function (cs) {
|
|
1478
|
+
return "<tr>" + cs.map(function (c) {
|
|
1479
|
+
return "<" + c.tag + (c.colspan > 1 ? ' colspan="' + c.colspan + '"' : "") + (c.rowspan > 1 ? ' rowspan="' + c.rowspan + '"' : "") + ">" + c.html + "</" + c.tag + ">";
|
|
1480
|
+
}).join("") + "</tr>";
|
|
1481
|
+
}).join("") + "</table>";
|
|
1315
1482
|
}
|
|
1316
1483
|
}
|
|
1317
1484
|
|
|
1318
1485
|
function notify(msg) {
|
|
1319
1486
|
try {
|
|
1320
1487
|
if (editor && typeof editor.createDialog === "function") {
|
|
1321
|
-
|
|
1488
|
+
// 2026-10-18 getLangText returns the KEY when a pack lacks it, so this dialog was titled "importtitle" on every
|
|
1489
|
+
// page; the title and messages read config.text_ keys. tools/i18n/raw-key-titles.mjs
|
|
1490
|
+
var d = editor.createDialog((config && config.text_importtitle) || "Import document", "rte-dialog-import");
|
|
1322
1491
|
var w = d.ownerDocument.createElement("div");
|
|
1323
1492
|
w.style.cssText = "padding:16px;max-width:420px;font:13px -apple-system,Segoe UI,sans-serif;line-height:1.5";
|
|
1324
1493
|
w.textContent = msg;
|