autumnnote 1.4.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +102 -21
- package/dist/autumnnote.css +324 -2
- package/dist/autumnnote.es.js +700 -228
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +691 -235
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +8 -1
- package/src/js/Context.js +8 -3
- package/src/js/core/detectLang.js +98 -0
- package/src/js/core/dom.js +62 -6
- package/src/js/core/markdown.js +14 -13
- package/src/js/core/range.js +2 -2
- package/src/js/editing/History.js +6 -6
- package/src/js/editing/Style.js +19 -19
- package/src/js/editing/Table.js +4 -6
- package/src/js/editing/Typing.js +6 -6
- package/src/js/i18n/en.js +2 -0
- package/src/js/i18n/vi.js +2 -0
- package/src/js/index.js +4 -3
- package/src/js/module/BubbleToolbar.js +30 -13
- package/src/js/module/Buttons.js +16 -14
- package/src/js/module/Clipboard.js +5 -5
- package/src/js/module/CodeTooltip.js +42 -16
- package/src/js/module/Codeview.js +2 -2
- package/src/js/module/ContextMenu.js +12 -12
- package/src/js/module/Editor.js +64 -12
- package/src/js/module/EmojiDialog.js +19 -13
- package/src/js/module/FindReplace.js +85 -59
- package/src/js/module/Fullscreen.js +1 -1
- package/src/js/module/IconDialog.js +26 -20
- package/src/js/module/ImageCropOverlay.js +6 -6
- package/src/js/module/ImageDialog.js +18 -12
- package/src/js/module/ImageResizer.js +1 -1
- package/src/js/module/ImageTooltip.js +8 -4
- package/src/js/module/LinkDialog.js +18 -12
- package/src/js/module/LinkTooltip.js +5 -2
- package/src/js/module/Mention.js +3 -3
- package/src/js/module/Statusbar.js +2 -2
- package/src/js/module/TableTooltip.js +180 -18
- package/src/js/module/Toolbar.js +16 -15
- package/src/js/module/VideoDialog.js +8 -2
- package/src/js/module/VideoResizer.js +3 -3
- package/src/js/module/VideoTooltip.js +11 -6
- package/src/js/renderer.js +4 -2
- package/src/js/settings.js +53 -36
- package/src/styles/autumnnote.scss +332 -4
package/dist/autumnnote.umd.js
CHANGED
|
@@ -125,16 +125,60 @@
|
|
|
125
125
|
if (e.shiftKey) {
|
|
126
126
|
if (document.activeElement === first) {
|
|
127
127
|
e.preventDefault();
|
|
128
|
-
last.focus();
|
|
128
|
+
/** @type {HTMLElement} */ last.focus();
|
|
129
129
|
}
|
|
130
130
|
} else if (document.activeElement === last) {
|
|
131
131
|
e.preventDefault();
|
|
132
|
-
first.focus();
|
|
132
|
+
/** @type {HTMLElement} */ first.focus();
|
|
133
133
|
}
|
|
134
134
|
};
|
|
135
135
|
document.addEventListener("keydown", handler);
|
|
136
136
|
return () => document.removeEventListener("keydown", handler);
|
|
137
137
|
}
|
|
138
|
+
/**
|
|
139
|
+
* Makes a dialog box draggable by its handle element.
|
|
140
|
+
* On first drag the box is pinned to its current viewport coordinates via
|
|
141
|
+
* `position:fixed`, freeing it from the parent flex container's centering.
|
|
142
|
+
* The position is clamped to the visible viewport.
|
|
143
|
+
*
|
|
144
|
+
* @param {HTMLElement} handle Element the user grabs (title bar / header)
|
|
145
|
+
* @param {HTMLElement} box Element that actually moves
|
|
146
|
+
* @returns {Function} Cleanup function (removes the mousedown listener)
|
|
147
|
+
*/
|
|
148
|
+
function makeDraggable(handle, box) {
|
|
149
|
+
handle.style.cursor = "grab";
|
|
150
|
+
const onMousedown = (e) => {
|
|
151
|
+
if (e.button !== 0) return;
|
|
152
|
+
if (e.target.closest("button, input, select, textarea, a")) return;
|
|
153
|
+
e.preventDefault();
|
|
154
|
+
if (!box.dataset.anDragPinned) {
|
|
155
|
+
const r = box.getBoundingClientRect();
|
|
156
|
+
box.style.position = "fixed";
|
|
157
|
+
box.style.margin = "0";
|
|
158
|
+
box.style.left = `${r.left}px`;
|
|
159
|
+
box.style.top = `${r.top}px`;
|
|
160
|
+
box.dataset.anDragPinned = "1";
|
|
161
|
+
}
|
|
162
|
+
const startX = e.clientX - parseFloat(box.style.left);
|
|
163
|
+
const startY = e.clientY - parseFloat(box.style.top);
|
|
164
|
+
handle.style.cursor = "grabbing";
|
|
165
|
+
const onMove = (ev) => {
|
|
166
|
+
const bw = box.offsetWidth;
|
|
167
|
+
const bh = box.offsetHeight;
|
|
168
|
+
box.style.left = `${Math.max(0, Math.min(ev.clientX - startX, window.innerWidth - bw))}px`;
|
|
169
|
+
box.style.top = `${Math.max(0, Math.min(ev.clientY - startY, window.innerHeight - bh))}px`;
|
|
170
|
+
};
|
|
171
|
+
const onUp = () => {
|
|
172
|
+
handle.style.cursor = "grab";
|
|
173
|
+
document.removeEventListener("mousemove", onMove);
|
|
174
|
+
document.removeEventListener("mouseup", onUp);
|
|
175
|
+
};
|
|
176
|
+
document.addEventListener("mousemove", onMove);
|
|
177
|
+
document.addEventListener("mouseup", onUp);
|
|
178
|
+
};
|
|
179
|
+
handle.addEventListener("mousedown", onMousedown);
|
|
180
|
+
return () => handle.removeEventListener("mousedown", onMousedown);
|
|
181
|
+
}
|
|
138
182
|
//#endregion
|
|
139
183
|
//#region src/js/core/range.js
|
|
140
184
|
/**
|
|
@@ -280,7 +324,7 @@
|
|
|
280
324
|
if (!sel || !sel.rangeCount) return;
|
|
281
325
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
282
326
|
if (container.nodeType === 3) container = container.parentElement;
|
|
283
|
-
const uEl = container && container.closest
|
|
327
|
+
const uEl = container && container.closest("u");
|
|
284
328
|
const nativeState = document.queryCommandState("underline");
|
|
285
329
|
if (uEl && !nativeState) {
|
|
286
330
|
const parent = uEl.parentNode;
|
|
@@ -300,7 +344,7 @@
|
|
|
300
344
|
if (!sel || !sel.rangeCount) return;
|
|
301
345
|
let sc = sel.getRangeAt(0).startContainer;
|
|
302
346
|
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
303
|
-
const sEl = sc &&
|
|
347
|
+
const sEl = sc && (sc.closest("s") || sc.closest("strike"));
|
|
304
348
|
const nativeState = document.queryCommandState("strikeThrough");
|
|
305
349
|
if (sEl && !nativeState) {
|
|
306
350
|
const parent = sEl.parentNode;
|
|
@@ -337,7 +381,7 @@
|
|
|
337
381
|
* Sets the font size (in pt or with unit) for the selection.
|
|
338
382
|
* Uses a span-based approach to set px sizes precisely.
|
|
339
383
|
* @param {string} size - e.g. '14px'
|
|
340
|
-
* @param {HTMLElement} [editable] - scoping element to avoid touching nodes outside this editor
|
|
384
|
+
* @param {HTMLElement|Document} [editable] - scoping element to avoid touching nodes outside this editor
|
|
341
385
|
*/
|
|
342
386
|
function fontSize(size, editable = document) {
|
|
343
387
|
const sel = window.getSelection();
|
|
@@ -419,7 +463,7 @@
|
|
|
419
463
|
if (sel && sel.rangeCount) {
|
|
420
464
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
421
465
|
if (container.nodeType === 3) container = container.parentElement;
|
|
422
|
-
const checkLi = container && container.closest
|
|
466
|
+
const checkLi = container && container.closest(".an-checklist li");
|
|
423
467
|
if (checkLi) {
|
|
424
468
|
_checklistItemToP(checkLi);
|
|
425
469
|
return;
|
|
@@ -541,15 +585,15 @@
|
|
|
541
585
|
/**
|
|
542
586
|
* Wraps the selection in an inline <code> element, or unwraps it if the
|
|
543
587
|
* cursor is already inside a <code> that is not inside a <pre>.
|
|
544
|
-
* @param {HTMLElement} [
|
|
588
|
+
* @param {HTMLElement} [_editable]
|
|
545
589
|
*/
|
|
546
|
-
function toggleInlineCode(
|
|
590
|
+
function toggleInlineCode(_editable) {
|
|
547
591
|
const sel = window.getSelection();
|
|
548
592
|
if (!sel || !sel.rangeCount) return;
|
|
549
593
|
const range = sel.getRangeAt(0);
|
|
550
594
|
let container = range.commonAncestorContainer;
|
|
551
595
|
if (container.nodeType === 3) container = container.parentElement;
|
|
552
|
-
const codeEl = container && container.closest
|
|
596
|
+
const codeEl = container && container.closest("code");
|
|
553
597
|
if (codeEl && !codeEl.closest("pre")) {
|
|
554
598
|
const parent = codeEl.parentNode;
|
|
555
599
|
const prevSibling = codeEl.previousSibling;
|
|
@@ -604,7 +648,7 @@
|
|
|
604
648
|
if (!sel || !sel.rangeCount) return false;
|
|
605
649
|
let sc = sel.getRangeAt(0).startContainer;
|
|
606
650
|
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
607
|
-
const code = sc && sc.closest
|
|
651
|
+
const code = sc && sc.closest("code");
|
|
608
652
|
return !!(code && !code.closest("pre"));
|
|
609
653
|
}
|
|
610
654
|
/**
|
|
@@ -627,11 +671,11 @@
|
|
|
627
671
|
const range = sel.getRangeAt(0);
|
|
628
672
|
let container = range.commonAncestorContainer;
|
|
629
673
|
if (container.nodeType === 3) container = container.parentElement;
|
|
630
|
-
const ul = container
|
|
674
|
+
const ul = container && container.closest(".an-checklist");
|
|
631
675
|
if (ul) {
|
|
632
676
|
const selectedLis = Array.from(ul.querySelectorAll("li")).filter((li) => sel.containsNode(li, true));
|
|
633
677
|
if (selectedLis.length > 0) {
|
|
634
|
-
let firstP = null;
|
|
678
|
+
/** @type {HTMLElement|null} */ let firstP = null;
|
|
635
679
|
selectedLis.forEach((li) => {
|
|
636
680
|
const p = document.createElement("p");
|
|
637
681
|
for (const child of li.childNodes) {
|
|
@@ -729,7 +773,7 @@
|
|
|
729
773
|
if (blocks.length === 0) return;
|
|
730
774
|
const newUl = document.createElement("ul");
|
|
731
775
|
newUl.className = "an-checklist";
|
|
732
|
-
let lastTextNode = null;
|
|
776
|
+
/** @type {Text|null} */ let lastTextNode = null;
|
|
733
777
|
blocks.forEach((block) => {
|
|
734
778
|
const li = document.createElement("li");
|
|
735
779
|
const cb = document.createElement("input");
|
|
@@ -762,7 +806,7 @@
|
|
|
762
806
|
if (!sel || !sel.rangeCount) return false;
|
|
763
807
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
764
808
|
if (container.nodeType === 3) container = container.parentElement;
|
|
765
|
-
return !!(container && container.closest
|
|
809
|
+
return !!(container && container.closest(".an-checklist li"));
|
|
766
810
|
}
|
|
767
811
|
//#endregion
|
|
768
812
|
//#region src/js/module/Buttons.js
|
|
@@ -773,12 +817,14 @@
|
|
|
773
817
|
*/
|
|
774
818
|
/**
|
|
775
819
|
* @typedef {object} DropdownDef
|
|
776
|
-
* @property {string} name
|
|
777
|
-
* @property {'select'} type
|
|
820
|
+
* @property {string} name - unique identifier
|
|
821
|
+
* @property {'select'} type - discriminator for Toolbar renderer
|
|
778
822
|
* @property {string} tooltip
|
|
779
|
-
* @property {string
|
|
780
|
-
* @property {Function} action
|
|
781
|
-
* @property {Function} [getValue]
|
|
823
|
+
* @property {Array<string|{value:string,label:string,disabled?:boolean}>} [items] - overridden at render time from options
|
|
824
|
+
* @property {Function} action - called with (context, value)
|
|
825
|
+
* @property {Function} [getValue] - called with (context) to get current value
|
|
826
|
+
* @property {string} [selectClass] - extra CSS class(es) for the <select>
|
|
827
|
+
* @property {string} [placeholder] - placeholder text for the empty option
|
|
782
828
|
*/
|
|
783
829
|
/**
|
|
784
830
|
* @typedef {object} ButtonDef
|
|
@@ -846,7 +892,7 @@
|
|
|
846
892
|
if (!sel || !sel.rangeCount) return false;
|
|
847
893
|
let sc = sel.getRangeAt(0).startContainer;
|
|
848
894
|
if (sc.nodeType === 3) sc = sc.parentElement;
|
|
849
|
-
return !!(sc && sc.closest
|
|
895
|
+
return !!(sc && sc.closest("u"));
|
|
850
896
|
});
|
|
851
897
|
var strikeBtn = btn("strikethrough", "strikethrough", "Strikethrough", () => strikethrough(), () => document.queryCommandState("strikeThrough"));
|
|
852
898
|
var superscriptBtn = btn("superscript", "superscript", "Superscript", () => superscript(), () => document.queryCommandState("superscript"));
|
|
@@ -908,9 +954,9 @@
|
|
|
908
954
|
const sel = window.getSelection();
|
|
909
955
|
if (sel && sel.rangeCount) {
|
|
910
956
|
let el = sel.getRangeAt(0).startContainer;
|
|
911
|
-
if (el.nodeType === 3) el = el.parentElement;
|
|
957
|
+
if (el && el.nodeType === 3) el = el.parentElement;
|
|
912
958
|
while (el && el.nodeType === 1 && !el.style.fontSize) el = el.parentElement;
|
|
913
|
-
const size = el && el.style
|
|
959
|
+
const size = el && el.style.fontSize ? el.style.fontSize : "";
|
|
914
960
|
if (size) return size;
|
|
915
961
|
}
|
|
916
962
|
const editable = ctx && ctx.layoutInfo && ctx.layoutInfo.editable;
|
|
@@ -922,13 +968,6 @@
|
|
|
922
968
|
}
|
|
923
969
|
};
|
|
924
970
|
var removeFormatBtn = btn("removeFormat", "remove-format", "Remove Format", () => execCommand("removeFormat"));
|
|
925
|
-
btn("direction", "direction", "Toggle Text Direction (LTR / RTL)", (ctx) => {
|
|
926
|
-
const editable = ctx.layoutInfo.editable;
|
|
927
|
-
const next = (editable.getAttribute("dir") || "ltr") === "ltr" ? "rtl" : "ltr";
|
|
928
|
-
editable.setAttribute("dir", next);
|
|
929
|
-
editable.style.textAlign = next === "rtl" ? "right" : "left";
|
|
930
|
-
ctx.invoke("editor.afterCommand");
|
|
931
|
-
});
|
|
932
971
|
/** @type {DropdownDef} */
|
|
933
972
|
var fontFamilyBtn = {
|
|
934
973
|
name: "fontFamily",
|
|
@@ -1035,8 +1074,11 @@
|
|
|
1035
1074
|
"TH"
|
|
1036
1075
|
]);
|
|
1037
1076
|
let el = sel.getRangeAt(0).startContainer;
|
|
1038
|
-
if (el.nodeType === 3) el = el.parentElement;
|
|
1039
|
-
while (el && !BLOCKS.has(
|
|
1077
|
+
if (el && el.nodeType === 3) el = el.parentElement;
|
|
1078
|
+
while (el && !BLOCKS.has(
|
|
1079
|
+
/** @type {Element} */
|
|
1080
|
+
el.tagName
|
|
1081
|
+
)) el = el.parentElement;
|
|
1040
1082
|
if (!el) return "";
|
|
1041
1083
|
return el.style.lineHeight || getComputedStyle(el).lineHeight || "";
|
|
1042
1084
|
} catch {
|
|
@@ -1048,7 +1090,6 @@
|
|
|
1048
1090
|
var fullscreenBtn = btn("fullscreen", "expand", "Fullscreen", (ctx) => ctx.invoke("fullscreen.toggle"), (ctx) => ctx.invoke("fullscreen.isActive"));
|
|
1049
1091
|
var shortcutsBtn = btn("shortcuts", "keyboard", "Keyboard Shortcuts (Ctrl+Shift+/)", (ctx) => ctx.invoke("shortcutsDialog.show"));
|
|
1050
1092
|
var findBtn = btn("find", "search", "Find (Ctrl+F)", (ctx) => ctx.invoke("findReplace.show", "find"));
|
|
1051
|
-
btn("findReplace", "find-replace", "Find & Replace (Ctrl+H)", (ctx) => ctx.invoke("findReplace.show", "replace"));
|
|
1052
1093
|
var inlineCodeBtn = btn("inlineCode", "inline-code", "Inline Code (Ctrl+`)", (ctx) => ctx.invoke("editor.inlineCode"), () => isInlineCode());
|
|
1053
1094
|
var checklistBtn = btn("checklist", "checklist", "Checklist", (ctx) => ctx.invoke("editor.toggleChecklist"), () => isInChecklist());
|
|
1054
1095
|
var printBtn = btn("print", "print", "Print", (ctx) => ctx.invoke("editor.print"));
|
|
@@ -1060,47 +1101,64 @@
|
|
|
1060
1101
|
*/
|
|
1061
1102
|
/**
|
|
1062
1103
|
* @typedef {object} AsnOptions
|
|
1063
|
-
* @property {string} [placeholder]
|
|
1064
|
-
* @property {number} [height]
|
|
1065
|
-
* @property {number} [minHeight]
|
|
1066
|
-
* @property {number} [maxHeight]
|
|
1067
|
-
* @property {boolean} [focus]
|
|
1068
|
-
* @property {boolean} [resizable]
|
|
1069
|
-
* @property {Array} [toolbar]
|
|
1070
|
-
* @property {boolean} [
|
|
1071
|
-
* @property {
|
|
1104
|
+
* @property {string} [placeholder] - Placeholder text when editor is empty
|
|
1105
|
+
* @property {number} [height] - Editor height in px (min)
|
|
1106
|
+
* @property {number} [minHeight] - Minimum height in px
|
|
1107
|
+
* @property {number} [maxHeight] - Maximum height in px (0 = unlimited)
|
|
1108
|
+
* @property {boolean} [focus] - Auto-focus on init
|
|
1109
|
+
* @property {boolean} [resizable] - Show resize handle
|
|
1110
|
+
* @property {Array} [toolbar] - Toolbar button group config
|
|
1111
|
+
* @property {boolean} [useBootstrap] - Use Bootstrap button classes on toolbar buttons
|
|
1112
|
+
* @property {string} [toolbarButtonClass] - CSS classes for Bootstrap toolbar buttons
|
|
1113
|
+
* @property {boolean} [useFontAwesome] - Use Font Awesome icons (default: true)
|
|
1114
|
+
* @property {string} [fontAwesomeClass] - Font Awesome prefix class, e.g. 'fas' or 'fa-solid'
|
|
1115
|
+
* @property {boolean} [pasteAsPlainText] - Force plain-text paste
|
|
1116
|
+
* @property {boolean} [pasteCleanHTML] - Sanitise HTML on paste
|
|
1072
1117
|
* @property {boolean} [pasteStripAttributes] - Strip class/style/data-* from pasted HTML (default: false)
|
|
1073
|
-
* @property {boolean} [allowImageUpload]
|
|
1074
|
-
* @property {number} [maxImageSize]
|
|
1075
|
-
* @property {number} [tabSize]
|
|
1076
|
-
* @property {
|
|
1077
|
-
* @property {
|
|
1078
|
-
* @property {
|
|
1079
|
-
* @property {
|
|
1080
|
-
* @property {
|
|
1081
|
-
* @property {
|
|
1082
|
-
* @property {
|
|
1083
|
-
* @property {
|
|
1084
|
-
* @property {
|
|
1085
|
-
* @property {
|
|
1086
|
-
* @property {boolean} [
|
|
1087
|
-
* @property {
|
|
1088
|
-
* @property {string} [
|
|
1089
|
-
* @property {
|
|
1090
|
-
* @property {
|
|
1091
|
-
* @property {
|
|
1092
|
-
* @property {
|
|
1093
|
-
* @property {
|
|
1094
|
-
* @property {
|
|
1095
|
-
* @property {
|
|
1096
|
-
* @property {
|
|
1097
|
-
* @property {string
|
|
1118
|
+
* @property {boolean} [allowImageUpload] - Allow file upload in image dialog
|
|
1119
|
+
* @property {number} [maxImageSize] - Max upload size in MB
|
|
1120
|
+
* @property {number} [tabSize] - Spaces per tab in non-list context
|
|
1121
|
+
* @property {number} [historyLimit] - Maximum undo/redo history steps
|
|
1122
|
+
* @property {string} [defaultFontFamily] - Default font family applied to the editable area on init
|
|
1123
|
+
* @property {string} [defaultFontSize] - Default font size applied to the editable area on init (e.g. '14px')
|
|
1124
|
+
* @property {string[]} [fontFamilies] - Font families shown in the font-family toolbar dropdown
|
|
1125
|
+
* @property {Function} [onChange] - Callback on content change
|
|
1126
|
+
* @property {Function} [onFocus] - Callback on focus
|
|
1127
|
+
* @property {Function} [onBlur] - Callback on blur
|
|
1128
|
+
* @property {Function} [onInit] - Callback after the editor has initialised
|
|
1129
|
+
* @property {Function} [onImageUpload] - Custom upload handler: (files) => void
|
|
1130
|
+
* @property {Function} [onImageError] - Callback when an image upload error occurs
|
|
1131
|
+
* @property {boolean} [stickyToolbar] - Stick the toolbar to the viewport top when scrolling
|
|
1132
|
+
* @property {number} [stickyToolbarOffset] - Top offset in px for sticky toolbar (e.g. fixed nav height)
|
|
1133
|
+
* @property {string} [theme] - 'light' (default) | 'dark'
|
|
1134
|
+
* @property {boolean} [codeHighlight] - Auto-load Prism.js for syntax highlighting of code blocks
|
|
1135
|
+
* @property {string} [codeHighlightCDN] - CDN base URL for Prism assets (defaults to cdnjs)
|
|
1136
|
+
* @property {boolean} [markdownPaste] - Convert pasted Markdown text to HTML (default: true)
|
|
1137
|
+
* @property {boolean} [readOnly] - Start editor in read-only / non-editable mode
|
|
1138
|
+
* @property {boolean} [spellcheck] - Enable browser spellcheck in the editable area (default: true)
|
|
1139
|
+
* @property {string} [direction] - Text direction: 'ltr' (default) | 'rtl'
|
|
1140
|
+
* @property {string} [toolbarOverflow] - Toolbar overflow strategy: 'wrap' (default) | 'scroll'
|
|
1141
|
+
* @property {boolean} [autoSave] - Auto-save content to localStorage on change
|
|
1142
|
+
* @property {string} [autoSaveKey] - localStorage key used for auto-save (default: 'autumnnote-autosave')
|
|
1143
|
+
* @property {number} [maxChars] - Maximum character count (0 = unlimited). Shows warning in statusbar.
|
|
1144
|
+
* @property {number} [maxWords] - Maximum word count (0 = unlimited). Shows warning in statusbar.
|
|
1145
|
+
* @property {boolean} [tableHeaderRow] - Insert a header row (<thead><th>) when creating tables
|
|
1146
|
+
* @property {Function} [onPaste] - Callback fired on every paste: ({ text, html }) => void
|
|
1147
|
+
* @property {Function} [onSelectionChange] - Callback fired on cursor/selection change: (context) => void
|
|
1148
|
+
* @property {string[]} [colorSwatches] - Custom brand colour swatches prepended to the colour-picker palette
|
|
1098
1149
|
* @property {Function} [onDestroy] - Callback fired when the editor is destroyed: (context) => void
|
|
1099
1150
|
* @property {Function} [onCharLimitReached] - Callback fired when the character limit is hit: (context) => void
|
|
1100
1151
|
* @property {Function} [onWordLimitReached] - Callback fired when the word limit is hit: (context) => void
|
|
1101
|
-
* @property {string} [focusColor]
|
|
1152
|
+
* @property {string} [focusColor] - Custom focus ring colour, e.g. '#f97316'. Overrides the default blue.
|
|
1153
|
+
* @property {boolean} [autoSaveRestore] - Show a restore banner when a previously auto-saved draft exists
|
|
1154
|
+
* @property {number} [autoSaveRestoreTimeout] - Maximum age in days for a draft to be offered for restore (0 = no expiry)
|
|
1155
|
+
* @property {Function} [onAutoSaveRestore] - Callback fired after the user chooses to restore a draft
|
|
1156
|
+
* @property {boolean} [markdownShortcuts] - Convert markdown syntax typed inline to HTML
|
|
1157
|
+
* @property {boolean} [bubbleToolbar] - Show a mini floating toolbar above text selections
|
|
1158
|
+
* @property {string[]} [bubbleToolbarItems] - Button names for the bubble toolbar
|
|
1159
|
+
* @property {object|null} [mention] - @mention configuration (onSearch, minChars, ...)
|
|
1160
|
+
* @property {string} [lang] - Display language or partial locale object override
|
|
1102
1161
|
*/
|
|
1103
|
-
/** @type {AsnOptions} */
|
|
1104
1162
|
var defaultOptions = {
|
|
1105
1163
|
placeholder: "",
|
|
1106
1164
|
height: 200,
|
|
@@ -1295,6 +1353,7 @@
|
|
|
1295
1353
|
chooseHighlightColor: "Choose highlight color",
|
|
1296
1354
|
customColor: "Custom color",
|
|
1297
1355
|
insertTableLabel: "Insert Table",
|
|
1356
|
+
/** Map of paragraph-style value → label (only values needing translation) */
|
|
1298
1357
|
paragraphItems: {
|
|
1299
1358
|
p: "Normal",
|
|
1300
1359
|
blockquote: "Quote",
|
|
@@ -1337,6 +1396,7 @@
|
|
|
1337
1396
|
widthPlaceholder: "560",
|
|
1338
1397
|
insertBtn: "Insert",
|
|
1339
1398
|
cancelBtn: "Cancel",
|
|
1399
|
+
/** @param {string} type */
|
|
1340
1400
|
detected: (type) => `Detected: ${type}`,
|
|
1341
1401
|
unknownFormat: "Unknown format — will try direct video embed",
|
|
1342
1402
|
invalidUrl: "Invalid URL — please enter a valid video link."
|
|
@@ -1499,9 +1559,13 @@
|
|
|
1499
1559
|
},
|
|
1500
1560
|
statusbar: {
|
|
1501
1561
|
resizeHandle: "Resize editor",
|
|
1562
|
+
/** @param {number} n */
|
|
1502
1563
|
words: (n) => `Words: ${n}`,
|
|
1564
|
+
/** @param {number} n @param {number} max */
|
|
1503
1565
|
wordsLimit: (n, max) => `Words: ${n}/${max}`,
|
|
1566
|
+
/** @param {number} n */
|
|
1504
1567
|
chars: (n) => `Chars: ${n}`,
|
|
1568
|
+
/** @param {number} n @param {number} max */
|
|
1505
1569
|
charsLimit: (n, max) => `Chars: ${n}/${max}`
|
|
1506
1570
|
},
|
|
1507
1571
|
tooltips: {
|
|
@@ -1554,6 +1618,8 @@
|
|
|
1554
1618
|
rowHeight: "Row Height",
|
|
1555
1619
|
tableBorderWidth: "Table Border Width",
|
|
1556
1620
|
deleteTable: "Delete Table",
|
|
1621
|
+
cellBackground: "Cell Background",
|
|
1622
|
+
noShading: "No Shading",
|
|
1557
1623
|
columnWidthPx: "Column Width (px)",
|
|
1558
1624
|
rowHeightPx: "Row Height (px)",
|
|
1559
1625
|
tableBorderWidthPx: "Table Border Width (px)",
|
|
@@ -1574,7 +1640,9 @@
|
|
|
1574
1640
|
}
|
|
1575
1641
|
},
|
|
1576
1642
|
errors: {
|
|
1643
|
+
/** @param {string} type */
|
|
1577
1644
|
imageFormat: (type) => `Format "${type}" is not supported for display in web browsers. Please convert to JPEG, PNG, or WebP first.`,
|
|
1645
|
+
/** @param {number} maxSize */
|
|
1578
1646
|
imageSize: (maxSize) => `Image file is too large. Maximum allowed size is ${maxSize} MB.`
|
|
1579
1647
|
}
|
|
1580
1648
|
};
|
|
@@ -1907,6 +1975,8 @@
|
|
|
1907
1975
|
rowHeight: "Chiều cao hàng",
|
|
1908
1976
|
tableBorderWidth: "Độ rộng viền bảng",
|
|
1909
1977
|
deleteTable: "Xóa bảng",
|
|
1978
|
+
cellBackground: "Màu Nền Ô",
|
|
1979
|
+
noShading: "Xóa Màu Nền",
|
|
1910
1980
|
columnWidthPx: "Chiều rộng cột (px)",
|
|
1911
1981
|
rowHeightPx: "Chiều cao hàng (px)",
|
|
1912
1982
|
tableBorderWidthPx: "Độ rộng viền bảng (px)",
|
|
@@ -4108,7 +4178,10 @@
|
|
|
4108
4178
|
else if (options.minHeight) editable.style.minHeight = `${options.minHeight}px`;
|
|
4109
4179
|
if (options.maxHeight) editable.style.maxHeight = `${options.maxHeight}px`;
|
|
4110
4180
|
container.appendChild(editable);
|
|
4111
|
-
if (options.theme === "dark")
|
|
4181
|
+
if (options.theme === "dark") {
|
|
4182
|
+
container.classList.add("an-theme-dark");
|
|
4183
|
+
document.body.classList.add("an-theme-dark");
|
|
4184
|
+
}
|
|
4112
4185
|
if (options.readOnly) {
|
|
4113
4186
|
container.classList.add("an-disabled");
|
|
4114
4187
|
editable.querySelectorAll("ul.an-checklist input[type=\"checkbox\"]").forEach((cb) => {
|
|
@@ -4146,7 +4219,7 @@
|
|
|
4146
4219
|
constructor(editable, limit = 100) {
|
|
4147
4220
|
this.editable = editable;
|
|
4148
4221
|
this._limit = limit;
|
|
4149
|
-
/** @type {Array<{html: string,
|
|
4222
|
+
/** @type {Array<{html: string, images?: Record<string,string>, sel: {start: number, end: number}|null}>} */
|
|
4150
4223
|
this.stack = [];
|
|
4151
4224
|
this.stackOffset = -1;
|
|
4152
4225
|
this._savePoint();
|
|
@@ -4345,8 +4418,7 @@
|
|
|
4345
4418
|
* Build an HTML table with the given number of columns and rows, optionally including a header row.
|
|
4346
4419
|
* @param {number} cols - Number of columns in each row.
|
|
4347
4420
|
* @param {number} rows - Total number of rows to create (including header when `headerRow` is true).
|
|
4348
|
-
* @param {{ headerRow?: boolean }} [opts] - Options
|
|
4349
|
-
* @param {boolean} [opts.headerRow=false] - When true and `rows > 0`, creates a header row (`<thead>`) plus body rows for the remainder.
|
|
4421
|
+
* @param {{ headerRow?: boolean }} [opts] - Options: `headerRow` creates a `<thead>` when true.
|
|
4350
4422
|
* @returns {HTMLTableElement} The constructed `<table>` element with a `<tbody>` and optional `<thead>`; each cell contains a `<br>` placeholder.
|
|
4351
4423
|
*/
|
|
4352
4424
|
function createTable(cols, rows, opts = {}) {
|
|
@@ -4380,7 +4452,6 @@
|
|
|
4380
4452
|
* @param {number} cols - Number of columns for the new table.
|
|
4381
4453
|
* @param {number} rows - Number of rows for the new table.
|
|
4382
4454
|
* @param {{ headerRow?: boolean }} [opts] - Options for table creation.
|
|
4383
|
-
* @param {boolean} [opts.headerRow=false] - If true, include a header row as the first row.
|
|
4384
4455
|
*/
|
|
4385
4456
|
function insertTable(cols, rows, opts = {}) {
|
|
4386
4457
|
if (cols <= 0 || rows <= 0) return;
|
|
@@ -4403,7 +4474,7 @@
|
|
|
4403
4474
|
"PRE"
|
|
4404
4475
|
]);
|
|
4405
4476
|
let anchor = range.startContainer;
|
|
4406
|
-
if (anchor.nodeType === 3) anchor = anchor.parentElement;
|
|
4477
|
+
if (anchor && anchor.nodeType === 3) anchor = anchor.parentElement;
|
|
4407
4478
|
while (anchor && !BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentElement) anchor = anchor.parentElement;
|
|
4408
4479
|
if (anchor && BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentNode) {
|
|
4409
4480
|
anchor.after(table);
|
|
@@ -4521,7 +4592,7 @@
|
|
|
4521
4592
|
const textNode = r.startContainer;
|
|
4522
4593
|
if (r.startOffset === 0 && isFAIcon(textNode.previousSibling)) {
|
|
4523
4594
|
event.preventDefault();
|
|
4524
|
-
textNode.previousSibling.remove();
|
|
4595
|
+
/** @type {ChildNode} */ textNode.previousSibling.remove();
|
|
4525
4596
|
return true;
|
|
4526
4597
|
}
|
|
4527
4598
|
if (r.startOffset === 1 && textNode.textContent === "" && isFAIcon(textNode.previousSibling)) {
|
|
@@ -4667,7 +4738,7 @@
|
|
|
4667
4738
|
}
|
|
4668
4739
|
return false;
|
|
4669
4740
|
}
|
|
4670
|
-
const videoWrapper = el && el.closest
|
|
4741
|
+
const videoWrapper = el && el.closest(".an-video-wrapper");
|
|
4671
4742
|
if (videoWrapper) {
|
|
4672
4743
|
event.preventDefault();
|
|
4673
4744
|
const p = document.createElement("p");
|
|
@@ -4681,7 +4752,7 @@
|
|
|
4681
4752
|
sel.addRange(nr);
|
|
4682
4753
|
return true;
|
|
4683
4754
|
}
|
|
4684
|
-
const checkLi = el && el.closest
|
|
4755
|
+
const checkLi = el && el.closest(".an-checklist li");
|
|
4685
4756
|
if (checkLi) {
|
|
4686
4757
|
event.preventDefault();
|
|
4687
4758
|
const ul = checkLi.closest(".an-checklist");
|
|
@@ -4781,8 +4852,9 @@
|
|
|
4781
4852
|
function _domToMd(node, depth = 0) {
|
|
4782
4853
|
if (node.nodeType === 3) return node.textContent.replace(/\s+/g, " ");
|
|
4783
4854
|
if (node.nodeType !== 1) return "";
|
|
4784
|
-
const
|
|
4785
|
-
const
|
|
4855
|
+
const el = node;
|
|
4856
|
+
const tag = el.nodeName.toLowerCase();
|
|
4857
|
+
const inner = () => Array.from(el.childNodes).map((n) => _domToMd(n, depth)).join("");
|
|
4786
4858
|
switch (tag) {
|
|
4787
4859
|
case "p":
|
|
4788
4860
|
case "div": return `\n\n${inner()}\n\n`;
|
|
@@ -4800,34 +4872,34 @@
|
|
|
4800
4872
|
case "del":
|
|
4801
4873
|
case "s":
|
|
4802
4874
|
case "strike": return `~~${inner()}~~`;
|
|
4803
|
-
case "sup": return `^${inner()}
|
|
4804
|
-
case "sub": return `~${inner()}
|
|
4875
|
+
case "sup": return `^${inner()}^`;
|
|
4876
|
+
case "sub": return `~${inner()}~`;
|
|
4805
4877
|
case "code":
|
|
4806
|
-
if (
|
|
4878
|
+
if (el.closest("pre")) return inner();
|
|
4807
4879
|
return `\`${inner()}\``;
|
|
4808
4880
|
case "pre": {
|
|
4809
|
-
const codeEl =
|
|
4881
|
+
const codeEl = el.querySelector("code");
|
|
4810
4882
|
const langMatch = (codeEl && codeEl.className || "").match(/language-(\S+)/);
|
|
4811
|
-
return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl ||
|
|
4883
|
+
return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || el).textContent || ""}\n\`\`\`\n\n`;
|
|
4812
4884
|
}
|
|
4813
4885
|
case "blockquote": return `\n\n${inner().trim().split("\n").map((l) => `> ${l}`).join("\n")}\n\n`;
|
|
4814
4886
|
case "a": {
|
|
4815
|
-
const href =
|
|
4887
|
+
const href = el.getAttribute("href") || "";
|
|
4816
4888
|
return `[${inner()}](${href})`;
|
|
4817
4889
|
}
|
|
4818
4890
|
case "img": {
|
|
4819
|
-
const src =
|
|
4820
|
-
return ``;
|
|
4821
4893
|
}
|
|
4822
4894
|
case "ul": {
|
|
4823
|
-
const items = Array.from(
|
|
4895
|
+
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
4824
4896
|
if (!items.length) return inner();
|
|
4825
4897
|
const indent = " ".repeat(depth);
|
|
4826
4898
|
const lines = items.map((li) => `${indent}- ${_domToMd(li, depth + 1).trim()}`).join("\n");
|
|
4827
4899
|
return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
|
|
4828
4900
|
}
|
|
4829
4901
|
case "ol": {
|
|
4830
|
-
const items = Array.from(
|
|
4902
|
+
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
4831
4903
|
if (!items.length) return inner();
|
|
4832
4904
|
const indent = " ".repeat(depth);
|
|
4833
4905
|
const lines = items.map((li, i) => `${indent}${i + 1}. ${_domToMd(li, depth + 1).trim()}`).join("\n");
|
|
@@ -4836,7 +4908,7 @@
|
|
|
4836
4908
|
case "li": return inner();
|
|
4837
4909
|
case "hr": return "\n\n---\n\n";
|
|
4838
4910
|
case "table": {
|
|
4839
|
-
const rows = Array.from(
|
|
4911
|
+
const rows = Array.from(el.querySelectorAll("tr"));
|
|
4840
4912
|
if (!rows.length) return inner();
|
|
4841
4913
|
const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replace(/\|/g, "\\|")));
|
|
4842
4914
|
const cols = Math.max(...cellTexts.map((r) => r.length));
|
|
@@ -4984,6 +5056,47 @@
|
|
|
4984
5056
|
return String(v).replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
4985
5057
|
}
|
|
4986
5058
|
//#endregion
|
|
5059
|
+
//#region src/js/core/detectLang.js
|
|
5060
|
+
/**
|
|
5061
|
+
* detectLang.js — Heuristic programming-language detection for code snippets.
|
|
5062
|
+
*
|
|
5063
|
+
* Returns a Prism.js language identifier or null when no language can be
|
|
5064
|
+
* determined with reasonable confidence.
|
|
5065
|
+
*
|
|
5066
|
+
* Detection order (conflicts in parentheses):
|
|
5067
|
+
* TypeScript → Rust → PHP → Java → Kotlin → Swift → Go
|
|
5068
|
+
* → JavaScript → HTML → CSS → JSON → SQL → Python → Ruby
|
|
5069
|
+
* → Bash → C++ → C# → C → XML
|
|
5070
|
+
*
|
|
5071
|
+
* @param {string} code
|
|
5072
|
+
* @returns {string|null}
|
|
5073
|
+
*/
|
|
5074
|
+
function detectLang(code) {
|
|
5075
|
+
if (!code || !code.trim()) return null;
|
|
5076
|
+
const s = code.trim();
|
|
5077
|
+
if (/(:\s*(string|number|boolean|void|never|any|unknown)\b|interface\s+\w+\s*\{|type\s+\w+\s*[=<(]|<\w+>\s*[;,)]|readonly\s+\w|enum\s+\w+\s*\{|\?\s*:\s*\w|as\s+\w+\s*[;,)\]])/.test(s)) return "typescript";
|
|
5078
|
+
if (/\bprintln!\s*\(|\bprint!\s*\(|\bfn\s+\w+\s*(<[^>]*>)?\s*\(|\blet\s+mut\s|\bpub\s+fn\s|\buse\s+std::|\bimpl\s+\w+|\bOption<|\bResult<\w+/.test(s)) return "rust";
|
|
5079
|
+
if (/(<\?php\b|<\?=|\becho\s+.*\$\w|\$this->|\$\w+\s*=\s*\w|\bforeach\s*\(\s*\$|Illuminate\\)/.test(s)) return "php";
|
|
5080
|
+
if (/\bpublic\s+(class|static|void|int|String)\s+\w|System\.out\.(print|println)\s*\(|@(Override|Autowired|Component|Service|Controller)\b|import\s+java\.(util|io|lang|net)\.|throws\s+\w+Exception/.test(s)) return "java";
|
|
5081
|
+
if (/\bfun\s+\w+\s*\(|\bdata\s+class\s+\w+|\bcompanion\s+object\b|\bval\s+\w+\s*:\s*\w|\bprintln\s*\(/.test(s)) return "kotlin";
|
|
5082
|
+
if (/\bguard\s+(let|var)\b|\bprotocol\s+\w+\s*\{|\bextension\s+\w+|\bfunc\s+\w+[^(]*\([^)]*\)\s*->\s*\w|\blet\s+\w+\s*:\s*[A-Z]\w*|\bSwiftUI\b/.test(s)) return "swift";
|
|
5083
|
+
if (/\bpackage\s+\w+\b|\bfmt\.(Print|Println|Sprintf|Errorf|Fprintf)\s*\(|:=\s*\w|\bgoroutine\b|\bchan\s+\w|\bgo\s+func\b/.test(s)) return "go";
|
|
5084
|
+
if (/\b(const\s+\w|let\s+\w+\s*=|var\s+\w+\s*=|function\s+\w|\=>\s*[{(]|import\s+.*\bfrom\b\s*['"]|require\s*\(|console\.(log|error|warn|info)|document\.\w|window\.\w|async\s+function|\bPromise\b|React\.|useState\s*\(|\.then\s*\()/.test(s)) return "javascript";
|
|
5085
|
+
if (/^<!DOCTYPE html/i.test(s) || /<(html|head|body|div|section|article|nav|p|a|img|ul|ol|li|table|form|input|button|script|style)\b[^>]*>/i.test(s)) return "html";
|
|
5086
|
+
if (/(^|\n)\s*(\/\/\s+\S|&[:.[\w]|\$\w+\s*:|@(mixin|include|extend|each|if|for|use|forward)\b|#\{)/.test(s) && /[\w#.*&[\]:(),>+~ -]+\s*\{/.test(s)) return "scss";
|
|
5087
|
+
if (/(^|\n)\s*[\w#.*:[\]&, +-]+\s*\{[^}]*[\w-]+\s*:[^{}:;]+[;}\n]/m.test(s) && !/<\w|function\s|def\s|:\s*(string|number)/.test(s)) return "css";
|
|
5088
|
+
if (/^\s*[{[]/.test(s) && /"\w[\w\s-]*"\s*:/.test(s) && !/\bfunction\b|\bdef\b/.test(s)) return "json";
|
|
5089
|
+
if (/(^|\n)\s*(SELECT\s|INSERT\s+INTO|UPDATE\s+\w|DELETE\s+FROM|CREATE\s+(TABLE|DATABASE|INDEX|VIEW)|DROP\s+(TABLE|DATABASE)|ALTER\s+TABLE|WITH\s+\w+\s+AS\s*\()/im.test(s)) return "sql";
|
|
5090
|
+
if (/\bdef\s+\w+\s*\([^)]*\)\s*:|(^|\n)\s*class\s+\w+.*:\s*$|(^|\n)\s*import\s+\w|(^|\n)\s*from\s+\w+\s+import\s+|\bprint\s*\(|if\s+__name__\s*==\s*['"]__main__['"]/m.test(s)) return "python";
|
|
5091
|
+
if (/\bputs\s+\S|\battr_(accessor|reader|writer)\s|\.each\s+do\s*\|\w+\s*\||\bdo\s*\|\w+\s*\|.*\bend\b|\bdef\s+\w+[^:]*\n[\s\S]*?\bend\b/.test(s)) return "ruby";
|
|
5092
|
+
if (/^#!.*\/(ba|z|da|fi|k)?sh\b/m.test(s) || /\b(echo\s+["']|grep\s+|awk\s+|sed\s+['"\\/-]|chmod\s+|sudo\s+|apt(-get)?\s+install|brew\s+install|npm\s+(install|run|start|build)|pip\s+(install|3\s)|docker\s+(run|build|compose)|kubectl\s+|git\s+(clone|add|commit|push|pull|checkout))\b/.test(s)) return "bash";
|
|
5093
|
+
if (/\bcout\s*<<|\bcin\s*>>|using\s+namespace\s+std\b|std::\w|\btemplate\s*<\w|\b#include\s*<(iostream|vector|map|set|algorithm|string|memory)>/.test(s)) return "cpp";
|
|
5094
|
+
if (/\busing\s+System\b|Console\.(Write|WriteLine)\s*\(|\bget;\s*set;|\basync\s+Task[<\s]|IEnumerable<|\bLINQ\b|\.Select\s*\(|\.Where\s*\(/.test(s)) return "csharp";
|
|
5095
|
+
if (/\b#include\s*<(stdio|stdlib|string|math|time|ctype)\.h>|\bprintf\s*\(|\bscanf\s*\(|int\s+main\s*\(\s*(void|int\s+argc)|\bmalloc\s*\(|\bfree\s*\(/.test(s) && !/namespace|cout|cin|std::/.test(s)) return "c";
|
|
5096
|
+
if (/^<\?xml\s/i.test(s) || /xmlns:|<\/[\w:]+>/.test(s)) return "xml";
|
|
5097
|
+
return null;
|
|
5098
|
+
}
|
|
5099
|
+
//#endregion
|
|
4987
5100
|
//#region src/js/module/Editor.js
|
|
4988
5101
|
/**
|
|
4989
5102
|
* Editor.js - Core editing command module
|
|
@@ -5038,7 +5151,8 @@
|
|
|
5038
5151
|
if (!r.collapsed) return;
|
|
5039
5152
|
const sc = r.startContainer;
|
|
5040
5153
|
if (sc.nodeType !== Node.ELEMENT_NODE) return;
|
|
5041
|
-
const
|
|
5154
|
+
const scEl = sc;
|
|
5155
|
+
const li = scEl.matches(".an-checklist li") ? scEl : null;
|
|
5042
5156
|
if (!li) return;
|
|
5043
5157
|
const cb = li.querySelector("input[type=\"checkbox\"]");
|
|
5044
5158
|
if (!cb) return;
|
|
@@ -5078,7 +5192,7 @@
|
|
|
5078
5192
|
return;
|
|
5079
5193
|
}
|
|
5080
5194
|
const target = e.target;
|
|
5081
|
-
if (target && (target.nodeName === "IFRAME" || target.closest
|
|
5195
|
+
if (target && (target.nodeName === "IFRAME" || target.closest(".an-video-wrapper"))) e.preventDefault();
|
|
5082
5196
|
}), on(editable, "drop", (e) => {
|
|
5083
5197
|
if (isReadOnly()) e.preventDefault();
|
|
5084
5198
|
}));
|
|
@@ -5092,9 +5206,12 @@
|
|
|
5092
5206
|
}
|
|
5093
5207
|
let node = sel.getRangeAt(0).startContainer;
|
|
5094
5208
|
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
5095
|
-
if (node
|
|
5096
|
-
|
|
5097
|
-
|
|
5209
|
+
if (node) {
|
|
5210
|
+
const el = node;
|
|
5211
|
+
if (el.closest("sup")) _compositionSupSub = "superscript";
|
|
5212
|
+
else if (el.closest("sub")) _compositionSupSub = "subscript";
|
|
5213
|
+
else _compositionSupSub = null;
|
|
5214
|
+
}
|
|
5098
5215
|
};
|
|
5099
5216
|
const onCompositionEnd = () => {
|
|
5100
5217
|
const tag = _compositionSupSub;
|
|
@@ -5104,7 +5221,8 @@
|
|
|
5104
5221
|
if (!sel || !sel.rangeCount) return;
|
|
5105
5222
|
let node = sel.getRangeAt(0).startContainer;
|
|
5106
5223
|
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
5107
|
-
|
|
5224
|
+
const el = node;
|
|
5225
|
+
if (!(el && (tag === "superscript" ? el.closest("sup") : el.closest("sub")))) document.execCommand(tag);
|
|
5108
5226
|
};
|
|
5109
5227
|
this._disposers.push(on(editable, "compositionstart", onCompositionStart), on(editable, "compositionend", onCompositionEnd));
|
|
5110
5228
|
}
|
|
@@ -5193,6 +5311,7 @@
|
|
|
5193
5311
|
}
|
|
5194
5312
|
afterCommand() {
|
|
5195
5313
|
this._cleanOrphanedFigures();
|
|
5314
|
+
this._ensureTrailingParagraph();
|
|
5196
5315
|
this.context.invoke("toolbar.refresh");
|
|
5197
5316
|
this.context.invoke("statusbar.update");
|
|
5198
5317
|
this._scheduleSnapshot();
|
|
@@ -5219,6 +5338,31 @@
|
|
|
5219
5338
|
if (!fig.querySelector("img")) fig.parentNode.removeChild(fig);
|
|
5220
5339
|
});
|
|
5221
5340
|
}
|
|
5341
|
+
/**
|
|
5342
|
+
* Ensures the editable always ends with a plain paragraph so the cursor can
|
|
5343
|
+
* be placed after block elements that do not naturally allow it
|
|
5344
|
+
* (pre, blockquote, table, figure, ul, ol, hr).
|
|
5345
|
+
* Without this, clicking below the last such element does nothing.
|
|
5346
|
+
*/
|
|
5347
|
+
_ensureTrailingParagraph() {
|
|
5348
|
+
const editable = this.context.layoutInfo.editable;
|
|
5349
|
+
if (!editable) return;
|
|
5350
|
+
const last = editable.lastElementChild;
|
|
5351
|
+
if (!last) return;
|
|
5352
|
+
if (new Set([
|
|
5353
|
+
"PRE",
|
|
5354
|
+
"BLOCKQUOTE",
|
|
5355
|
+
"TABLE",
|
|
5356
|
+
"FIGURE",
|
|
5357
|
+
"UL",
|
|
5358
|
+
"OL",
|
|
5359
|
+
"HR"
|
|
5360
|
+
]).has(last.nodeName)) {
|
|
5361
|
+
const p = document.createElement("p");
|
|
5362
|
+
p.innerHTML = "<br>";
|
|
5363
|
+
editable.appendChild(p);
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5222
5366
|
focus() {
|
|
5223
5367
|
this.context.layoutInfo.editable.focus();
|
|
5224
5368
|
}
|
|
@@ -5402,10 +5546,24 @@
|
|
|
5402
5546
|
this.context.print();
|
|
5403
5547
|
}
|
|
5404
5548
|
/**
|
|
5405
|
-
* @param {string} tagName - e.g. 'h1', 'p', 'blockquote'
|
|
5549
|
+
* @param {string} tagName - e.g. 'h1', 'p', 'blockquote', 'pre'
|
|
5406
5550
|
*/
|
|
5407
5551
|
formatBlock(tagName) {
|
|
5408
5552
|
formatBlock(tagName);
|
|
5553
|
+
if (tagName === "pre") {
|
|
5554
|
+
const sel = window.getSelection();
|
|
5555
|
+
if (sel && sel.rangeCount > 0) {
|
|
5556
|
+
const container = sel.getRangeAt(0).commonAncestorContainer;
|
|
5557
|
+
const pre = container.nodeType === 1 ? container.closest("pre") : container.parentElement?.closest("pre");
|
|
5558
|
+
if (pre && !pre.getAttribute("data-language")) {
|
|
5559
|
+
const lang = detectLang(pre.textContent || "");
|
|
5560
|
+
if (lang) {
|
|
5561
|
+
this.context.invoke("codeTooltip.applyLanguage", pre, lang);
|
|
5562
|
+
return;
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
}
|
|
5566
|
+
}
|
|
5409
5567
|
this.afterCommand();
|
|
5410
5568
|
}
|
|
5411
5569
|
/**
|
|
@@ -5462,8 +5620,8 @@
|
|
|
5462
5620
|
if (openInNewTab) {
|
|
5463
5621
|
const link = this._getClosestAnchor();
|
|
5464
5622
|
if (link) {
|
|
5465
|
-
link.setAttribute("target", "_blank");
|
|
5466
|
-
link.setAttribute("rel", "noopener noreferrer");
|
|
5623
|
+
/** @type {Element} */ link.setAttribute("target", "_blank");
|
|
5624
|
+
/** @type {Element} */ link.setAttribute("rel", "noopener noreferrer");
|
|
5467
5625
|
}
|
|
5468
5626
|
}
|
|
5469
5627
|
}
|
|
@@ -5745,13 +5903,13 @@
|
|
|
5745
5903
|
else openPopup();
|
|
5746
5904
|
});
|
|
5747
5905
|
const d2 = on(grid, "mouseover", (e) => {
|
|
5748
|
-
const cell = e.target
|
|
5906
|
+
const cell = e.target?.closest(".an-table-cell");
|
|
5749
5907
|
if (!cell) return;
|
|
5750
5908
|
setHighlight(+cell.getAttribute("data-row"), +cell.getAttribute("data-col"));
|
|
5751
5909
|
});
|
|
5752
5910
|
const d3 = on(grid, "mouseleave", () => setHighlight(0, 0));
|
|
5753
5911
|
const d4 = on(grid, "click", (e) => {
|
|
5754
|
-
const cell = e.target
|
|
5912
|
+
const cell = e.target?.closest(".an-table-cell");
|
|
5755
5913
|
if (!cell) return;
|
|
5756
5914
|
const rows = +cell.getAttribute("data-row");
|
|
5757
5915
|
const cols = +cell.getAttribute("data-col");
|
|
@@ -5914,11 +6072,17 @@
|
|
|
5914
6072
|
e.preventDefault();
|
|
5915
6073
|
});
|
|
5916
6074
|
const d3b = on(swatches, "click", (e) => {
|
|
5917
|
-
const sw = e.target
|
|
5918
|
-
if (sw) applyColor(
|
|
6075
|
+
const sw = e.target?.closest(".an-color-swatch");
|
|
6076
|
+
if (sw) applyColor(
|
|
6077
|
+
/** @type {HTMLElement} */
|
|
6078
|
+
sw.dataset.color
|
|
6079
|
+
);
|
|
5919
6080
|
});
|
|
5920
6081
|
const d4 = on(colorInput, "change", (e) => {
|
|
5921
|
-
applyColor(
|
|
6082
|
+
applyColor(
|
|
6083
|
+
/** @type {HTMLInputElement} */
|
|
6084
|
+
e.target.value
|
|
6085
|
+
);
|
|
5922
6086
|
});
|
|
5923
6087
|
const d5 = on(document, "click", (e) => {
|
|
5924
6088
|
if (isOpen && !wrap.contains(e.target) && !popup.contains(e.target)) closePopup();
|
|
@@ -6053,15 +6217,17 @@
|
|
|
6053
6217
|
this.el.querySelectorAll("button[data-btn]").forEach((btn) => {
|
|
6054
6218
|
const def = btnMap.get(btn.getAttribute("data-btn"));
|
|
6055
6219
|
if (def && typeof def.isActive === "function") btn.classList.toggle("active", !!def.isActive(this.context));
|
|
6056
|
-
if (def && typeof def.isDisabled === "function")
|
|
6220
|
+
if (def && typeof def.isDisabled === "function")
|
|
6221
|
+
/** @type {HTMLButtonElement} */ btn.disabled = !!def.isDisabled(this.context);
|
|
6057
6222
|
});
|
|
6058
6223
|
this.el.querySelectorAll("select[data-btn]").forEach((select) => {
|
|
6059
6224
|
const def = btnMap.get(select.getAttribute("data-btn"));
|
|
6060
6225
|
if (!def || typeof def.getValue !== "function") return;
|
|
6061
6226
|
let raw = (def.getValue(this.context) || "").replace(/["']/g, "").trim();
|
|
6062
6227
|
if (!raw) raw = this.options.defaultFontFamily || this.options.fontFamilies && this.options.fontFamilies[0] || "";
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6228
|
+
const sel = select;
|
|
6229
|
+
const matched = Array.from(sel.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
|
|
6230
|
+
sel.value = matched ? matched.value : "";
|
|
6065
6231
|
});
|
|
6066
6232
|
}
|
|
6067
6233
|
/**
|
|
@@ -6855,8 +7021,13 @@
|
|
|
6855
7021
|
"aria-label": L.ariaLabel
|
|
6856
7022
|
});
|
|
6857
7023
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7024
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7025
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7026
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>`;
|
|
6858
7027
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
6859
7028
|
title.textContent = L.title;
|
|
7029
|
+
header.appendChild(iconEl);
|
|
7030
|
+
header.appendChild(title);
|
|
6860
7031
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
6861
7032
|
urlLabel.textContent = L.url;
|
|
6862
7033
|
const urlInput = createElement("input", {
|
|
@@ -6901,8 +7072,9 @@
|
|
|
6901
7072
|
cancelBtn.textContent = L.cancelBtn;
|
|
6902
7073
|
btnRow.appendChild(insertBtn);
|
|
6903
7074
|
btnRow.appendChild(cancelBtn);
|
|
6904
|
-
box.append(
|
|
7075
|
+
box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
|
|
6905
7076
|
overlay.appendChild(box);
|
|
7077
|
+
makeDraggable(header, box);
|
|
6906
7078
|
const d1 = on(insertBtn, "click", () => this._onInsert());
|
|
6907
7079
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
6908
7080
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -7032,8 +7204,13 @@
|
|
|
7032
7204
|
"aria-label": L.ariaLabel
|
|
7033
7205
|
});
|
|
7034
7206
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7207
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7208
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7209
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" 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="2"/><circle cx="8.5" cy="8.5" r="1.5"/><polyline points="21 15 16 10 5 21"/></svg>`;
|
|
7035
7210
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
7036
7211
|
title.textContent = L.title;
|
|
7212
|
+
header.appendChild(iconEl);
|
|
7213
|
+
header.appendChild(title);
|
|
7037
7214
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
7038
7215
|
urlLabel.textContent = L.imageUrl;
|
|
7039
7216
|
const urlInput = createElement("input", {
|
|
@@ -7052,7 +7229,7 @@
|
|
|
7052
7229
|
autocomplete: "off"
|
|
7053
7230
|
});
|
|
7054
7231
|
this._altInput = altInput;
|
|
7055
|
-
box.append(
|
|
7232
|
+
box.append(header, urlLabel, urlInput, altLabel, altInput);
|
|
7056
7233
|
const alignLabel = createElement("label", { class: "an-label" });
|
|
7057
7234
|
alignLabel.textContent = L.alignment;
|
|
7058
7235
|
const alignRow = createElement("div", { class: "an-align-row" });
|
|
@@ -7121,6 +7298,7 @@
|
|
|
7121
7298
|
btnRow.appendChild(cancelBtn);
|
|
7122
7299
|
box.append(btnRow);
|
|
7123
7300
|
overlay.appendChild(box);
|
|
7301
|
+
makeDraggable(header, box);
|
|
7124
7302
|
const d1 = on(insertBtn, "click", () => this._onInsert());
|
|
7125
7303
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
7126
7304
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -7258,8 +7436,13 @@
|
|
|
7258
7436
|
"aria-label": L.ariaLabel
|
|
7259
7437
|
});
|
|
7260
7438
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7439
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7440
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7441
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="23 7 16 12 23 17 23 7"/><rect x="1" y="5" width="15" height="14" rx="2"/></svg>`;
|
|
7261
7442
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
7262
7443
|
title.textContent = L.title;
|
|
7444
|
+
header.appendChild(iconEl);
|
|
7445
|
+
header.appendChild(title);
|
|
7263
7446
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
7264
7447
|
urlLabel.textContent = L.videoUrl;
|
|
7265
7448
|
const urlInput = createElement("input", {
|
|
@@ -7295,8 +7478,9 @@
|
|
|
7295
7478
|
cancelBtn.textContent = L.cancelBtn;
|
|
7296
7479
|
btnRow.appendChild(insertBtn);
|
|
7297
7480
|
btnRow.appendChild(cancelBtn);
|
|
7298
|
-
box.append(
|
|
7481
|
+
box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
|
|
7299
7482
|
overlay.appendChild(box);
|
|
7483
|
+
makeDraggable(header, box);
|
|
7300
7484
|
const d0 = on(urlInput, "input", () => {
|
|
7301
7485
|
const info = this._parseVideoUrl(urlInput.value.trim());
|
|
7302
7486
|
hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
|
|
@@ -7479,7 +7663,7 @@
|
|
|
7479
7663
|
};
|
|
7480
7664
|
this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
|
|
7481
7665
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
7482
|
-
const img = e.target
|
|
7666
|
+
const img = e.target?.closest("img");
|
|
7483
7667
|
if (img) this._select(img);
|
|
7484
7668
|
}), 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 }));
|
|
7485
7669
|
return this;
|
|
@@ -7889,12 +8073,12 @@
|
|
|
7889
8073
|
document.body.appendChild(this._el);
|
|
7890
8074
|
const editable = this.context.layoutInfo.editable;
|
|
7891
8075
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
7892
|
-
const anchor = e.target
|
|
8076
|
+
const anchor = e.target?.closest("a[href]");
|
|
7893
8077
|
if (anchor && editable.contains(anchor)) this._scheduleShow(anchor);
|
|
7894
8078
|
}), on(editable, "mouseout", (e) => {
|
|
7895
8079
|
const to = e.relatedTarget;
|
|
7896
8080
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
7897
|
-
}));
|
|
8081
|
+
}), on(window, "scroll", () => this._hide(), { passive: true }), on(window, "resize", () => this._hide(), { passive: true }));
|
|
7898
8082
|
return this;
|
|
7899
8083
|
}
|
|
7900
8084
|
destroy() {
|
|
@@ -8079,14 +8263,15 @@
|
|
|
8079
8263
|
const editable = this.context.layoutInfo.editable;
|
|
8080
8264
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8081
8265
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8082
|
-
const img = e.target
|
|
8266
|
+
const img = e.target?.closest("img");
|
|
8083
8267
|
if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
|
|
8084
8268
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
8085
8269
|
const to = e.relatedTarget;
|
|
8086
8270
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
8087
8271
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8088
|
-
|
|
8089
|
-
|
|
8272
|
+
const et = e.target;
|
|
8273
|
+
if (this._activeImg && !this._activeImg.contains(et) && !this._el.contains(et)) this._hide();
|
|
8274
|
+
}), on(window, "scroll", () => this._hide(), { passive: true }), on(window, "resize", () => this._hide(), { passive: true }));
|
|
8090
8275
|
return this;
|
|
8091
8276
|
}
|
|
8092
8277
|
destroy() {
|
|
@@ -8170,7 +8355,7 @@
|
|
|
8170
8355
|
clearTimeout(this._hideTimer);
|
|
8171
8356
|
this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$3);
|
|
8172
8357
|
}
|
|
8173
|
-
_show(
|
|
8358
|
+
_show(_img) {
|
|
8174
8359
|
this._el.style.display = "flex";
|
|
8175
8360
|
requestAnimationFrame(() => {
|
|
8176
8361
|
if (this._activeImg) this._positionNear(this._activeImg);
|
|
@@ -8362,14 +8547,16 @@
|
|
|
8362
8547
|
const editable = this.context.layoutInfo.editable;
|
|
8363
8548
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8364
8549
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8365
|
-
const
|
|
8550
|
+
const target = e.target;
|
|
8551
|
+
const wrapper = target && target.closest ? target.closest(".an-video-wrapper") : null;
|
|
8366
8552
|
if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
|
|
8367
8553
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
8368
8554
|
const to = e.relatedTarget;
|
|
8369
8555
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
8370
8556
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8371
|
-
|
|
8372
|
-
|
|
8557
|
+
const target = e.target;
|
|
8558
|
+
if (this._activeWrapper && !this._activeWrapper.contains(target) && !this._el.contains(target)) this._hide();
|
|
8559
|
+
}), on(window, "scroll", () => this._hide(), { passive: true }), on(window, "resize", () => this._hide(), { passive: true }));
|
|
8373
8560
|
return this;
|
|
8374
8561
|
}
|
|
8375
8562
|
destroy() {
|
|
@@ -8449,7 +8636,7 @@
|
|
|
8449
8636
|
if (this._hideTimer) return;
|
|
8450
8637
|
this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$2);
|
|
8451
8638
|
}
|
|
8452
|
-
_show(
|
|
8639
|
+
_show(_wrapper) {
|
|
8453
8640
|
this._el.style.display = "flex";
|
|
8454
8641
|
requestAnimationFrame(() => {
|
|
8455
8642
|
if (this._activeWrapper) this._positionNear(this._activeWrapper);
|
|
@@ -8673,8 +8860,35 @@
|
|
|
8673
8860
|
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>`,
|
|
8674
8861
|
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>`,
|
|
8675
8862
|
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>`,
|
|
8676
|
-
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
|
|
8863
|
+
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>`,
|
|
8864
|
+
cellShade: `<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"><path d="M19 11L8.93 3.36a1 1 0 0 0-1.29.08L3.22 7.8a1 1 0 0 0-.07 1.29L11 20"/><path d="m5 14 5-5"/><path d="M22 22a2 2 0 0 1-2 2h-3a2 2 0 0 1-2-2c0-1.5 2.5-5 3.5-5s3.5 3.5 3.5 5z"/></svg>`
|
|
8677
8865
|
};
|
|
8866
|
+
var SHADE_PRESETS = [
|
|
8867
|
+
"#000000",
|
|
8868
|
+
"#434343",
|
|
8869
|
+
"#666666",
|
|
8870
|
+
"#999999",
|
|
8871
|
+
"#b7b7b7",
|
|
8872
|
+
"#cccccc",
|
|
8873
|
+
"#efefef",
|
|
8874
|
+
"#ffffff",
|
|
8875
|
+
"#ff0000",
|
|
8876
|
+
"#ff9900",
|
|
8877
|
+
"#ffff00",
|
|
8878
|
+
"#00ff00",
|
|
8879
|
+
"#00ffff",
|
|
8880
|
+
"#4a86e8",
|
|
8881
|
+
"#9900ff",
|
|
8882
|
+
"#ff00ff",
|
|
8883
|
+
"#f4cccc",
|
|
8884
|
+
"#fce5cd",
|
|
8885
|
+
"#fff2cc",
|
|
8886
|
+
"#d9ead3",
|
|
8887
|
+
"#d0e0e3",
|
|
8888
|
+
"#c9daf8",
|
|
8889
|
+
"#d9d2e9",
|
|
8890
|
+
"#ead1dc"
|
|
8891
|
+
];
|
|
8678
8892
|
var TableTooltip = class {
|
|
8679
8893
|
/** @param {import('../Context.js').Context} context */
|
|
8680
8894
|
constructor(context) {
|
|
@@ -8689,6 +8903,9 @@
|
|
|
8689
8903
|
this._sizeApply = null;
|
|
8690
8904
|
this._sizeTitleEl = null;
|
|
8691
8905
|
this._sizeInputEl = null;
|
|
8906
|
+
this._shadePopover = null;
|
|
8907
|
+
this._shadeTitleEl = null;
|
|
8908
|
+
this._shadeColorStrip = null;
|
|
8692
8909
|
this._selectMode = false;
|
|
8693
8910
|
this._selectedCells = [];
|
|
8694
8911
|
this._selectStart = null;
|
|
@@ -8701,6 +8918,8 @@
|
|
|
8701
8918
|
document.body.appendChild(this._el);
|
|
8702
8919
|
this._sizePopover = this._buildSizePopover();
|
|
8703
8920
|
document.body.appendChild(this._sizePopover);
|
|
8921
|
+
this._shadePopover = this._buildCellShadePopover();
|
|
8922
|
+
document.body.appendChild(this._shadePopover);
|
|
8704
8923
|
const editable = this.context.layoutInfo.editable;
|
|
8705
8924
|
this._editable = editable;
|
|
8706
8925
|
const onSelMousedown = (e) => {
|
|
@@ -8727,10 +8946,13 @@
|
|
|
8727
8946
|
this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
|
|
8728
8947
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8729
8948
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8730
|
-
const table = e.target
|
|
8949
|
+
const table = e.target?.closest("table");
|
|
8731
8950
|
if (table && editable.contains(table)) {
|
|
8732
|
-
const cell = e.target
|
|
8733
|
-
if (cell)
|
|
8951
|
+
const cell = e.target?.closest("td, th");
|
|
8952
|
+
if (cell) {
|
|
8953
|
+
this._activeCell = cell;
|
|
8954
|
+
this._syncShadeStrip();
|
|
8955
|
+
}
|
|
8734
8956
|
this._scheduleShow(table);
|
|
8735
8957
|
}
|
|
8736
8958
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
@@ -8738,9 +8960,10 @@
|
|
|
8738
8960
|
const to = e.relatedTarget;
|
|
8739
8961
|
if (!to || !editable.contains(to) && !this._el.contains(to) && !(this._sizePopover && this._sizePopover.contains(to))) this._scheduleHide();
|
|
8740
8962
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8741
|
-
|
|
8742
|
-
if (this.
|
|
8743
|
-
|
|
8963
|
+
const et = e.target;
|
|
8964
|
+
if (this._selectMode && this._activeTable && this._activeTable.contains(et)) return;
|
|
8965
|
+
if (this._activeTable && !this._activeTable.contains(et) && !this._el.contains(et) && !(this._sizePopover && this._sizePopover.contains(et))) this._hide();
|
|
8966
|
+
}), on(document, "selectionchange", () => this._syncShadeStrip()), on(window, "scroll", () => this._hide(), { passive: true }), on(window, "resize", () => this._hide(), { passive: true }));
|
|
8744
8967
|
this._initResize();
|
|
8745
8968
|
return this;
|
|
8746
8969
|
}
|
|
@@ -8870,6 +9093,8 @@
|
|
|
8870
9093
|
this._el = null;
|
|
8871
9094
|
if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.parentNode.removeChild(this._sizePopover);
|
|
8872
9095
|
this._sizePopover = null;
|
|
9096
|
+
if (this._shadePopover && this._shadePopover.parentNode) this._shadePopover.parentNode.removeChild(this._shadePopover);
|
|
9097
|
+
this._shadePopover = null;
|
|
8873
9098
|
}
|
|
8874
9099
|
_buildTooltip() {
|
|
8875
9100
|
const L = this.context.locale.tooltips.table;
|
|
@@ -8897,6 +9122,24 @@
|
|
|
8897
9122
|
el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
|
|
8898
9123
|
el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
|
|
8899
9124
|
el.appendChild(this._sep());
|
|
9125
|
+
const shadeBtn = createElement("button", {
|
|
9126
|
+
type: "button",
|
|
9127
|
+
class: "an-link-tooltip-btn an-link-tooltip-btn--shade",
|
|
9128
|
+
title: L.cellBackground
|
|
9129
|
+
});
|
|
9130
|
+
const shadeSvgWrap = createElement("span", { class: "an-bubble-btn-svg" });
|
|
9131
|
+
shadeSvgWrap.innerHTML = ICONS$2.cellShade;
|
|
9132
|
+
const shadeStrip = createElement("span", { class: "an-link-tooltip-color-strip" });
|
|
9133
|
+
shadeBtn.appendChild(shadeSvgWrap);
|
|
9134
|
+
shadeBtn.appendChild(shadeStrip);
|
|
9135
|
+
this._shadeColorStrip = shadeStrip;
|
|
9136
|
+
this._disposers.push(on(shadeBtn, "click", (e) => {
|
|
9137
|
+
e.preventDefault();
|
|
9138
|
+
e.stopPropagation();
|
|
9139
|
+
this._openCellShadePopover();
|
|
9140
|
+
}));
|
|
9141
|
+
el.appendChild(shadeBtn);
|
|
9142
|
+
el.appendChild(this._sep());
|
|
8900
9143
|
el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
|
|
8901
9144
|
el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
|
|
8902
9145
|
el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
|
|
@@ -8905,6 +9148,7 @@
|
|
|
8905
9148
|
this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
|
|
8906
9149
|
if (this._selectMode) return;
|
|
8907
9150
|
if (this._sizePopover && this._sizePopover.style.display !== "none") return;
|
|
9151
|
+
if (this._shadePopover && this._shadePopover.style.display !== "none") return;
|
|
8908
9152
|
this._scheduleHide();
|
|
8909
9153
|
}));
|
|
8910
9154
|
return el;
|
|
@@ -8951,10 +9195,16 @@
|
|
|
8951
9195
|
_show() {
|
|
8952
9196
|
if (!this._activeTable) return;
|
|
8953
9197
|
this._el.style.display = "flex";
|
|
9198
|
+
this._syncShadeStrip();
|
|
8954
9199
|
requestAnimationFrame(() => {
|
|
8955
9200
|
if (this._activeTable) this._positionNear(this._activeTable);
|
|
8956
9201
|
});
|
|
8957
9202
|
}
|
|
9203
|
+
_syncShadeStrip() {
|
|
9204
|
+
if (!this._shadeColorStrip || !this._el || this._el.style.display === "none") return;
|
|
9205
|
+
const cell = this._getCell();
|
|
9206
|
+
this._shadeColorStrip.style.background = cell && cell.style.backgroundColor || "transparent";
|
|
9207
|
+
}
|
|
8958
9208
|
_hide() {
|
|
8959
9209
|
this._el.style.display = "none";
|
|
8960
9210
|
this._activeTable = null;
|
|
@@ -8993,7 +9243,7 @@
|
|
|
8993
9243
|
if (sel && sel.rangeCount) {
|
|
8994
9244
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
8995
9245
|
if (container.nodeType === 3) container = container.parentElement;
|
|
8996
|
-
const cellFromSel = container && container.closest
|
|
9246
|
+
const cellFromSel = container && container.closest("td, th");
|
|
8997
9247
|
if (cellFromSel && this._activeTable && this._activeTable.contains(cellFromSel)) return cellFromSel;
|
|
8998
9248
|
}
|
|
8999
9249
|
return this._activeCell || this._activeTable && this._activeTable.querySelector("td, th");
|
|
@@ -9320,14 +9570,16 @@
|
|
|
9320
9570
|
});
|
|
9321
9571
|
const d2 = on(cancelBtn, "click", () => this._hideSizePopover());
|
|
9322
9572
|
const d3 = on(inputEl, "keydown", (e) => {
|
|
9323
|
-
|
|
9573
|
+
const ke = e;
|
|
9574
|
+
if (ke.key === "Enter") {
|
|
9324
9575
|
e.preventDefault();
|
|
9325
9576
|
applyBtn.click();
|
|
9326
9577
|
}
|
|
9327
|
-
if (
|
|
9578
|
+
if (ke.key === "Escape") this._hideSizePopover();
|
|
9328
9579
|
});
|
|
9329
9580
|
const d4 = on(document, "click", (e) => {
|
|
9330
|
-
|
|
9581
|
+
const et = e.target;
|
|
9582
|
+
if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(et) && !this._el.contains(et)) this._hideSizePopover();
|
|
9331
9583
|
});
|
|
9332
9584
|
const d5 = on(popover, "mouseenter", () => this._clearTimers());
|
|
9333
9585
|
const d6 = on(popover, "mouseleave", () => this._scheduleHide());
|
|
@@ -9345,7 +9597,7 @@
|
|
|
9345
9597
|
this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
|
|
9346
9598
|
this._sizeInputEl.min = "0";
|
|
9347
9599
|
this._sizeInputEl.max = "10";
|
|
9348
|
-
this._sizeInputEl.value = currentPx;
|
|
9600
|
+
this._sizeInputEl.value = String(currentPx);
|
|
9349
9601
|
this._sizeApply = (val) => {
|
|
9350
9602
|
const cells = Array.from(table.querySelectorAll("td, th"));
|
|
9351
9603
|
if (val === 0) cells.forEach((c) => {
|
|
@@ -9414,6 +9666,85 @@
|
|
|
9414
9666
|
if (this._sizePopover) this._sizePopover.style.display = "none";
|
|
9415
9667
|
this._sizeApply = null;
|
|
9416
9668
|
}
|
|
9669
|
+
_buildCellShadePopover() {
|
|
9670
|
+
const pop = createElement("div", { class: "an-cell-shade-popover" });
|
|
9671
|
+
pop.style.display = "none";
|
|
9672
|
+
const title = createElement("div", { class: "an-size-popover-title" });
|
|
9673
|
+
pop.appendChild(title);
|
|
9674
|
+
this._shadeTitleEl = title;
|
|
9675
|
+
const palette = createElement("div", { class: "an-context-color-palette" });
|
|
9676
|
+
SHADE_PRESETS.forEach((color) => {
|
|
9677
|
+
const sw = createElement("div", {
|
|
9678
|
+
class: "an-context-color-swatch",
|
|
9679
|
+
title: color
|
|
9680
|
+
});
|
|
9681
|
+
sw.style.background = color;
|
|
9682
|
+
this._disposers.push(on(sw, "click", (e) => {
|
|
9683
|
+
e.stopPropagation();
|
|
9684
|
+
this._applyCellShade(color);
|
|
9685
|
+
}));
|
|
9686
|
+
palette.appendChild(sw);
|
|
9687
|
+
});
|
|
9688
|
+
pop.appendChild(palette);
|
|
9689
|
+
const noShadeRow = createElement("div", { class: "an-context-color-custom" });
|
|
9690
|
+
const noShadeBtn = createElement("button", {
|
|
9691
|
+
type: "button",
|
|
9692
|
+
class: "an-shade-no-color"
|
|
9693
|
+
});
|
|
9694
|
+
this._disposers.push(on(noShadeBtn, "click", () => this._applyCellShade("")));
|
|
9695
|
+
noShadeRow.appendChild(noShadeBtn);
|
|
9696
|
+
pop.appendChild(noShadeRow);
|
|
9697
|
+
this._shadeNoBtn = noShadeBtn;
|
|
9698
|
+
const customRow = createElement("div", { class: "an-context-color-custom" });
|
|
9699
|
+
const colorInput = createElement("input", {
|
|
9700
|
+
type: "color",
|
|
9701
|
+
class: "an-shade-color-input",
|
|
9702
|
+
value: "#ffffff"
|
|
9703
|
+
});
|
|
9704
|
+
const customLabel = createElement("span");
|
|
9705
|
+
customLabel.textContent = "Custom…";
|
|
9706
|
+
this._disposers.push(on(colorInput, "change", () => this._applyCellShade(colorInput.value)));
|
|
9707
|
+
customRow.appendChild(colorInput);
|
|
9708
|
+
customRow.appendChild(customLabel);
|
|
9709
|
+
pop.appendChild(customRow);
|
|
9710
|
+
this._disposers.push(on(pop, "mousedown", (e) => e.preventDefault()));
|
|
9711
|
+
this._disposers.push(on(pop, "mouseenter", () => this._clearTimers()), on(pop, "mouseleave", () => this._scheduleHide()));
|
|
9712
|
+
this._disposers.push(on(document, "click", (e) => {
|
|
9713
|
+
const et = e.target;
|
|
9714
|
+
if (this._shadePopover && this._shadePopover.style.display !== "none" && !this._shadePopover.contains(et) && !(this._el && this._el.contains(et))) this._hideCellShadePopover();
|
|
9715
|
+
}));
|
|
9716
|
+
return pop;
|
|
9717
|
+
}
|
|
9718
|
+
_openCellShadePopover() {
|
|
9719
|
+
if (!this._shadePopover) return;
|
|
9720
|
+
const L = this.context.locale.tooltips.table;
|
|
9721
|
+
if (this._shadeTitleEl) this._shadeTitleEl.textContent = L.cellBackground;
|
|
9722
|
+
if (this._shadeNoBtn) this._shadeNoBtn.textContent = L.noShading;
|
|
9723
|
+
this._shadePopover.style.display = "block";
|
|
9724
|
+
requestAnimationFrame(() => {
|
|
9725
|
+
if (!this._shadePopover || !this._el) return;
|
|
9726
|
+
const pw = this._shadePopover.offsetWidth || 170;
|
|
9727
|
+
const ph = this._shadePopover.offsetHeight || 120;
|
|
9728
|
+
const tipRect = this._el.getBoundingClientRect();
|
|
9729
|
+
let left = tipRect.left;
|
|
9730
|
+
let top = tipRect.bottom + 6;
|
|
9731
|
+
if (left + pw > window.innerWidth - 8) left = window.innerWidth - pw - 8;
|
|
9732
|
+
if (top + ph > window.innerHeight - 8) top = tipRect.top - ph - 6;
|
|
9733
|
+
this._shadePopover.style.left = `${Math.max(8, left)}px`;
|
|
9734
|
+
this._shadePopover.style.top = `${Math.max(8, top)}px`;
|
|
9735
|
+
});
|
|
9736
|
+
}
|
|
9737
|
+
_hideCellShadePopover() {
|
|
9738
|
+
if (this._shadePopover) this._shadePopover.style.display = "none";
|
|
9739
|
+
}
|
|
9740
|
+
_applyCellShade(color) {
|
|
9741
|
+
(this._selectMode ? this._selectedCells : [this._getCell()]).forEach((cell) => {
|
|
9742
|
+
if (cell) cell.style.backgroundColor = color;
|
|
9743
|
+
});
|
|
9744
|
+
if (this._shadeColorStrip) this._shadeColorStrip.style.background = color || "transparent";
|
|
9745
|
+
this._hideCellShadePopover();
|
|
9746
|
+
this.context.invoke("editor.afterCommand");
|
|
9747
|
+
}
|
|
9417
9748
|
};
|
|
9418
9749
|
//#endregion
|
|
9419
9750
|
//#region src/js/module/CodeTooltip.js
|
|
@@ -9446,13 +9777,14 @@
|
|
|
9446
9777
|
const editable = this.context.layoutInfo.editable;
|
|
9447
9778
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
9448
9779
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
9449
|
-
const pre = e.target
|
|
9780
|
+
const pre = e.target?.closest("pre");
|
|
9450
9781
|
if (pre && editable.contains(pre)) this._scheduleShow(pre);
|
|
9451
9782
|
}), on(editable, "mouseout", (e) => {
|
|
9452
9783
|
const to = e.relatedTarget;
|
|
9453
9784
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
9454
9785
|
}), on(document, "click", (e) => {
|
|
9455
|
-
|
|
9786
|
+
const et = e.target;
|
|
9787
|
+
if (this._activePre && !this._activePre.contains(et) && !this._el.contains(et)) this._hide();
|
|
9456
9788
|
}));
|
|
9457
9789
|
return this;
|
|
9458
9790
|
}
|
|
@@ -9487,6 +9819,7 @@
|
|
|
9487
9819
|
["python", "Python"],
|
|
9488
9820
|
["html", "HTML"],
|
|
9489
9821
|
["css", "CSS"],
|
|
9822
|
+
["scss", "SCSS"],
|
|
9490
9823
|
["json", "JSON"],
|
|
9491
9824
|
["xml", "XML"],
|
|
9492
9825
|
["bash", "Bash / Shell"],
|
|
@@ -9642,10 +9975,27 @@
|
|
|
9642
9975
|
this.context.invoke("editor.afterCommand");
|
|
9643
9976
|
this._positionNear(pre);
|
|
9644
9977
|
}
|
|
9978
|
+
/**
|
|
9979
|
+
* Applies a language to a given <pre> element: sets classes, data-language,
|
|
9980
|
+
* and triggers Prism highlighting. Called by the auto-detect flow.
|
|
9981
|
+
* @param {HTMLElement} pre
|
|
9982
|
+
* @param {string} lang - Prism language identifier, e.g. 'javascript'
|
|
9983
|
+
*/
|
|
9984
|
+
applyLanguage(pre, lang) {
|
|
9985
|
+
if (!pre || !lang) return;
|
|
9986
|
+
const savedPre = this._activePre;
|
|
9987
|
+
this._langSelect && this._langSelect.value;
|
|
9988
|
+
this._activePre = pre;
|
|
9989
|
+
if (this._langSelect) this._langSelect.value = lang;
|
|
9990
|
+
this._onLangChange();
|
|
9991
|
+
if (this._langSelect) this._langSelect.value = lang;
|
|
9992
|
+
this._activePre = savedPre || pre;
|
|
9993
|
+
}
|
|
9645
9994
|
_onLangChange() {
|
|
9646
9995
|
const pre = this._activePre;
|
|
9647
9996
|
if (!pre) return;
|
|
9648
9997
|
const lang = this._langSelect.value;
|
|
9998
|
+
const _w = window;
|
|
9649
9999
|
let codeEl = pre.querySelector("code");
|
|
9650
10000
|
if (!codeEl) {
|
|
9651
10001
|
codeEl = document.createElement("code");
|
|
@@ -9659,12 +10009,12 @@
|
|
|
9659
10009
|
else pre.removeAttribute("data-language");
|
|
9660
10010
|
const applyPrism = () => {
|
|
9661
10011
|
codeEl.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
|
|
9662
|
-
|
|
10012
|
+
_w.Prism.highlightElement(codeEl);
|
|
9663
10013
|
this.context.invoke("editor.afterCommand");
|
|
9664
10014
|
};
|
|
9665
10015
|
if (lang) {
|
|
9666
|
-
if (typeof
|
|
9667
|
-
if (
|
|
10016
|
+
if (typeof _w.Prism !== "undefined") {
|
|
10017
|
+
if (_w.Prism.languages[lang]) {
|
|
9668
10018
|
applyPrism();
|
|
9669
10019
|
return;
|
|
9670
10020
|
}
|
|
@@ -9672,7 +10022,7 @@
|
|
|
9672
10022
|
return;
|
|
9673
10023
|
} else if (this._prismScript) {
|
|
9674
10024
|
this._prismScript.addEventListener("load", () => {
|
|
9675
|
-
if (
|
|
10025
|
+
if (_w.Prism.languages[lang]) applyPrism();
|
|
9676
10026
|
else this._loadPrismComponent(lang, applyPrism);
|
|
9677
10027
|
}, { once: true });
|
|
9678
10028
|
return;
|
|
@@ -9685,7 +10035,8 @@
|
|
|
9685
10035
|
* Called once at initialize time. Fire-and-forget; errors are silent.
|
|
9686
10036
|
*/
|
|
9687
10037
|
_ensurePrism() {
|
|
9688
|
-
|
|
10038
|
+
const _w = window;
|
|
10039
|
+
if (!this.context.options.codeHighlight || _w.Prism) return;
|
|
9689
10040
|
const cdn = this.context.options.codeHighlightCDN;
|
|
9690
10041
|
const themeHref = `${cdn}/themes/prism-tomorrow.min.css`;
|
|
9691
10042
|
const scriptSrc = `${cdn}/prism.min.js`;
|
|
@@ -9697,7 +10048,7 @@
|
|
|
9697
10048
|
}
|
|
9698
10049
|
const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
|
|
9699
10050
|
if (existingScript) {
|
|
9700
|
-
this._prismScript =
|
|
10051
|
+
this._prismScript = _w.Prism ? null : existingScript;
|
|
9701
10052
|
return;
|
|
9702
10053
|
}
|
|
9703
10054
|
const script = document.createElement("script");
|
|
@@ -9717,10 +10068,11 @@
|
|
|
9717
10068
|
* @param {Function} cb – called once the grammar is ready
|
|
9718
10069
|
*/
|
|
9719
10070
|
_loadPrismComponent(lang, cb) {
|
|
10071
|
+
const _w = window;
|
|
9720
10072
|
const src = `${this.context.options.codeHighlightCDN}/components/prism-${lang}.min.js`;
|
|
9721
10073
|
if (document.querySelector(`script[src="${src}"]`)) {
|
|
9722
10074
|
const poll = setInterval(() => {
|
|
9723
|
-
if (
|
|
10075
|
+
if (_w.Prism && _w.Prism.languages[lang]) {
|
|
9724
10076
|
clearInterval(poll);
|
|
9725
10077
|
cb();
|
|
9726
10078
|
}
|
|
@@ -12133,15 +12485,19 @@
|
|
|
12133
12485
|
});
|
|
12134
12486
|
const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
|
|
12135
12487
|
const titleRow = createElement("div", { class: "an-icon-title-row" });
|
|
12488
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
12489
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
12490
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M8 13s1.5 2 4 2 4-2 4-2"/><line x1="9" y1="9" x2="9.01" y2="9"/><line x1="15" y1="9" x2="15.01" y2="9"/></svg>`;
|
|
12136
12491
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
12137
12492
|
title.textContent = L.title;
|
|
12493
|
+
titleGroup.append(iconEl, title);
|
|
12138
12494
|
const closeBtn = createElement("button", {
|
|
12139
12495
|
type: "button",
|
|
12140
12496
|
class: "an-icon-close",
|
|
12141
12497
|
"aria-label": L.close
|
|
12142
12498
|
});
|
|
12143
12499
|
closeBtn.innerHTML = "×";
|
|
12144
|
-
titleRow.append(
|
|
12500
|
+
titleRow.append(titleGroup, closeBtn);
|
|
12145
12501
|
const searchInput = createElement("input", {
|
|
12146
12502
|
type: "search",
|
|
12147
12503
|
class: "an-input an-icon-search",
|
|
@@ -12190,6 +12546,7 @@
|
|
|
12190
12546
|
btnRow.appendChild(cancelBtn);
|
|
12191
12547
|
box.append(titleRow, searchInput, catBar, grid, btnRow);
|
|
12192
12548
|
overlay.appendChild(box);
|
|
12549
|
+
makeDraggable(titleRow, box);
|
|
12193
12550
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
12194
12551
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
12195
12552
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -12197,7 +12554,7 @@
|
|
|
12197
12554
|
});
|
|
12198
12555
|
const d4 = on(searchInput, "input", () => this._filterEmojis(searchInput.value, this._activeCat));
|
|
12199
12556
|
const d5 = on(catBar, "click", (e) => {
|
|
12200
|
-
const tab = e.target
|
|
12557
|
+
const tab = e.target?.closest("[data-cat]");
|
|
12201
12558
|
if (tab) {
|
|
12202
12559
|
this._activeCat = tab.dataset.cat;
|
|
12203
12560
|
this._updateCatTabs();
|
|
@@ -12205,7 +12562,7 @@
|
|
|
12205
12562
|
}
|
|
12206
12563
|
});
|
|
12207
12564
|
const d6 = on(grid, "click", (e) => {
|
|
12208
|
-
const cell = e.target
|
|
12565
|
+
const cell = e.target?.closest(".an-emoji-cell");
|
|
12209
12566
|
if (cell) this._onEmojiClick(cell.dataset.char);
|
|
12210
12567
|
});
|
|
12211
12568
|
this._disposers.push(d1, d2, d3, d4, d5, d6);
|
|
@@ -12213,17 +12570,22 @@
|
|
|
12213
12570
|
}
|
|
12214
12571
|
_updateCatTabs() {
|
|
12215
12572
|
this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
|
|
12216
|
-
tab.classList.toggle(
|
|
12573
|
+
tab.classList.toggle(
|
|
12574
|
+
"active",
|
|
12575
|
+
/** @type {HTMLElement} */
|
|
12576
|
+
tab.dataset.cat === this._activeCat
|
|
12577
|
+
);
|
|
12217
12578
|
});
|
|
12218
12579
|
}
|
|
12219
12580
|
_filterEmojis(query, cat) {
|
|
12220
12581
|
const q = (query || "").trim().toLowerCase();
|
|
12221
12582
|
let count = 0;
|
|
12222
12583
|
this._grid.querySelectorAll(".an-emoji-cell").forEach((cell) => {
|
|
12223
|
-
const
|
|
12224
|
-
const
|
|
12584
|
+
const hCell = cell;
|
|
12585
|
+
const matchCat = !cat || cat === "all" || hCell.dataset.cat === cat;
|
|
12586
|
+
const matchQuery = !q || hCell.dataset.keywords.includes(q) || hCell.dataset.char === q;
|
|
12225
12587
|
const visible = matchCat && matchQuery;
|
|
12226
|
-
|
|
12588
|
+
hCell.style.display = visible ? "" : "none";
|
|
12227
12589
|
if (visible) count++;
|
|
12228
12590
|
});
|
|
12229
12591
|
let empty = this._grid.querySelector(".an-icon-empty");
|
|
@@ -12232,7 +12594,7 @@
|
|
|
12232
12594
|
empty.textContent = "No emojis found";
|
|
12233
12595
|
this._grid.appendChild(empty);
|
|
12234
12596
|
}
|
|
12235
|
-
empty.style.display = count > 0 ? "none" : "";
|
|
12597
|
+
/** @type {HTMLElement} */ empty.style.display = count > 0 ? "none" : "";
|
|
12236
12598
|
}
|
|
12237
12599
|
_onEmojiClick(char) {
|
|
12238
12600
|
const savedRange = this._savedRange;
|
|
@@ -12246,7 +12608,7 @@
|
|
|
12246
12608
|
range.collapse(false);
|
|
12247
12609
|
}
|
|
12248
12610
|
const _sc = range.startContainer;
|
|
12249
|
-
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest
|
|
12611
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
|
|
12250
12612
|
range.deleteContents();
|
|
12251
12613
|
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
12252
12614
|
range.setStart(_tdAnchor, 0);
|
|
@@ -12606,15 +12968,19 @@
|
|
|
12606
12968
|
});
|
|
12607
12969
|
const box = createElement("div", { class: "an-dialog-box an-icon-box" });
|
|
12608
12970
|
const titleRow = createElement("div", { class: "an-icon-title-row" });
|
|
12971
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
12972
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
12973
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>`;
|
|
12609
12974
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
12610
12975
|
title.textContent = L.title;
|
|
12976
|
+
titleGroup.append(iconEl, title);
|
|
12611
12977
|
const closeBtn = createElement("button", {
|
|
12612
12978
|
type: "button",
|
|
12613
12979
|
class: "an-icon-close",
|
|
12614
12980
|
"aria-label": L.close
|
|
12615
12981
|
});
|
|
12616
12982
|
closeBtn.innerHTML = "×";
|
|
12617
|
-
titleRow.append(
|
|
12983
|
+
titleRow.append(titleGroup, closeBtn);
|
|
12618
12984
|
const searchInput = createElement("input", {
|
|
12619
12985
|
type: "search",
|
|
12620
12986
|
class: "an-input an-icon-search",
|
|
@@ -12729,6 +13095,7 @@
|
|
|
12729
13095
|
this._insertBtn = insertBtn;
|
|
12730
13096
|
box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
|
|
12731
13097
|
overlay.appendChild(box);
|
|
13098
|
+
makeDraggable(titleRow, box);
|
|
12732
13099
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
12733
13100
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
12734
13101
|
const d3 = on(insertBtn, "click", () => this._onInsert());
|
|
@@ -12737,7 +13104,7 @@
|
|
|
12737
13104
|
});
|
|
12738
13105
|
const d5 = on(searchInput, "input", () => this._filterIcons(searchInput.value, this._activeCat));
|
|
12739
13106
|
const d6 = on(catBar, "click", (e) => {
|
|
12740
|
-
const tab = e.target
|
|
13107
|
+
const tab = e.target?.closest("[data-cat]");
|
|
12741
13108
|
if (tab) {
|
|
12742
13109
|
this._activeCat = tab.dataset.cat;
|
|
12743
13110
|
this._updateCatTabs();
|
|
@@ -12745,7 +13112,7 @@
|
|
|
12745
13112
|
}
|
|
12746
13113
|
});
|
|
12747
13114
|
const d7 = on(grid, "click", (e) => {
|
|
12748
|
-
const cell = e.target
|
|
13115
|
+
const cell = e.target?.closest(".an-icon-cell");
|
|
12749
13116
|
if (cell) this._selectIcon(cell.dataset.name);
|
|
12750
13117
|
});
|
|
12751
13118
|
const d8 = on(styleSelect, "change", () => this._updatePreview(this._selectedIcon));
|
|
@@ -12757,19 +13124,24 @@
|
|
|
12757
13124
|
}
|
|
12758
13125
|
_updateCatTabs() {
|
|
12759
13126
|
this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
|
|
12760
|
-
tab.classList.toggle(
|
|
13127
|
+
tab.classList.toggle(
|
|
13128
|
+
"active",
|
|
13129
|
+
/** @type {HTMLElement} */
|
|
13130
|
+
tab.dataset.cat === this._activeCat
|
|
13131
|
+
);
|
|
12761
13132
|
});
|
|
12762
13133
|
}
|
|
12763
13134
|
_filterIcons(query, cat) {
|
|
12764
13135
|
const q = (query || "").trim().toLowerCase();
|
|
12765
13136
|
let visibleCount = 0;
|
|
12766
13137
|
this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
|
|
12767
|
-
const
|
|
12768
|
-
const
|
|
13138
|
+
const hCell = cell;
|
|
13139
|
+
const name = hCell.dataset.name;
|
|
13140
|
+
const cellCat = hCell.dataset.cat;
|
|
12769
13141
|
const matchesCat = !cat || cat === "all" || cellCat === cat;
|
|
12770
13142
|
const matchesQuery = !q || name.includes(q);
|
|
12771
13143
|
const visible = matchesCat && matchesQuery;
|
|
12772
|
-
|
|
13144
|
+
hCell.style.display = visible ? "" : "none";
|
|
12773
13145
|
if (visible) visibleCount++;
|
|
12774
13146
|
});
|
|
12775
13147
|
let empty = this._grid.querySelector(".an-icon-empty");
|
|
@@ -12778,12 +13150,16 @@
|
|
|
12778
13150
|
empty.textContent = "No icons found";
|
|
12779
13151
|
this._grid.appendChild(empty);
|
|
12780
13152
|
}
|
|
12781
|
-
empty.style.display = visibleCount > 0 ? "none" : "";
|
|
13153
|
+
/** @type {HTMLElement} */ empty.style.display = visibleCount > 0 ? "none" : "";
|
|
12782
13154
|
}
|
|
12783
13155
|
_selectIcon(name) {
|
|
12784
13156
|
this._selectedIcon = name;
|
|
12785
13157
|
this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
|
|
12786
|
-
cell.classList.toggle(
|
|
13158
|
+
cell.classList.toggle(
|
|
13159
|
+
"active",
|
|
13160
|
+
/** @type {HTMLElement} */
|
|
13161
|
+
cell.dataset.name === name
|
|
13162
|
+
);
|
|
12787
13163
|
});
|
|
12788
13164
|
this._insertBtn.removeAttribute("disabled");
|
|
12789
13165
|
this._updatePreview(name);
|
|
@@ -12822,7 +13198,7 @@
|
|
|
12822
13198
|
range.collapse(false);
|
|
12823
13199
|
}
|
|
12824
13200
|
const _sc = range.startContainer;
|
|
12825
|
-
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest
|
|
13201
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
|
|
12826
13202
|
range.deleteContents();
|
|
12827
13203
|
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
12828
13204
|
range.setStart(_tdAnchor, 0);
|
|
@@ -13093,13 +13469,13 @@
|
|
|
13093
13469
|
this._menuDisposers.forEach((d) => {
|
|
13094
13470
|
try {
|
|
13095
13471
|
d();
|
|
13096
|
-
} catch (
|
|
13472
|
+
} catch (_e) {}
|
|
13097
13473
|
});
|
|
13098
13474
|
this._menuDisposers = [];
|
|
13099
13475
|
this._disposers.forEach((d) => {
|
|
13100
13476
|
try {
|
|
13101
13477
|
d();
|
|
13102
|
-
} catch (
|
|
13478
|
+
} catch (_e) {}
|
|
13103
13479
|
});
|
|
13104
13480
|
this._disposers = [];
|
|
13105
13481
|
if (this.el && this.el.parentNode) this.el.parentNode.removeChild(this.el);
|
|
@@ -13290,13 +13666,13 @@
|
|
|
13290
13666
|
});
|
|
13291
13667
|
this._menuDisposers.push(offHeader);
|
|
13292
13668
|
const offMove = on(gridEl, "mousemove", (e) => {
|
|
13293
|
-
const cell = e.target
|
|
13669
|
+
const cell = e.target?.closest("[data-row]");
|
|
13294
13670
|
if (!cell) return;
|
|
13295
13671
|
setHighlight(+cell.dataset.row, +cell.dataset.col);
|
|
13296
13672
|
});
|
|
13297
13673
|
const offLeave = on(gridEl, "mouseleave", () => setHighlight(0, 0));
|
|
13298
13674
|
const offClick = on(gridEl, "click", (e) => {
|
|
13299
|
-
const cell = e.target
|
|
13675
|
+
const cell = e.target?.closest("[data-row]");
|
|
13300
13676
|
if (!cell) return;
|
|
13301
13677
|
const rows = +cell.dataset.row;
|
|
13302
13678
|
const cols = +cell.dataset.col;
|
|
@@ -13321,7 +13697,7 @@
|
|
|
13321
13697
|
class: "an-context-item",
|
|
13322
13698
|
"data-name": it.name || ""
|
|
13323
13699
|
});
|
|
13324
|
-
if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) btn.disabled = true;
|
|
13700
|
+
if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) /** @type {HTMLButtonElement} */ btn.disabled = true;
|
|
13325
13701
|
if (it.icon) {
|
|
13326
13702
|
const iconSpan = createElement("span", {
|
|
13327
13703
|
class: "an-context-icon",
|
|
@@ -13353,7 +13729,7 @@
|
|
|
13353
13729
|
const winSel = window.getSelection();
|
|
13354
13730
|
this._savedRange = winSel && winSel.rangeCount > 0 ? winSel.getRangeAt(0).cloneRange() : null;
|
|
13355
13731
|
this._renderItems(this._items);
|
|
13356
|
-
|
|
13732
|
+
const openX = event.clientX;
|
|
13357
13733
|
let openY = event.clientY;
|
|
13358
13734
|
if (this._savedRange && !this._savedRange.collapsed) try {
|
|
13359
13735
|
const selRect = this._savedRange.getBoundingClientRect();
|
|
@@ -13519,7 +13895,7 @@
|
|
|
13519
13895
|
while (el = iter.nextNode()) {
|
|
13520
13896
|
if (!editable.contains(el) || el === editable) continue;
|
|
13521
13897
|
try {
|
|
13522
|
-
if (range.intersectsNode(el)) el.removeAttribute("style");
|
|
13898
|
+
if (range.intersectsNode(el)) /** @type {Element} */ el.removeAttribute("style");
|
|
13523
13899
|
} catch {}
|
|
13524
13900
|
}
|
|
13525
13901
|
this.context.invoke("editor.afterCommand");
|
|
@@ -13781,8 +14157,8 @@
|
|
|
13781
14157
|
const replaceActions = this._dialog.querySelector(".an-fr-replace-actions");
|
|
13782
14158
|
const title = this._dialog.querySelector(".an-dialog-title");
|
|
13783
14159
|
const isReplace = this._mode === "replace";
|
|
13784
|
-
if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
|
|
13785
|
-
if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
|
|
14160
|
+
if (replaceRow) /** @type {HTMLElement} */ replaceRow.style.display = isReplace ? "" : "none";
|
|
14161
|
+
if (replaceActions) /** @type {HTMLElement} */ replaceActions.style.display = isReplace ? "" : "none";
|
|
13786
14162
|
if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
|
|
13787
14163
|
}
|
|
13788
14164
|
_buildDialog() {
|
|
@@ -13793,106 +14169,123 @@
|
|
|
13793
14169
|
"aria-modal": "true",
|
|
13794
14170
|
"aria-label": L.findReplaceTitle
|
|
13795
14171
|
});
|
|
13796
|
-
const box = createElement("div", { class: "an-dialog-box" });
|
|
13797
|
-
const
|
|
14172
|
+
const box = createElement("div", { class: "an-dialog-box an-fr-box" });
|
|
14173
|
+
const header = createElement("div", { class: "an-fr-header" });
|
|
14174
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
14175
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
14176
|
+
iconEl.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>`;
|
|
13798
14177
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
13799
14178
|
title.textContent = L.findTitle;
|
|
14179
|
+
titleGroup.append(iconEl, title);
|
|
13800
14180
|
const closeBtn = createElement("button", {
|
|
13801
14181
|
type: "button",
|
|
13802
14182
|
class: "an-icon-close",
|
|
13803
|
-
|
|
14183
|
+
title: L.close,
|
|
14184
|
+
"aria-label": L.close
|
|
13804
14185
|
});
|
|
13805
|
-
closeBtn.
|
|
14186
|
+
closeBtn.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>`;
|
|
13806
14187
|
this._closeBtn = closeBtn;
|
|
13807
|
-
|
|
13808
|
-
box.appendChild(
|
|
13809
|
-
const
|
|
14188
|
+
header.append(titleGroup, closeBtn);
|
|
14189
|
+
box.appendChild(header);
|
|
14190
|
+
const searchBar = createElement("div", { class: "an-fr-search-bar" });
|
|
13810
14191
|
const findInput = createElement("input", {
|
|
13811
14192
|
type: "text",
|
|
13812
|
-
class: "an-input",
|
|
14193
|
+
class: "an-input an-fr-input",
|
|
13813
14194
|
placeholder: L.findPlaceholder,
|
|
13814
14195
|
"aria-label": L.searchAriaLabel
|
|
13815
14196
|
});
|
|
13816
14197
|
this._findInput = findInput;
|
|
13817
|
-
findRow.appendChild(findInput);
|
|
13818
|
-
box.appendChild(findRow);
|
|
13819
|
-
const optRow = createElement("div", { class: "an-fr-options-row" });
|
|
13820
|
-
const caseLabel = createElement("label", { class: "an-label an-label-inline" });
|
|
13821
14198
|
const caseCheckbox = createElement("input", {
|
|
13822
14199
|
type: "checkbox",
|
|
13823
|
-
|
|
14200
|
+
style: "display:none",
|
|
14201
|
+
"aria-hidden": "true"
|
|
13824
14202
|
});
|
|
13825
14203
|
this._caseCheckbox = caseCheckbox;
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
14204
|
+
const caseBtn = createElement("button", {
|
|
14205
|
+
type: "button",
|
|
14206
|
+
class: "an-fr-icon-btn",
|
|
14207
|
+
title: "Case sensitive",
|
|
14208
|
+
"aria-label": "Case sensitive"
|
|
14209
|
+
});
|
|
14210
|
+
caseBtn.textContent = "Aa";
|
|
13832
14211
|
const prevBtn = createElement("button", {
|
|
13833
14212
|
type: "button",
|
|
13834
|
-
class: "an-btn"
|
|
14213
|
+
class: "an-fr-icon-btn",
|
|
14214
|
+
title: "Previous (Shift+Enter)",
|
|
14215
|
+
"aria-label": "Previous"
|
|
13835
14216
|
});
|
|
13836
|
-
prevBtn.
|
|
14217
|
+
prevBtn.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="18 15 12 9 6 15"/></svg>`;
|
|
13837
14218
|
const nextBtn = createElement("button", {
|
|
13838
14219
|
type: "button",
|
|
13839
|
-
class: "an-
|
|
14220
|
+
class: "an-fr-icon-btn",
|
|
14221
|
+
title: "Next (Enter)",
|
|
14222
|
+
"aria-label": "Next"
|
|
13840
14223
|
});
|
|
13841
|
-
nextBtn.
|
|
13842
|
-
|
|
13843
|
-
|
|
14224
|
+
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>`;
|
|
14225
|
+
const counter = createElement("span", { class: "an-fr-counter" });
|
|
14226
|
+
this._counterEl = counter;
|
|
14227
|
+
searchBar.append(findInput, caseCheckbox, caseBtn, prevBtn, nextBtn, counter);
|
|
14228
|
+
box.appendChild(searchBar);
|
|
13844
14229
|
const replaceRow = createElement("div", { class: "an-fr-replace-row" });
|
|
13845
14230
|
replaceRow.style.display = "none";
|
|
13846
14231
|
const replaceInput = createElement("input", {
|
|
13847
14232
|
type: "text",
|
|
13848
|
-
class: "an-input",
|
|
14233
|
+
class: "an-input an-fr-input",
|
|
13849
14234
|
placeholder: L.replacePlaceholder,
|
|
13850
14235
|
"aria-label": L.replaceAriaLabel
|
|
13851
14236
|
});
|
|
13852
14237
|
this._replaceInput = replaceInput;
|
|
13853
|
-
replaceRow.appendChild(replaceInput);
|
|
13854
|
-
box.appendChild(replaceRow);
|
|
13855
|
-
const replaceActions = createElement("div", { class: "an-dialog-actions an-fr-replace-actions" });
|
|
13856
|
-
replaceActions.style.display = "none";
|
|
13857
14238
|
const replaceBtn = createElement("button", {
|
|
13858
14239
|
type: "button",
|
|
13859
|
-
class: "an-btn"
|
|
14240
|
+
class: "an-btn an-fr-replace-btn"
|
|
13860
14241
|
});
|
|
13861
14242
|
replaceBtn.textContent = L.replaceBtn;
|
|
13862
14243
|
const replaceAllBtn = createElement("button", {
|
|
13863
14244
|
type: "button",
|
|
13864
|
-
class: "an-btn an-btn-primary"
|
|
14245
|
+
class: "an-btn an-btn-primary an-fr-replace-btn"
|
|
13865
14246
|
});
|
|
13866
14247
|
replaceAllBtn.textContent = L.replaceAllBtn;
|
|
13867
|
-
|
|
14248
|
+
replaceRow.append(replaceInput, replaceBtn, replaceAllBtn);
|
|
14249
|
+
box.appendChild(replaceRow);
|
|
14250
|
+
const replaceActions = createElement("div", { class: "an-fr-replace-actions" });
|
|
14251
|
+
replaceActions.style.display = "none";
|
|
13868
14252
|
box.appendChild(replaceActions);
|
|
13869
14253
|
overlay.appendChild(box);
|
|
14254
|
+
makeDraggable(header, box);
|
|
13870
14255
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
13871
14256
|
const d2 = on(overlay, "click", (e) => {
|
|
13872
14257
|
if (e.target === overlay) this._close();
|
|
13873
14258
|
});
|
|
13874
14259
|
const d3 = on(findInput, "input", () => this._onSearch());
|
|
13875
|
-
const d4 = on(
|
|
14260
|
+
const d4 = on(caseBtn, "click", () => {
|
|
14261
|
+
this._caseSensitive = !this._caseSensitive;
|
|
14262
|
+
caseCheckbox.checked = this._caseSensitive;
|
|
14263
|
+
caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
|
|
14264
|
+
this._onSearch();
|
|
14265
|
+
});
|
|
14266
|
+
const d5 = on(caseCheckbox, "change", () => {
|
|
13876
14267
|
this._caseSensitive = caseCheckbox.checked;
|
|
14268
|
+
caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
|
|
13877
14269
|
this._onSearch();
|
|
13878
14270
|
});
|
|
13879
|
-
const
|
|
13880
|
-
const
|
|
13881
|
-
const
|
|
13882
|
-
const
|
|
13883
|
-
const
|
|
13884
|
-
|
|
14271
|
+
const d6 = on(nextBtn, "click", () => this._next());
|
|
14272
|
+
const d7 = on(prevBtn, "click", () => this._prev());
|
|
14273
|
+
const d8 = on(replaceBtn, "click", () => this._replace());
|
|
14274
|
+
const d9 = on(replaceAllBtn, "click", () => this._replaceAll());
|
|
14275
|
+
const d10 = on(findInput, "keydown", (e) => {
|
|
14276
|
+
const ke = e;
|
|
14277
|
+
if (ke.key === "Enter") {
|
|
13885
14278
|
e.preventDefault();
|
|
13886
|
-
|
|
14279
|
+
ke.shiftKey ? this._prev() : this._next();
|
|
13887
14280
|
}
|
|
13888
14281
|
});
|
|
13889
|
-
const
|
|
14282
|
+
const d11 = on(replaceInput, "keydown", (e) => {
|
|
13890
14283
|
if (e.key === "Enter") {
|
|
13891
14284
|
e.preventDefault();
|
|
13892
14285
|
this._replace();
|
|
13893
14286
|
}
|
|
13894
14287
|
});
|
|
13895
|
-
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
|
|
14288
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
|
|
13896
14289
|
return overlay;
|
|
13897
14290
|
}
|
|
13898
14291
|
_onSearch() {
|
|
@@ -14253,9 +14646,10 @@
|
|
|
14253
14646
|
}), on(h, "touchstart", (e) => {
|
|
14254
14647
|
e.preventDefault();
|
|
14255
14648
|
e.stopPropagation();
|
|
14649
|
+
const te = e;
|
|
14256
14650
|
this._startHandleDrag({
|
|
14257
|
-
clientX:
|
|
14258
|
-
clientY:
|
|
14651
|
+
clientX: te.touches[0].clientX,
|
|
14652
|
+
clientY: te.touches[0].clientY
|
|
14259
14653
|
}, id);
|
|
14260
14654
|
}, { passive: false }));
|
|
14261
14655
|
this._handles[id] = h;
|
|
@@ -14272,9 +14666,10 @@
|
|
|
14272
14666
|
if (e.target !== cropBox && e.target !== grid) return;
|
|
14273
14667
|
e.preventDefault();
|
|
14274
14668
|
e.stopPropagation();
|
|
14669
|
+
const te2 = e;
|
|
14275
14670
|
this._startBoxMove({
|
|
14276
|
-
clientX:
|
|
14277
|
-
clientY:
|
|
14671
|
+
clientX: te2.touches[0].clientX,
|
|
14672
|
+
clientY: te2.touches[0].clientY
|
|
14278
14673
|
});
|
|
14279
14674
|
}, { passive: false }));
|
|
14280
14675
|
const infoEl = document.createElement("div");
|
|
@@ -14436,9 +14831,10 @@
|
|
|
14436
14831
|
_attachDocDrag(onMove) {
|
|
14437
14832
|
const onTouchMove = (e) => {
|
|
14438
14833
|
e.preventDefault();
|
|
14834
|
+
const te3 = e;
|
|
14439
14835
|
onMove({
|
|
14440
|
-
clientX:
|
|
14441
|
-
clientY:
|
|
14836
|
+
clientX: te3.touches[0].clientX,
|
|
14837
|
+
clientY: te3.touches[0].clientY
|
|
14442
14838
|
});
|
|
14443
14839
|
};
|
|
14444
14840
|
const cleanup = () => {
|
|
@@ -15015,7 +15411,9 @@
|
|
|
15015
15411
|
const d6 = this.context.on("contextMenu:hide", () => {
|
|
15016
15412
|
this._contextMenuOpen = false;
|
|
15017
15413
|
});
|
|
15018
|
-
|
|
15414
|
+
const d7 = on(window, "scroll", () => this._hide(), { passive: true });
|
|
15415
|
+
const d8 = on(window, "resize", () => this._hide(), { passive: true });
|
|
15416
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8);
|
|
15019
15417
|
return this;
|
|
15020
15418
|
}
|
|
15021
15419
|
destroy() {
|
|
@@ -15129,20 +15527,22 @@
|
|
|
15129
15527
|
picker.appendChild(customRow);
|
|
15130
15528
|
document.body.appendChild(picker);
|
|
15131
15529
|
this._picker = picker;
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15530
|
+
const pickerAny = picker;
|
|
15531
|
+
pickerAny._paletteEl = palette;
|
|
15532
|
+
pickerAny._noColorBtn = noColorBtn;
|
|
15533
|
+
pickerAny._colorInput = colorInput;
|
|
15135
15534
|
}
|
|
15136
15535
|
_openColorPicker(type, anchorBtn) {
|
|
15137
15536
|
const sel = window.getSelection();
|
|
15138
15537
|
if (sel && sel.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
|
|
15139
15538
|
this._pickerType = type;
|
|
15140
|
-
const
|
|
15141
|
-
const
|
|
15539
|
+
const pickerAny = this._picker;
|
|
15540
|
+
const palette = pickerAny._paletteEl;
|
|
15541
|
+
const noColorBtn = pickerAny._noColorBtn;
|
|
15142
15542
|
if (type === "hiliteColor") {
|
|
15143
15543
|
if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
|
|
15144
15544
|
} else if (palette.contains(noColorBtn)) palette.removeChild(noColorBtn);
|
|
15145
|
-
this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15545
|
+
/** @type {any} */ this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15146
15546
|
this._picker.style.display = "block";
|
|
15147
15547
|
const pw = this._picker.offsetWidth;
|
|
15148
15548
|
const ph = this._picker.offsetHeight;
|
|
@@ -15176,7 +15576,7 @@
|
|
|
15176
15576
|
const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
|
|
15177
15577
|
const btn = this._el && this._el.querySelector(`[data-name="${name}"]`);
|
|
15178
15578
|
const strip = btn && btn.querySelector(".an-bubble-color-strip");
|
|
15179
|
-
if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15579
|
+
if (strip) /** @type {HTMLElement} */ strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15180
15580
|
this._closeColorPicker();
|
|
15181
15581
|
this._syncActive();
|
|
15182
15582
|
}
|
|
@@ -15192,6 +15592,14 @@
|
|
|
15192
15592
|
let top = rect.top - bh - gap;
|
|
15193
15593
|
left = Math.max(8, Math.min(left, window.innerWidth - bw - 8));
|
|
15194
15594
|
if (top < 8) top = rect.bottom + gap;
|
|
15595
|
+
const tableTooltipEl = document.querySelector(".an-table-tooltip");
|
|
15596
|
+
if (tableTooltipEl && tableTooltipEl.style.display !== "none") {
|
|
15597
|
+
const ttRect = tableTooltipEl.getBoundingClientRect();
|
|
15598
|
+
if (top < ttRect.bottom + gap && top + bh > ttRect.top - gap) {
|
|
15599
|
+
top = rect.bottom + gap;
|
|
15600
|
+
if (top + bh > window.innerHeight - 8) top = ttRect.bottom + gap;
|
|
15601
|
+
}
|
|
15602
|
+
}
|
|
15195
15603
|
el.style.top = `${top}px`;
|
|
15196
15604
|
el.style.left = `${left}px`;
|
|
15197
15605
|
el.style.visibility = "";
|
|
@@ -15223,12 +15631,12 @@
|
|
|
15223
15631
|
const cs = window.getComputedStyle(node);
|
|
15224
15632
|
const foreBtn = this._el.querySelector("[data-name=\"foreColor\"]");
|
|
15225
15633
|
const foreStrip = foreBtn && foreBtn.querySelector(".an-bubble-color-strip");
|
|
15226
|
-
if (foreStrip) foreStrip.style.background = cs.color || "#000000";
|
|
15634
|
+
if (foreStrip) /** @type {HTMLElement} */ foreStrip.style.background = cs.color || "#000000";
|
|
15227
15635
|
const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
|
|
15228
15636
|
const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
|
|
15229
15637
|
if (hiliteStrip) {
|
|
15230
15638
|
const bg = cs.backgroundColor;
|
|
15231
|
-
hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15639
|
+
/** @type {HTMLElement} */ hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15232
15640
|
}
|
|
15233
15641
|
}
|
|
15234
15642
|
_onSelectionChange() {
|
|
@@ -15356,11 +15764,11 @@
|
|
|
15356
15764
|
el.setAttribute("role", "listbox");
|
|
15357
15765
|
el.addEventListener("mousedown", (e) => e.preventDefault());
|
|
15358
15766
|
el.addEventListener("click", (e) => {
|
|
15359
|
-
const item = e.target
|
|
15767
|
+
const item = e.target?.closest(".an-mention-item");
|
|
15360
15768
|
if (item) this._select(+item.dataset.index);
|
|
15361
15769
|
});
|
|
15362
15770
|
el.addEventListener("mousemove", (e) => {
|
|
15363
|
-
const item = e.target
|
|
15771
|
+
const item = e.target?.closest(".an-mention-item");
|
|
15364
15772
|
if (item) this._highlightItem(+item.dataset.index);
|
|
15365
15773
|
});
|
|
15366
15774
|
document.body.appendChild(el);
|
|
@@ -15375,7 +15783,7 @@
|
|
|
15375
15783
|
const li = document.createElement("div");
|
|
15376
15784
|
li.className = "an-mention-item";
|
|
15377
15785
|
li.setAttribute("role", "option");
|
|
15378
|
-
li.dataset.index = i;
|
|
15786
|
+
li.dataset.index = String(i);
|
|
15379
15787
|
if (item.avatar) {
|
|
15380
15788
|
const img = document.createElement("img");
|
|
15381
15789
|
img.src = item.avatar;
|
|
@@ -15625,7 +16033,7 @@
|
|
|
15625
16033
|
/**
|
|
15626
16034
|
* Registers and initialises a custom module on this instance.
|
|
15627
16035
|
* @param {string} name
|
|
15628
|
-
* @param {
|
|
16036
|
+
* @param {new (ctx: this) => any} ModuleClass
|
|
15629
16037
|
* @returns {this}
|
|
15630
16038
|
*/
|
|
15631
16039
|
registerModule(name, ModuleClass) {
|
|
@@ -15932,10 +16340,12 @@
|
|
|
15932
16340
|
this._disposers.forEach((d) => d());
|
|
15933
16341
|
this._disposers = [];
|
|
15934
16342
|
const container = this.layoutInfo.container;
|
|
16343
|
+
const wasDark = container && container.classList.contains("an-theme-dark");
|
|
15935
16344
|
if (container && container.parentNode) {
|
|
15936
16345
|
this.targetEl.style.display = "";
|
|
15937
16346
|
container.parentNode.removeChild(container);
|
|
15938
16347
|
}
|
|
16348
|
+
if (wasDark && !document.querySelector(".an-container.an-theme-dark")) document.body.classList.remove("an-theme-dark");
|
|
15939
16349
|
if (typeof this.options.onDestroy === "function") this.options.onDestroy(this);
|
|
15940
16350
|
this._alive = false;
|
|
15941
16351
|
this._listeners.clear();
|
|
@@ -15944,7 +16354,8 @@
|
|
|
15944
16354
|
* Syncs editor HTML back into the original textarea/input for form submission.
|
|
15945
16355
|
*/
|
|
15946
16356
|
_syncToTarget() {
|
|
15947
|
-
if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
|
|
16357
|
+
if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
|
|
16358
|
+
/** @type {HTMLInputElement} */ this.targetEl.value = this.getHTML();
|
|
15948
16359
|
}
|
|
15949
16360
|
};
|
|
15950
16361
|
//#endregion
|
|
@@ -15961,6 +16372,13 @@
|
|
|
15961
16372
|
/** @type {WeakMap<Element, Context>} */
|
|
15962
16373
|
var instances = /* @__PURE__ */ new WeakMap();
|
|
15963
16374
|
var AutumnNote = {
|
|
16375
|
+
/**
|
|
16376
|
+
* Creates (or returns existing) editor instance on one or more elements.
|
|
16377
|
+
*
|
|
16378
|
+
* @param {string|Element|NodeList|Element[]} selector
|
|
16379
|
+
* @param {import('./settings.js').AsnOptions} [options]
|
|
16380
|
+
* @returns {Context|Context[]} single Context or array of Contexts
|
|
16381
|
+
*/
|
|
15964
16382
|
create(selector, options = {}) {
|
|
15965
16383
|
const ctxs = resolveElements(selector).map((el) => {
|
|
15966
16384
|
if (instances.has(el)) return instances.get(el);
|
|
@@ -15971,6 +16389,10 @@
|
|
|
15971
16389
|
});
|
|
15972
16390
|
return ctxs.length === 1 ? ctxs[0] : ctxs;
|
|
15973
16391
|
},
|
|
16392
|
+
/**
|
|
16393
|
+
* Destroys the editor(s) on the given selector.
|
|
16394
|
+
* @param {string|Element|NodeList|Element[]} selector
|
|
16395
|
+
*/
|
|
15974
16396
|
destroy(selector) {
|
|
15975
16397
|
resolveElements(selector).forEach((el) => {
|
|
15976
16398
|
const ctx = instances.get(el);
|
|
@@ -15980,23 +16402,45 @@
|
|
|
15980
16402
|
}
|
|
15981
16403
|
});
|
|
15982
16404
|
},
|
|
16405
|
+
/**
|
|
16406
|
+
* Returns the Context instance for a given element (or null).
|
|
16407
|
+
* @param {string|Element} selector
|
|
16408
|
+
* @returns {Context|null}
|
|
16409
|
+
*/
|
|
15983
16410
|
getInstance(selector) {
|
|
15984
16411
|
const el = typeof selector === "string" ? document.querySelector(selector) : selector;
|
|
15985
16412
|
return el ? instances.get(el) || null : null;
|
|
15986
16413
|
},
|
|
16414
|
+
/** Returns a shallow copy of the default options (read-only snapshot). */
|
|
15987
16415
|
get defaults() {
|
|
15988
16416
|
return { ...defaultOptions };
|
|
15989
16417
|
},
|
|
16418
|
+
/** Merges properties into the global defaults, applied to all future instances. */
|
|
15990
16419
|
setDefaults(overrides) {
|
|
15991
16420
|
Object.assign(defaultOptions, overrides);
|
|
15992
16421
|
},
|
|
16422
|
+
/** Restores global defaults to their original factory values. */
|
|
15993
16423
|
resetDefaults() {
|
|
15994
16424
|
Object.keys(defaultOptions).forEach((k) => delete defaultOptions[k]);
|
|
15995
16425
|
Object.assign(defaultOptions, _originalDefaults);
|
|
15996
16426
|
},
|
|
16427
|
+
/**
|
|
16428
|
+
* Registers a custom module to be included in every new editor instance.
|
|
16429
|
+
* @param {string} name - unique module key used for ctx.invoke() calls
|
|
16430
|
+
* @param {Function} ModuleClass - class with initialize() and optional destroy()
|
|
16431
|
+
*/
|
|
15997
16432
|
registerModule(name, ModuleClass) {
|
|
15998
16433
|
_customModules.set(name, ModuleClass);
|
|
15999
16434
|
},
|
|
16435
|
+
/**
|
|
16436
|
+
* Installs a plugin globally — applied to every future editor instance.
|
|
16437
|
+
* Plugin `buttons` are registered to the global button registry immediately
|
|
16438
|
+
* so they are available when Toolbar initialises inside create().
|
|
16439
|
+
* Plugin `install()` is called after all built-in modules have initialised.
|
|
16440
|
+
* @param {object} plugin - { name, version?, buttons?, install?, uninstall? }
|
|
16441
|
+
* @param {object} [options] - Forwarded to plugin.install(context, options)
|
|
16442
|
+
* @returns {typeof AutumnNote}
|
|
16443
|
+
*/
|
|
16000
16444
|
use(plugin, options = {}) {
|
|
16001
16445
|
if (!plugin || typeof plugin.name !== "string") throw new TypeError("[AutumnNote] AutumnNote.use: plugin must have a string `name` property.");
|
|
16002
16446
|
if (_globalPlugins.has(plugin.name)) {
|
|
@@ -16010,14 +16454,26 @@
|
|
|
16010
16454
|
});
|
|
16011
16455
|
return this;
|
|
16012
16456
|
},
|
|
16457
|
+
/**
|
|
16458
|
+
* Returns true if a plugin with the given name has been registered globally.
|
|
16459
|
+
* @param {string} name
|
|
16460
|
+
* @returns {boolean}
|
|
16461
|
+
*/
|
|
16013
16462
|
hasPlugin(name) {
|
|
16014
16463
|
return _globalPlugins.has(name);
|
|
16015
16464
|
},
|
|
16465
|
+
/**
|
|
16466
|
+
* Registers a single button definition in the global button registry.
|
|
16467
|
+
* After create(), call ctx.invoke('toolbar.rebuild') to render new buttons.
|
|
16468
|
+
* @param {object} btnDef - ButtonDef-compatible object with a `name` string
|
|
16469
|
+
* @returns {typeof AutumnNote}
|
|
16470
|
+
*/
|
|
16016
16471
|
registerButton(btnDef) {
|
|
16017
16472
|
registerButton(btnDef);
|
|
16018
16473
|
return this;
|
|
16019
16474
|
},
|
|
16020
|
-
version
|
|
16475
|
+
/** Library version */
|
|
16476
|
+
version: "1.5.0"
|
|
16021
16477
|
};
|
|
16022
16478
|
/**
|
|
16023
16479
|
* @param {string|Element|NodeList|Element[]} selector
|