autumnnote 1.0.6 → 1.0.8
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 +3 -3
- package/dist/autumnnote.css +168 -4
- package/dist/autumnnote.es.js +886 -115
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +886 -115
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +3 -2
- package/src/js/Context.js +6 -0
- package/src/js/core/sanitise.js +20 -2
- package/src/js/editing/Style.js +288 -24
- package/src/js/editing/Typing.js +2 -2
- package/src/js/module/Buttons.js +5 -4
- package/src/js/module/Clipboard.js +8 -0
- package/src/js/module/CodeTooltip.js +1 -0
- package/src/js/module/ContextMenu.js +146 -8
- package/src/js/module/Editor.js +73 -1
- package/src/js/module/EmojiDialog.js +11 -0
- package/src/js/module/IconDialog.js +11 -0
- package/src/js/module/ImageCropOverlay.js +2 -2
- package/src/js/module/ImageDialog.js +22 -3
- package/src/js/module/ImageResizer.js +2 -0
- package/src/js/module/ImageTooltip.js +9 -0
- package/src/js/module/LinkTooltip.js +4 -0
- package/src/js/module/Placeholder.js +7 -1
- package/src/js/module/TableTooltip.js +399 -86
- package/src/js/module/Toolbar.js +21 -3
- package/src/js/module/VideoDialog.js +5 -5
- package/src/js/module/VideoResizer.js +11 -0
- package/src/js/module/VideoTooltip.js +1 -0
- package/src/js/renderer.js +3 -0
- package/src/styles/autumnnote.scss +194 -12
package/dist/autumnnote.es.js
CHANGED
|
@@ -566,8 +566,24 @@ function underline() {
|
|
|
566
566
|
}
|
|
567
567
|
/**
|
|
568
568
|
* Strikethrough / removes strikethrough.
|
|
569
|
+
* Falls back to manual DOM manipulation inside nested formats where
|
|
570
|
+
* execCommand's state detection is unreliable (mirrors underline() logic).
|
|
569
571
|
*/
|
|
570
|
-
|
|
572
|
+
function strikethrough() {
|
|
573
|
+
const sel = window.getSelection();
|
|
574
|
+
if (!sel || !sel.rangeCount) return;
|
|
575
|
+
let sc = sel.getRangeAt(0).startContainer;
|
|
576
|
+
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
577
|
+
const sEl = sc && sc.closest && (sc.closest("s") || sc.closest("strike"));
|
|
578
|
+
const nativeState = document.queryCommandState("strikeThrough");
|
|
579
|
+
if (sEl && !nativeState) {
|
|
580
|
+
const parent = sEl.parentNode;
|
|
581
|
+
while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
|
|
582
|
+
parent.removeChild(sEl);
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
execCommand("strikeThrough");
|
|
586
|
+
}
|
|
571
587
|
/**
|
|
572
588
|
* Superscript toggle.
|
|
573
589
|
*/
|
|
@@ -598,14 +614,48 @@ var fontName = (name) => execCommand("fontName", name);
|
|
|
598
614
|
* @param {HTMLElement} [editable] - scoping element to avoid touching nodes outside this editor
|
|
599
615
|
*/
|
|
600
616
|
function fontSize(size, editable = document) {
|
|
617
|
+
const sel = window.getSelection();
|
|
618
|
+
const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
|
|
619
|
+
if (wasCollapsed && sel && sel.rangeCount > 0) {
|
|
620
|
+
try {
|
|
621
|
+
const range = sel.getRangeAt(0);
|
|
622
|
+
const span = document.createElement("span");
|
|
623
|
+
span.style.fontSize = size;
|
|
624
|
+
const zwsNode = document.createTextNode("");
|
|
625
|
+
span.appendChild(zwsNode);
|
|
626
|
+
range.insertNode(span);
|
|
627
|
+
const nr = document.createRange();
|
|
628
|
+
nr.setStart(zwsNode, zwsNode.textContent.length);
|
|
629
|
+
nr.collapse(true);
|
|
630
|
+
sel.removeAllRanges();
|
|
631
|
+
sel.addRange(nr);
|
|
632
|
+
} catch (_) {}
|
|
633
|
+
return;
|
|
634
|
+
}
|
|
601
635
|
execCommand("fontSize", "7");
|
|
602
|
-
editable
|
|
636
|
+
const scope = editable instanceof HTMLElement ? editable : document;
|
|
637
|
+
const newSpans = [];
|
|
638
|
+
scope.querySelectorAll("font[size=\"7\"]").forEach((el) => {
|
|
603
639
|
const span = document.createElement("span");
|
|
604
640
|
span.style.fontSize = size;
|
|
605
641
|
el.parentNode.insertBefore(span, el);
|
|
606
642
|
while (el.firstChild) span.appendChild(el.firstChild);
|
|
607
643
|
el.parentNode.removeChild(el);
|
|
644
|
+
newSpans.push(span);
|
|
608
645
|
});
|
|
646
|
+
if (!wasCollapsed && sel && newSpans.length > 0) {
|
|
647
|
+
const first = newSpans[0];
|
|
648
|
+
const last = newSpans[newSpans.length - 1];
|
|
649
|
+
try {
|
|
650
|
+
const nr = document.createRange();
|
|
651
|
+
const startNode = first.firstChild || first;
|
|
652
|
+
const endNode = last.lastChild || last;
|
|
653
|
+
nr.setStart(startNode, 0);
|
|
654
|
+
nr.setEnd(endNode, endNode.nodeType === Node.TEXT_NODE ? endNode.textContent.length : endNode.childNodes.length);
|
|
655
|
+
sel.removeAllRanges();
|
|
656
|
+
sel.addRange(nr);
|
|
657
|
+
} catch (_) {}
|
|
658
|
+
}
|
|
609
659
|
}
|
|
610
660
|
/**
|
|
611
661
|
* Wraps the selection in the given block tag (p, h1-h6, blockquote, pre).
|
|
@@ -634,8 +684,57 @@ var justifyFull = () => execCommand("justifyFull");
|
|
|
634
684
|
var indent = () => execCommand("indent");
|
|
635
685
|
/**
|
|
636
686
|
* Outdents the list or block.
|
|
687
|
+
* G.5: When cursor is inside a checklist item, "outdent" means converting
|
|
688
|
+
* that item back to a regular <p> element rather than calling execCommand
|
|
689
|
+
* (which would destroy the ul > li checklist structure).
|
|
690
|
+
*/
|
|
691
|
+
function outdent() {
|
|
692
|
+
const sel = window.getSelection();
|
|
693
|
+
if (sel && sel.rangeCount) {
|
|
694
|
+
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
695
|
+
if (container.nodeType === 3) container = container.parentElement;
|
|
696
|
+
const checkLi = container && container.closest && container.closest(".an-checklist li");
|
|
697
|
+
if (checkLi) {
|
|
698
|
+
_checklistItemToP(checkLi);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
execCommand("outdent");
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* G.5 helper: splits a checklist at checkLi, converts it to a <p>,
|
|
706
|
+
* and keeps items before/after as separate checklists.
|
|
707
|
+
* @param {HTMLElement} checkLi
|
|
637
708
|
*/
|
|
638
|
-
|
|
709
|
+
function _checklistItemToP(checkLi) {
|
|
710
|
+
const checkUl = checkLi.closest(".an-checklist");
|
|
711
|
+
if (!checkUl) return;
|
|
712
|
+
const allLis = Array.from(checkUl.children);
|
|
713
|
+
const liIndex = allLis.indexOf(checkLi);
|
|
714
|
+
const afterLis = allLis.slice(liIndex + 1);
|
|
715
|
+
const p = document.createElement("p");
|
|
716
|
+
p.textContent = Array.from(checkLi.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/\u200B/g, "").trim() || "\xA0";
|
|
717
|
+
if (afterLis.length > 0) {
|
|
718
|
+
const newUl = document.createElement("ul");
|
|
719
|
+
newUl.className = "an-checklist";
|
|
720
|
+
afterLis.forEach((li) => newUl.appendChild(li));
|
|
721
|
+
checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
|
|
722
|
+
}
|
|
723
|
+
checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
|
|
724
|
+
checkUl.removeChild(checkLi);
|
|
725
|
+
if (checkUl.children.length === 0) checkUl.parentNode.removeChild(checkUl);
|
|
726
|
+
try {
|
|
727
|
+
const nr = document.createRange();
|
|
728
|
+
const firstChild = p.firstChild;
|
|
729
|
+
nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
|
|
730
|
+
nr.collapse(true);
|
|
731
|
+
const s = window.getSelection();
|
|
732
|
+
if (s) {
|
|
733
|
+
s.removeAllRanges();
|
|
734
|
+
s.addRange(nr);
|
|
735
|
+
}
|
|
736
|
+
} catch {}
|
|
737
|
+
}
|
|
639
738
|
/**
|
|
640
739
|
* Inserts an unordered list or converts selection.
|
|
641
740
|
*/
|
|
@@ -710,9 +809,24 @@ function toggleInlineCode(editable) {
|
|
|
710
809
|
const codeEl = container && container.closest ? container.closest("code") : null;
|
|
711
810
|
if (codeEl && !codeEl.closest("pre")) {
|
|
712
811
|
const parent = codeEl.parentNode;
|
|
812
|
+
const prevSibling = codeEl.previousSibling;
|
|
813
|
+
const movedChildren = Array.from(codeEl.childNodes);
|
|
713
814
|
while (codeEl.firstChild) parent.insertBefore(codeEl.firstChild, codeEl);
|
|
714
815
|
parent.removeChild(codeEl);
|
|
715
|
-
if (
|
|
816
|
+
if (parent && parent.normalize) parent.normalize();
|
|
817
|
+
if (movedChildren.length > 0) try {
|
|
818
|
+
const firstMoved = movedChildren[0];
|
|
819
|
+
const lastMoved = movedChildren[movedChildren.length - 1];
|
|
820
|
+
const nr = document.createRange();
|
|
821
|
+
const anchorNode = firstMoved.parentNode === parent ? firstMoved : prevSibling ? prevSibling.nextSibling : parent.firstChild;
|
|
822
|
+
if (anchorNode) {
|
|
823
|
+
nr.setStart(anchorNode, 0);
|
|
824
|
+
const endAnchor = lastMoved.parentNode === parent ? lastMoved : anchorNode;
|
|
825
|
+
nr.setEnd(endAnchor, endAnchor.nodeType === Node.TEXT_NODE ? endAnchor.textContent.length : endAnchor.childNodes.length);
|
|
826
|
+
sel.removeAllRanges();
|
|
827
|
+
sel.addRange(nr);
|
|
828
|
+
}
|
|
829
|
+
} catch (_) {}
|
|
716
830
|
} else {
|
|
717
831
|
if (range.collapsed) return;
|
|
718
832
|
try {
|
|
@@ -737,14 +851,17 @@ function toggleInlineCode(editable) {
|
|
|
737
851
|
/**
|
|
738
852
|
* Returns true when the cursor / selection is inside an inline <code>
|
|
739
853
|
* (not nested in a <pre>).
|
|
854
|
+
* Uses startContainer for reliable cross-browser detection regardless of
|
|
855
|
+
* whether the selection is collapsed or a range (commonAncestorContainer
|
|
856
|
+
* can behave inconsistently for range selections on some browsers).
|
|
740
857
|
* @returns {boolean}
|
|
741
858
|
*/
|
|
742
859
|
function isInlineCode() {
|
|
743
860
|
const sel = window.getSelection();
|
|
744
861
|
if (!sel || !sel.rangeCount) return false;
|
|
745
|
-
let
|
|
746
|
-
if (
|
|
747
|
-
const code =
|
|
862
|
+
let sc = sel.getRangeAt(0).startContainer;
|
|
863
|
+
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
864
|
+
const code = sc && sc.closest ? sc.closest("code") : null;
|
|
748
865
|
return !!(code && !code.closest("pre"));
|
|
749
866
|
}
|
|
750
867
|
/**
|
|
@@ -755,7 +872,8 @@ function isInlineCode() {
|
|
|
755
872
|
function toggleChecklist() {
|
|
756
873
|
const sel = window.getSelection();
|
|
757
874
|
if (!sel || !sel.rangeCount) return;
|
|
758
|
-
|
|
875
|
+
const range = sel.getRangeAt(0);
|
|
876
|
+
let container = range.commonAncestorContainer;
|
|
759
877
|
if (container.nodeType === 3) container = container.parentElement;
|
|
760
878
|
const ul = container.closest && container.closest(".an-checklist");
|
|
761
879
|
if (ul) {
|
|
@@ -781,10 +899,99 @@ function toggleChecklist() {
|
|
|
781
899
|
return;
|
|
782
900
|
}
|
|
783
901
|
}
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
902
|
+
if (range.collapsed) {
|
|
903
|
+
const BLOCK_TAGS = new Set([
|
|
904
|
+
"P",
|
|
905
|
+
"DIV",
|
|
906
|
+
"H1",
|
|
907
|
+
"H2",
|
|
908
|
+
"H3",
|
|
909
|
+
"H4",
|
|
910
|
+
"H5",
|
|
911
|
+
"H6",
|
|
912
|
+
"BLOCKQUOTE",
|
|
913
|
+
"LI"
|
|
914
|
+
]);
|
|
915
|
+
let block = container;
|
|
916
|
+
while (block && block.parentNode && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
|
|
917
|
+
const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/\u00a0/g, " ") : "";
|
|
918
|
+
const ul = document.createElement("ul");
|
|
919
|
+
ul.className = "an-checklist";
|
|
920
|
+
const li = document.createElement("li");
|
|
921
|
+
const checkbox = document.createElement("input");
|
|
922
|
+
checkbox.type = "checkbox";
|
|
923
|
+
checkbox.contentEditable = "false";
|
|
924
|
+
li.appendChild(checkbox);
|
|
925
|
+
li.appendChild(document.createTextNode(itemText || ""));
|
|
926
|
+
ul.appendChild(li);
|
|
927
|
+
if (block && BLOCK_TAGS.has(block.tagName)) block.parentNode.replaceChild(ul, block);
|
|
928
|
+
else {
|
|
929
|
+
document.execCommand("insertHTML", false, ul.outerHTML);
|
|
930
|
+
return;
|
|
931
|
+
}
|
|
932
|
+
const textNode = li.lastChild;
|
|
933
|
+
const nr = document.createRange();
|
|
934
|
+
const offset = textNode.nodeType === Node.TEXT_NODE ? textNode.textContent.length : 0;
|
|
935
|
+
nr.setStart(textNode, offset);
|
|
936
|
+
nr.collapse(true);
|
|
937
|
+
sel.removeAllRanges();
|
|
938
|
+
sel.addRange(nr);
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
if (!sel.toString().replace(/[\u00a0\u200B]/g, " ").trim()) return;
|
|
942
|
+
const BLOCK_TAGS_MULTI = new Set([
|
|
943
|
+
"P",
|
|
944
|
+
"DIV",
|
|
945
|
+
"H1",
|
|
946
|
+
"H2",
|
|
947
|
+
"H3",
|
|
948
|
+
"H4",
|
|
949
|
+
"H5",
|
|
950
|
+
"H6",
|
|
951
|
+
"BLOCKQUOTE",
|
|
952
|
+
"PRE",
|
|
953
|
+
"LI"
|
|
954
|
+
]);
|
|
955
|
+
const blocks = [];
|
|
956
|
+
const seenBlocks = /* @__PURE__ */ new Set();
|
|
957
|
+
const commonAncestor = range.commonAncestorContainer;
|
|
958
|
+
const iter = document.createNodeIterator(commonAncestor.nodeType === Node.TEXT_NODE ? commonAncestor.parentNode : commonAncestor, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, null);
|
|
959
|
+
let node;
|
|
960
|
+
while (node = iter.nextNode()) {
|
|
961
|
+
if (!range.intersectsNode(node)) continue;
|
|
962
|
+
let block = node.nodeType === Node.TEXT_NODE ? node.parentElement : node;
|
|
963
|
+
while (block && !BLOCK_TAGS_MULTI.has(block.tagName)) block = block.parentElement;
|
|
964
|
+
if (block && !seenBlocks.has(block)) {
|
|
965
|
+
seenBlocks.add(block);
|
|
966
|
+
blocks.push(block);
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
if (blocks.length === 0) return;
|
|
970
|
+
const newUl = document.createElement("ul");
|
|
971
|
+
newUl.className = "an-checklist";
|
|
972
|
+
let lastTextNode = null;
|
|
973
|
+
blocks.forEach((block) => {
|
|
974
|
+
const li = document.createElement("li");
|
|
975
|
+
const cb = document.createElement("input");
|
|
976
|
+
cb.type = "checkbox";
|
|
977
|
+
cb.setAttribute("contenteditable", "false");
|
|
978
|
+
li.appendChild(cb);
|
|
979
|
+
const blockText = Array.from(block.childNodes).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
980
|
+
const tn = document.createTextNode(blockText || "");
|
|
981
|
+
li.appendChild(tn);
|
|
982
|
+
newUl.appendChild(li);
|
|
983
|
+
lastTextNode = tn;
|
|
984
|
+
});
|
|
985
|
+
const firstBlock = blocks[0];
|
|
986
|
+
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
987
|
+
blocks.forEach((block) => block.parentNode && block.parentNode.removeChild(block));
|
|
988
|
+
if (lastTextNode) {
|
|
989
|
+
const nr = document.createRange();
|
|
990
|
+
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
991
|
+
nr.collapse(true);
|
|
992
|
+
sel.removeAllRanges();
|
|
993
|
+
sel.addRange(nr);
|
|
994
|
+
}
|
|
788
995
|
}
|
|
789
996
|
/**
|
|
790
997
|
* Returns true when the cursor is inside a checklist item.
|
|
@@ -849,9 +1056,9 @@ var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => und
|
|
|
849
1056
|
if (document.queryCommandState("underline")) return true;
|
|
850
1057
|
const sel = window.getSelection();
|
|
851
1058
|
if (!sel || !sel.rangeCount) return false;
|
|
852
|
-
let
|
|
853
|
-
if (
|
|
854
|
-
return !!(
|
|
1059
|
+
let sc = sel.getRangeAt(0).startContainer;
|
|
1060
|
+
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
1061
|
+
return !!(sc && sc.closest && sc.closest("u"));
|
|
855
1062
|
});
|
|
856
1063
|
var strikeBtn = btn("strikethrough", "strikethrough", "Strikethrough", () => strikethrough(), () => document.queryCommandState("strikeThrough"));
|
|
857
1064
|
var superscriptBtn = btn("superscript", "superscript", "Superscript", () => superscript(), () => document.queryCommandState("superscript"));
|
|
@@ -1254,7 +1461,6 @@ var PROHIBITED_TAGS = [
|
|
|
1254
1461
|
"object",
|
|
1255
1462
|
"embed",
|
|
1256
1463
|
"form",
|
|
1257
|
-
"input",
|
|
1258
1464
|
"button"
|
|
1259
1465
|
];
|
|
1260
1466
|
/** Attributes whose values must be sanitised as URLs. */
|
|
@@ -1278,7 +1484,8 @@ var TRUSTED_IFRAME_HOSTS = new Set([
|
|
|
1278
1484
|
* Uses DOMParser so the sanitisation follows normal browser parsing rules —
|
|
1279
1485
|
* no regex shortcuts that can be bypassed by encoding tricks.
|
|
1280
1486
|
*
|
|
1281
|
-
* - Strips PROHIBITED_TAGS (script, style, iframe, object, embed, form,
|
|
1487
|
+
* - Strips PROHIBITED_TAGS (script, style, iframe, object, embed, form, button)
|
|
1488
|
+
* - Allows input[type="checkbox"] only inside ul.an-checklist li; removes all other <input>
|
|
1282
1489
|
* - Removes all on* event-handler attributes
|
|
1283
1490
|
* - Rejects javascript: and vbscript: URLs in URL attributes
|
|
1284
1491
|
* - Rejects data: URIs everywhere except img[src] (base64 uploads)
|
|
@@ -1317,6 +1524,16 @@ function sanitiseHTML(html, { allowIframes = false } = {}) {
|
|
|
1317
1524
|
}
|
|
1318
1525
|
});
|
|
1319
1526
|
});
|
|
1527
|
+
doc.querySelectorAll("input").forEach((el) => {
|
|
1528
|
+
if (!(el.closest("ul.an-checklist") !== null && el.closest("li") !== null) || el.getAttribute("type") !== "checkbox") el.remove();
|
|
1529
|
+
else Array.from(el.attributes).forEach((attr) => {
|
|
1530
|
+
if (![
|
|
1531
|
+
"type",
|
|
1532
|
+
"checked",
|
|
1533
|
+
"contenteditable"
|
|
1534
|
+
].includes(attr.name)) el.removeAttribute(attr.name);
|
|
1535
|
+
});
|
|
1536
|
+
});
|
|
1320
1537
|
return doc.body.innerHTML;
|
|
1321
1538
|
}
|
|
1322
1539
|
/**
|
|
@@ -1397,7 +1614,12 @@ function renderLayout(targetEl, options) {
|
|
|
1397
1614
|
if (options.maxHeight) editable.style.maxHeight = `${options.maxHeight}px`;
|
|
1398
1615
|
container.appendChild(editable);
|
|
1399
1616
|
if (options.theme === "dark") container.classList.add("an-theme-dark");
|
|
1400
|
-
if (options.readOnly)
|
|
1617
|
+
if (options.readOnly) {
|
|
1618
|
+
container.classList.add("an-disabled");
|
|
1619
|
+
editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
|
|
1620
|
+
cb.setAttribute("disabled", "");
|
|
1621
|
+
});
|
|
1622
|
+
}
|
|
1401
1623
|
if (options.direction === "rtl") {
|
|
1402
1624
|
editable.setAttribute("dir", "rtl");
|
|
1403
1625
|
container.classList.add("an-dir-rtl");
|
|
@@ -1857,7 +2079,7 @@ function handleKeydown(event, editable, options = {}) {
|
|
|
1857
2079
|
const para = closestPara(range.sc, editable);
|
|
1858
2080
|
if (para && isLi(para)) {
|
|
1859
2081
|
event.preventDefault();
|
|
1860
|
-
if (event.shiftKey)
|
|
2082
|
+
if (event.shiftKey) outdent();
|
|
1861
2083
|
else execCommand("indent");
|
|
1862
2084
|
return true;
|
|
1863
2085
|
}
|
|
@@ -2276,7 +2498,42 @@ var Editor = class {
|
|
|
2276
2498
|
sel.removeAllRanges();
|
|
2277
2499
|
sel.addRange(nr);
|
|
2278
2500
|
};
|
|
2279
|
-
|
|
2501
|
+
const isReadOnly = () => this.context.layoutInfo.container.classList.contains("an-disabled");
|
|
2502
|
+
this._disposers.push(on(editable, "keydown", onKeydown), on(editable, "beforeinput", onBeforeInput), on(editable, "input", onInput), on(document, "selectionchange", onSelChange), on(editable, "click", onCheckboxClick), on(editable, "mouseup", fixChecklistCursor), on(editable, "keyup", fixChecklistCursor), on(editable, "dragstart", (e) => {
|
|
2503
|
+
if (isReadOnly()) {
|
|
2504
|
+
e.preventDefault();
|
|
2505
|
+
return;
|
|
2506
|
+
}
|
|
2507
|
+
const target = e.target;
|
|
2508
|
+
if (target && (target.nodeName === "IFRAME" || target.closest && target.closest(".an-video-wrapper"))) e.preventDefault();
|
|
2509
|
+
}), on(editable, "drop", (e) => {
|
|
2510
|
+
if (isReadOnly()) e.preventDefault();
|
|
2511
|
+
}));
|
|
2512
|
+
/** @type {string|null} 'superscript' | 'subscript' | null */
|
|
2513
|
+
let _compositionSupSub = null;
|
|
2514
|
+
const onCompositionStart = () => {
|
|
2515
|
+
const sel = window.getSelection();
|
|
2516
|
+
if (!sel || !sel.rangeCount) {
|
|
2517
|
+
_compositionSupSub = null;
|
|
2518
|
+
return;
|
|
2519
|
+
}
|
|
2520
|
+
let node = sel.getRangeAt(0).startContainer;
|
|
2521
|
+
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
2522
|
+
if (node && node.closest) if (node.closest("sup")) _compositionSupSub = "superscript";
|
|
2523
|
+
else if (node.closest("sub")) _compositionSupSub = "subscript";
|
|
2524
|
+
else _compositionSupSub = null;
|
|
2525
|
+
};
|
|
2526
|
+
const onCompositionEnd = () => {
|
|
2527
|
+
const tag = _compositionSupSub;
|
|
2528
|
+
_compositionSupSub = null;
|
|
2529
|
+
if (!tag) return;
|
|
2530
|
+
const sel = window.getSelection();
|
|
2531
|
+
if (!sel || !sel.rangeCount) return;
|
|
2532
|
+
let node = sel.getRangeAt(0).startContainer;
|
|
2533
|
+
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
2534
|
+
if (!(node && node.closest && (tag === "superscript" ? node.closest("sup") : node.closest("sub")))) document.execCommand(tag);
|
|
2535
|
+
};
|
|
2536
|
+
this._disposers.push(on(editable, "compositionstart", onCompositionStart), on(editable, "compositionend", onCompositionEnd));
|
|
2280
2537
|
}
|
|
2281
2538
|
_onKeydown(event) {
|
|
2282
2539
|
const editable = this.context.layoutInfo.editable;
|
|
@@ -2362,6 +2619,7 @@ var Editor = class {
|
|
|
2362
2619
|
}
|
|
2363
2620
|
}
|
|
2364
2621
|
afterCommand() {
|
|
2622
|
+
this._cleanOrphanedFigures();
|
|
2365
2623
|
this.context.invoke("toolbar.refresh");
|
|
2366
2624
|
this.context.invoke("statusbar.update");
|
|
2367
2625
|
this._scheduleSnapshot();
|
|
@@ -2378,6 +2636,16 @@ var Editor = class {
|
|
|
2378
2636
|
this.context.triggerEvent("change", this.getHTML());
|
|
2379
2637
|
}, 400);
|
|
2380
2638
|
}
|
|
2639
|
+
/**
|
|
2640
|
+
* C4: Removes figure.an-figure elements that no longer contain an <img>.
|
|
2641
|
+
* This happens when a user selects only the image (not the whole figure)
|
|
2642
|
+
* and deletes or replaces it, leaving a dangling figcaption.
|
|
2643
|
+
*/
|
|
2644
|
+
_cleanOrphanedFigures() {
|
|
2645
|
+
this.context.layoutInfo.editable.querySelectorAll("figure.an-figure").forEach((fig) => {
|
|
2646
|
+
if (!fig.querySelector("img")) fig.parentNode.removeChild(fig);
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2381
2649
|
focus() {
|
|
2382
2650
|
this.context.layoutInfo.editable.focus();
|
|
2383
2651
|
}
|
|
@@ -2394,7 +2662,7 @@ var Editor = class {
|
|
|
2394
2662
|
* @param {string} html - HTML string (will be sanitised)
|
|
2395
2663
|
*/
|
|
2396
2664
|
setHTML(html) {
|
|
2397
|
-
this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html);
|
|
2665
|
+
this.context.layoutInfo.editable.innerHTML = sanitiseHTML(html, { allowIframes: true });
|
|
2398
2666
|
if (this._history) this._history.reset();
|
|
2399
2667
|
this.afterCommand();
|
|
2400
2668
|
}
|
|
@@ -3109,15 +3377,28 @@ var Toolbar = class {
|
|
|
3109
3377
|
if (def.name === "fontFamily" && !isHeader) opt.style.fontFamily = value;
|
|
3110
3378
|
select.appendChild(opt);
|
|
3111
3379
|
});
|
|
3380
|
+
/** @type {Range|null} */
|
|
3381
|
+
let _savedRange = null;
|
|
3382
|
+
const dMousedown = on(select, "mousedown", () => {
|
|
3383
|
+
const sel = window.getSelection();
|
|
3384
|
+
_savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
3385
|
+
});
|
|
3112
3386
|
const disposer = on(select, "change", (e) => {
|
|
3113
3387
|
const value = e.target.value;
|
|
3114
3388
|
const selectedOpt = e.target.options[e.target.selectedIndex];
|
|
3115
3389
|
if (!value || selectedOpt.disabled) return;
|
|
3116
3390
|
this.context.invoke("editor.focus");
|
|
3391
|
+
if (_savedRange) try {
|
|
3392
|
+
const sel = window.getSelection();
|
|
3393
|
+
if (sel) {
|
|
3394
|
+
sel.removeAllRanges();
|
|
3395
|
+
sel.addRange(_savedRange);
|
|
3396
|
+
}
|
|
3397
|
+
} catch (_) {}
|
|
3117
3398
|
def.action(this.context, value);
|
|
3118
3399
|
this.context.invoke("editor.afterCommand");
|
|
3119
3400
|
});
|
|
3120
|
-
this._disposers.push(disposer);
|
|
3401
|
+
this._disposers.push(dMousedown, disposer);
|
|
3121
3402
|
return select;
|
|
3122
3403
|
}
|
|
3123
3404
|
/**
|
|
@@ -3574,9 +3855,25 @@ var Clipboard = class {
|
|
|
3574
3855
|
this.options.onImageUpload(files);
|
|
3575
3856
|
return;
|
|
3576
3857
|
}
|
|
3858
|
+
const UNSUPPORTED = [
|
|
3859
|
+
"image/tiff",
|
|
3860
|
+
"image/x-tiff",
|
|
3861
|
+
"image/bmp",
|
|
3862
|
+
"image/x-bmp",
|
|
3863
|
+
"image/x-ms-bmp"
|
|
3864
|
+
];
|
|
3577
3865
|
const maxBytes = (this.options.maxImageSize || 5) * 1024 * 1024;
|
|
3578
3866
|
files.forEach((file) => {
|
|
3579
3867
|
if (!file || !file.type.startsWith("image/")) return;
|
|
3868
|
+
if (UNSUPPORTED.includes(file.type)) {
|
|
3869
|
+
const message = `Image format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
|
|
3870
|
+
this.context.triggerEvent("imageError", {
|
|
3871
|
+
file,
|
|
3872
|
+
message
|
|
3873
|
+
});
|
|
3874
|
+
console.warn("[AutumnNote]", message);
|
|
3875
|
+
return;
|
|
3876
|
+
}
|
|
3580
3877
|
if (file.size > maxBytes) {
|
|
3581
3878
|
const message = `Image "${file.name}" exceeds the ${this.options.maxImageSize || 5} MB size limit.`;
|
|
3582
3879
|
this.context.triggerEvent("imageError", {
|
|
@@ -3740,7 +4037,7 @@ var Placeholder = class {
|
|
|
3740
4037
|
_update() {
|
|
3741
4038
|
const editable = this.context.layoutInfo.editable;
|
|
3742
4039
|
const isFocused = document.activeElement === editable;
|
|
3743
|
-
const isEmpty = !editable.textContent.trim() && !editable.querySelector("img, table, hr");
|
|
4040
|
+
const isEmpty = !(editable.textContent.replace(/\u200B/g, "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
|
|
3744
4041
|
editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
|
|
3745
4042
|
}
|
|
3746
4043
|
};
|
|
@@ -4175,12 +4472,14 @@ var ImageDialog = class {
|
|
|
4175
4472
|
const fileInput = createElement("input", {
|
|
4176
4473
|
type: "file",
|
|
4177
4474
|
class: "an-input",
|
|
4178
|
-
accept: "image
|
|
4475
|
+
accept: "image/jpeg,image/png,image/gif,image/webp,image/svg+xml,image/avif"
|
|
4179
4476
|
});
|
|
4180
4477
|
this._fileInput = fileInput;
|
|
4478
|
+
const fileHint = createElement("p", { class: "an-dialog-hint" });
|
|
4479
|
+
this._fileHint = fileHint;
|
|
4181
4480
|
const d = on(fileInput, "change", () => this._onFileChange());
|
|
4182
4481
|
this._disposers.push(d);
|
|
4183
|
-
box.append(fileLabel, fileInput);
|
|
4482
|
+
box.append(fileLabel, fileInput, fileHint);
|
|
4184
4483
|
}
|
|
4185
4484
|
const btnRow = createElement("div", { class: "an-dialog-actions" });
|
|
4186
4485
|
const insertBtn = createElement("button", {
|
|
@@ -4220,9 +4519,28 @@ var ImageDialog = class {
|
|
|
4220
4519
|
_onFileChange() {
|
|
4221
4520
|
const file = this._fileInput && this._fileInput.files && this._fileInput.files[0];
|
|
4222
4521
|
if (!file || !file.type.startsWith("image/")) return;
|
|
4522
|
+
if (!new Set([
|
|
4523
|
+
"image/jpeg",
|
|
4524
|
+
"image/png",
|
|
4525
|
+
"image/gif",
|
|
4526
|
+
"image/webp",
|
|
4527
|
+
"image/svg+xml",
|
|
4528
|
+
"image/avif"
|
|
4529
|
+
]).has(file.type)) {
|
|
4530
|
+
const message = `Format "${file.type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`;
|
|
4531
|
+
if (this._fileHint) this._fileHint.textContent = message;
|
|
4532
|
+
this.context.triggerEvent("imageError", {
|
|
4533
|
+
file,
|
|
4534
|
+
message
|
|
4535
|
+
});
|
|
4536
|
+
this._fileInput.value = "";
|
|
4537
|
+
return;
|
|
4538
|
+
}
|
|
4539
|
+
if (this._fileHint) this._fileHint.textContent = "";
|
|
4223
4540
|
const maxSize = (this.options.maxImageSize || 5) * 1024 * 1024;
|
|
4224
4541
|
if (file.size > maxSize) {
|
|
4225
4542
|
const message = `Image file is too large. Maximum allowed size is ${this.options.maxImageSize || 5} MB.`;
|
|
4543
|
+
if (this._fileHint) this._fileHint.textContent = message;
|
|
4226
4544
|
console.warn("[AutumnNote] ImageDialog:", message);
|
|
4227
4545
|
this.context.triggerEvent("imageError", {
|
|
4228
4546
|
file,
|
|
@@ -4534,6 +4852,7 @@ var ImageResizer = class {
|
|
|
4534
4852
|
_resizeDebounce = setTimeout(() => this._updateOverlayPosition(), 100);
|
|
4535
4853
|
};
|
|
4536
4854
|
this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
|
|
4855
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
4537
4856
|
const img = e.target.closest("img");
|
|
4538
4857
|
if (img) this._select(img);
|
|
4539
4858
|
}), on(document, "click", (e) => this._onDocClick(e)), on(window, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(window, "resize", onWindowResize, { passive: true }), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }));
|
|
@@ -4584,6 +4903,7 @@ var ImageResizer = class {
|
|
|
4584
4903
|
return overlay;
|
|
4585
4904
|
}
|
|
4586
4905
|
_onEditorClick(e) {
|
|
4906
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
4587
4907
|
const img = e.target.closest("img");
|
|
4588
4908
|
if (img) {
|
|
4589
4909
|
e.preventDefault();
|
|
@@ -4740,9 +5060,12 @@ var VideoResizer = class {
|
|
|
4740
5060
|
_resizeDebounce = setTimeout(() => this._updateOverlayPosition(), 100);
|
|
4741
5061
|
};
|
|
4742
5062
|
this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
|
|
5063
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
4743
5064
|
const wrapper = this._findWrapper(e.target);
|
|
4744
5065
|
if (wrapper) this._select(wrapper);
|
|
4745
|
-
}), on(document, "click", (e) => this._onDocClick(e)), on(window, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(window, "resize", onWindowResize), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }))
|
|
5066
|
+
}), on(document, "click", (e) => this._onDocClick(e)), on(window, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(window, "resize", onWindowResize), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(editable, "dragstart", (e) => {
|
|
5067
|
+
if (e.target instanceof Element && e.target.closest(".an-video-wrapper")) e.preventDefault();
|
|
5068
|
+
}));
|
|
4746
5069
|
return this;
|
|
4747
5070
|
}
|
|
4748
5071
|
destroy() {
|
|
@@ -4801,6 +5124,7 @@ var VideoResizer = class {
|
|
|
4801
5124
|
return overlay;
|
|
4802
5125
|
}
|
|
4803
5126
|
_onEditorClick(e) {
|
|
5127
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
4804
5128
|
const wrapper = this._findWrapper(e.target);
|
|
4805
5129
|
if (wrapper) {
|
|
4806
5130
|
e.preventDefault();
|
|
@@ -4999,9 +5323,12 @@ var LinkTooltip = class {
|
|
|
4999
5323
|
}, HIDE_DELAY$4);
|
|
5000
5324
|
}
|
|
5001
5325
|
_show(anchor) {
|
|
5326
|
+
const isReadOnly = this.context.layoutInfo.container.classList.contains("an-disabled");
|
|
5002
5327
|
const url = anchor.getAttribute("href") || "";
|
|
5003
5328
|
this._urlLabel.textContent = this._truncateUrl(url);
|
|
5004
5329
|
this._urlLabel.title = url;
|
|
5330
|
+
this._editBtn.style.display = isReadOnly ? "none" : "";
|
|
5331
|
+
this._unlinkBtn.style.display = isReadOnly ? "none" : "";
|
|
5005
5332
|
this._el.style.display = "flex";
|
|
5006
5333
|
this._positionNear(anchor);
|
|
5007
5334
|
}
|
|
@@ -5115,6 +5442,7 @@ var ImageTooltip = class {
|
|
|
5115
5442
|
document.body.appendChild(this._el);
|
|
5116
5443
|
const editable = this.context.layoutInfo.editable;
|
|
5117
5444
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
5445
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
5118
5446
|
const img = e.target.closest("img");
|
|
5119
5447
|
if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
|
|
5120
5448
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
@@ -5241,6 +5569,7 @@ var ImageTooltip = class {
|
|
|
5241
5569
|
const target = img.closest("figure.an-figure") || img;
|
|
5242
5570
|
target.style.float = value;
|
|
5243
5571
|
target.style.display = "";
|
|
5572
|
+
if (target !== img) target.style.width = "";
|
|
5244
5573
|
target.style.marginLeft = value === "right" ? "12px" : "";
|
|
5245
5574
|
target.style.marginRight = value === "left" ? "12px" : "";
|
|
5246
5575
|
this.context.invoke("editor.afterCommand");
|
|
@@ -5255,6 +5584,7 @@ var ImageTooltip = class {
|
|
|
5255
5584
|
const target = img.closest("figure.an-figure") || img;
|
|
5256
5585
|
target.style.float = "";
|
|
5257
5586
|
target.style.display = "block";
|
|
5587
|
+
if (target !== img) target.style.width = "fit-content";
|
|
5258
5588
|
target.style.marginLeft = "auto";
|
|
5259
5589
|
target.style.marginRight = "auto";
|
|
5260
5590
|
this.context.invoke("editor.afterCommand");
|
|
@@ -5339,6 +5669,7 @@ var ImageTooltip = class {
|
|
|
5339
5669
|
img.style.marginRight = "";
|
|
5340
5670
|
} else if (img.style.display === "block" && img.style.marginLeft === "auto") {
|
|
5341
5671
|
figure.style.display = "block";
|
|
5672
|
+
figure.style.width = "fit-content";
|
|
5342
5673
|
figure.style.marginLeft = "auto";
|
|
5343
5674
|
figure.style.marginRight = "auto";
|
|
5344
5675
|
img.style.display = "";
|
|
@@ -5393,6 +5724,7 @@ var VideoTooltip = class {
|
|
|
5393
5724
|
document.body.appendChild(this._el);
|
|
5394
5725
|
const editable = this.context.layoutInfo.editable;
|
|
5395
5726
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
5727
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
5396
5728
|
const wrapper = e.target.closest(".an-video-wrapper");
|
|
5397
5729
|
if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
|
|
5398
5730
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
@@ -5649,6 +5981,47 @@ function getCellAfterVisualCol(row, visualIdx) {
|
|
|
5649
5981
|
}
|
|
5650
5982
|
return null;
|
|
5651
5983
|
}
|
|
5984
|
+
/**
|
|
5985
|
+
* Build a 2D grid map of the table, accounting for both rowspan and colspan.
|
|
5986
|
+
*
|
|
5987
|
+
* gridMap[r][c] = the DOM cell occupying visual grid position (r, c).
|
|
5988
|
+
* cellPos = WeakMap: cell → { r, c, rs, cs } (top-left grid origin + span).
|
|
5989
|
+
*
|
|
5990
|
+
* Uses HTMLTableElement.rows which is scoped to the table itself and never
|
|
5991
|
+
* includes rows from nested tables.
|
|
5992
|
+
*
|
|
5993
|
+
* @param {HTMLTableElement} table
|
|
5994
|
+
* @returns {{ gridMap: Object, cellPos: WeakMap }}
|
|
5995
|
+
*/
|
|
5996
|
+
function buildGridMap(table) {
|
|
5997
|
+
const rows = Array.from(table.rows);
|
|
5998
|
+
const gridMap = {};
|
|
5999
|
+
const cellPos = /* @__PURE__ */ new WeakMap();
|
|
6000
|
+
rows.forEach((row, r) => {
|
|
6001
|
+
if (!gridMap[r]) gridMap[r] = {};
|
|
6002
|
+
let c = 0;
|
|
6003
|
+
for (const cell of row.cells) {
|
|
6004
|
+
while (gridMap[r][c]) c++;
|
|
6005
|
+
const rs = cell.rowSpan || 1;
|
|
6006
|
+
const cs = cell.colSpan || 1;
|
|
6007
|
+
cellPos.set(cell, {
|
|
6008
|
+
r,
|
|
6009
|
+
c,
|
|
6010
|
+
rs,
|
|
6011
|
+
cs
|
|
6012
|
+
});
|
|
6013
|
+
for (let dr = 0; dr < rs; dr++) {
|
|
6014
|
+
if (!gridMap[r + dr]) gridMap[r + dr] = {};
|
|
6015
|
+
for (let dc = 0; dc < cs; dc++) gridMap[r + dr][c + dc] = cell;
|
|
6016
|
+
}
|
|
6017
|
+
c += cs;
|
|
6018
|
+
}
|
|
6019
|
+
});
|
|
6020
|
+
return {
|
|
6021
|
+
gridMap,
|
|
6022
|
+
cellPos
|
|
6023
|
+
};
|
|
6024
|
+
}
|
|
5652
6025
|
var ICONS$2 = {
|
|
5653
6026
|
rowAbove: `<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="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 3v7"/><path d="M9 7l3-4 3 4"/></svg>`,
|
|
5654
6027
|
rowBelow: `<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="3" width="18" height="18" rx="1"/><line x1="3" y1="12" x2="21" y2="12"/><path d="M12 12v7"/><path d="M9 17l3 4 3-4"/></svg>`,
|
|
@@ -5657,10 +6030,12 @@ var ICONS$2 = {
|
|
|
5657
6030
|
colRight: `<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="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><path d="M12 12h9"/><path d="M17 8l4 4-4 4"/></svg>`,
|
|
5658
6031
|
deleteCol: `<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="3" width="18" height="18" rx="1"/><line x1="12" y1="3" x2="12" y2="21"/><line x1="15" y1="6" x2="21" y2="12"/><line x1="21" y1="6" x2="15" y2="12"/></svg>`,
|
|
5659
6032
|
mergeCells: `<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="2" y="7" width="8" height="10" rx="1"/><rect x="14" y="7" width="8" height="10" rx="1"/><path d="M10 12h4"/><path d="M12 10l2 2-2 2"/></svg>`,
|
|
6033
|
+
unmergeCells: `<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="2" y="5" width="20" height="14" rx="1"/><line x1="12" y1="5" x2="12" y2="19" stroke-dasharray="2.5 2"/><line x1="2" y1="12" x2="22" y2="12" stroke-dasharray="2.5 2"/><path d="M9 9 L6 12 L9 15"/><path d="M15 9 L18 12 L15 15"/></svg>`,
|
|
5660
6034
|
colWidth: `<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"><line x1="7" y1="4" x2="7" y2="20"/><line x1="17" y1="4" x2="17" y2="20"/><line x1="7" y1="12" x2="17" y2="12"/><path d="M10 9l-3 3 3 3"/><path d="M14 9l3 3-3 3"/></svg>`,
|
|
5661
6035
|
rowHeight: `<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"><line x1="4" y1="7" x2="20" y2="7"/><line x1="4" y1="17" x2="20" y2="17"/><line x1="12" y1="7" x2="12" y2="17"/><path d="M9 10l3-3 3 3"/><path d="M9 14l3 3 3-3"/></svg>`,
|
|
5662
6036
|
tableBorder: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-linecap="round"><line x1="3" y1="6" x2="21" y2="6" stroke-width="1"/><line x1="3" y1="13" x2="21" y2="13" stroke-width="2"/><line x1="3" y1="20" x2="21" y2="20" stroke-width="3"/></svg>`,
|
|
5663
|
-
deleteTable: `<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="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg
|
|
6037
|
+
deleteTable: `<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="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/><line x1="16" y1="16" x2="22" y2="22" stroke="#ef4444"/><line x1="22" y1="16" x2="16" y2="22" stroke="#ef4444"/></svg>`,
|
|
6038
|
+
selectCells: `<svg xmlns="http://www.w3.org/2000/svg" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z" fill="currentColor" opacity="0.15"/><path d="M4 4 L4 20 L9 15 L12 21 L14 20 L11 14 L17 14 Z"/></svg>`
|
|
5664
6039
|
};
|
|
5665
6040
|
var TableTooltip = class {
|
|
5666
6041
|
/** @param {import('../Context.js').Context} context */
|
|
@@ -5676,6 +6051,12 @@ var TableTooltip = class {
|
|
|
5676
6051
|
this._sizeApply = null;
|
|
5677
6052
|
this._sizeTitleEl = null;
|
|
5678
6053
|
this._sizeInputEl = null;
|
|
6054
|
+
this._selectMode = false;
|
|
6055
|
+
this._selectedCells = [];
|
|
6056
|
+
this._selectStart = null;
|
|
6057
|
+
this._selectDragging = false;
|
|
6058
|
+
this._selectBtn = null;
|
|
6059
|
+
this._editable = null;
|
|
5679
6060
|
}
|
|
5680
6061
|
initialize() {
|
|
5681
6062
|
this._el = this._buildTooltip();
|
|
@@ -5683,7 +6064,31 @@ var TableTooltip = class {
|
|
|
5683
6064
|
this._sizePopover = this._buildSizePopover();
|
|
5684
6065
|
document.body.appendChild(this._sizePopover);
|
|
5685
6066
|
const editable = this.context.layoutInfo.editable;
|
|
6067
|
+
this._editable = editable;
|
|
6068
|
+
const onSelMousedown = (e) => {
|
|
6069
|
+
if (!this._selectMode) return;
|
|
6070
|
+
const cell = e.target.closest("td, th");
|
|
6071
|
+
if (!cell || !editable.contains(cell)) return;
|
|
6072
|
+
if (cell.style.cursor === "col-resize" || cell.style.cursor === "row-resize") return;
|
|
6073
|
+
e.preventDefault();
|
|
6074
|
+
this._activeTable = cell.closest("table");
|
|
6075
|
+
this._selectStart = cell;
|
|
6076
|
+
this._selectDragging = true;
|
|
6077
|
+
this._setSelection([cell]);
|
|
6078
|
+
};
|
|
6079
|
+
const onSelMousemove = (e) => {
|
|
6080
|
+
if (!this._selectMode || !this._selectDragging || !this._selectStart) return;
|
|
6081
|
+
const cell = e.target.closest("td, th");
|
|
6082
|
+
if (!cell || !editable.contains(cell)) return;
|
|
6083
|
+
if (cell.closest("table") !== this._activeTable) return;
|
|
6084
|
+
this._setSelection(this._getRectCells(this._selectStart, cell));
|
|
6085
|
+
};
|
|
6086
|
+
const onSelMouseup = () => {
|
|
6087
|
+
this._selectDragging = false;
|
|
6088
|
+
};
|
|
6089
|
+
this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
|
|
5686
6090
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
6091
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
5687
6092
|
const table = e.target.closest("table");
|
|
5688
6093
|
if (table && editable.contains(table)) {
|
|
5689
6094
|
const cell = e.target.closest("td, th");
|
|
@@ -5691,9 +6096,11 @@ var TableTooltip = class {
|
|
|
5691
6096
|
this._scheduleShow(table);
|
|
5692
6097
|
}
|
|
5693
6098
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
6099
|
+
if (this._selectMode) return;
|
|
5694
6100
|
const to = e.relatedTarget;
|
|
5695
6101
|
if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
|
|
5696
6102
|
}, { passive: true }), on(document, "click", (e) => {
|
|
6103
|
+
if (this._selectMode && this._activeTable && this._activeTable.contains(e.target)) return;
|
|
5697
6104
|
if (this._activeTable && !this._activeTable.contains(e.target) && !this._el.contains(e.target) && !(this._sizePopover && this._sizePopover.contains(e.target))) this._hide();
|
|
5698
6105
|
}));
|
|
5699
6106
|
this._initResize();
|
|
@@ -5725,6 +6132,10 @@ var TableTooltip = class {
|
|
|
5725
6132
|
};
|
|
5726
6133
|
const onEditorMove = (e) => {
|
|
5727
6134
|
if (_resizing) return;
|
|
6135
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) {
|
|
6136
|
+
clearHover();
|
|
6137
|
+
return;
|
|
6138
|
+
}
|
|
5728
6139
|
if (_rafEditorMove !== null) return;
|
|
5729
6140
|
const target = e.target;
|
|
5730
6141
|
const clientX = e.clientX;
|
|
@@ -5752,6 +6163,7 @@ var TableTooltip = class {
|
|
|
5752
6163
|
});
|
|
5753
6164
|
};
|
|
5754
6165
|
const onEditorDown = (e) => {
|
|
6166
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
5755
6167
|
if (!_nearCell || !_nearEdge) return;
|
|
5756
6168
|
_resizing = true;
|
|
5757
6169
|
_edge = _nearEdge;
|
|
@@ -5761,7 +6173,7 @@ var TableTooltip = class {
|
|
|
5761
6173
|
if (_edge === "col") {
|
|
5762
6174
|
_startW = _nearCell.offsetWidth;
|
|
5763
6175
|
_colIdx = getVisualColIndex(_nearCell);
|
|
5764
|
-
_colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean) : [];
|
|
6176
|
+
_colCells = _colIdx >= 0 ? Array.from(_table.querySelectorAll("tr")).map((r) => getCellAtVisualCol(r, _colIdx)).filter(Boolean).filter((c) => (c.colSpan || 1) === 1) : [];
|
|
5765
6177
|
document.body.style.cursor = "col-resize";
|
|
5766
6178
|
} else {
|
|
5767
6179
|
_row = _nearCell.closest("tr");
|
|
@@ -5832,6 +6244,9 @@ var TableTooltip = class {
|
|
|
5832
6244
|
this._label.textContent = "Table";
|
|
5833
6245
|
el.appendChild(this._label);
|
|
5834
6246
|
el.appendChild(this._sep());
|
|
6247
|
+
this._selectBtn = this._makeBtn(ICONS$2.selectCells, "Select Cells", () => this._toggleSelectMode());
|
|
6248
|
+
el.appendChild(this._selectBtn);
|
|
6249
|
+
el.appendChild(this._sep());
|
|
5835
6250
|
el.appendChild(this._makeBtn(ICONS$2.rowAbove, "Add Row Above", () => this._addRow("above")));
|
|
5836
6251
|
el.appendChild(this._makeBtn(ICONS$2.rowBelow, "Add Row Below", () => this._addRow("below")));
|
|
5837
6252
|
el.appendChild(this._makeBtn(ICONS$2.deleteRow, "Delete Row", () => this._deleteRow()));
|
|
@@ -5841,6 +6256,7 @@ var TableTooltip = class {
|
|
|
5841
6256
|
el.appendChild(this._makeBtn(ICONS$2.deleteCol, "Delete Column", () => this._deleteColumn()));
|
|
5842
6257
|
el.appendChild(this._sep());
|
|
5843
6258
|
el.appendChild(this._makeBtn(ICONS$2.mergeCells, "Merge Cells", () => this._mergeCells()));
|
|
6259
|
+
el.appendChild(this._makeBtn(ICONS$2.unmergeCells, "Unmerge Cells", () => this._unmergeCells()));
|
|
5844
6260
|
el.appendChild(this._sep());
|
|
5845
6261
|
el.appendChild(this._makeBtn(ICONS$2.colWidth, "Column Width", () => this._openSizePopover("col")));
|
|
5846
6262
|
el.appendChild(this._makeBtn(ICONS$2.rowHeight, "Row Height", () => this._openSizePopover("row")));
|
|
@@ -5848,6 +6264,7 @@ var TableTooltip = class {
|
|
|
5848
6264
|
el.appendChild(this._sep());
|
|
5849
6265
|
el.appendChild(this._makeBtn(ICONS$2.deleteTable, "Delete Table", () => this._deleteTable(), true));
|
|
5850
6266
|
this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
|
|
6267
|
+
if (this._selectMode) return;
|
|
5851
6268
|
if (this._sizePopover && this._sizePopover.style.display !== "none") return;
|
|
5852
6269
|
this._scheduleHide();
|
|
5853
6270
|
}));
|
|
@@ -5903,6 +6320,12 @@ var TableTooltip = class {
|
|
|
5903
6320
|
this._el.style.display = "none";
|
|
5904
6321
|
this._activeTable = null;
|
|
5905
6322
|
this._activeCell = null;
|
|
6323
|
+
if (this._selectMode) {
|
|
6324
|
+
this._selectMode = false;
|
|
6325
|
+
if (this._selectBtn) this._selectBtn.classList.remove("an-link-tooltip-btn--active");
|
|
6326
|
+
if (this._editable) this._editable.classList.remove("an-table-select-mode");
|
|
6327
|
+
}
|
|
6328
|
+
this._clearSelection();
|
|
5906
6329
|
this._clearTimers();
|
|
5907
6330
|
this._hideSizePopover();
|
|
5908
6331
|
}
|
|
@@ -5936,14 +6359,111 @@ var TableTooltip = class {
|
|
|
5936
6359
|
}
|
|
5937
6360
|
return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
|
|
5938
6361
|
}
|
|
6362
|
+
_toggleSelectMode() {
|
|
6363
|
+
this._selectMode = !this._selectMode;
|
|
6364
|
+
if (this._selectBtn) this._selectBtn.classList.toggle("an-link-tooltip-btn--active", this._selectMode);
|
|
6365
|
+
if (this._editable) this._editable.classList.toggle("an-table-select-mode", this._selectMode);
|
|
6366
|
+
if (!this._selectMode) this._clearSelection();
|
|
6367
|
+
}
|
|
6368
|
+
_clearSelection() {
|
|
6369
|
+
this._selectedCells.forEach((c) => c.classList.remove("an-cell-selected"));
|
|
6370
|
+
this._selectedCells = [];
|
|
6371
|
+
this._selectStart = null;
|
|
6372
|
+
}
|
|
6373
|
+
_setSelection(cells) {
|
|
6374
|
+
this._selectedCells.forEach((c) => {
|
|
6375
|
+
if (!cells.includes(c)) c.classList.remove("an-cell-selected");
|
|
6376
|
+
});
|
|
6377
|
+
this._selectedCells = cells;
|
|
6378
|
+
cells.forEach((c) => c.classList.add("an-cell-selected"));
|
|
6379
|
+
}
|
|
6380
|
+
/**
|
|
6381
|
+
* Returns all cells in the rectangular area between startCell and endCell,
|
|
6382
|
+
* correctly handling rowspan/colspan by using the grid map.
|
|
6383
|
+
* The rect is expanded iteratively until it is stable — this ensures any
|
|
6384
|
+
* merged cell that starts outside the initial rect but spans into it is
|
|
6385
|
+
* fully included.
|
|
6386
|
+
*/
|
|
6387
|
+
_getRectCells(startCell, endCell) {
|
|
6388
|
+
if (!startCell) return [];
|
|
6389
|
+
if (!endCell || startCell === endCell) return [startCell];
|
|
6390
|
+
const table = startCell.closest("table");
|
|
6391
|
+
if (!table || !table.contains(endCell)) return [startCell];
|
|
6392
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
6393
|
+
const sp = cellPos.get(startCell);
|
|
6394
|
+
const ep = cellPos.get(endCell);
|
|
6395
|
+
if (!sp || !ep) return [startCell];
|
|
6396
|
+
let minR = Math.min(sp.r, ep.r);
|
|
6397
|
+
let maxR = Math.max(sp.r + sp.rs - 1, ep.r + ep.rs - 1);
|
|
6398
|
+
let minC = Math.min(sp.c, ep.c);
|
|
6399
|
+
let maxC = Math.max(sp.c + sp.cs - 1, ep.c + ep.cs - 1);
|
|
6400
|
+
let changed = true;
|
|
6401
|
+
while (changed) {
|
|
6402
|
+
changed = false;
|
|
6403
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6404
|
+
const rowMap = gridMap[r];
|
|
6405
|
+
if (!rowMap) continue;
|
|
6406
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6407
|
+
const cell = rowMap[c];
|
|
6408
|
+
if (!cell) continue;
|
|
6409
|
+
const pos = cellPos.get(cell);
|
|
6410
|
+
if (!pos) continue;
|
|
6411
|
+
if (pos.r < minR) {
|
|
6412
|
+
minR = pos.r;
|
|
6413
|
+
changed = true;
|
|
6414
|
+
}
|
|
6415
|
+
if (pos.r + pos.rs - 1 > maxR) {
|
|
6416
|
+
maxR = pos.r + pos.rs - 1;
|
|
6417
|
+
changed = true;
|
|
6418
|
+
}
|
|
6419
|
+
if (pos.c < minC) {
|
|
6420
|
+
minC = pos.c;
|
|
6421
|
+
changed = true;
|
|
6422
|
+
}
|
|
6423
|
+
if (pos.c + pos.cs - 1 > maxC) {
|
|
6424
|
+
maxC = pos.c + pos.cs - 1;
|
|
6425
|
+
changed = true;
|
|
6426
|
+
}
|
|
6427
|
+
}
|
|
6428
|
+
}
|
|
6429
|
+
}
|
|
6430
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6431
|
+
const result = [];
|
|
6432
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6433
|
+
const rowMap = gridMap[r];
|
|
6434
|
+
if (!rowMap) continue;
|
|
6435
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6436
|
+
const cell = rowMap[c];
|
|
6437
|
+
if (cell && !seen.has(cell)) {
|
|
6438
|
+
seen.add(cell);
|
|
6439
|
+
result.push(cell);
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
}
|
|
6443
|
+
return result.length > 0 ? result : [startCell];
|
|
6444
|
+
}
|
|
6445
|
+
/**
|
|
6446
|
+
* Returns the active cell set: user-selected cells when available,
|
|
6447
|
+
* otherwise the single active/cursor cell.
|
|
6448
|
+
* @returns {HTMLTableCellElement[]}
|
|
6449
|
+
*/
|
|
6450
|
+
_getSelectedCells() {
|
|
6451
|
+
return this._selectedCells.length > 0 ? this._selectedCells : [this._getCell()].filter(Boolean);
|
|
6452
|
+
}
|
|
5939
6453
|
_addRow(position) {
|
|
5940
|
-
const
|
|
5941
|
-
if (!
|
|
5942
|
-
const
|
|
5943
|
-
if (!
|
|
5944
|
-
const
|
|
6454
|
+
const cells = this._getSelectedCells();
|
|
6455
|
+
if (!cells.length) return;
|
|
6456
|
+
const table = cells[0].closest("table");
|
|
6457
|
+
if (!table) return;
|
|
6458
|
+
const allRows = Array.from(table.querySelectorAll("tr"));
|
|
6459
|
+
const refRow = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))].reduce((best, r) => {
|
|
6460
|
+
const bi = allRows.indexOf(best);
|
|
6461
|
+
const ri = allRows.indexOf(r);
|
|
6462
|
+
return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
|
|
6463
|
+
});
|
|
6464
|
+
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
5945
6465
|
const newRow = document.createElement("tr");
|
|
5946
|
-
const refCells = Array.from(
|
|
6466
|
+
const refCells = Array.from(refRow.cells);
|
|
5947
6467
|
for (let i = 0; i < colCount; i++) {
|
|
5948
6468
|
const td = createElement("td", {}, ["\xA0"]);
|
|
5949
6469
|
const ref = refCells[i];
|
|
@@ -5951,19 +6471,20 @@ var TableTooltip = class {
|
|
|
5951
6471
|
if (ref && ref.style.minWidth) td.style.minWidth = ref.style.minWidth;
|
|
5952
6472
|
newRow.appendChild(td);
|
|
5953
6473
|
}
|
|
5954
|
-
if (position === "above")
|
|
5955
|
-
else
|
|
6474
|
+
if (position === "above") refRow.parentElement?.insertBefore(newRow, refRow);
|
|
6475
|
+
else refRow.insertAdjacentElement("afterend", newRow);
|
|
5956
6476
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
5957
6477
|
this.context.invoke("editor.afterCommand");
|
|
5958
6478
|
}
|
|
5959
6479
|
_addColumn(position) {
|
|
5960
|
-
const
|
|
5961
|
-
if (!
|
|
5962
|
-
const table =
|
|
6480
|
+
const cells = this._getSelectedCells();
|
|
6481
|
+
if (!cells.length) return;
|
|
6482
|
+
const table = cells[0].closest("table");
|
|
5963
6483
|
if (!table) return;
|
|
5964
|
-
const
|
|
6484
|
+
const colIndices = cells.map((c) => getVisualColIndex(c));
|
|
6485
|
+
const targetColIdx = position === "left" ? Math.min(...colIndices) : Math.max(...colIndices);
|
|
5965
6486
|
const rows = Array.from(table.querySelectorAll("tr"));
|
|
5966
|
-
const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r,
|
|
6487
|
+
const refs = rows.map((r) => position === "left" ? getCellAtVisualCol(r, targetColIdx) : getCellAfterVisualCol(r, targetColIdx));
|
|
5967
6488
|
const isHeaders = rows.map((r) => r.closest("thead") !== null);
|
|
5968
6489
|
rows.forEach((r, i) => {
|
|
5969
6490
|
r.insertBefore(createElement(isHeaders[i] ? "th" : "td", {}, ["\xA0"]), refs[i]);
|
|
@@ -5972,68 +6493,93 @@ var TableTooltip = class {
|
|
|
5972
6493
|
this.context.invoke("editor.afterCommand");
|
|
5973
6494
|
}
|
|
5974
6495
|
_deleteRow() {
|
|
5975
|
-
const
|
|
5976
|
-
if (!
|
|
5977
|
-
const
|
|
5978
|
-
|
|
5979
|
-
if (!row || !table) return;
|
|
6496
|
+
const cells = this._getSelectedCells();
|
|
6497
|
+
if (!cells.length) return;
|
|
6498
|
+
const table = cells[0].closest("table");
|
|
6499
|
+
if (!table) return;
|
|
5980
6500
|
const tbody = table.querySelector("tbody");
|
|
5981
|
-
|
|
6501
|
+
const totalBodyRows = tbody ? tbody.querySelectorAll("tr").length : table.querySelectorAll("tr").length;
|
|
6502
|
+
const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
|
|
6503
|
+
if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
|
|
5982
6504
|
this._activeCell = null;
|
|
5983
|
-
|
|
6505
|
+
this._clearSelection();
|
|
6506
|
+
selectedRows.forEach((r) => r.parentElement?.removeChild(r));
|
|
5984
6507
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
5985
6508
|
this.context.invoke("editor.afterCommand");
|
|
5986
6509
|
}
|
|
5987
6510
|
_deleteColumn() {
|
|
5988
|
-
const
|
|
5989
|
-
if (!
|
|
5990
|
-
const table =
|
|
6511
|
+
const cells = this._getSelectedCells();
|
|
6512
|
+
if (!cells.length) return;
|
|
6513
|
+
const table = cells[0].closest("table");
|
|
5991
6514
|
if (!table) return;
|
|
5992
|
-
const
|
|
5993
|
-
if (
|
|
5994
|
-
const
|
|
5995
|
-
|
|
5996
|
-
const
|
|
5997
|
-
|
|
5998
|
-
|
|
6515
|
+
const tableRows = Array.from(table.querySelectorAll("tr"));
|
|
6516
|
+
if (tableRows[0] && tableRows[0].cells.length <= 1) return;
|
|
6517
|
+
const colIndices = [...new Set(cells.map((c) => getVisualColIndex(c)))];
|
|
6518
|
+
if (colIndices.length >= (tableRows[0]?.cells.length ?? 1)) return;
|
|
6519
|
+
const cellsToDelete = [];
|
|
6520
|
+
colIndices.forEach((colIdx) => {
|
|
6521
|
+
tableRows.forEach((r) => {
|
|
6522
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
6523
|
+
if (c) cellsToDelete.push(c);
|
|
6524
|
+
});
|
|
5999
6525
|
});
|
|
6526
|
+
this._activeCell = null;
|
|
6527
|
+
this._clearSelection();
|
|
6528
|
+
cellsToDelete.forEach((c) => c.parentElement?.removeChild(c));
|
|
6000
6529
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
6001
6530
|
this.context.invoke("editor.afterCommand");
|
|
6002
6531
|
}
|
|
6003
6532
|
_mergeCells() {
|
|
6004
6533
|
const cell = this._getCell();
|
|
6005
6534
|
if (!cell) return;
|
|
6006
|
-
const sel = window.getSelection();
|
|
6007
|
-
if (!sel || sel.rangeCount === 0) return;
|
|
6008
|
-
const range = sel.getRangeAt(0);
|
|
6009
6535
|
const table = cell.closest("table");
|
|
6010
6536
|
if (!table) return;
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
const rowSelected = Array.from(row.cells).filter((c) => selected.includes(c));
|
|
6023
|
-
if (rowSelected.length < 2) return;
|
|
6024
|
-
const first = rowSelected[0];
|
|
6025
|
-
first.colSpan = rowSelected.reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
6026
|
-
first.innerHTML = rowSelected.map((c) => c.innerHTML).join("");
|
|
6027
|
-
rowSelected.slice(1).forEach((c) => row.removeChild(c));
|
|
6028
|
-
} else {
|
|
6029
|
-
if ([...new Set(selected.map((c) => getVisualColIndex(c)))].length !== 1) return;
|
|
6030
|
-
const first = selected[0];
|
|
6031
|
-
first.rowSpan = selected.reduce((sum, c) => sum + (c.rowSpan || 1), 0);
|
|
6032
|
-
first.innerHTML = selected.map((c) => c.innerHTML).join("");
|
|
6033
|
-
selected.slice(1).forEach((c) => {
|
|
6034
|
-
if (c.closest("tr")) c.closest("tr").removeChild(c);
|
|
6537
|
+
let selected = this._getSelectedCells().filter((c) => table.contains(c));
|
|
6538
|
+
if (selected.length < 2) {
|
|
6539
|
+
const sel = window.getSelection();
|
|
6540
|
+
if (!sel || sel.rangeCount === 0) return;
|
|
6541
|
+
const range = sel.getRangeAt(0);
|
|
6542
|
+
selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
|
|
6543
|
+
try {
|
|
6544
|
+
return range.intersectsNode(c);
|
|
6545
|
+
} catch {
|
|
6546
|
+
return false;
|
|
6547
|
+
}
|
|
6035
6548
|
});
|
|
6549
|
+
if (selected.length < 2) return;
|
|
6550
|
+
}
|
|
6551
|
+
const { gridMap, cellPos } = buildGridMap(table);
|
|
6552
|
+
let minR = Infinity, maxR = -Infinity, minC = Infinity, maxC = -Infinity;
|
|
6553
|
+
selected.forEach((c) => {
|
|
6554
|
+
const pos = cellPos.get(c);
|
|
6555
|
+
if (!pos) return;
|
|
6556
|
+
if (pos.r < minR) minR = pos.r;
|
|
6557
|
+
if (pos.r + pos.rs - 1 > maxR) maxR = pos.r + pos.rs - 1;
|
|
6558
|
+
if (pos.c < minC) minC = pos.c;
|
|
6559
|
+
if (pos.c + pos.cs - 1 > maxC) maxC = pos.c + pos.cs - 1;
|
|
6560
|
+
});
|
|
6561
|
+
if (minR === Infinity) return;
|
|
6562
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6563
|
+
const rectCells = [];
|
|
6564
|
+
for (let r = minR; r <= maxR; r++) {
|
|
6565
|
+
const rowMap = gridMap[r];
|
|
6566
|
+
if (!rowMap) continue;
|
|
6567
|
+
for (let c = minC; c <= maxC; c++) {
|
|
6568
|
+
const tc = rowMap[c];
|
|
6569
|
+
if (tc && !seen.has(tc)) {
|
|
6570
|
+
seen.add(tc);
|
|
6571
|
+
rectCells.push(tc);
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6036
6574
|
}
|
|
6575
|
+
if (rectCells.length < 2) return;
|
|
6576
|
+
const first = rectCells[0];
|
|
6577
|
+
first.colSpan = maxC - minC + 1;
|
|
6578
|
+
first.rowSpan = maxR - minR + 1;
|
|
6579
|
+
first.style.verticalAlign = "middle";
|
|
6580
|
+
first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
|
|
6581
|
+
rectCells.slice(1).forEach((c) => c.parentElement?.removeChild(c));
|
|
6582
|
+
this._clearSelection();
|
|
6037
6583
|
this.context.invoke("editor.afterCommand");
|
|
6038
6584
|
}
|
|
6039
6585
|
_deleteTable() {
|
|
@@ -6043,6 +6589,57 @@ var TableTooltip = class {
|
|
|
6043
6589
|
if (table.parentNode) table.parentNode.removeChild(table);
|
|
6044
6590
|
this.context.invoke("editor.afterCommand");
|
|
6045
6591
|
}
|
|
6592
|
+
_unmergeCells() {
|
|
6593
|
+
const cells = this._getSelectedCells();
|
|
6594
|
+
if (!cells.length) return;
|
|
6595
|
+
const table = cells[0].closest("table");
|
|
6596
|
+
if (!table) return;
|
|
6597
|
+
const mergedCells = cells.filter((c) => table.contains(c) && ((c.colSpan || 1) > 1 || (c.rowSpan || 1) > 1));
|
|
6598
|
+
if (!mergedCells.length) return;
|
|
6599
|
+
mergedCells.forEach((cell) => {
|
|
6600
|
+
if (table.contains(cell)) this._unmergeOne(cell, table);
|
|
6601
|
+
});
|
|
6602
|
+
this._clearSelection();
|
|
6603
|
+
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
6604
|
+
this.context.invoke("editor.afterCommand");
|
|
6605
|
+
}
|
|
6606
|
+
/**
|
|
6607
|
+
* Split a single merged cell (colspan/rowspan > 1) back into individual cells.
|
|
6608
|
+
* New cells are empty ( ); the original cell retains its content.
|
|
6609
|
+
* @param {HTMLTableCellElement} cell
|
|
6610
|
+
* @param {HTMLTableElement} table
|
|
6611
|
+
*/
|
|
6612
|
+
_unmergeOne(cell, table) {
|
|
6613
|
+
const cs = cell.colSpan || 1;
|
|
6614
|
+
const rs = cell.rowSpan || 1;
|
|
6615
|
+
if (cs === 1 && rs === 1) return;
|
|
6616
|
+
const { cellPos } = buildGridMap(table);
|
|
6617
|
+
const pos = cellPos.get(cell);
|
|
6618
|
+
if (!pos) return;
|
|
6619
|
+
const { r, c } = pos;
|
|
6620
|
+
const tableRows = Array.from(table.rows);
|
|
6621
|
+
const tag = cell.tagName.toLowerCase();
|
|
6622
|
+
cell.rowSpan = 1;
|
|
6623
|
+
cell.colSpan = 1;
|
|
6624
|
+
cell.style.verticalAlign = "";
|
|
6625
|
+
if (cs > 1) {
|
|
6626
|
+
const insertRef = cell.nextElementSibling;
|
|
6627
|
+
for (let dc = 1; dc < cs; dc++) tableRows[r].insertBefore(createElement(tag, {}, ["\xA0"]), insertRef);
|
|
6628
|
+
}
|
|
6629
|
+
for (let dr = 1; dr < rs; dr++) {
|
|
6630
|
+
const targetRow = tableRows[r + dr];
|
|
6631
|
+
if (!targetRow) continue;
|
|
6632
|
+
let ref = null;
|
|
6633
|
+
for (const tc of targetRow.cells) {
|
|
6634
|
+
const tp = cellPos.get(tc);
|
|
6635
|
+
if (tp && tp.c > c) {
|
|
6636
|
+
ref = tc;
|
|
6637
|
+
break;
|
|
6638
|
+
}
|
|
6639
|
+
}
|
|
6640
|
+
for (let dc = 0; dc < cs; dc++) targetRow.insertBefore(createElement(tag, {}, ["\xA0"]), ref);
|
|
6641
|
+
}
|
|
6642
|
+
}
|
|
6046
6643
|
_buildSizePopover() {
|
|
6047
6644
|
const popover = createElement("div", { class: "an-size-popover" });
|
|
6048
6645
|
popover.style.display = "none";
|
|
@@ -6124,27 +6721,35 @@ var TableTooltip = class {
|
|
|
6124
6721
|
};
|
|
6125
6722
|
} else {
|
|
6126
6723
|
const isCol = type === "col";
|
|
6724
|
+
const activeCells = this._getSelectedCells().filter((c) => {
|
|
6725
|
+
const t = c.closest("table");
|
|
6726
|
+
return t && t === cell.closest("table");
|
|
6727
|
+
});
|
|
6127
6728
|
this._sizeTitleEl.textContent = isCol ? "Column Width (px)" : "Row Height (px)";
|
|
6128
6729
|
this._sizeInputEl.min = "1";
|
|
6129
6730
|
this._sizeInputEl.max = "2000";
|
|
6130
6731
|
this._sizeInputEl.value = isCol ? cell.offsetWidth || 120 : cell.closest("tr") ? cell.closest("tr").offsetHeight || 40 : 40;
|
|
6131
6732
|
this._sizeApply = (val) => {
|
|
6733
|
+
const table = cell.closest("table");
|
|
6734
|
+
if (!table) return;
|
|
6132
6735
|
if (isCol) {
|
|
6133
|
-
const
|
|
6134
|
-
const
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
c
|
|
6138
|
-
c.
|
|
6139
|
-
|
|
6736
|
+
const colIndices = [...new Set(activeCells.map((c) => getVisualColIndex(c)))];
|
|
6737
|
+
const tableRows = Array.from(table.querySelectorAll("tr"));
|
|
6738
|
+
colIndices.forEach((colIdx) => {
|
|
6739
|
+
tableRows.forEach((r) => {
|
|
6740
|
+
const c = getCellAtVisualCol(r, colIdx);
|
|
6741
|
+
if (c && (c.colSpan || 1) === 1) {
|
|
6742
|
+
c.style.width = `${val}px`;
|
|
6743
|
+
c.style.minWidth = `${val}px`;
|
|
6744
|
+
}
|
|
6745
|
+
});
|
|
6140
6746
|
});
|
|
6141
|
-
} else {
|
|
6142
|
-
const
|
|
6143
|
-
if (row) for (const c of row.cells) {
|
|
6747
|
+
} else [...new Set(activeCells.map((c) => c.closest("tr")).filter(Boolean))].forEach((row) => {
|
|
6748
|
+
for (const c of row.cells) {
|
|
6144
6749
|
c.style.height = `${val}px`;
|
|
6145
6750
|
c.style.minHeight = `${val}px`;
|
|
6146
6751
|
}
|
|
6147
|
-
}
|
|
6752
|
+
});
|
|
6148
6753
|
this.context.invoke("editor.afterCommand");
|
|
6149
6754
|
};
|
|
6150
6755
|
}
|
|
@@ -6201,6 +6806,7 @@ var CodeTooltip = class {
|
|
|
6201
6806
|
this._ensurePrism();
|
|
6202
6807
|
const editable = this.context.layoutInfo.editable;
|
|
6203
6808
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
6809
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
6204
6810
|
const pre = e.target.closest("pre");
|
|
6205
6811
|
if (pre && editable.contains(pre)) this._scheduleShow(pre);
|
|
6206
6812
|
}), on(editable, "mouseout", (e) => {
|
|
@@ -8998,7 +9604,13 @@ var EmojiDialog = class {
|
|
|
8998
9604
|
range.selectNodeContents(editable);
|
|
8999
9605
|
range.collapse(false);
|
|
9000
9606
|
}
|
|
9607
|
+
const _sc = range.startContainer;
|
|
9608
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
|
|
9001
9609
|
range.deleteContents();
|
|
9610
|
+
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
9611
|
+
range.setStart(_tdAnchor, 0);
|
|
9612
|
+
range.collapse(true);
|
|
9613
|
+
}
|
|
9002
9614
|
const textNode = document.createTextNode(char);
|
|
9003
9615
|
range.insertNode(textNode);
|
|
9004
9616
|
range.setStartAfter(textNode);
|
|
@@ -9567,7 +10179,13 @@ var IconDialog = class {
|
|
|
9567
10179
|
range.selectNodeContents(editable);
|
|
9568
10180
|
range.collapse(false);
|
|
9569
10181
|
}
|
|
10182
|
+
const _sc = range.startContainer;
|
|
10183
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest?.("td, th");
|
|
9570
10184
|
range.deleteContents();
|
|
10185
|
+
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
10186
|
+
range.setStart(_tdAnchor, 0);
|
|
10187
|
+
range.collapse(true);
|
|
10188
|
+
}
|
|
9571
10189
|
range.insertNode(iconEl);
|
|
9572
10190
|
let caretTextNode = iconEl.nextSibling;
|
|
9573
10191
|
if (!caretTextNode || caretTextNode.nodeType !== Node.TEXT_NODE) {
|
|
@@ -9631,22 +10249,50 @@ var ICONS = {
|
|
|
9631
10249
|
copyFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>`,
|
|
9632
10250
|
pasteFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1"/><path d="m9 14 2 2 4-4"/></svg>`,
|
|
9633
10251
|
removeFormat: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m7 21-4.3-4.3c-1-1-1-2.5 0-3.4l9.6-9.6c1-1 2.5-1 3.4 0l5.6 5.6c1 1 1 2.5 0 3.4L13 21"/><path d="M22 21H7"/><path d="m5 11 9 9"/></svg>`,
|
|
9634
|
-
table: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg
|
|
10252
|
+
table: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="1"/><line x1="3" y1="9" x2="21" y2="9"/><line x1="3" y1="15" x2="21" y2="15"/><line x1="9" y1="3" x2="9" y2="21"/><line x1="15" y1="3" x2="15" y2="21"/></svg>`,
|
|
10253
|
+
textColor: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 20L12 4L20 20"/><line x1="7.5" y1="14" x2="16.5" y2="14"/></svg>`,
|
|
10254
|
+
highlightColor: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 21v-4l9-9 4 4-9 9z"/><path d="M12 8l4 4"/><line x1="3" y1="21" x2="21" y2="21"/></svg>`,
|
|
10255
|
+
noColor: `<svg xmlns="http://www.w3.org/2000/svg" width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round"><line x1="4" y1="4" x2="20" y2="20"/><line x1="20" y1="4" x2="4" y2="20"/></svg>`,
|
|
10256
|
+
back: `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"/></svg>`
|
|
9635
10257
|
};
|
|
10258
|
+
var COLOR_PRESETS = [
|
|
10259
|
+
"#000000",
|
|
10260
|
+
"#434343",
|
|
10261
|
+
"#666666",
|
|
10262
|
+
"#999999",
|
|
10263
|
+
"#b7b7b7",
|
|
10264
|
+
"#cccccc",
|
|
10265
|
+
"#efefef",
|
|
10266
|
+
"#ffffff",
|
|
10267
|
+
"#ff0000",
|
|
10268
|
+
"#ff9900",
|
|
10269
|
+
"#ffff00",
|
|
10270
|
+
"#00ff00",
|
|
10271
|
+
"#00ffff",
|
|
10272
|
+
"#4a86e8",
|
|
10273
|
+
"#9900ff",
|
|
10274
|
+
"#ff00ff",
|
|
10275
|
+
"#f4cccc",
|
|
10276
|
+
"#fce5cd",
|
|
10277
|
+
"#fff2cc",
|
|
10278
|
+
"#d9ead3",
|
|
10279
|
+
"#d0e0e3",
|
|
10280
|
+
"#c9daf8",
|
|
10281
|
+
"#d9d2e9",
|
|
10282
|
+
"#ead1dc"
|
|
10283
|
+
];
|
|
10284
|
+
function makeColorSubItems(colorType) {
|
|
10285
|
+
const label = colorType === "foreColor" ? "Text Color" : "Highlight Color";
|
|
10286
|
+
return () => [{
|
|
10287
|
+
back: true,
|
|
10288
|
+
label,
|
|
10289
|
+
navigate: () => defaultItems
|
|
10290
|
+
}, {
|
|
10291
|
+
colorPalette: true,
|
|
10292
|
+
colorType
|
|
10293
|
+
}];
|
|
10294
|
+
}
|
|
9636
10295
|
var defaultItems = [
|
|
9637
|
-
{
|
|
9638
|
-
name: "undo",
|
|
9639
|
-
label: "Undo",
|
|
9640
|
-
icon: ICONS.undo,
|
|
9641
|
-
action: (ctx) => ctx.invoke("editor.undo")
|
|
9642
|
-
},
|
|
9643
|
-
{
|
|
9644
|
-
name: "redo",
|
|
9645
|
-
label: "Redo",
|
|
9646
|
-
icon: ICONS.redo,
|
|
9647
|
-
action: (ctx) => ctx.invoke("editor.redo")
|
|
9648
|
-
},
|
|
9649
|
-
{ separator: true },
|
|
9650
10296
|
{
|
|
9651
10297
|
name: "cut",
|
|
9652
10298
|
label: "Cut",
|
|
@@ -9707,6 +10353,21 @@ var defaultItems = [
|
|
|
9707
10353
|
action: (ctx) => ctx.invoke("editor.underline")
|
|
9708
10354
|
},
|
|
9709
10355
|
{ separator: true },
|
|
10356
|
+
{
|
|
10357
|
+
name: "textColor",
|
|
10358
|
+
label: "Text Color",
|
|
10359
|
+
icon: ICONS.textColor,
|
|
10360
|
+
colorStrip: "foreColor",
|
|
10361
|
+
navigate: makeColorSubItems("foreColor")
|
|
10362
|
+
},
|
|
10363
|
+
{
|
|
10364
|
+
name: "highlightColor",
|
|
10365
|
+
label: "Highlight Color",
|
|
10366
|
+
icon: ICONS.highlightColor,
|
|
10367
|
+
colorStrip: "hiliteColor",
|
|
10368
|
+
navigate: makeColorSubItems("hiliteColor")
|
|
10369
|
+
},
|
|
10370
|
+
{ separator: true },
|
|
9710
10371
|
{
|
|
9711
10372
|
name: "copyFormat",
|
|
9712
10373
|
label: "Copy Format",
|
|
@@ -9824,8 +10485,10 @@ var ContextMenu = class {
|
|
|
9824
10485
|
backBtn.appendChild(createElement("span", { class: "an-context-label" }, [it.label || "Back"]));
|
|
9825
10486
|
const off = on(backBtn, "click", (e) => {
|
|
9826
10487
|
e.stopPropagation();
|
|
10488
|
+
const curLeft = parseFloat(this.el.style.left);
|
|
10489
|
+
const curTop = parseFloat(this.el.style.top);
|
|
9827
10490
|
this._renderItems(it.navigate());
|
|
9828
|
-
this._reposition();
|
|
10491
|
+
this._reposition(curLeft, curTop);
|
|
9829
10492
|
});
|
|
9830
10493
|
this._menuDisposers.push(off);
|
|
9831
10494
|
this.el.appendChild(backBtn);
|
|
@@ -9837,7 +10500,19 @@ var ContextMenu = class {
|
|
|
9837
10500
|
class: "an-context-item an-context-submenu",
|
|
9838
10501
|
"data-name": it.name || ""
|
|
9839
10502
|
});
|
|
9840
|
-
if (it.icon) {
|
|
10503
|
+
if (it.icon) if (it.colorStrip) {
|
|
10504
|
+
const iconWrap = createElement("span", {
|
|
10505
|
+
class: "an-context-icon an-context-icon--color",
|
|
10506
|
+
"aria-hidden": "true"
|
|
10507
|
+
});
|
|
10508
|
+
const svgSpan = createElement("span", { class: "an-context-icon-svg" });
|
|
10509
|
+
svgSpan.innerHTML = it.icon;
|
|
10510
|
+
const strip = createElement("span", { class: "an-context-color-strip" });
|
|
10511
|
+
strip.style.background = this._getSelectionColor(it.colorStrip);
|
|
10512
|
+
iconWrap.appendChild(svgSpan);
|
|
10513
|
+
iconWrap.appendChild(strip);
|
|
10514
|
+
btn.appendChild(iconWrap);
|
|
10515
|
+
} else {
|
|
9841
10516
|
const iconSpan = createElement("span", {
|
|
9842
10517
|
class: "an-context-icon",
|
|
9843
10518
|
"aria-hidden": "true"
|
|
@@ -9854,13 +10529,63 @@ var ContextMenu = class {
|
|
|
9854
10529
|
btn.appendChild(chevron);
|
|
9855
10530
|
const off = on(btn, "click", (e) => {
|
|
9856
10531
|
e.stopPropagation();
|
|
10532
|
+
const curLeft = parseFloat(this.el.style.left);
|
|
10533
|
+
const curTop = parseFloat(this.el.style.top);
|
|
9857
10534
|
this._renderItems(it.navigate());
|
|
9858
|
-
this._reposition();
|
|
10535
|
+
this._reposition(curLeft, curTop);
|
|
9859
10536
|
});
|
|
9860
10537
|
this._menuDisposers.push(off);
|
|
9861
10538
|
this.el.appendChild(btn);
|
|
9862
10539
|
return;
|
|
9863
10540
|
}
|
|
10541
|
+
if (it.colorPalette) {
|
|
10542
|
+
const palette = createElement("div", { class: "an-context-color-palette" });
|
|
10543
|
+
COLOR_PRESETS.forEach((color) => {
|
|
10544
|
+
const sw = createElement("div", {
|
|
10545
|
+
class: "an-context-color-swatch",
|
|
10546
|
+
title: color,
|
|
10547
|
+
role: "button",
|
|
10548
|
+
"aria-label": color
|
|
10549
|
+
});
|
|
10550
|
+
sw.style.background = color;
|
|
10551
|
+
const offSw = on(sw, "click", (e) => {
|
|
10552
|
+
e.stopPropagation();
|
|
10553
|
+
this._applyColor(it.colorType, color);
|
|
10554
|
+
});
|
|
10555
|
+
this._menuDisposers.push(offSw);
|
|
10556
|
+
palette.appendChild(sw);
|
|
10557
|
+
});
|
|
10558
|
+
if (it.colorType === "hiliteColor") {
|
|
10559
|
+
const noColor = createElement("div", {
|
|
10560
|
+
class: "an-context-color-swatch an-context-color-none",
|
|
10561
|
+
title: "No highlight",
|
|
10562
|
+
role: "button",
|
|
10563
|
+
"aria-label": "No highlight"
|
|
10564
|
+
});
|
|
10565
|
+
noColor.innerHTML = ICONS.noColor;
|
|
10566
|
+
const offNo = on(noColor, "click", (e) => {
|
|
10567
|
+
e.stopPropagation();
|
|
10568
|
+
this._applyColor("hiliteColor", "transparent");
|
|
10569
|
+
});
|
|
10570
|
+
this._menuDisposers.push(offNo);
|
|
10571
|
+
palette.appendChild(noColor);
|
|
10572
|
+
}
|
|
10573
|
+
this.el.appendChild(palette);
|
|
10574
|
+
const customRow = createElement("div", { class: "an-context-color-custom" });
|
|
10575
|
+
const colorInput = createElement("input", {
|
|
10576
|
+
type: "color",
|
|
10577
|
+
value: it.colorType === "foreColor" ? "#000000" : "#ffff00",
|
|
10578
|
+
title: "Custom color",
|
|
10579
|
+
"aria-label": "Custom color"
|
|
10580
|
+
});
|
|
10581
|
+
const customLabel = createElement("span", {}, ["Custom…"]);
|
|
10582
|
+
const offCustom = on(colorInput, "change", () => this._applyColor(it.colorType, colorInput.value));
|
|
10583
|
+
this._menuDisposers.push(offCustom);
|
|
10584
|
+
customRow.appendChild(colorInput);
|
|
10585
|
+
customRow.appendChild(customLabel);
|
|
10586
|
+
this.el.appendChild(customRow);
|
|
10587
|
+
return;
|
|
10588
|
+
}
|
|
9864
10589
|
if (it.tableGrid) {
|
|
9865
10590
|
const GRID_ROWS = 8, GRID_COLS = 8;
|
|
9866
10591
|
const wrapper = createElement("div", { class: "an-context-table-wrap" });
|
|
@@ -9978,6 +10703,7 @@ var ContextMenu = class {
|
|
|
9978
10703
|
const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
|
|
9979
10704
|
if (!editable) return;
|
|
9980
10705
|
if (!editable.contains(event.target)) return;
|
|
10706
|
+
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
9981
10707
|
event.preventDefault();
|
|
9982
10708
|
this._lastX = event.clientX;
|
|
9983
10709
|
this._lastY = event.clientY;
|
|
@@ -10004,7 +10730,19 @@ var ContextMenu = class {
|
|
|
10004
10730
|
let left = rx;
|
|
10005
10731
|
let top = ry;
|
|
10006
10732
|
if (left + rect.width > window.innerWidth) left = window.innerWidth - rect.width - 8;
|
|
10733
|
+
if (left < 8) left = 8;
|
|
10007
10734
|
if (top + rect.height > window.innerHeight) top = window.innerHeight - rect.height - 8;
|
|
10735
|
+
if (top < 8) top = 8;
|
|
10736
|
+
if (this._savedRange) try {
|
|
10737
|
+
const sel = this._savedRange.getBoundingClientRect();
|
|
10738
|
+
if (sel.width > 0 || sel.height > 0) {
|
|
10739
|
+
if (top < sel.bottom && top + rect.height > sel.top && left < sel.right && left + rect.width > sel.left) {
|
|
10740
|
+
const belowTop = sel.bottom + 6;
|
|
10741
|
+
if (belowTop + rect.height <= window.innerHeight - 8) top = belowTop;
|
|
10742
|
+
else top = Math.max(8, sel.top - rect.height - 6);
|
|
10743
|
+
}
|
|
10744
|
+
}
|
|
10745
|
+
} catch (_) {}
|
|
10008
10746
|
this.el.style.left = `${left}px`;
|
|
10009
10747
|
this.el.style.top = `${top}px`;
|
|
10010
10748
|
}
|
|
@@ -10013,6 +10751,33 @@ var ContextMenu = class {
|
|
|
10013
10751
|
this.el.style.display = "none";
|
|
10014
10752
|
this.el.setAttribute("aria-hidden", "true");
|
|
10015
10753
|
}
|
|
10754
|
+
/** Read the current selection's text or highlight color for the strip.
|
|
10755
|
+
* @param {'foreColor'|'hiliteColor'} type
|
|
10756
|
+
* @returns {string} CSS color string
|
|
10757
|
+
*/
|
|
10758
|
+
_getSelectionColor(type) {
|
|
10759
|
+
const range = this._savedRange;
|
|
10760
|
+
if (!range) return type === "foreColor" ? "#000000" : "transparent";
|
|
10761
|
+
let node = range.startContainer;
|
|
10762
|
+
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
10763
|
+
if (!node) return type === "foreColor" ? "#000000" : "transparent";
|
|
10764
|
+
const cs = window.getComputedStyle(node);
|
|
10765
|
+
if (type === "foreColor") return cs.color || "#000000";
|
|
10766
|
+
const bg = cs.backgroundColor;
|
|
10767
|
+
return !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
10768
|
+
}
|
|
10769
|
+
/** Restore selection, apply a color command, then hide the menu. */
|
|
10770
|
+
_applyColor(type, color) {
|
|
10771
|
+
const editable = this.context.layoutInfo && this.context.layoutInfo.editable;
|
|
10772
|
+
if (!editable || !this._savedRange) return;
|
|
10773
|
+
editable.focus();
|
|
10774
|
+
const sel = window.getSelection();
|
|
10775
|
+
sel.removeAllRanges();
|
|
10776
|
+
sel.addRange(this._savedRange.cloneRange());
|
|
10777
|
+
document.execCommand(type, false, color);
|
|
10778
|
+
this.context.invoke("editor.afterCommand");
|
|
10779
|
+
this.hide();
|
|
10780
|
+
}
|
|
10016
10781
|
/** Returns true if a format has been copied — used to disable Paste Format. */
|
|
10017
10782
|
hasCopiedFormat() {
|
|
10018
10783
|
return !!this._copiedFormat;
|
|
@@ -10680,7 +11445,7 @@ function drawCropToCanvas(img, naturalRect, renderW, renderH) {
|
|
|
10680
11445
|
resolve(null);
|
|
10681
11446
|
}
|
|
10682
11447
|
};
|
|
10683
|
-
if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(location.origin)) {
|
|
11448
|
+
if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(window.location.origin)) {
|
|
10684
11449
|
tryDraw(img);
|
|
10685
11450
|
return;
|
|
10686
11451
|
}
|
|
@@ -11056,7 +11821,7 @@ var ImageCropOverlay = class {
|
|
|
11056
11821
|
height: natH
|
|
11057
11822
|
}, w, h);
|
|
11058
11823
|
if (!canvas) {
|
|
11059
|
-
alert("Cannot crop this image: the image server does not allow cross-origin access.\nUpload the image directly to use the crop tool.");
|
|
11824
|
+
window.alert("Cannot crop this image: the image server does not allow cross-origin access.\nUpload the image directly to use the crop tool.");
|
|
11060
11825
|
this._close(false);
|
|
11061
11826
|
return;
|
|
11062
11827
|
}
|
|
@@ -11417,9 +12182,15 @@ var Context = class {
|
|
|
11417
12182
|
if (disabled) {
|
|
11418
12183
|
editable.setAttribute("contenteditable", "false");
|
|
11419
12184
|
this.layoutInfo.container.classList.add("an-disabled");
|
|
12185
|
+
editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
|
|
12186
|
+
cb.setAttribute("disabled", "");
|
|
12187
|
+
});
|
|
11420
12188
|
} else {
|
|
11421
12189
|
editable.setAttribute("contenteditable", "true");
|
|
11422
12190
|
this.layoutInfo.container.classList.remove("an-disabled");
|
|
12191
|
+
editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
|
|
12192
|
+
cb.removeAttribute("disabled");
|
|
12193
|
+
});
|
|
11423
12194
|
}
|
|
11424
12195
|
}
|
|
11425
12196
|
/**
|