autumnnote 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -3
- package/dist/autumnnote.css +24 -1
- package/dist/autumnnote.es.js +509 -134
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +587 -195
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/js/Context.js +26 -1
- package/src/js/core/markdown.js +32 -6
- package/src/js/editing/Style.js +288 -159
- package/src/js/editing/Table.js +10 -2
- package/src/js/editing/Typing.js +21 -4
- package/src/js/i18n/de.js +17 -0
- package/src/js/i18n/en.js +8 -0
- package/src/js/i18n/es.js +17 -0
- package/src/js/i18n/fr.js +18 -1
- package/src/js/i18n/ja.js +17 -0
- package/src/js/i18n/ko.js +17 -0
- package/src/js/i18n/vi.js +15 -0
- package/src/js/i18n/zh.js +17 -0
- package/src/js/index.js +5 -2
- package/src/js/module/Buttons.js +49 -0
- package/src/js/module/Clipboard.js +13 -1
- package/src/js/module/FindReplace.js +24 -5
- package/src/js/module/ImageDialog.js +1 -0
- package/src/js/module/ImageResizer.js +8 -7
- package/src/js/module/Mention.js +13 -2
- package/src/js/module/TableTooltip.js +21 -0
- package/src/styles/autumnnote.scss +23 -3
- package/types/index.d.ts +84 -3
package/dist/autumnnote.umd.js
CHANGED
|
@@ -520,13 +520,74 @@
|
|
|
520
520
|
} catch {}
|
|
521
521
|
}
|
|
522
522
|
/**
|
|
523
|
-
* Inserts an unordered list or converts
|
|
523
|
+
* Inserts an unordered (bulleted) list, or converts the current list to `<ul>`.
|
|
524
|
+
*
|
|
525
|
+
* When the cursor is already inside a list, direct DOM manipulation is used to
|
|
526
|
+
* transition between list types — `execCommand` alone cannot handle checklist →
|
|
527
|
+
* UL/OL conversions because it has no awareness of the `an-checklist` class or
|
|
528
|
+
* the checkbox `<input>` elements.
|
|
529
|
+
*
|
|
530
|
+
* Transition paths:
|
|
531
|
+
* - **Checklist → UL**: strips `an-checklist` class and all checkbox inputs;
|
|
532
|
+
* converts `<ol>` container to `<ul>` via `changeTagName()` if needed.
|
|
533
|
+
* - **OL → UL**: swaps the container tag via `changeTagName()`.
|
|
534
|
+
* - **UL → paragraphs**: falls back to `execCommand('insertUnorderedList')`
|
|
535
|
+
* which toggles the list off (browser-native behaviour).
|
|
536
|
+
* - **No list → UL**: falls back to `execCommand('insertUnorderedList')`.
|
|
537
|
+
*/
|
|
538
|
+
/**
|
|
539
|
+
* Helper to get the closest ul/ol element containing the current selection.
|
|
540
|
+
* @returns {Element|null}
|
|
524
541
|
*/
|
|
525
|
-
|
|
542
|
+
function getSelectedList() {
|
|
543
|
+
const sel = globalThis.getSelection();
|
|
544
|
+
if (!sel?.rangeCount) return null;
|
|
545
|
+
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
546
|
+
if (container.nodeType === 3) container = container.parentElement;
|
|
547
|
+
return container?.closest("ul, ol") || null;
|
|
548
|
+
}
|
|
549
|
+
/**
|
|
550
|
+
* Strips the checklist class and checkbox inputs from a list element.
|
|
551
|
+
* @param {Element} listEl
|
|
552
|
+
*/
|
|
553
|
+
function stripChecklist(listEl) {
|
|
554
|
+
listEl.classList.remove("an-checklist");
|
|
555
|
+
listEl.querySelectorAll("input[type=\"checkbox\"]").forEach((cb) => cb.remove());
|
|
556
|
+
}
|
|
557
|
+
function insertUnorderedList() {
|
|
558
|
+
const listEl = getSelectedList();
|
|
559
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
560
|
+
stripChecklist(listEl);
|
|
561
|
+
if (listEl.tagName === "OL") changeTagName(listEl, "ul");
|
|
562
|
+
} else if (listEl.tagName === "OL") changeTagName(listEl, "ul");
|
|
563
|
+
else execCommand("insertUnorderedList");
|
|
564
|
+
else execCommand("insertUnorderedList");
|
|
565
|
+
}
|
|
526
566
|
/**
|
|
527
|
-
* Inserts an ordered list or converts
|
|
567
|
+
* Inserts an ordered (numbered) list, or converts the current list to `<ol>`.
|
|
568
|
+
*
|
|
569
|
+
* When the cursor is already inside a list, direct DOM manipulation is used to
|
|
570
|
+
* transition between list types — `execCommand` alone cannot handle checklist →
|
|
571
|
+
* UL/OL conversions because it has no awareness of the `an-checklist` class or
|
|
572
|
+
* the checkbox `<input>` elements.
|
|
573
|
+
*
|
|
574
|
+
* Transition paths:
|
|
575
|
+
* - **Checklist → OL**: strips `an-checklist` class and all checkbox inputs;
|
|
576
|
+
* converts container to `<ol>` via `changeTagName()`.
|
|
577
|
+
* - **UL → OL**: swaps the container tag via `changeTagName()`.
|
|
578
|
+
* - **OL → paragraphs**: falls back to `execCommand('insertOrderedList')`
|
|
579
|
+
* which toggles the list off (browser-native behaviour).
|
|
580
|
+
* - **No list → OL**: falls back to `execCommand('insertOrderedList')`.
|
|
528
581
|
*/
|
|
529
|
-
|
|
582
|
+
function insertOrderedList() {
|
|
583
|
+
const listEl = getSelectedList();
|
|
584
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
585
|
+
stripChecklist(listEl);
|
|
586
|
+
changeTagName(listEl, "ol");
|
|
587
|
+
} else if (listEl.tagName === "UL") changeTagName(listEl, "ol");
|
|
588
|
+
else execCommand("insertOrderedList");
|
|
589
|
+
else execCommand("insertOrderedList");
|
|
590
|
+
}
|
|
530
591
|
/**
|
|
531
592
|
* Set the line-height on every block-level element that intersects the current selection.
|
|
532
593
|
*
|
|
@@ -652,18 +713,34 @@
|
|
|
652
713
|
return !!(code && !code.closest("pre"));
|
|
653
714
|
}
|
|
654
715
|
/**
|
|
716
|
+
* Changes the tag name of an element in the DOM while preserving attributes and children.
|
|
717
|
+
* @param {Element} el
|
|
718
|
+
* @param {string} newTagName
|
|
719
|
+
* @returns {HTMLElement}
|
|
720
|
+
*/
|
|
721
|
+
function changeTagName(el, newTagName) {
|
|
722
|
+
const newEl = document.createElement(newTagName);
|
|
723
|
+
for (const attr of el.attributes) newEl.setAttribute(attr.name, attr.value);
|
|
724
|
+
while (el.firstChild) newEl.appendChild(el.firstChild);
|
|
725
|
+
el.parentNode.replaceChild(newEl, el);
|
|
726
|
+
return newEl;
|
|
727
|
+
}
|
|
728
|
+
/**
|
|
729
|
+
* Ensures all list items under the list element have a checkbox.
|
|
730
|
+
* @param {Element} listEl
|
|
731
|
+
*/
|
|
732
|
+
function ensureCheckboxes(listEl) {
|
|
733
|
+
listEl.querySelectorAll("li").forEach((li) => {
|
|
734
|
+
if (!li.querySelector("input[type=\"checkbox\"]")) {
|
|
735
|
+
const cb = document.createElement("input");
|
|
736
|
+
cb.type = "checkbox";
|
|
737
|
+
cb.contentEditable = "false";
|
|
738
|
+
li.insertBefore(cb, li.firstChild);
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
655
743
|
* Toggle a checklist at the current selection or caret.
|
|
656
|
-
*
|
|
657
|
-
* When the selection is inside an existing checklist `<ul class="an-checklist">`,
|
|
658
|
-
* converts the selected `<li>` items back into `<p>` paragraphs and places the caret
|
|
659
|
-
* at the start of the first converted paragraph. Otherwise creates a checklist:
|
|
660
|
-
* - If the selection is collapsed, converts the nearest block-level ancestor (or inserts
|
|
661
|
-
* a single checklist item at the editable root) into a checklist with one item containing
|
|
662
|
-
* that block's text and places the caret inside the new item.
|
|
663
|
-
* - If the selection is a range, converts each intersecting block element into one checklist
|
|
664
|
-
* item (preserving textual content) and places the caret at the end of the last item.
|
|
665
|
-
*
|
|
666
|
-
* Empty or whitespace-only selections do not create a checklist.
|
|
667
744
|
*/
|
|
668
745
|
function toggleChecklist() {
|
|
669
746
|
const sel = globalThis.getSelection();
|
|
@@ -671,27 +748,26 @@
|
|
|
671
748
|
const range = sel.getRangeAt(0);
|
|
672
749
|
let container = range.commonAncestorContainer;
|
|
673
750
|
if (container.nodeType === 3) container = container.parentElement;
|
|
674
|
-
const
|
|
675
|
-
if (
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
751
|
+
const listEl = container?.closest("ul, ol");
|
|
752
|
+
if (listEl) if (listEl.classList.contains("an-checklist")) {
|
|
753
|
+
if (listEl.parentNode) {
|
|
754
|
+
const lis = Array.from(listEl.children);
|
|
755
|
+
let firstP = null;
|
|
756
|
+
lis.forEach((li) => {
|
|
680
757
|
const p = document.createElement("p");
|
|
681
758
|
for (const child of li.childNodes) {
|
|
682
759
|
if (child.nodeType === 1 && child.tagName === "INPUT") continue;
|
|
683
760
|
p.appendChild(child.cloneNode(true));
|
|
684
761
|
}
|
|
685
|
-
p.innerHTML = p.innerHTML.replaceAll("", "");
|
|
762
|
+
p.innerHTML = p.innerHTML.replaceAll("", "").replaceAll("", "");
|
|
686
763
|
if (!p.hasChildNodes() || !p.textContent.trim()) {
|
|
687
764
|
p.innerHTML = "";
|
|
688
765
|
p.appendChild(document.createTextNode("\xA0"));
|
|
689
766
|
}
|
|
690
|
-
|
|
767
|
+
listEl.before(p);
|
|
691
768
|
if (!firstP) firstP = p;
|
|
692
|
-
li.remove();
|
|
693
769
|
});
|
|
694
|
-
|
|
770
|
+
listEl.remove();
|
|
695
771
|
if (firstP) {
|
|
696
772
|
const nr = document.createRange();
|
|
697
773
|
nr.setStart(firstP.firstChild || firstP, 0);
|
|
@@ -699,11 +775,65 @@
|
|
|
699
775
|
sel.removeAllRanges();
|
|
700
776
|
sel.addRange(nr);
|
|
701
777
|
}
|
|
702
|
-
|
|
778
|
+
}
|
|
779
|
+
} else {
|
|
780
|
+
const targetUl = changeTagName(listEl, "ul");
|
|
781
|
+
targetUl.classList.add("an-checklist");
|
|
782
|
+
ensureCheckboxes(targetUl);
|
|
783
|
+
const firstLi = targetUl.querySelector("li");
|
|
784
|
+
if (firstLi) {
|
|
785
|
+
const nr = document.createRange();
|
|
786
|
+
nr.selectNodeContents(firstLi);
|
|
787
|
+
nr.collapse(false);
|
|
788
|
+
sel.removeAllRanges();
|
|
789
|
+
sel.addRange(nr);
|
|
703
790
|
}
|
|
704
791
|
}
|
|
705
|
-
|
|
706
|
-
const
|
|
792
|
+
else {
|
|
793
|
+
const editableRoot = container?.closest("[contenteditable=\"true\"]");
|
|
794
|
+
if (range.collapsed) {
|
|
795
|
+
const BLOCK_TAGS = new Set([
|
|
796
|
+
"P",
|
|
797
|
+
"DIV",
|
|
798
|
+
"H1",
|
|
799
|
+
"H2",
|
|
800
|
+
"H3",
|
|
801
|
+
"H4",
|
|
802
|
+
"H5",
|
|
803
|
+
"H6",
|
|
804
|
+
"BLOCKQUOTE",
|
|
805
|
+
"LI"
|
|
806
|
+
]);
|
|
807
|
+
let block = container;
|
|
808
|
+
while (block?.parentNode && block !== editableRoot && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
|
|
809
|
+
if (block === editableRoot) block = null;
|
|
810
|
+
const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replaceAll("\xA0", " ") : "";
|
|
811
|
+
const newUl = document.createElement("ul");
|
|
812
|
+
newUl.className = "an-checklist";
|
|
813
|
+
const li = document.createElement("li");
|
|
814
|
+
const checkbox = document.createElement("input");
|
|
815
|
+
checkbox.type = "checkbox";
|
|
816
|
+
checkbox.contentEditable = "false";
|
|
817
|
+
li.appendChild(checkbox);
|
|
818
|
+
li.appendChild(document.createTextNode(itemText || ""));
|
|
819
|
+
newUl.appendChild(li);
|
|
820
|
+
if (block && BLOCK_TAGS.has(block.tagName)) block.parentNode.replaceChild(newUl, block);
|
|
821
|
+
else {
|
|
822
|
+
const nativeRange = sel.getRangeAt(0);
|
|
823
|
+
nativeRange.deleteContents();
|
|
824
|
+
nativeRange.insertNode(newUl);
|
|
825
|
+
}
|
|
826
|
+
const textNode = li.lastChild;
|
|
827
|
+
const nr = document.createRange();
|
|
828
|
+
const offset = textNode.nodeType === Node.TEXT_NODE ? textNode.textContent.length : 0;
|
|
829
|
+
nr.setStart(textNode, offset);
|
|
830
|
+
nr.collapse(true);
|
|
831
|
+
sel.removeAllRanges();
|
|
832
|
+
sel.addRange(nr);
|
|
833
|
+
return;
|
|
834
|
+
}
|
|
835
|
+
if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
|
|
836
|
+
const BLOCK_TAGS_MULTI = new Set([
|
|
707
837
|
"P",
|
|
708
838
|
"DIV",
|
|
709
839
|
"H1",
|
|
@@ -713,89 +843,51 @@
|
|
|
713
843
|
"H5",
|
|
714
844
|
"H6",
|
|
715
845
|
"BLOCKQUOTE",
|
|
846
|
+
"PRE",
|
|
716
847
|
"LI"
|
|
717
848
|
]);
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
const
|
|
721
|
-
const
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
const nativeRange = sel.getRangeAt(0);
|
|
733
|
-
nativeRange.deleteContents();
|
|
734
|
-
nativeRange.insertNode(ul);
|
|
849
|
+
const blocks = [];
|
|
850
|
+
const seenBlocks = /* @__PURE__ */ new Set();
|
|
851
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
852
|
+
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
853
|
+
let node;
|
|
854
|
+
while (node = iter.nextNode()) {
|
|
855
|
+
if (!range.intersectsNode(node)) continue;
|
|
856
|
+
let blockEl = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
857
|
+
while (blockEl && blockEl !== editableRoot && !BLOCK_TAGS_MULTI.has(blockEl.tagName)) blockEl = blockEl.parentElement;
|
|
858
|
+
if (blockEl === editableRoot) blockEl = null;
|
|
859
|
+
if (blockEl && !seenBlocks.has(blockEl)) {
|
|
860
|
+
seenBlocks.add(blockEl);
|
|
861
|
+
blocks.push(blockEl);
|
|
862
|
+
}
|
|
735
863
|
}
|
|
736
|
-
|
|
737
|
-
const
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const commonAncestor = range.commonAncestorContainer;
|
|
762
|
-
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
763
|
-
let node;
|
|
764
|
-
while (node = iter.nextNode()) {
|
|
765
|
-
if (!range.intersectsNode(node)) continue;
|
|
766
|
-
let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
767
|
-
while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
|
|
768
|
-
if (block && !seenBlocks.has(block)) {
|
|
769
|
-
seenBlocks.add(block);
|
|
770
|
-
blocks.push(block);
|
|
864
|
+
if (blocks.length === 0) return;
|
|
865
|
+
const newUl = document.createElement("ul");
|
|
866
|
+
newUl.className = "an-checklist";
|
|
867
|
+
/** @type {Text|null} */ let lastTextNode = null;
|
|
868
|
+
blocks.forEach((block) => {
|
|
869
|
+
const li = document.createElement("li");
|
|
870
|
+
const cb = document.createElement("input");
|
|
871
|
+
cb.type = "checkbox";
|
|
872
|
+
cb.contentEditable = "false";
|
|
873
|
+
li.appendChild(cb);
|
|
874
|
+
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
875
|
+
const tn = document.createTextNode(blockText || "");
|
|
876
|
+
li.appendChild(tn);
|
|
877
|
+
newUl.appendChild(li);
|
|
878
|
+
lastTextNode = tn;
|
|
879
|
+
});
|
|
880
|
+
const firstBlock = blocks[0];
|
|
881
|
+
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
882
|
+
blocks.forEach((block) => block.remove());
|
|
883
|
+
if (lastTextNode) {
|
|
884
|
+
const nr = document.createRange();
|
|
885
|
+
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
886
|
+
nr.collapse(true);
|
|
887
|
+
sel.removeAllRanges();
|
|
888
|
+
sel.addRange(nr);
|
|
771
889
|
}
|
|
772
890
|
}
|
|
773
|
-
if (blocks.length === 0) return;
|
|
774
|
-
const newUl = document.createElement("ul");
|
|
775
|
-
newUl.className = "an-checklist";
|
|
776
|
-
/** @type {Text|null} */ let lastTextNode = null;
|
|
777
|
-
blocks.forEach((block) => {
|
|
778
|
-
const li = document.createElement("li");
|
|
779
|
-
const cb = document.createElement("input");
|
|
780
|
-
cb.type = "checkbox";
|
|
781
|
-
cb.setAttribute("contenteditable", "false");
|
|
782
|
-
li.appendChild(cb);
|
|
783
|
-
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
784
|
-
const tn = document.createTextNode(blockText || "");
|
|
785
|
-
li.appendChild(tn);
|
|
786
|
-
newUl.appendChild(li);
|
|
787
|
-
lastTextNode = tn;
|
|
788
|
-
});
|
|
789
|
-
const firstBlock = blocks[0];
|
|
790
|
-
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
791
|
-
blocks.forEach((block) => block.remove());
|
|
792
|
-
if (lastTextNode) {
|
|
793
|
-
const nr = document.createRange();
|
|
794
|
-
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
795
|
-
nr.collapse(true);
|
|
796
|
-
sel.removeAllRanges();
|
|
797
|
-
sel.addRange(nr);
|
|
798
|
-
}
|
|
799
891
|
}
|
|
800
892
|
/**
|
|
801
893
|
* Returns true when the cursor is inside a checklist item.
|
|
@@ -968,6 +1060,13 @@
|
|
|
968
1060
|
}
|
|
969
1061
|
};
|
|
970
1062
|
var removeFormatBtn = btn("removeFormat", "remove-format", "Remove Format", () => execCommand("removeFormat"));
|
|
1063
|
+
var directionBtn = btn("direction", "direction", "Toggle Text Direction (LTR / RTL)", (ctx) => {
|
|
1064
|
+
const editable = ctx.layoutInfo.editable;
|
|
1065
|
+
const next = (editable.getAttribute("dir") || "ltr") === "ltr" ? "rtl" : "ltr";
|
|
1066
|
+
editable.setAttribute("dir", next);
|
|
1067
|
+
editable.style.textAlign = next === "rtl" ? "right" : "left";
|
|
1068
|
+
ctx.invoke("editor.afterCommand");
|
|
1069
|
+
});
|
|
971
1070
|
/** @type {DropdownDef} */
|
|
972
1071
|
var fontFamilyBtn = {
|
|
973
1072
|
name: "fontFamily",
|
|
@@ -1090,9 +1189,122 @@
|
|
|
1090
1189
|
var fullscreenBtn = btn("fullscreen", "expand", "Fullscreen", (ctx) => ctx.invoke("fullscreen.toggle"), (ctx) => ctx.invoke("fullscreen.isActive"));
|
|
1091
1190
|
var shortcutsBtn = btn("shortcuts", "keyboard", "Keyboard Shortcuts (Ctrl+Shift+/)", (ctx) => ctx.invoke("shortcutsDialog.show"));
|
|
1092
1191
|
var findBtn = btn("find", "search", "Find (Ctrl+F)", (ctx) => ctx.invoke("findReplace.show", "find"));
|
|
1192
|
+
var findReplaceBtn = btn("findReplace", "find-replace", "Find & Replace (Ctrl+H)", (ctx) => ctx.invoke("findReplace.show", "replace"));
|
|
1093
1193
|
var inlineCodeBtn = btn("inlineCode", "inline-code", "Inline Code (Ctrl+`)", (ctx) => ctx.invoke("editor.inlineCode"), () => isInlineCode());
|
|
1094
1194
|
var checklistBtn = btn("checklist", "checklist", "Checklist", (ctx) => ctx.invoke("editor.toggleChecklist"), () => isInChecklist());
|
|
1095
1195
|
var printBtn = btn("print", "print", "Print", (ctx) => ctx.invoke("editor.print"));
|
|
1196
|
+
/** @type {{ name: string, type: 'colorpicker', icon: string, tooltip: string, defaultColor: string, action: Function }} */
|
|
1197
|
+
var foreColorBtn = {
|
|
1198
|
+
name: "foreColor",
|
|
1199
|
+
type: "colorpicker",
|
|
1200
|
+
icon: "foreColor",
|
|
1201
|
+
tooltip: "Text Color",
|
|
1202
|
+
defaultColor: "#e11d48",
|
|
1203
|
+
action: (ctx, color) => foreColor(color)
|
|
1204
|
+
};
|
|
1205
|
+
/** @type {{ name: string, type: 'colorpicker', icon: string, tooltip: string, defaultColor: string, action: Function }} */
|
|
1206
|
+
var backColorBtn = {
|
|
1207
|
+
name: "backColor",
|
|
1208
|
+
type: "colorpicker",
|
|
1209
|
+
icon: "backColor",
|
|
1210
|
+
tooltip: "Highlight Color",
|
|
1211
|
+
defaultColor: "#fbbf24",
|
|
1212
|
+
action: (ctx, color) => backColor(color)
|
|
1213
|
+
};
|
|
1214
|
+
/**
|
|
1215
|
+
* The default toolbar button groups.
|
|
1216
|
+
* Each sub-array is a button group (separated by a divider).
|
|
1217
|
+
*/
|
|
1218
|
+
var defaultToolbar = [
|
|
1219
|
+
[
|
|
1220
|
+
paragraphStyleBtn,
|
|
1221
|
+
fontFamilyBtn,
|
|
1222
|
+
fontSizeBtn,
|
|
1223
|
+
lineHeightBtn
|
|
1224
|
+
],
|
|
1225
|
+
[undoBtn, redoBtn],
|
|
1226
|
+
[
|
|
1227
|
+
boldBtn,
|
|
1228
|
+
italicBtn,
|
|
1229
|
+
underlineBtn,
|
|
1230
|
+
strikeBtn,
|
|
1231
|
+
inlineCodeBtn
|
|
1232
|
+
],
|
|
1233
|
+
[superscriptBtn, subscriptBtn],
|
|
1234
|
+
[foreColorBtn, backColorBtn],
|
|
1235
|
+
[
|
|
1236
|
+
alignLeftBtn,
|
|
1237
|
+
alignCenterBtn,
|
|
1238
|
+
alignRightBtn,
|
|
1239
|
+
alignJustifyBtn
|
|
1240
|
+
],
|
|
1241
|
+
[
|
|
1242
|
+
ulBtn,
|
|
1243
|
+
olBtn,
|
|
1244
|
+
checklistBtn,
|
|
1245
|
+
indentBtn,
|
|
1246
|
+
outdentBtn
|
|
1247
|
+
],
|
|
1248
|
+
[
|
|
1249
|
+
hrBtn,
|
|
1250
|
+
linkBtn,
|
|
1251
|
+
imageBtn,
|
|
1252
|
+
videoBtn,
|
|
1253
|
+
tableBtn,
|
|
1254
|
+
emojiBtn,
|
|
1255
|
+
iconBtn
|
|
1256
|
+
],
|
|
1257
|
+
[
|
|
1258
|
+
removeFormatBtn,
|
|
1259
|
+
codeviewBtn,
|
|
1260
|
+
fullscreenBtn,
|
|
1261
|
+
findBtn,
|
|
1262
|
+
printBtn,
|
|
1263
|
+
shortcutsBtn
|
|
1264
|
+
]
|
|
1265
|
+
];
|
|
1266
|
+
var buttons = {
|
|
1267
|
+
boldBtn,
|
|
1268
|
+
italicBtn,
|
|
1269
|
+
underlineBtn,
|
|
1270
|
+
strikeBtn,
|
|
1271
|
+
superscriptBtn,
|
|
1272
|
+
subscriptBtn,
|
|
1273
|
+
alignLeftBtn,
|
|
1274
|
+
alignCenterBtn,
|
|
1275
|
+
alignRightBtn,
|
|
1276
|
+
alignJustifyBtn,
|
|
1277
|
+
ulBtn,
|
|
1278
|
+
olBtn,
|
|
1279
|
+
indentBtn,
|
|
1280
|
+
outdentBtn,
|
|
1281
|
+
undoBtn,
|
|
1282
|
+
redoBtn,
|
|
1283
|
+
hrBtn,
|
|
1284
|
+
linkBtn,
|
|
1285
|
+
imageBtn,
|
|
1286
|
+
videoBtn,
|
|
1287
|
+
emojiBtn,
|
|
1288
|
+
iconBtn,
|
|
1289
|
+
tableBtn,
|
|
1290
|
+
fontSizeBtn,
|
|
1291
|
+
removeFormatBtn,
|
|
1292
|
+
directionBtn,
|
|
1293
|
+
fontFamilyBtn,
|
|
1294
|
+
paragraphStyleBtn,
|
|
1295
|
+
lineHeightBtn,
|
|
1296
|
+
codeviewBtn,
|
|
1297
|
+
fullscreenBtn,
|
|
1298
|
+
shortcutsBtn,
|
|
1299
|
+
findBtn,
|
|
1300
|
+
findReplaceBtn,
|
|
1301
|
+
inlineCodeBtn,
|
|
1302
|
+
checklistBtn,
|
|
1303
|
+
printBtn,
|
|
1304
|
+
foreColorBtn,
|
|
1305
|
+
backColorBtn,
|
|
1306
|
+
defaultToolbar
|
|
1307
|
+
};
|
|
1096
1308
|
//#endregion
|
|
1097
1309
|
//#region src/js/settings.js
|
|
1098
1310
|
/**
|
|
@@ -1166,68 +1378,7 @@
|
|
|
1166
1378
|
maxHeight: 0,
|
|
1167
1379
|
focus: false,
|
|
1168
1380
|
resizable: true,
|
|
1169
|
-
toolbar:
|
|
1170
|
-
[
|
|
1171
|
-
paragraphStyleBtn,
|
|
1172
|
-
fontFamilyBtn,
|
|
1173
|
-
fontSizeBtn,
|
|
1174
|
-
lineHeightBtn
|
|
1175
|
-
],
|
|
1176
|
-
[undoBtn, redoBtn],
|
|
1177
|
-
[
|
|
1178
|
-
boldBtn,
|
|
1179
|
-
italicBtn,
|
|
1180
|
-
underlineBtn,
|
|
1181
|
-
strikeBtn,
|
|
1182
|
-
inlineCodeBtn
|
|
1183
|
-
],
|
|
1184
|
-
[superscriptBtn, subscriptBtn],
|
|
1185
|
-
[{
|
|
1186
|
-
name: "foreColor",
|
|
1187
|
-
type: "colorpicker",
|
|
1188
|
-
icon: "foreColor",
|
|
1189
|
-
tooltip: "Text Color",
|
|
1190
|
-
defaultColor: "#e11d48",
|
|
1191
|
-
action: (ctx, color) => foreColor(color)
|
|
1192
|
-
}, {
|
|
1193
|
-
name: "backColor",
|
|
1194
|
-
type: "colorpicker",
|
|
1195
|
-
icon: "backColor",
|
|
1196
|
-
tooltip: "Highlight Color",
|
|
1197
|
-
defaultColor: "#fbbf24",
|
|
1198
|
-
action: (ctx, color) => backColor(color)
|
|
1199
|
-
}],
|
|
1200
|
-
[
|
|
1201
|
-
alignLeftBtn,
|
|
1202
|
-
alignCenterBtn,
|
|
1203
|
-
alignRightBtn,
|
|
1204
|
-
alignJustifyBtn
|
|
1205
|
-
],
|
|
1206
|
-
[
|
|
1207
|
-
ulBtn,
|
|
1208
|
-
olBtn,
|
|
1209
|
-
checklistBtn,
|
|
1210
|
-
indentBtn,
|
|
1211
|
-
outdentBtn
|
|
1212
|
-
],
|
|
1213
|
-
[
|
|
1214
|
-
hrBtn,
|
|
1215
|
-
linkBtn,
|
|
1216
|
-
imageBtn,
|
|
1217
|
-
videoBtn,
|
|
1218
|
-
tableBtn,
|
|
1219
|
-
emojiBtn,
|
|
1220
|
-
iconBtn
|
|
1221
|
-
],
|
|
1222
|
-
[
|
|
1223
|
-
removeFormatBtn,
|
|
1224
|
-
codeviewBtn,
|
|
1225
|
-
fullscreenBtn,
|
|
1226
|
-
findBtn,
|
|
1227
|
-
printBtn,
|
|
1228
|
-
shortcutsBtn
|
|
1229
|
-
]
|
|
1230
|
-
],
|
|
1381
|
+
toolbar: defaultToolbar,
|
|
1231
1382
|
useBootstrap: false,
|
|
1232
1383
|
toolbarButtonClass: "btn btn-sm btn-light",
|
|
1233
1384
|
useFontAwesome: true,
|
|
@@ -1448,6 +1599,7 @@
|
|
|
1448
1599
|
findPlaceholder: "Find…",
|
|
1449
1600
|
searchAriaLabel: "Search text",
|
|
1450
1601
|
caseSensitive: "\xA0Case sensitive",
|
|
1602
|
+
wholeWord: "Whole Word",
|
|
1451
1603
|
prevBtn: "← Prev",
|
|
1452
1604
|
nextBtn: "Next →",
|
|
1453
1605
|
replacePlaceholder: "Replace with…",
|
|
@@ -1458,6 +1610,12 @@
|
|
|
1458
1610
|
useRegex: "Use Regular Expression",
|
|
1459
1611
|
close: "×"
|
|
1460
1612
|
},
|
|
1613
|
+
autoSaveRestore: {
|
|
1614
|
+
found: "Draft found. Restore?",
|
|
1615
|
+
foundAt: "Draft from {date}. Restore?",
|
|
1616
|
+
restore: "Restore",
|
|
1617
|
+
discard: "Discard"
|
|
1618
|
+
},
|
|
1461
1619
|
shortcutsDialog: {
|
|
1462
1620
|
title: "Keyboard Shortcuts",
|
|
1463
1621
|
ariaLabel: "Keyboard Shortcuts",
|
|
@@ -1823,6 +1981,7 @@
|
|
|
1823
1981
|
findPlaceholder: "Tìm…",
|
|
1824
1982
|
searchAriaLabel: "Văn bản tìm kiếm",
|
|
1825
1983
|
caseSensitive: "\xA0Phân biệt hoa thường",
|
|
1984
|
+
wholeWord: "Toàn bộ từ",
|
|
1826
1985
|
prevBtn: "← Trước",
|
|
1827
1986
|
nextBtn: "Tiếp →",
|
|
1828
1987
|
replacePlaceholder: "Thay thế bằng…",
|
|
@@ -1990,9 +2149,16 @@
|
|
|
1990
2149
|
columnWidth: "Chiều rộng cột",
|
|
1991
2150
|
rowHeight: "Chiều cao hàng",
|
|
1992
2151
|
tableBorderWidth: "Độ rộng viền bảng",
|
|
2152
|
+
tableBorderColor: "Màu viền bảng",
|
|
1993
2153
|
deleteTable: "Xóa bảng",
|
|
2154
|
+
cellAlignLeft: "Căn trái",
|
|
2155
|
+
cellAlignCenter: "Căn giữa",
|
|
2156
|
+
cellAlignRight: "Căn phải",
|
|
2157
|
+
cellAlignJustify: "Căn đều",
|
|
2158
|
+
toggleHeaderRow: "Bật/tắt hàng tiêu đề",
|
|
1994
2159
|
cellBackground: "Màu Nền Ô",
|
|
1995
2160
|
noShading: "Xóa Màu Nền",
|
|
2161
|
+
noBorderColor: "Xóa màu viền",
|
|
1996
2162
|
columnWidthPx: "Chiều rộng cột (px)",
|
|
1997
2163
|
rowHeightPx: "Chiều cao hàng (px)",
|
|
1998
2164
|
tableBorderWidthPx: "Độ rộng viền bảng (px)",
|
|
@@ -2020,6 +2186,12 @@
|
|
|
2020
2186
|
errors: {
|
|
2021
2187
|
imageFormat: (type) => `Định dạng "${type}" không được hỗ trợ hiển thị trên trình duyệt. Vui lòng chuyển đổi sang JPEG, PNG hoặc WebP.`,
|
|
2022
2188
|
imageSize: (maxSize) => `Tệp hình ảnh quá lớn. Kích thước tối đa cho phép là ${maxSize} MB.`
|
|
2189
|
+
},
|
|
2190
|
+
autoSaveRestore: {
|
|
2191
|
+
found: "Tìm thấy bản nháp. Khôi phục?",
|
|
2192
|
+
foundAt: "Bản nháp từ {date}. Khôi phục?",
|
|
2193
|
+
restore: "Khôi phục",
|
|
2194
|
+
discard: "Bỏ qua"
|
|
2023
2195
|
}
|
|
2024
2196
|
},
|
|
2025
2197
|
ja: {
|
|
@@ -2163,6 +2335,7 @@
|
|
|
2163
2335
|
findPlaceholder: "検索…",
|
|
2164
2336
|
searchAriaLabel: "検索テキスト",
|
|
2165
2337
|
caseSensitive: "\xA0大文字/小文字を区別",
|
|
2338
|
+
wholeWord: "単語単位",
|
|
2166
2339
|
prevBtn: "← 前へ",
|
|
2167
2340
|
nextBtn: "次へ →",
|
|
2168
2341
|
replacePlaceholder: "置換後…",
|
|
@@ -2330,7 +2503,16 @@
|
|
|
2330
2503
|
columnWidth: "列幅",
|
|
2331
2504
|
rowHeight: "行の高さ",
|
|
2332
2505
|
tableBorderWidth: "テーブルの枠幅",
|
|
2506
|
+
tableBorderColor: "テーブルの枠線色",
|
|
2333
2507
|
deleteTable: "テーブルを削除",
|
|
2508
|
+
cellAlignLeft: "左揃え",
|
|
2509
|
+
cellAlignCenter: "中央揃え",
|
|
2510
|
+
cellAlignRight: "右揃え",
|
|
2511
|
+
cellAlignJustify: "両端揃え",
|
|
2512
|
+
toggleHeaderRow: "ヘッダー行の切り替え",
|
|
2513
|
+
cellBackground: "セル背景色",
|
|
2514
|
+
noShading: "背景色なし",
|
|
2515
|
+
noBorderColor: "枠線色なし",
|
|
2334
2516
|
columnWidthPx: "列幅 (px)",
|
|
2335
2517
|
rowHeightPx: "行の高さ (px)",
|
|
2336
2518
|
tableBorderWidthPx: "テーブルの枠幅 (px)",
|
|
@@ -2358,6 +2540,12 @@
|
|
|
2358
2540
|
errors: {
|
|
2359
2541
|
imageFormat: (type) => `形式 "${type}" はブラウザでの表示をサポートしていません。JPEG、PNG、または WebP に変換してください。`,
|
|
2360
2542
|
imageSize: (maxSize) => `画像ファイルが大きすぎます。最大許容サイズは ${maxSize} MB です。`
|
|
2543
|
+
},
|
|
2544
|
+
autoSaveRestore: {
|
|
2545
|
+
found: "下書きが見つかりました。復元しますか?",
|
|
2546
|
+
foundAt: "{date} の下書きが見つかりました。復元しますか?",
|
|
2547
|
+
restore: "復元",
|
|
2548
|
+
discard: "破棄"
|
|
2361
2549
|
}
|
|
2362
2550
|
},
|
|
2363
2551
|
zh: {
|
|
@@ -2501,6 +2689,7 @@
|
|
|
2501
2689
|
findPlaceholder: "查找…",
|
|
2502
2690
|
searchAriaLabel: "搜索文字",
|
|
2503
2691
|
caseSensitive: "\xA0区分大小写",
|
|
2692
|
+
wholeWord: "全字匹配",
|
|
2504
2693
|
prevBtn: "← 上一个",
|
|
2505
2694
|
nextBtn: "下一个 →",
|
|
2506
2695
|
replacePlaceholder: "替换为…",
|
|
@@ -2668,7 +2857,16 @@
|
|
|
2668
2857
|
columnWidth: "列宽",
|
|
2669
2858
|
rowHeight: "行高",
|
|
2670
2859
|
tableBorderWidth: "表格边框宽度",
|
|
2860
|
+
tableBorderColor: "表格边框颜色",
|
|
2671
2861
|
deleteTable: "删除表格",
|
|
2862
|
+
cellAlignLeft: "左对齐",
|
|
2863
|
+
cellAlignCenter: "居中对齐",
|
|
2864
|
+
cellAlignRight: "右对齐",
|
|
2865
|
+
cellAlignJustify: "两端对齐",
|
|
2866
|
+
toggleHeaderRow: "切换标题行",
|
|
2867
|
+
cellBackground: "单元格背景",
|
|
2868
|
+
noShading: "无底纹",
|
|
2869
|
+
noBorderColor: "无边框颜色",
|
|
2672
2870
|
columnWidthPx: "列宽 (px)",
|
|
2673
2871
|
rowHeightPx: "行高 (px)",
|
|
2674
2872
|
tableBorderWidthPx: "表格边框宽度 (px)",
|
|
@@ -2696,6 +2894,12 @@
|
|
|
2696
2894
|
errors: {
|
|
2697
2895
|
imageFormat: (type) => `格式 "${type}" 不支持在浏览器中显示。请转换为 JPEG、PNG 或 WebP。`,
|
|
2698
2896
|
imageSize: (maxSize) => `图片文件过大。最大允许大小为 ${maxSize} MB。`
|
|
2897
|
+
},
|
|
2898
|
+
autoSaveRestore: {
|
|
2899
|
+
found: "找到草稿。是否恢复?",
|
|
2900
|
+
foundAt: "发现 {date} 的草稿。是否恢复?",
|
|
2901
|
+
restore: "恢复",
|
|
2902
|
+
discard: "放弃"
|
|
2699
2903
|
}
|
|
2700
2904
|
},
|
|
2701
2905
|
fr: {
|
|
@@ -2839,6 +3043,7 @@
|
|
|
2839
3043
|
findPlaceholder: "Rechercher…",
|
|
2840
3044
|
searchAriaLabel: "Texte à rechercher",
|
|
2841
3045
|
caseSensitive: "\xA0Respecter la casse",
|
|
3046
|
+
wholeWord: "Mot entier",
|
|
2842
3047
|
prevBtn: "← Préc.",
|
|
2843
3048
|
nextBtn: "Suiv. →",
|
|
2844
3049
|
replacePlaceholder: "Remplacer par…",
|
|
@@ -3006,7 +3211,16 @@
|
|
|
3006
3211
|
columnWidth: "Largeur de colonne",
|
|
3007
3212
|
rowHeight: "Hauteur de ligne",
|
|
3008
3213
|
tableBorderWidth: "Épaisseur des bordures",
|
|
3214
|
+
tableBorderColor: "Couleur de bordure",
|
|
3009
3215
|
deleteTable: "Supprimer le tableau",
|
|
3216
|
+
cellAlignLeft: "Aligner à gauche",
|
|
3217
|
+
cellAlignCenter: "Centrer",
|
|
3218
|
+
cellAlignRight: "Aligner à droite",
|
|
3219
|
+
cellAlignJustify: "Justifier",
|
|
3220
|
+
toggleHeaderRow: "Ligne d'en-tête",
|
|
3221
|
+
cellBackground: "Couleur de fond",
|
|
3222
|
+
noShading: "Aucun fond",
|
|
3223
|
+
noBorderColor: "Aucune bordure",
|
|
3010
3224
|
columnWidthPx: "Largeur de colonne (px)",
|
|
3011
3225
|
rowHeightPx: "Hauteur de ligne (px)",
|
|
3012
3226
|
tableBorderWidthPx: "Épaisseur des bordures (px)",
|
|
@@ -3034,6 +3248,12 @@
|
|
|
3034
3248
|
errors: {
|
|
3035
3249
|
imageFormat: (type) => `Le format "${type}" n'est pas pris en charge par le navigateur. Veuillez le convertir en JPEG, PNG ou WebP.`,
|
|
3036
3250
|
imageSize: (maxSize) => `Le fichier image est trop volumineux. La taille maximale autorisée est de ${maxSize}\u00a0Mo.`
|
|
3251
|
+
},
|
|
3252
|
+
autoSaveRestore: {
|
|
3253
|
+
found: "Brouillon trouvé. Restaurer ?",
|
|
3254
|
+
foundAt: "Brouillon du {date}. Restaurer ?",
|
|
3255
|
+
restore: "Restaurer",
|
|
3256
|
+
discard: "Ignorer"
|
|
3037
3257
|
}
|
|
3038
3258
|
},
|
|
3039
3259
|
de: {
|
|
@@ -3177,6 +3397,7 @@
|
|
|
3177
3397
|
findPlaceholder: "Suchen…",
|
|
3178
3398
|
searchAriaLabel: "Suchtext",
|
|
3179
3399
|
caseSensitive: "\xA0Groß-/Kleinschreibung",
|
|
3400
|
+
wholeWord: "Ganzes Wort",
|
|
3180
3401
|
prevBtn: "← Zurück",
|
|
3181
3402
|
nextBtn: "Weiter →",
|
|
3182
3403
|
replacePlaceholder: "Ersetzen durch…",
|
|
@@ -3344,7 +3565,16 @@
|
|
|
3344
3565
|
columnWidth: "Spaltenbreite",
|
|
3345
3566
|
rowHeight: "Zeilenhöhe",
|
|
3346
3567
|
tableBorderWidth: "Tabellenrahmenbreite",
|
|
3568
|
+
tableBorderColor: "Tabellenrahmenfarbe",
|
|
3347
3569
|
deleteTable: "Tabelle löschen",
|
|
3570
|
+
cellAlignLeft: "Linksbündig",
|
|
3571
|
+
cellAlignCenter: "Zentrieren",
|
|
3572
|
+
cellAlignRight: "Rechtsbündig",
|
|
3573
|
+
cellAlignJustify: "Blocksatz",
|
|
3574
|
+
toggleHeaderRow: "Kopfzeile umschalten",
|
|
3575
|
+
cellBackground: "Zellhintergrund",
|
|
3576
|
+
noShading: "Kein Hintergrund",
|
|
3577
|
+
noBorderColor: "Keine Rahmenfarbe",
|
|
3348
3578
|
columnWidthPx: "Spaltenbreite (px)",
|
|
3349
3579
|
rowHeightPx: "Zeilenhöhe (px)",
|
|
3350
3580
|
tableBorderWidthPx: "Tabellenrahmenbreite (px)",
|
|
@@ -3372,6 +3602,12 @@
|
|
|
3372
3602
|
errors: {
|
|
3373
3603
|
imageFormat: (type) => `Das Format „${type}" wird in Webbrowsern nicht unterstützt. Bitte konvertieren Sie es zuerst in JPEG, PNG oder WebP.`,
|
|
3374
3604
|
imageSize: (maxSize) => `Die Bilddatei ist zu groß. Die maximal zulässige Größe beträgt ${maxSize} MB.`
|
|
3605
|
+
},
|
|
3606
|
+
autoSaveRestore: {
|
|
3607
|
+
found: "Entwurf gefunden. Wiederherstellen?",
|
|
3608
|
+
foundAt: "Entwurf vom {date}. Wiederherstellen?",
|
|
3609
|
+
restore: "Wiederherstellen",
|
|
3610
|
+
discard: "Verwerfen"
|
|
3375
3611
|
}
|
|
3376
3612
|
},
|
|
3377
3613
|
es: {
|
|
@@ -3515,6 +3751,7 @@
|
|
|
3515
3751
|
findPlaceholder: "Buscar…",
|
|
3516
3752
|
searchAriaLabel: "Texto de búsqueda",
|
|
3517
3753
|
caseSensitive: "\xA0Distinguir mayúsculas",
|
|
3754
|
+
wholeWord: "Palabra completa",
|
|
3518
3755
|
prevBtn: "← Anterior",
|
|
3519
3756
|
nextBtn: "Siguiente →",
|
|
3520
3757
|
replacePlaceholder: "Reemplazar con…",
|
|
@@ -3682,7 +3919,16 @@
|
|
|
3682
3919
|
columnWidth: "Ancho de columna",
|
|
3683
3920
|
rowHeight: "Alto de fila",
|
|
3684
3921
|
tableBorderWidth: "Grosor del borde de la tabla",
|
|
3922
|
+
tableBorderColor: "Color del borde de la tabla",
|
|
3685
3923
|
deleteTable: "Eliminar tabla",
|
|
3924
|
+
cellAlignLeft: "Alinear a la izquierda",
|
|
3925
|
+
cellAlignCenter: "Centrar",
|
|
3926
|
+
cellAlignRight: "Alinear a la derecha",
|
|
3927
|
+
cellAlignJustify: "Justificar",
|
|
3928
|
+
toggleHeaderRow: "Activar fila de encabezado",
|
|
3929
|
+
cellBackground: "Fondo de celda",
|
|
3930
|
+
noShading: "Sin fondo",
|
|
3931
|
+
noBorderColor: "Sin color de borde",
|
|
3686
3932
|
columnWidthPx: "Ancho de columna (px)",
|
|
3687
3933
|
rowHeightPx: "Alto de fila (px)",
|
|
3688
3934
|
tableBorderWidthPx: "Grosor del borde (px)",
|
|
@@ -3710,6 +3956,12 @@
|
|
|
3710
3956
|
errors: {
|
|
3711
3957
|
imageFormat: (type) => `El formato "${type}" no es compatible con los navegadores web. Por favor, conviértalo primero a JPEG, PNG o WebP.`,
|
|
3712
3958
|
imageSize: (maxSize) => `El archivo de imagen es demasiado grande. El tamaño máximo permitido es ${maxSize} MB.`
|
|
3959
|
+
},
|
|
3960
|
+
autoSaveRestore: {
|
|
3961
|
+
found: "Borrador encontrado. ¿Restaurar?",
|
|
3962
|
+
foundAt: "Borrador del {date}. ¿Restaurar?",
|
|
3963
|
+
restore: "Restaurar",
|
|
3964
|
+
discard: "Descartar"
|
|
3713
3965
|
}
|
|
3714
3966
|
},
|
|
3715
3967
|
ko: {
|
|
@@ -3853,6 +4105,7 @@
|
|
|
3853
4105
|
findPlaceholder: "찾기…",
|
|
3854
4106
|
searchAriaLabel: "검색 텍스트",
|
|
3855
4107
|
caseSensitive: "\xA0대소문자 구분",
|
|
4108
|
+
wholeWord: "전체 단어",
|
|
3856
4109
|
prevBtn: "← 이전",
|
|
3857
4110
|
nextBtn: "다음 →",
|
|
3858
4111
|
replacePlaceholder: "바꿀 내용…",
|
|
@@ -4020,7 +4273,16 @@
|
|
|
4020
4273
|
columnWidth: "열 너비",
|
|
4021
4274
|
rowHeight: "행 높이",
|
|
4022
4275
|
tableBorderWidth: "표 테두리 너비",
|
|
4276
|
+
tableBorderColor: "표 테두리 색상",
|
|
4023
4277
|
deleteTable: "표 삭제",
|
|
4278
|
+
cellAlignLeft: "왼쪽 정렬",
|
|
4279
|
+
cellAlignCenter: "가운데 정렬",
|
|
4280
|
+
cellAlignRight: "오른쪽 정렬",
|
|
4281
|
+
cellAlignJustify: "양쪽 정렬",
|
|
4282
|
+
toggleHeaderRow: "머리글 행 전환",
|
|
4283
|
+
cellBackground: "셀 배경색",
|
|
4284
|
+
noShading: "배경 없음",
|
|
4285
|
+
noBorderColor: "테두리 색 없음",
|
|
4024
4286
|
columnWidthPx: "열 너비 (px)",
|
|
4025
4287
|
rowHeightPx: "행 높이 (px)",
|
|
4026
4288
|
tableBorderWidthPx: "표 테두리 너비 (px)",
|
|
@@ -4048,6 +4310,12 @@
|
|
|
4048
4310
|
errors: {
|
|
4049
4311
|
imageFormat: (type) => `"${type}" 형식은 웹 브라우저에서 지원되지 않습니다. JPEG, PNG 또는 WebP로 변환해 주세요.`,
|
|
4050
4312
|
imageSize: (maxSize) => `이미지 파일이 너무 큽니다. 최대 허용 크기는 ${maxSize} MB입니다.`
|
|
4313
|
+
},
|
|
4314
|
+
autoSaveRestore: {
|
|
4315
|
+
found: "초안을 찾았습니다. 복원하시겠습니까?",
|
|
4316
|
+
foundAt: "{date}의 초안을 찾았습니다. 복원하시겠습니까?",
|
|
4317
|
+
restore: "복원",
|
|
4318
|
+
discard: "삭제"
|
|
4051
4319
|
}
|
|
4052
4320
|
}
|
|
4053
4321
|
};
|
|
@@ -4532,7 +4800,11 @@
|
|
|
4532
4800
|
const sel = globalThis.getSelection();
|
|
4533
4801
|
if (!sel || sel.rangeCount === 0) return;
|
|
4534
4802
|
const range = sel.getRangeAt(0);
|
|
4535
|
-
|
|
4803
|
+
try {
|
|
4804
|
+
range.deleteContents();
|
|
4805
|
+
} catch (_) {
|
|
4806
|
+
return;
|
|
4807
|
+
}
|
|
4536
4808
|
const BLOCK = new Set([
|
|
4537
4809
|
"P",
|
|
4538
4810
|
"DIV",
|
|
@@ -4557,7 +4829,11 @@
|
|
|
4557
4829
|
table.after(p);
|
|
4558
4830
|
}
|
|
4559
4831
|
if (!anchor.textContent.trim() && !anchor.querySelector("img, video, table")) anchor.remove();
|
|
4560
|
-
} else
|
|
4832
|
+
} else try {
|
|
4833
|
+
range.insertNode(table);
|
|
4834
|
+
} catch (_) {
|
|
4835
|
+
return;
|
|
4836
|
+
}
|
|
4561
4837
|
const firstCell = table.querySelector("td, th");
|
|
4562
4838
|
if (firstCell) {
|
|
4563
4839
|
const nr = document.createRange();
|
|
@@ -4640,6 +4916,23 @@
|
|
|
4640
4916
|
var isFAIcon = (n) => !!(n?.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
|
|
4641
4917
|
var isZwsAnchor = (n) => !!(n?.nodeType === Node.TEXT_NODE && (n.textContent === "" || n.textContent === ""));
|
|
4642
4918
|
/**
|
|
4919
|
+
* Extracts the content from `startContainer:startOffset` to the end of `li`.
|
|
4920
|
+
* Returns an empty fragment if the range is invalid (e.g. detached node).
|
|
4921
|
+
* @param {Range} nativeRange
|
|
4922
|
+
* @param {Element} li
|
|
4923
|
+
* @returns {DocumentFragment}
|
|
4924
|
+
*/
|
|
4925
|
+
function extractAfterContent(nativeRange, li) {
|
|
4926
|
+
try {
|
|
4927
|
+
const r = document.createRange();
|
|
4928
|
+
r.setStart(nativeRange.startContainer, nativeRange.startOffset);
|
|
4929
|
+
r.setEnd(li, li.childNodes.length);
|
|
4930
|
+
return r.extractContents();
|
|
4931
|
+
} catch (_) {
|
|
4932
|
+
return document.createDocumentFragment();
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
/**
|
|
4643
4936
|
* Handles special keydown behaviour inside the editor.
|
|
4644
4937
|
* @param {KeyboardEvent} event
|
|
4645
4938
|
* @param {HTMLElement} editable
|
|
@@ -4847,12 +5140,10 @@
|
|
|
4847
5140
|
}
|
|
4848
5141
|
if (!nativeRange.collapsed) {
|
|
4849
5142
|
nativeRange.deleteContents();
|
|
5143
|
+
if (sel.rangeCount === 0 || !checkLi.isConnected) return true;
|
|
4850
5144
|
nativeRange = sel.getRangeAt(0);
|
|
4851
5145
|
}
|
|
4852
|
-
const
|
|
4853
|
-
afterRange.setStart(nativeRange.startContainer, nativeRange.startOffset);
|
|
4854
|
-
afterRange.setEnd(checkLi, checkLi.childNodes.length);
|
|
4855
|
-
const afterFrag = afterRange.extractContents();
|
|
5146
|
+
const afterFrag = extractAfterContent(nativeRange, checkLi);
|
|
4856
5147
|
const newLi = document.createElement("li");
|
|
4857
5148
|
const cb = document.createElement("input");
|
|
4858
5149
|
cb.type = "checkbox";
|
|
@@ -4968,7 +5259,15 @@
|
|
|
4968
5259
|
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
4969
5260
|
if (!items.length) return inner();
|
|
4970
5261
|
const indent = " ".repeat(depth);
|
|
4971
|
-
const
|
|
5262
|
+
const isChecklist = el.classList.contains("an-checklist");
|
|
5263
|
+
const lines = items.map((li) => {
|
|
5264
|
+
let prefix = "- ";
|
|
5265
|
+
if (isChecklist) {
|
|
5266
|
+
const cb = li.querySelector("input[type=\"checkbox\"]");
|
|
5267
|
+
prefix = (cb ? cb.checked : false) ? "- [x] " : "- [ ] ";
|
|
5268
|
+
}
|
|
5269
|
+
return `${indent}${prefix}${_domToMd(li, depth + 1).trim()}`;
|
|
5270
|
+
}).join("\n");
|
|
4972
5271
|
return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
|
|
4973
5272
|
}
|
|
4974
5273
|
case "ol": {
|
|
@@ -5008,7 +5307,7 @@
|
|
|
5008
5307
|
* @returns {boolean} `true` if any Markdown-like pattern is present, `false` otherwise.
|
|
5009
5308
|
*/
|
|
5010
5309
|
function isMarkdown(text) {
|
|
5011
|
-
return /^#{1,6} \
|
|
5310
|
+
return /^#{1,6} [^\s]|^[ \t]*[-*+] [^\s]|^[ \t]*\d+\. [^\s]|^> [^\s]|^```|^\*{2}[^*\n]+\*{2}/m.test(text);
|
|
5012
5311
|
}
|
|
5013
5312
|
/**
|
|
5014
5313
|
* Converts a Markdown string to an HTML string.
|
|
@@ -5058,11 +5357,20 @@
|
|
|
5058
5357
|
}
|
|
5059
5358
|
if (/^[-*+] /.test(line)) {
|
|
5060
5359
|
const items = [];
|
|
5360
|
+
const isChecklist = /^[-*+]\s+\[[ xX]\]\s+/.test(line);
|
|
5361
|
+
const listTag = isChecklist ? "ul class=\"an-checklist\"" : "ul";
|
|
5061
5362
|
while (i < lines.length && /^[-*+] /.test(lines[i])) {
|
|
5062
|
-
|
|
5363
|
+
if (/^[-*+]\s+\[[ xX]\]\s+/.test(lines[i]) !== isChecklist) break;
|
|
5364
|
+
const content = lines[i].slice(2);
|
|
5365
|
+
if (isChecklist) {
|
|
5366
|
+
const cbMatch = /^\[([ xX])\][ \t]+/.exec(content);
|
|
5367
|
+
const cbHtml = `<input type="checkbox" contenteditable="false"${cbMatch?.[1]?.toLowerCase() === "x" ? " checked" : ""}>`;
|
|
5368
|
+
const textContent = cbMatch ? content.slice(cbMatch[0].length) : content;
|
|
5369
|
+
items.push(`<li>${cbHtml}${_inline(textContent)}</li>`);
|
|
5370
|
+
} else items.push(`<li>${_inline(content)}</li>`);
|
|
5063
5371
|
i++;
|
|
5064
5372
|
}
|
|
5065
|
-
out.push(
|
|
5373
|
+
out.push(`<${listTag}>${items.join("")}</${listTag.split(" ")[0]}>`);
|
|
5066
5374
|
continue;
|
|
5067
5375
|
}
|
|
5068
5376
|
if (/^\d+\. /.test(line)) {
|
|
@@ -5119,7 +5427,7 @@
|
|
|
5119
5427
|
text = text.replace(/_{2}([^_\n]+?)_{2}/g, (_, c) => `<strong>${_esc(c)}</strong>`);
|
|
5120
5428
|
text = text.replace(/\*([^*\n]+?)\*/g, (_, c) => `<em>${_esc(c)}</em>`);
|
|
5121
5429
|
text = text.replace(/_([^_\n]+?)_/g, (_, c) => `<em>${_esc(c)}</em>`);
|
|
5122
|
-
text = text.replace(/~~([
|
|
5430
|
+
text = text.replace(/~~([^~\n]+?)~~/g, (_, c) => `<del>${_esc(c)}</del>`);
|
|
5123
5431
|
text = text.replace(/`([^`]+)`/g, (_, c) => `<code>${_esc(c)}</code>`);
|
|
5124
5432
|
return text;
|
|
5125
5433
|
}
|
|
@@ -6673,6 +6981,15 @@
|
|
|
6673
6981
|
if (!clipboardData) return;
|
|
6674
6982
|
const forcePlain = this._forcePlain;
|
|
6675
6983
|
this._forcePlain = false;
|
|
6984
|
+
const maxBytes = (this.options.maxPasteSize ?? 5) * 1024 * 1024;
|
|
6985
|
+
if (maxBytes > 0) {
|
|
6986
|
+
const text = clipboardData.getData("text/plain") || "";
|
|
6987
|
+
const html = clipboardData.getData("text/html") || "";
|
|
6988
|
+
if (Math.max(text.length, html.length) > maxBytes) {
|
|
6989
|
+
event.preventDefault();
|
|
6990
|
+
return;
|
|
6991
|
+
}
|
|
6992
|
+
}
|
|
6676
6993
|
if (clipboardData.items) {
|
|
6677
6994
|
const imageItems = Array.from(clipboardData.items).filter((item) => item.kind === "file" && item.type.startsWith("image/"));
|
|
6678
6995
|
if (imageItems.length > 0) {
|
|
@@ -6809,7 +7126,7 @@
|
|
|
6809
7126
|
const mime = /:(.*?);/.exec(header)?.[1] ?? "image/png";
|
|
6810
7127
|
const binary = atob(b64);
|
|
6811
7128
|
const arr = new Uint8Array(binary.length);
|
|
6812
|
-
for (let i = 0; i < binary.length; i++) arr[i] = binary.
|
|
7129
|
+
for (let i = 0; i < binary.length; i++) arr[i] = binary.charCodeAt(i);
|
|
6813
7130
|
return new Blob([arr], { type: mime });
|
|
6814
7131
|
}
|
|
6815
7132
|
/**
|
|
@@ -7415,6 +7732,7 @@
|
|
|
7415
7732
|
return overlay;
|
|
7416
7733
|
}
|
|
7417
7734
|
_onFileChange() {
|
|
7735
|
+
if (this.context._alive === false) return;
|
|
7418
7736
|
const file = this._fileInput?.files?.[0];
|
|
7419
7737
|
if (!file?.type?.startsWith("image/")) return;
|
|
7420
7738
|
if (!new Set([
|
|
@@ -7795,6 +8113,7 @@
|
|
|
7795
8113
|
const aspectRatio = startW / (startH || 1);
|
|
7796
8114
|
const isCorner = pos.length === 2;
|
|
7797
8115
|
const editable = this.context.layoutInfo.editable;
|
|
8116
|
+
const minSz = this.context.options?.minImageSize ?? 20;
|
|
7798
8117
|
let _raf = null;
|
|
7799
8118
|
const onMove = (me) => {
|
|
7800
8119
|
if (_raf !== null) return;
|
|
@@ -7807,15 +8126,15 @@
|
|
|
7807
8126
|
const maxW = editable.clientWidth || Infinity;
|
|
7808
8127
|
let newW = startW;
|
|
7809
8128
|
let newH = startH;
|
|
7810
|
-
if (pos.includes("e")) newW = Math.max(
|
|
7811
|
-
if (pos.includes("w")) newW = Math.max(
|
|
7812
|
-
if (pos.includes("s")) newH = Math.max(
|
|
7813
|
-
if (pos.includes("n")) newH = Math.max(
|
|
8129
|
+
if (pos.includes("e")) newW = Math.max(minSz, startW + dx);
|
|
8130
|
+
if (pos.includes("w")) newW = Math.max(minSz, startW - dx);
|
|
8131
|
+
if (pos.includes("s")) newH = Math.max(minSz, startH + dy);
|
|
8132
|
+
if (pos.includes("n")) newH = Math.max(minSz, startH - dy);
|
|
7814
8133
|
newW = Math.min(newW, maxW);
|
|
7815
|
-
if (isCorner) if (Math.abs(dx) >= Math.abs(dy)) newH = Math.max(
|
|
8134
|
+
if (isCorner) if (Math.abs(dx) >= Math.abs(dy)) newH = Math.max(minSz, Math.round(newW / aspectRatio));
|
|
7816
8135
|
else {
|
|
7817
|
-
newW = Math.min(Math.max(
|
|
7818
|
-
newH = Math.max(
|
|
8136
|
+
newW = Math.min(Math.max(minSz, Math.round(newH * aspectRatio)), maxW);
|
|
8137
|
+
newH = Math.max(minSz, Math.round(newW / aspectRatio));
|
|
7819
8138
|
}
|
|
7820
8139
|
img.style.width = `${newW}px`;
|
|
7821
8140
|
img.style.height = `${newH}px`;
|
|
@@ -9979,8 +10298,28 @@
|
|
|
9979
10298
|
return direction === "asc" ? aText.localeCompare(bText) : bText.localeCompare(aText);
|
|
9980
10299
|
});
|
|
9981
10300
|
rows.forEach((row) => tbody.appendChild(row));
|
|
10301
|
+
this._markSortIndicator(table, colIdx, direction);
|
|
9982
10302
|
this.context.invoke("editor.afterCommand");
|
|
9983
10303
|
}
|
|
10304
|
+
/**
|
|
10305
|
+
* Marks the header cell of the sorted column with `an-sort-asc`/`an-sort-desc`
|
|
10306
|
+
* so the active sort column and direction are visible, and clears any previous
|
|
10307
|
+
* indicator. No-op for tables without a `<thead>` (no header row to mark).
|
|
10308
|
+
* @param {HTMLTableElement} table
|
|
10309
|
+
* @param {number} colIdx
|
|
10310
|
+
* @param {'asc'|'desc'} direction
|
|
10311
|
+
*/
|
|
10312
|
+
_markSortIndicator(table, colIdx, direction) {
|
|
10313
|
+
const thead = table.querySelector("thead");
|
|
10314
|
+
if (!thead) return;
|
|
10315
|
+
thead.querySelectorAll(".an-sort-asc, .an-sort-desc").forEach((el) => {
|
|
10316
|
+
el.classList.remove("an-sort-asc", "an-sort-desc");
|
|
10317
|
+
});
|
|
10318
|
+
const headerRow = thead.querySelector("tr");
|
|
10319
|
+
if (!headerRow) return;
|
|
10320
|
+
const headerCell = getCellAtVisualCol(headerRow, colIdx);
|
|
10321
|
+
if (headerCell) headerCell.classList.add(direction === "asc" ? "an-sort-asc" : "an-sort-desc");
|
|
10322
|
+
}
|
|
9984
10323
|
_exportTableCSV() {
|
|
9985
10324
|
const table = this._activeTable;
|
|
9986
10325
|
if (!table) return;
|
|
@@ -14258,6 +14597,7 @@
|
|
|
14258
14597
|
this._currentIndex = -1;
|
|
14259
14598
|
this._caseSensitive = false;
|
|
14260
14599
|
this._useRegex = false;
|
|
14600
|
+
this._wholeWord = false;
|
|
14261
14601
|
/** @type {'find'|'replace'} */
|
|
14262
14602
|
this._mode = "find";
|
|
14263
14603
|
/** Cached compiled regex — reused when query and case-sensitivity are unchanged */
|
|
@@ -14265,6 +14605,7 @@
|
|
|
14265
14605
|
this._lastQuery = null;
|
|
14266
14606
|
this._lastCaseSensitive = null;
|
|
14267
14607
|
this._lastUseRegex = null;
|
|
14608
|
+
this._lastWholeWord = null;
|
|
14268
14609
|
this._focusTimer = null;
|
|
14269
14610
|
}
|
|
14270
14611
|
destroy() {
|
|
@@ -14375,6 +14716,13 @@
|
|
|
14375
14716
|
"aria-label": L.useRegex
|
|
14376
14717
|
});
|
|
14377
14718
|
regexBtn.textContent = ".*";
|
|
14719
|
+
const wholeWordBtn = createElement("button", {
|
|
14720
|
+
type: "button",
|
|
14721
|
+
class: "an-fr-icon-btn",
|
|
14722
|
+
title: L.wholeWord,
|
|
14723
|
+
"aria-label": L.wholeWord
|
|
14724
|
+
});
|
|
14725
|
+
wholeWordBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="7" width="18" height="10" rx="2"/><line x1="7" y1="21" x2="7" y2="17"/><line x1="17" y1="21" x2="17" y2="17"/></svg>`;
|
|
14378
14726
|
const prevBtn = createElement("button", {
|
|
14379
14727
|
type: "button",
|
|
14380
14728
|
class: "an-fr-icon-btn",
|
|
@@ -14391,7 +14739,7 @@
|
|
|
14391
14739
|
nextBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 12 15 18 9"/></svg>`;
|
|
14392
14740
|
const counter = createElement("span", { class: "an-fr-counter" });
|
|
14393
14741
|
this._counterEl = counter;
|
|
14394
|
-
searchBar.append(findInput, caseCheckbox, caseBtn, regexBtn, prevBtn, nextBtn, counter);
|
|
14742
|
+
searchBar.append(findInput, caseCheckbox, caseBtn, regexBtn, wholeWordBtn, prevBtn, nextBtn, counter);
|
|
14395
14743
|
box.appendChild(searchBar);
|
|
14396
14744
|
const replaceRow = createElement("div", { class: "an-fr-replace-row" });
|
|
14397
14745
|
replaceRow.style.display = "none";
|
|
@@ -14459,7 +14807,14 @@
|
|
|
14459
14807
|
this._lastQuery = null;
|
|
14460
14808
|
this._onSearch();
|
|
14461
14809
|
});
|
|
14462
|
-
|
|
14810
|
+
const dWholeWord = on(wholeWordBtn, "click", () => {
|
|
14811
|
+
this._wholeWord = !this._wholeWord;
|
|
14812
|
+
wholeWordBtn.classList.toggle("an-fr-icon-btn--active", this._wholeWord);
|
|
14813
|
+
this._queryRegex = null;
|
|
14814
|
+
this._lastQuery = null;
|
|
14815
|
+
this._onSearch();
|
|
14816
|
+
});
|
|
14817
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, dRegex, dWholeWord);
|
|
14463
14818
|
return overlay;
|
|
14464
14819
|
}
|
|
14465
14820
|
_onSearch() {
|
|
@@ -14517,10 +14872,11 @@
|
|
|
14517
14872
|
*/
|
|
14518
14873
|
_findRawMatches(query, root) {
|
|
14519
14874
|
const results = [];
|
|
14520
|
-
if (this._lastQuery !== query || this._lastCaseSensitive !== this._caseSensitive || this._lastUseRegex !== this._useRegex) {
|
|
14875
|
+
if (this._lastQuery !== query || this._lastCaseSensitive !== this._caseSensitive || this._lastUseRegex !== this._useRegex || this._lastWholeWord !== this._wholeWord) {
|
|
14521
14876
|
const flags = this._caseSensitive ? "g" : "gi";
|
|
14522
14877
|
try {
|
|
14523
|
-
|
|
14878
|
+
let pattern = this._useRegex ? query : query.replace(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
|
|
14879
|
+
if (this._wholeWord) pattern = `\\b${pattern}\\b`;
|
|
14524
14880
|
this._queryRegex = new RegExp(pattern, flags);
|
|
14525
14881
|
} catch (_) {
|
|
14526
14882
|
this._queryRegex = null;
|
|
@@ -14528,6 +14884,7 @@
|
|
|
14528
14884
|
this._lastQuery = query;
|
|
14529
14885
|
this._lastCaseSensitive = this._caseSensitive;
|
|
14530
14886
|
this._lastUseRegex = this._useRegex;
|
|
14887
|
+
this._lastWholeWord = this._wholeWord;
|
|
14531
14888
|
}
|
|
14532
14889
|
if (!this._queryRegex) return results;
|
|
14533
14890
|
const re = this._queryRegex;
|
|
@@ -15881,6 +16238,7 @@
|
|
|
15881
16238
|
* debounce: 200,
|
|
15882
16239
|
* onSearch: (query, callback) => void,
|
|
15883
16240
|
* onInsert: (item) => string | null,
|
|
16241
|
+
* onError: (err: Error) => void,
|
|
15884
16242
|
* mentionClass: 'an-mention',
|
|
15885
16243
|
* allowSpaces: false,
|
|
15886
16244
|
* }
|
|
@@ -16073,8 +16431,18 @@
|
|
|
16073
16431
|
this._renderItems(items);
|
|
16074
16432
|
this._showDropdown();
|
|
16075
16433
|
};
|
|
16076
|
-
|
|
16077
|
-
|
|
16434
|
+
let result;
|
|
16435
|
+
try {
|
|
16436
|
+
result = this._cfg.onSearch(this._query, cb);
|
|
16437
|
+
} catch (err) {
|
|
16438
|
+
this._hideDropdown();
|
|
16439
|
+
if (typeof this._cfg.onError === "function") this._cfg.onError(err);
|
|
16440
|
+
return;
|
|
16441
|
+
}
|
|
16442
|
+
if (result && typeof result.then === "function") result.then(cb).catch((err) => {
|
|
16443
|
+
this._hideDropdown();
|
|
16444
|
+
if (typeof this._cfg.onError === "function") this._cfg.onError(err);
|
|
16445
|
+
});
|
|
16078
16446
|
}, this._cfg.debounce);
|
|
16079
16447
|
}
|
|
16080
16448
|
_onKeydown(e) {
|
|
@@ -16342,10 +16710,13 @@
|
|
|
16342
16710
|
}
|
|
16343
16711
|
/**
|
|
16344
16712
|
* Returns the current HTML content of the editor.
|
|
16713
|
+
* Zero-width spaces (U+200B) inserted by inline editing helpers are stripped
|
|
16714
|
+
* from the output so they don't leak into the consumer's HTML.
|
|
16345
16715
|
* @returns {string}
|
|
16346
16716
|
*/
|
|
16347
16717
|
getHTML() {
|
|
16348
|
-
|
|
16718
|
+
const html = this.invoke("editor.getHTML");
|
|
16719
|
+
return typeof html === "string" ? html.replace(//g, "") : html;
|
|
16349
16720
|
}
|
|
16350
16721
|
/**
|
|
16351
16722
|
* Sets the HTML content of the editor.
|
|
@@ -16516,6 +16887,25 @@
|
|
|
16516
16887
|
}));
|
|
16517
16888
|
}
|
|
16518
16889
|
/**
|
|
16890
|
+
* Moves focus into the editable area.
|
|
16891
|
+
*/
|
|
16892
|
+
focus() {
|
|
16893
|
+
this.layoutInfo.editable.focus();
|
|
16894
|
+
}
|
|
16895
|
+
/**
|
|
16896
|
+
* Removes focus from the editable area.
|
|
16897
|
+
*/
|
|
16898
|
+
blur() {
|
|
16899
|
+
this.layoutInfo.editable.blur();
|
|
16900
|
+
}
|
|
16901
|
+
/**
|
|
16902
|
+
* Returns true when the editor is currently in fullscreen mode.
|
|
16903
|
+
* @returns {boolean}
|
|
16904
|
+
*/
|
|
16905
|
+
isFullscreen() {
|
|
16906
|
+
return this.invoke("fullscreen.isActive") === true;
|
|
16907
|
+
}
|
|
16908
|
+
/**
|
|
16519
16909
|
* Sets whether the editor is disabled (readonly).
|
|
16520
16910
|
* @param {boolean} disabled
|
|
16521
16911
|
*/
|
|
@@ -16685,8 +17075,10 @@
|
|
|
16685
17075
|
registerButton(btnDef);
|
|
16686
17076
|
return this;
|
|
16687
17077
|
},
|
|
17078
|
+
/** All pre-built button definitions — accessible in every module format including UMD/CJS. */
|
|
17079
|
+
buttons,
|
|
16688
17080
|
/** Library version */
|
|
16689
|
-
version: "1.
|
|
17081
|
+
version: "1.8.0"
|
|
16690
17082
|
};
|
|
16691
17083
|
/**
|
|
16692
17084
|
* @param {string|Element|NodeList|Element[]} selector
|