autumnnote 1.5.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/autumnnote.css +324 -4
- package/dist/autumnnote.es.js +943 -526
- package/dist/autumnnote.es.js.map +1 -1
- package/dist/autumnnote.umd.js +935 -519
- package/dist/autumnnote.umd.js.map +1 -1
- package/package.json +21 -3
- package/src/js/Context.js +23 -16
- package/src/js/core/detectLang.js +98 -0
- package/src/js/core/dom.js +67 -11
- package/src/js/core/env.js +1 -1
- package/src/js/core/func.js +1 -1
- package/src/js/core/lists.js +1 -1
- package/src/js/core/markdown.js +32 -31
- package/src/js/core/range.js +8 -8
- package/src/js/editing/History.js +10 -10
- package/src/js/editing/Style.js +44 -44
- package/src/js/editing/Table.js +5 -7
- package/src/js/editing/Typing.js +19 -19
- package/src/js/i18n/en.js +2 -0
- package/src/js/i18n/vi.js +2 -0
- package/src/js/index.js +3 -2
- package/src/js/module/AutoSaveRestore.js +2 -4
- package/src/js/module/BubbleToolbar.js +51 -34
- package/src/js/module/Buttons.js +20 -18
- package/src/js/module/Clipboard.js +16 -17
- package/src/js/module/CodeTooltip.js +48 -24
- package/src/js/module/Codeview.js +5 -7
- package/src/js/module/ContextMenu.js +37 -37
- package/src/js/module/Editor.js +77 -26
- package/src/js/module/EmojiDialog.js +24 -18
- package/src/js/module/FindReplace.js +93 -66
- package/src/js/module/Fullscreen.js +1 -1
- package/src/js/module/IconDialog.js +39 -35
- package/src/js/module/ImageCropOverlay.js +13 -13
- package/src/js/module/ImageDialog.js +19 -13
- package/src/js/module/ImageResizer.js +5 -5
- package/src/js/module/ImageTooltip.js +20 -16
- package/src/js/module/LinkDialog.js +20 -14
- package/src/js/module/LinkTooltip.js +15 -12
- package/src/js/module/MarkdownShortcuts.js +11 -14
- package/src/js/module/Mention.js +11 -13
- package/src/js/module/Placeholder.js +1 -1
- package/src/js/module/ShortcutsDialog.js +2 -4
- package/src/js/module/Statusbar.js +5 -7
- package/src/js/module/TableTooltip.js +196 -36
- package/src/js/module/Toolbar.js +35 -34
- package/src/js/module/VideoDialog.js +16 -10
- package/src/js/module/VideoResizer.js +7 -9
- package/src/js/module/VideoTooltip.js +15 -10
- package/src/js/renderer.js +5 -3
- package/src/js/settings.js +53 -36
- package/src/styles/autumnnote.scss +332 -7
package/dist/autumnnote.umd.js
CHANGED
|
@@ -114,27 +114,71 @@
|
|
|
114
114
|
const handler = (e) => {
|
|
115
115
|
if (e.key === "Escape") {
|
|
116
116
|
e.stopPropagation();
|
|
117
|
-
onEscape
|
|
117
|
+
onEscape?.();
|
|
118
118
|
return;
|
|
119
119
|
}
|
|
120
120
|
if (e.key !== "Tab") return;
|
|
121
121
|
const els = getFocusable();
|
|
122
122
|
if (!els.length) return;
|
|
123
123
|
const first = els[0];
|
|
124
|
-
const last = els
|
|
124
|
+
const last = els.at(-1);
|
|
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 - Number.parseFloat(box.style.left);
|
|
163
|
+
const startY = e.clientY - Number.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, globalThis.innerWidth - bw))}px`;
|
|
169
|
+
box.style.top = `${Math.max(0, Math.min(ev.clientY - startY, globalThis.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
|
/**
|
|
@@ -168,10 +212,10 @@
|
|
|
168
212
|
return range;
|
|
169
213
|
}
|
|
170
214
|
/**
|
|
171
|
-
* Select this wrapped range in the
|
|
215
|
+
* Select this wrapped range in the globalThis.
|
|
172
216
|
*/
|
|
173
217
|
select() {
|
|
174
|
-
const sel =
|
|
218
|
+
const sel = globalThis.getSelection();
|
|
175
219
|
if (!sel) return;
|
|
176
220
|
sel.removeAllRanges();
|
|
177
221
|
sel.addRange(this.toNativeRange());
|
|
@@ -224,13 +268,13 @@
|
|
|
224
268
|
return new WrappedRange(range.startContainer, range.startOffset, range.endContainer, range.endOffset);
|
|
225
269
|
}
|
|
226
270
|
/**
|
|
227
|
-
* Returns a WrappedRange for the current
|
|
271
|
+
* Returns a WrappedRange for the current globalThis selection,
|
|
228
272
|
* optionally restricted to a given editable element.
|
|
229
273
|
* @param {HTMLElement} [editable]
|
|
230
274
|
* @returns {WrappedRange|null}
|
|
231
275
|
*/
|
|
232
276
|
function currentRange(editable) {
|
|
233
|
-
const sel =
|
|
277
|
+
const sel = globalThis.getSelection();
|
|
234
278
|
if (!sel || sel.rangeCount === 0) return null;
|
|
235
279
|
const native = sel.getRangeAt(0);
|
|
236
280
|
if (editable && !editable.contains(native.commonAncestorContainer)) return null;
|
|
@@ -241,7 +285,7 @@
|
|
|
241
285
|
* @param {Function} fn
|
|
242
286
|
*/
|
|
243
287
|
function withSavedRange(fn) {
|
|
244
|
-
const sel =
|
|
288
|
+
const sel = globalThis.getSelection();
|
|
245
289
|
if (!sel || sel.rangeCount === 0) {
|
|
246
290
|
fn(null);
|
|
247
291
|
return;
|
|
@@ -276,16 +320,16 @@
|
|
|
276
320
|
* execCommand's state detection is unreliable.
|
|
277
321
|
*/
|
|
278
322
|
function underline() {
|
|
279
|
-
const sel =
|
|
323
|
+
const sel = globalThis.getSelection();
|
|
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
|
|
327
|
+
const uEl = container?.closest("u");
|
|
284
328
|
const nativeState = document.queryCommandState("underline");
|
|
285
329
|
if (uEl && !nativeState) {
|
|
286
330
|
const parent = uEl.parentNode;
|
|
287
331
|
while (uEl.firstChild) parent.insertBefore(uEl.firstChild, uEl);
|
|
288
|
-
|
|
332
|
+
uEl.remove();
|
|
289
333
|
return;
|
|
290
334
|
}
|
|
291
335
|
execCommand("underline");
|
|
@@ -296,16 +340,16 @@
|
|
|
296
340
|
* execCommand's state detection is unreliable (mirrors underline() logic).
|
|
297
341
|
*/
|
|
298
342
|
function strikethrough() {
|
|
299
|
-
const sel =
|
|
343
|
+
const sel = globalThis.getSelection();
|
|
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?.closest("s") || sc?.closest("strike");
|
|
304
348
|
const nativeState = document.queryCommandState("strikeThrough");
|
|
305
349
|
if (sEl && !nativeState) {
|
|
306
350
|
const parent = sEl.parentNode;
|
|
307
351
|
while (sEl.firstChild) parent.insertBefore(sEl.firstChild, sEl);
|
|
308
|
-
|
|
352
|
+
sEl.remove();
|
|
309
353
|
return;
|
|
310
354
|
}
|
|
311
355
|
execCommand("strikeThrough");
|
|
@@ -337,10 +381,10 @@
|
|
|
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
|
-
const sel =
|
|
387
|
+
const sel = globalThis.getSelection();
|
|
344
388
|
const wasCollapsed = !sel || !sel.rangeCount || sel.getRangeAt(0).collapsed;
|
|
345
389
|
if (wasCollapsed && sel && sel.rangeCount > 0) {
|
|
346
390
|
try {
|
|
@@ -366,12 +410,12 @@
|
|
|
366
410
|
span.style.fontSize = size;
|
|
367
411
|
el.parentNode.insertBefore(span, el);
|
|
368
412
|
while (el.firstChild) span.appendChild(el.firstChild);
|
|
369
|
-
el.
|
|
413
|
+
el.remove();
|
|
370
414
|
newSpans.push(span);
|
|
371
415
|
});
|
|
372
416
|
if (!wasCollapsed && sel && newSpans.length > 0) {
|
|
373
417
|
const first = newSpans[0];
|
|
374
|
-
const last = newSpans
|
|
418
|
+
const last = newSpans.at(-1);
|
|
375
419
|
try {
|
|
376
420
|
const nr = document.createRange();
|
|
377
421
|
const startNode = first.firstChild || first;
|
|
@@ -415,11 +459,11 @@
|
|
|
415
459
|
* (which would destroy the ul > li checklist structure).
|
|
416
460
|
*/
|
|
417
461
|
function outdent() {
|
|
418
|
-
const sel =
|
|
462
|
+
const sel = globalThis.getSelection();
|
|
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
|
|
466
|
+
const checkLi = container?.closest(".an-checklist li");
|
|
423
467
|
if (checkLi) {
|
|
424
468
|
_checklistItemToP(checkLi);
|
|
425
469
|
return;
|
|
@@ -449,7 +493,7 @@
|
|
|
449
493
|
if (child.nodeType === 1 && child.tagName === "INPUT") continue;
|
|
450
494
|
p.appendChild(child.cloneNode(true));
|
|
451
495
|
}
|
|
452
|
-
p.innerHTML = p.innerHTML.
|
|
496
|
+
p.innerHTML = p.innerHTML.replaceAll("", "");
|
|
453
497
|
if (!p.hasChildNodes() || !p.textContent.trim()) {
|
|
454
498
|
p.innerHTML = "";
|
|
455
499
|
p.appendChild(document.createTextNode("\xA0"));
|
|
@@ -461,14 +505,14 @@
|
|
|
461
505
|
checkUl.parentNode.insertBefore(newUl, checkUl.nextSibling);
|
|
462
506
|
}
|
|
463
507
|
checkUl.parentNode.insertBefore(p, checkUl.nextSibling);
|
|
464
|
-
|
|
465
|
-
if (checkUl.children.length === 0) checkUl.
|
|
508
|
+
checkLi.remove();
|
|
509
|
+
if (checkUl.children.length === 0) checkUl.remove();
|
|
466
510
|
try {
|
|
467
511
|
const nr = document.createRange();
|
|
468
512
|
const firstChild = p.firstChild;
|
|
469
513
|
nr.setStart(firstChild && firstChild.nodeType === 3 ? firstChild : p, 0);
|
|
470
514
|
nr.collapse(true);
|
|
471
|
-
const s =
|
|
515
|
+
const s = globalThis.getSelection();
|
|
472
516
|
if (s) {
|
|
473
517
|
s.removeAllRanges();
|
|
474
518
|
s.addRange(nr);
|
|
@@ -492,7 +536,7 @@
|
|
|
492
536
|
* @param {string} value - Line-height value to apply; typically a unitless multiplier (for example, "1.5").
|
|
493
537
|
*/
|
|
494
538
|
function lineHeight(value) {
|
|
495
|
-
const sel =
|
|
539
|
+
const sel = globalThis.getSelection();
|
|
496
540
|
if (!sel || sel.rangeCount === 0) return;
|
|
497
541
|
const range = sel.getRangeAt(0);
|
|
498
542
|
const BLOCK_TAGS = new Set([
|
|
@@ -541,25 +585,25 @@
|
|
|
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(
|
|
547
|
-
const sel =
|
|
590
|
+
function toggleInlineCode(_editable) {
|
|
591
|
+
const sel = globalThis.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
|
|
596
|
+
const codeEl = container?.closest("code");
|
|
553
597
|
if (codeEl && !codeEl.closest("pre")) {
|
|
554
598
|
const parent = codeEl.parentNode;
|
|
555
599
|
const prevSibling = codeEl.previousSibling;
|
|
556
600
|
const movedChildren = Array.from(codeEl.childNodes);
|
|
557
601
|
while (codeEl.firstChild) parent.insertBefore(codeEl.firstChild, codeEl);
|
|
558
|
-
|
|
559
|
-
|
|
602
|
+
codeEl.remove();
|
|
603
|
+
parent?.normalize();
|
|
560
604
|
if (movedChildren.length > 0) try {
|
|
561
605
|
const firstMoved = movedChildren[0];
|
|
562
|
-
const lastMoved = movedChildren
|
|
606
|
+
const lastMoved = movedChildren.at(-1);
|
|
563
607
|
const nr = document.createRange();
|
|
564
608
|
const anchorNode = firstMoved.parentNode === parent ? firstMoved : prevSibling ? prevSibling.nextSibling : parent.firstChild;
|
|
565
609
|
if (anchorNode) {
|
|
@@ -600,11 +644,11 @@
|
|
|
600
644
|
* @returns {boolean}
|
|
601
645
|
*/
|
|
602
646
|
function isInlineCode() {
|
|
603
|
-
const sel =
|
|
647
|
+
const sel = globalThis.getSelection();
|
|
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
|
|
651
|
+
const code = sc?.closest("code");
|
|
608
652
|
return !!(code && !code.closest("pre"));
|
|
609
653
|
}
|
|
610
654
|
/**
|
|
@@ -622,30 +666,30 @@
|
|
|
622
666
|
* Empty or whitespace-only selections do not create a checklist.
|
|
623
667
|
*/
|
|
624
668
|
function toggleChecklist() {
|
|
625
|
-
const sel =
|
|
669
|
+
const sel = globalThis.getSelection();
|
|
626
670
|
if (!sel || !sel.rangeCount) return;
|
|
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?.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) {
|
|
638
682
|
if (child.nodeType === 1 && child.tagName === "INPUT") continue;
|
|
639
683
|
p.appendChild(child.cloneNode(true));
|
|
640
684
|
}
|
|
641
|
-
p.innerHTML = p.innerHTML.
|
|
685
|
+
p.innerHTML = p.innerHTML.replaceAll("", "");
|
|
642
686
|
if (!p.hasChildNodes() || !p.textContent.trim()) {
|
|
643
687
|
p.innerHTML = "";
|
|
644
688
|
p.appendChild(document.createTextNode("\xA0"));
|
|
645
689
|
}
|
|
646
690
|
ul.parentNode.insertBefore(p, ul);
|
|
647
691
|
if (!firstP) firstP = p;
|
|
648
|
-
|
|
692
|
+
li.remove();
|
|
649
693
|
});
|
|
650
694
|
if (ul.children.length === 0) ul.remove();
|
|
651
695
|
if (firstP) {
|
|
@@ -673,7 +717,7 @@
|
|
|
673
717
|
]);
|
|
674
718
|
let block = container;
|
|
675
719
|
while (block && block.parentNode && !BLOCK_TAGS.has(block.tagName)) block = block.parentNode;
|
|
676
|
-
const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").
|
|
720
|
+
const itemText = block && BLOCK_TAGS.has(block.tagName) ? Array.from(block.childNodes).map((n) => n.textContent).join("").replaceAll("\xA0", " ") : "";
|
|
677
721
|
const ul = document.createElement("ul");
|
|
678
722
|
ul.className = "an-checklist";
|
|
679
723
|
const li = document.createElement("li");
|
|
@@ -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");
|
|
@@ -744,7 +788,7 @@
|
|
|
744
788
|
});
|
|
745
789
|
const firstBlock = blocks[0];
|
|
746
790
|
firstBlock.parentNode.insertBefore(newUl, firstBlock);
|
|
747
|
-
blocks.forEach((block) => block.
|
|
791
|
+
blocks.forEach((block) => block.remove());
|
|
748
792
|
if (lastTextNode) {
|
|
749
793
|
const nr = document.createRange();
|
|
750
794
|
nr.setStart(lastTextNode, lastTextNode.textContent.length);
|
|
@@ -758,11 +802,11 @@
|
|
|
758
802
|
* @returns {boolean}
|
|
759
803
|
*/
|
|
760
804
|
function isInChecklist() {
|
|
761
|
-
const sel =
|
|
805
|
+
const sel = globalThis.getSelection();
|
|
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 !!
|
|
809
|
+
return !!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
|
|
@@ -842,11 +888,11 @@
|
|
|
842
888
|
var italicBtn = btn("italic", "italic", "Italic (Ctrl+I)", () => italic(), () => document.queryCommandState("italic"));
|
|
843
889
|
var underlineBtn = btn("underline", "underline", "Underline (Ctrl+U)", () => underline(), () => {
|
|
844
890
|
if (document.queryCommandState("underline")) return true;
|
|
845
|
-
const sel =
|
|
891
|
+
const sel = globalThis.getSelection();
|
|
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"));
|
|
@@ -905,15 +951,15 @@
|
|
|
905
951
|
action: (ctx, value) => fontSize(value, ctx.layoutInfo.editable),
|
|
906
952
|
getValue: (ctx) => {
|
|
907
953
|
try {
|
|
908
|
-
const sel =
|
|
954
|
+
const sel = globalThis.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
|
-
const editable = ctx
|
|
962
|
+
const editable = ctx?.layoutInfo?.editable;
|
|
917
963
|
if (editable) return editable.style.fontSize || "";
|
|
918
964
|
return "";
|
|
919
965
|
} catch {
|
|
@@ -1010,7 +1056,7 @@
|
|
|
1010
1056
|
action: (_ctx, value) => lineHeight(value),
|
|
1011
1057
|
getValue: () => {
|
|
1012
1058
|
try {
|
|
1013
|
-
const sel =
|
|
1059
|
+
const sel = globalThis.getSelection();
|
|
1014
1060
|
if (!sel || !sel.rangeCount) return "";
|
|
1015
1061
|
const BLOCKS = new Set([
|
|
1016
1062
|
"P",
|
|
@@ -1028,8 +1074,11 @@
|
|
|
1028
1074
|
"TH"
|
|
1029
1075
|
]);
|
|
1030
1076
|
let el = sel.getRangeAt(0).startContainer;
|
|
1031
|
-
if (el.nodeType === 3) el = el.parentElement;
|
|
1032
|
-
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;
|
|
1033
1082
|
if (!el) return "";
|
|
1034
1083
|
return el.style.lineHeight || getComputedStyle(el).lineHeight || "";
|
|
1035
1084
|
} catch {
|
|
@@ -1052,47 +1101,64 @@
|
|
|
1052
1101
|
*/
|
|
1053
1102
|
/**
|
|
1054
1103
|
* @typedef {object} AsnOptions
|
|
1055
|
-
* @property {string} [placeholder]
|
|
1056
|
-
* @property {number} [height]
|
|
1057
|
-
* @property {number} [minHeight]
|
|
1058
|
-
* @property {number} [maxHeight]
|
|
1059
|
-
* @property {boolean} [focus]
|
|
1060
|
-
* @property {boolean} [resizable]
|
|
1061
|
-
* @property {Array} [toolbar]
|
|
1062
|
-
* @property {boolean} [
|
|
1063
|
-
* @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
|
|
1064
1117
|
* @property {boolean} [pasteStripAttributes] - Strip class/style/data-* from pasted HTML (default: false)
|
|
1065
|
-
* @property {boolean} [allowImageUpload]
|
|
1066
|
-
* @property {number} [maxImageSize]
|
|
1067
|
-
* @property {number} [tabSize]
|
|
1068
|
-
* @property {
|
|
1069
|
-
* @property {
|
|
1070
|
-
* @property {
|
|
1071
|
-
* @property {
|
|
1072
|
-
* @property {
|
|
1073
|
-
* @property {
|
|
1074
|
-
* @property {
|
|
1075
|
-
* @property {
|
|
1076
|
-
* @property {
|
|
1077
|
-
* @property {
|
|
1078
|
-
* @property {boolean} [
|
|
1079
|
-
* @property {
|
|
1080
|
-
* @property {string} [
|
|
1081
|
-
* @property {
|
|
1082
|
-
* @property {
|
|
1083
|
-
* @property {
|
|
1084
|
-
* @property {
|
|
1085
|
-
* @property {
|
|
1086
|
-
* @property {
|
|
1087
|
-
* @property {
|
|
1088
|
-
* @property {
|
|
1089
|
-
* @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
|
|
1090
1149
|
* @property {Function} [onDestroy] - Callback fired when the editor is destroyed: (context) => void
|
|
1091
1150
|
* @property {Function} [onCharLimitReached] - Callback fired when the character limit is hit: (context) => void
|
|
1092
1151
|
* @property {Function} [onWordLimitReached] - Callback fired when the word limit is hit: (context) => void
|
|
1093
|
-
* @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
|
|
1094
1161
|
*/
|
|
1095
|
-
/** @type {AsnOptions} */
|
|
1096
1162
|
var defaultOptions = {
|
|
1097
1163
|
placeholder: "",
|
|
1098
1164
|
height: 200,
|
|
@@ -1552,6 +1618,8 @@
|
|
|
1552
1618
|
rowHeight: "Row Height",
|
|
1553
1619
|
tableBorderWidth: "Table Border Width",
|
|
1554
1620
|
deleteTable: "Delete Table",
|
|
1621
|
+
cellBackground: "Cell Background",
|
|
1622
|
+
noShading: "No Shading",
|
|
1555
1623
|
columnWidthPx: "Column Width (px)",
|
|
1556
1624
|
rowHeightPx: "Row Height (px)",
|
|
1557
1625
|
tableBorderWidthPx: "Table Border Width (px)",
|
|
@@ -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)",
|
|
@@ -4101,14 +4171,17 @@
|
|
|
4101
4171
|
} catch (_) {}
|
|
4102
4172
|
if (!initialContent) initialContent = targetEl.tagName === "TEXTAREA" ? (targetEl.value || "").trim() : (targetEl.innerHTML || "").trim();
|
|
4103
4173
|
editable.innerHTML = sanitiseHTML(initialContent, { allowIframes: true });
|
|
4104
|
-
const defaultFont = options.defaultFontFamily || options.fontFamilies
|
|
4174
|
+
const defaultFont = options.defaultFontFamily || options.fontFamilies?.[0];
|
|
4105
4175
|
if (defaultFont) editable.style.fontFamily = defaultFont;
|
|
4106
4176
|
if (options.defaultFontSize) editable.style.fontSize = options.defaultFontSize;
|
|
4107
4177
|
if (options.height) editable.style.minHeight = `${options.height}px`;
|
|
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();
|
|
@@ -4160,7 +4233,7 @@
|
|
|
4160
4233
|
* @returns {{ start: number, end: number }|null}
|
|
4161
4234
|
*/
|
|
4162
4235
|
_serializeSelection() {
|
|
4163
|
-
const sel =
|
|
4236
|
+
const sel = globalThis.getSelection();
|
|
4164
4237
|
if (!sel || sel.rangeCount === 0) return null;
|
|
4165
4238
|
const range = sel.getRangeAt(0);
|
|
4166
4239
|
if (!this.editable.contains(range.startContainer)) return null;
|
|
@@ -4226,7 +4299,7 @@
|
|
|
4226
4299
|
const range = document.createRange();
|
|
4227
4300
|
range.setStart(startNode, startOff);
|
|
4228
4301
|
range.setEnd(endNode, endOff);
|
|
4229
|
-
const sel =
|
|
4302
|
+
const sel = globalThis.getSelection();
|
|
4230
4303
|
sel.removeAllRanges();
|
|
4231
4304
|
sel.addRange(range);
|
|
4232
4305
|
} catch (_) {
|
|
@@ -4234,7 +4307,7 @@
|
|
|
4234
4307
|
const fb = document.createRange();
|
|
4235
4308
|
fb.setStart(this.editable, 0);
|
|
4236
4309
|
fb.collapse(true);
|
|
4237
|
-
const s =
|
|
4310
|
+
const s = globalThis.getSelection();
|
|
4238
4311
|
if (s) {
|
|
4239
4312
|
s.removeAllRanges();
|
|
4240
4313
|
s.addRange(fb);
|
|
@@ -4298,8 +4371,7 @@
|
|
|
4298
4371
|
recordUndo() {
|
|
4299
4372
|
const current = this._serialize();
|
|
4300
4373
|
const { html: tokenized } = this._tokenizeImages(current);
|
|
4301
|
-
|
|
4302
|
-
if (prev && prev.html === tokenized) return;
|
|
4374
|
+
if (this.stack[this.stackOffset]?.html === tokenized) return;
|
|
4303
4375
|
this._savePoint();
|
|
4304
4376
|
}
|
|
4305
4377
|
/**
|
|
@@ -4345,8 +4417,7 @@
|
|
|
4345
4417
|
* Build an HTML table with the given number of columns and rows, optionally including a header row.
|
|
4346
4418
|
* @param {number} cols - Number of columns in each row.
|
|
4347
4419
|
* @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.
|
|
4420
|
+
* @param {{ headerRow?: boolean }} [opts] - Options: `headerRow` creates a `<thead>` when true.
|
|
4350
4421
|
* @returns {HTMLTableElement} The constructed `<table>` element with a `<tbody>` and optional `<thead>`; each cell contains a `<br>` placeholder.
|
|
4351
4422
|
*/
|
|
4352
4423
|
function createTable(cols, rows, opts = {}) {
|
|
@@ -4380,12 +4451,11 @@
|
|
|
4380
4451
|
* @param {number} cols - Number of columns for the new table.
|
|
4381
4452
|
* @param {number} rows - Number of rows for the new table.
|
|
4382
4453
|
* @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
4454
|
*/
|
|
4385
4455
|
function insertTable(cols, rows, opts = {}) {
|
|
4386
4456
|
if (cols <= 0 || rows <= 0) return;
|
|
4387
4457
|
const table = createTable(cols, rows, opts);
|
|
4388
|
-
const sel =
|
|
4458
|
+
const sel = globalThis.getSelection();
|
|
4389
4459
|
if (!sel || sel.rangeCount === 0) return;
|
|
4390
4460
|
const range = sel.getRangeAt(0);
|
|
4391
4461
|
range.deleteContents();
|
|
@@ -4403,7 +4473,7 @@
|
|
|
4403
4473
|
"PRE"
|
|
4404
4474
|
]);
|
|
4405
4475
|
let anchor = range.startContainer;
|
|
4406
|
-
if (anchor
|
|
4476
|
+
if (anchor?.nodeType === 3) anchor = anchor.parentElement;
|
|
4407
4477
|
while (anchor && !BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentElement) anchor = anchor.parentElement;
|
|
4408
4478
|
if (anchor && BLOCK.has(anchor.tagName?.toUpperCase()) && anchor.parentNode) {
|
|
4409
4479
|
anchor.after(table);
|
|
@@ -4493,8 +4563,8 @@
|
|
|
4493
4563
|
* Inspired by Summernote's Typing module
|
|
4494
4564
|
*/
|
|
4495
4565
|
var _FA_PATTERN = /\bfa-/;
|
|
4496
|
-
var isFAIcon = (n) => !!(n
|
|
4497
|
-
var isZwsAnchor = (n) => !!(n
|
|
4566
|
+
var isFAIcon = (n) => !!(n?.nodeName === "I" && _FA_PATTERN.test(n.className || ""));
|
|
4567
|
+
var isZwsAnchor = (n) => !!(n?.nodeType === Node.TEXT_NODE && (n.textContent === "" || n.textContent === ""));
|
|
4498
4568
|
/**
|
|
4499
4569
|
* Handles special keydown behaviour inside the editor.
|
|
4500
4570
|
* @param {KeyboardEvent} event
|
|
@@ -4504,7 +4574,7 @@
|
|
|
4504
4574
|
*/
|
|
4505
4575
|
function handleKeydown(event, editable, options = {}) {
|
|
4506
4576
|
const moveCaret = (setFn) => {
|
|
4507
|
-
const sel =
|
|
4577
|
+
const sel = globalThis.getSelection();
|
|
4508
4578
|
if (!sel) return false;
|
|
4509
4579
|
const nr = document.createRange();
|
|
4510
4580
|
setFn(nr);
|
|
@@ -4514,14 +4584,14 @@
|
|
|
4514
4584
|
return true;
|
|
4515
4585
|
};
|
|
4516
4586
|
if (isKey(event, key.BACKSPACE)) {
|
|
4517
|
-
const sel =
|
|
4518
|
-
if (sel
|
|
4587
|
+
const sel = globalThis.getSelection();
|
|
4588
|
+
if (sel?.rangeCount > 0) {
|
|
4519
4589
|
const r = sel.getRangeAt(0);
|
|
4520
4590
|
if (r.collapsed && r.startContainer.nodeType === Node.TEXT_NODE) {
|
|
4521
4591
|
const textNode = r.startContainer;
|
|
4522
4592
|
if (r.startOffset === 0 && isFAIcon(textNode.previousSibling)) {
|
|
4523
4593
|
event.preventDefault();
|
|
4524
|
-
textNode.previousSibling.remove();
|
|
4594
|
+
/** @type {ChildNode} */ textNode.previousSibling.remove();
|
|
4525
4595
|
return true;
|
|
4526
4596
|
}
|
|
4527
4597
|
if (r.startOffset === 1 && textNode.textContent === "" && isFAIcon(textNode.previousSibling)) {
|
|
@@ -4545,7 +4615,7 @@
|
|
|
4545
4615
|
return false;
|
|
4546
4616
|
}
|
|
4547
4617
|
if (isKey(event, key.LEFT) || isKey(event, key.RIGHT)) {
|
|
4548
|
-
const sel =
|
|
4618
|
+
const sel = globalThis.getSelection();
|
|
4549
4619
|
if (!sel || sel.rangeCount === 0) return false;
|
|
4550
4620
|
const r = sel.getRangeAt(0);
|
|
4551
4621
|
if (!r.collapsed) return false;
|
|
@@ -4633,7 +4703,7 @@
|
|
|
4633
4703
|
else execCommand("indent");
|
|
4634
4704
|
return true;
|
|
4635
4705
|
}
|
|
4636
|
-
if (para
|
|
4706
|
+
if (para?.nodeName.toUpperCase() === "PRE") {
|
|
4637
4707
|
if (event.shiftKey) return false;
|
|
4638
4708
|
event.preventDefault();
|
|
4639
4709
|
execCommand("insertText", " ".repeat(options.tabSize || 4));
|
|
@@ -4656,18 +4726,18 @@
|
|
|
4656
4726
|
if (!range) return false;
|
|
4657
4727
|
const sc = range.sc;
|
|
4658
4728
|
const el = sc.nodeType === 3 ? sc.parentElement : sc;
|
|
4659
|
-
if (el
|
|
4729
|
+
if (el?.nodeName === "I" && /\bfa-/.test(el.className || "")) {
|
|
4660
4730
|
const nr = document.createRange();
|
|
4661
4731
|
nr.setStartAfter(el);
|
|
4662
4732
|
nr.collapse(true);
|
|
4663
|
-
const selI =
|
|
4733
|
+
const selI = globalThis.getSelection();
|
|
4664
4734
|
if (selI) {
|
|
4665
4735
|
selI.removeAllRanges();
|
|
4666
4736
|
selI.addRange(nr);
|
|
4667
4737
|
}
|
|
4668
4738
|
return false;
|
|
4669
4739
|
}
|
|
4670
|
-
const videoWrapper = el
|
|
4740
|
+
const videoWrapper = el?.closest(".an-video-wrapper");
|
|
4671
4741
|
if (videoWrapper) {
|
|
4672
4742
|
event.preventDefault();
|
|
4673
4743
|
const p = document.createElement("p");
|
|
@@ -4676,16 +4746,16 @@
|
|
|
4676
4746
|
const nr = document.createRange();
|
|
4677
4747
|
nr.setStart(p, 0);
|
|
4678
4748
|
nr.collapse(true);
|
|
4679
|
-
const sel =
|
|
4749
|
+
const sel = globalThis.getSelection();
|
|
4680
4750
|
sel.removeAllRanges();
|
|
4681
4751
|
sel.addRange(nr);
|
|
4682
4752
|
return true;
|
|
4683
4753
|
}
|
|
4684
|
-
const checkLi = el
|
|
4754
|
+
const checkLi = el?.closest(".an-checklist li");
|
|
4685
4755
|
if (checkLi) {
|
|
4686
4756
|
event.preventDefault();
|
|
4687
4757
|
const ul = checkLi.closest(".an-checklist");
|
|
4688
|
-
const sel =
|
|
4758
|
+
const sel = globalThis.getSelection();
|
|
4689
4759
|
let nativeRange = sel.getRangeAt(0);
|
|
4690
4760
|
const liText = (li) => Array.from(li.childNodes).filter((n) => !(n.nodeType === 1 && n.tagName === "INPUT")).map((n) => n.textContent).join("").replace(/[\u00a0\u200B]/g, " ").trim();
|
|
4691
4761
|
if (!liText(checkLi)) {
|
|
@@ -4729,12 +4799,12 @@
|
|
|
4729
4799
|
return true;
|
|
4730
4800
|
}
|
|
4731
4801
|
const para = closestPara(range.sc, editable);
|
|
4732
|
-
if (para
|
|
4802
|
+
if (para?.nodeName.toUpperCase() === "PRE") {
|
|
4733
4803
|
event.preventDefault();
|
|
4734
4804
|
execCommand("insertText", "\n");
|
|
4735
4805
|
return true;
|
|
4736
4806
|
}
|
|
4737
|
-
if (para
|
|
4807
|
+
if (para?.nodeName.toUpperCase() === "BLOCKQUOTE") {
|
|
4738
4808
|
const native = range.toNativeRange();
|
|
4739
4809
|
native.setEnd(para, para.childNodes.length);
|
|
4740
4810
|
if (native.toString() === "" && range.isCollapsed()) {
|
|
@@ -4781,8 +4851,9 @@
|
|
|
4781
4851
|
function _domToMd(node, depth = 0) {
|
|
4782
4852
|
if (node.nodeType === 3) return node.textContent.replace(/\s+/g, " ");
|
|
4783
4853
|
if (node.nodeType !== 1) return "";
|
|
4784
|
-
const
|
|
4785
|
-
const
|
|
4854
|
+
const el = node;
|
|
4855
|
+
const tag = el.nodeName.toLowerCase();
|
|
4856
|
+
const inner = () => Array.from(el.childNodes).map((n) => _domToMd(n, depth)).join("");
|
|
4786
4857
|
switch (tag) {
|
|
4787
4858
|
case "p":
|
|
4788
4859
|
case "div": return `\n\n${inner()}\n\n`;
|
|
@@ -4800,34 +4871,34 @@
|
|
|
4800
4871
|
case "del":
|
|
4801
4872
|
case "s":
|
|
4802
4873
|
case "strike": return `~~${inner()}~~`;
|
|
4803
|
-
case "sup": return `^${inner()}
|
|
4804
|
-
case "sub": return `~${inner()}
|
|
4874
|
+
case "sup": return `^${inner()}^`;
|
|
4875
|
+
case "sub": return `~${inner()}~`;
|
|
4805
4876
|
case "code":
|
|
4806
|
-
if (
|
|
4877
|
+
if (el.closest("pre")) return inner();
|
|
4807
4878
|
return `\`${inner()}\``;
|
|
4808
4879
|
case "pre": {
|
|
4809
|
-
const codeEl =
|
|
4810
|
-
const langMatch = (codeEl && codeEl.className || "")
|
|
4811
|
-
return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl ||
|
|
4880
|
+
const codeEl = el.querySelector("code");
|
|
4881
|
+
const langMatch = /language-(\S+)/.exec(codeEl && codeEl.className || "");
|
|
4882
|
+
return `\n\n\`\`\`${langMatch ? langMatch[1] : ""}\n${(codeEl || el).textContent || ""}\n\`\`\`\n\n`;
|
|
4812
4883
|
}
|
|
4813
4884
|
case "blockquote": return `\n\n${inner().trim().split("\n").map((l) => `> ${l}`).join("\n")}\n\n`;
|
|
4814
4885
|
case "a": {
|
|
4815
|
-
const href =
|
|
4886
|
+
const href = el.getAttribute("href") || "";
|
|
4816
4887
|
return `[${inner()}](${href})`;
|
|
4817
4888
|
}
|
|
4818
4889
|
case "img": {
|
|
4819
|
-
const src =
|
|
4820
|
-
return ``;
|
|
4821
4892
|
}
|
|
4822
4893
|
case "ul": {
|
|
4823
|
-
const items = Array.from(
|
|
4894
|
+
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
4824
4895
|
if (!items.length) return inner();
|
|
4825
4896
|
const indent = " ".repeat(depth);
|
|
4826
4897
|
const lines = items.map((li) => `${indent}- ${_domToMd(li, depth + 1).trim()}`).join("\n");
|
|
4827
4898
|
return depth === 0 ? `\n\n${lines}\n\n` : `\n${lines}`;
|
|
4828
4899
|
}
|
|
4829
4900
|
case "ol": {
|
|
4830
|
-
const items = Array.from(
|
|
4901
|
+
const items = Array.from(el.querySelectorAll(":scope > li"));
|
|
4831
4902
|
if (!items.length) return inner();
|
|
4832
4903
|
const indent = " ".repeat(depth);
|
|
4833
4904
|
const lines = items.map((li, i) => `${indent}${i + 1}. ${_domToMd(li, depth + 1).trim()}`).join("\n");
|
|
@@ -4836,9 +4907,9 @@
|
|
|
4836
4907
|
case "li": return inner();
|
|
4837
4908
|
case "hr": return "\n\n---\n\n";
|
|
4838
4909
|
case "table": {
|
|
4839
|
-
const rows = Array.from(
|
|
4910
|
+
const rows = Array.from(el.querySelectorAll("tr"));
|
|
4840
4911
|
if (!rows.length) return inner();
|
|
4841
|
-
const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().
|
|
4912
|
+
const cellTexts = rows.map((tr) => Array.from(tr.querySelectorAll("th, td")).map((c) => c.textContent.trim().replaceAll("|", "\\|")));
|
|
4842
4913
|
const cols = Math.max(...cellTexts.map((r) => r.length));
|
|
4843
4914
|
const padRow = (row) => {
|
|
4844
4915
|
const r = [...row];
|
|
@@ -4847,7 +4918,7 @@
|
|
|
4847
4918
|
};
|
|
4848
4919
|
let md = "\n\n";
|
|
4849
4920
|
md += `| ${padRow(cellTexts[0]).join(" | ")} |\n`;
|
|
4850
|
-
md += `| ${Array(cols).fill("---").join(" | ")} |\n`;
|
|
4921
|
+
md += `| ${new Array(cols).fill("---").join(" | ")} |\n`;
|
|
4851
4922
|
for (let r = 1; r < cellTexts.length; r++) md += `| ${padRow(cellTexts[r]).join(" | ")} |\n`;
|
|
4852
4923
|
return md + "\n";
|
|
4853
4924
|
}
|
|
@@ -4871,12 +4942,12 @@
|
|
|
4871
4942
|
* @returns {string}
|
|
4872
4943
|
*/
|
|
4873
4944
|
function markdownToHTML(text) {
|
|
4874
|
-
const lines = text.
|
|
4945
|
+
const lines = text.replaceAll("\r\n", "\n").replaceAll("\r", "\n").split("\n");
|
|
4875
4946
|
const out = [];
|
|
4876
4947
|
let i = 0;
|
|
4877
4948
|
while (i < lines.length) {
|
|
4878
4949
|
const line = lines[i];
|
|
4879
|
-
const fenceMatch =
|
|
4950
|
+
const fenceMatch = /^```(\S*)$/.exec(line);
|
|
4880
4951
|
if (fenceMatch) {
|
|
4881
4952
|
const lang = fenceMatch[1];
|
|
4882
4953
|
const codeLines = [];
|
|
@@ -4895,7 +4966,7 @@
|
|
|
4895
4966
|
i++;
|
|
4896
4967
|
continue;
|
|
4897
4968
|
}
|
|
4898
|
-
const hMatch =
|
|
4969
|
+
const hMatch = /^(#{1,6})\s+(.+)$/.exec(line);
|
|
4899
4970
|
if (hMatch) {
|
|
4900
4971
|
const level = hMatch[1].length;
|
|
4901
4972
|
out.push(`<h${level}>${_inline(hMatch[2])}</h${level}>`);
|
|
@@ -4942,7 +5013,8 @@
|
|
|
4942
5013
|
i++;
|
|
4943
5014
|
}
|
|
4944
5015
|
const thead = `<thead><tr>${headerCells.map((c) => `<th>${_inline(c)}</th>`).join("")}</tr></thead>`;
|
|
4945
|
-
const
|
|
5016
|
+
const renderRow = (row) => `<tr>${row.map((c) => `<td>${_inline(c)}</td>`).join("")}</tr>`;
|
|
5017
|
+
const tbody = bodyRows.length ? `<tbody>${bodyRows.map(renderRow).join("")}</tbody>` : "";
|
|
4946
5018
|
out.push(`<table>${thead}${tbody}</table>`);
|
|
4947
5019
|
continue;
|
|
4948
5020
|
}
|
|
@@ -4978,10 +5050,51 @@
|
|
|
4978
5050
|
return text;
|
|
4979
5051
|
}
|
|
4980
5052
|
function _esc(v) {
|
|
4981
|
-
return String(v).
|
|
5053
|
+
return String(v).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
4982
5054
|
}
|
|
4983
5055
|
function _escAttr(v) {
|
|
4984
|
-
return String(v).
|
|
5056
|
+
return String(v).replaceAll("&", "&").replaceAll("\"", """).replaceAll("'", "'").replaceAll("<", "<").replaceAll(">", ">");
|
|
5057
|
+
}
|
|
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;
|
|
4985
5098
|
}
|
|
4986
5099
|
//#endregion
|
|
4987
5100
|
//#region src/js/module/Editor.js
|
|
@@ -5022,7 +5135,7 @@
|
|
|
5022
5135
|
const onBeforeInput = (event) => this._enforceLimit(event);
|
|
5023
5136
|
const onSelChange = () => {
|
|
5024
5137
|
if (!this.context._alive) return;
|
|
5025
|
-
const sel =
|
|
5138
|
+
const sel = globalThis.getSelection();
|
|
5026
5139
|
if (sel && sel.rangeCount > 0 && editable.contains(sel.anchorNode)) {
|
|
5027
5140
|
this.context.invoke("toolbar.refresh");
|
|
5028
5141
|
if (typeof this.options.onSelectionChange === "function") this.options.onSelectionChange(this.context);
|
|
@@ -5032,13 +5145,14 @@
|
|
|
5032
5145
|
if (e.target.type === "checkbox" && e.target.closest(".an-checklist")) this.afterCommand();
|
|
5033
5146
|
};
|
|
5034
5147
|
const fixChecklistCursor = (event) => {
|
|
5035
|
-
const sel =
|
|
5148
|
+
const sel = globalThis.getSelection();
|
|
5036
5149
|
if (!sel || !sel.rangeCount) return;
|
|
5037
5150
|
const r = sel.getRangeAt(0);
|
|
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,33 +5192,37 @@
|
|
|
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
|
}));
|
|
5085
5199
|
/** @type {string|null} 'superscript' | 'subscript' | null */
|
|
5086
5200
|
let _compositionSupSub = null;
|
|
5087
5201
|
const onCompositionStart = () => {
|
|
5088
|
-
const sel =
|
|
5202
|
+
const sel = globalThis.getSelection();
|
|
5089
5203
|
if (!sel || !sel.rangeCount) {
|
|
5090
5204
|
_compositionSupSub = null;
|
|
5091
5205
|
return;
|
|
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;
|
|
5101
5218
|
_compositionSupSub = null;
|
|
5102
5219
|
if (!tag) return;
|
|
5103
|
-
const sel =
|
|
5220
|
+
const sel = globalThis.getSelection();
|
|
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 (!(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
|
}
|
|
@@ -5178,7 +5296,7 @@
|
|
|
5178
5296
|
if (type === "insertFromPaste" || type === "insertFromDrop") return;
|
|
5179
5297
|
if (!type.startsWith("insert")) return;
|
|
5180
5298
|
const text = this.context.layoutInfo.editable.innerText || "";
|
|
5181
|
-
const chars = text.
|
|
5299
|
+
const chars = text.replaceAll("\n", "").length;
|
|
5182
5300
|
if (maxChars && chars >= maxChars) {
|
|
5183
5301
|
event.preventDefault();
|
|
5184
5302
|
if (typeof this.options.onCharLimitReached === "function") this.options.onCharLimitReached(this.context);
|
|
@@ -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();
|
|
@@ -5216,9 +5335,34 @@
|
|
|
5216
5335
|
*/
|
|
5217
5336
|
_cleanOrphanedFigures() {
|
|
5218
5337
|
this.context.layoutInfo.editable.querySelectorAll("figure.an-figure").forEach((fig) => {
|
|
5219
|
-
if (!fig.querySelector("img")) fig.
|
|
5338
|
+
if (!fig.querySelector("img")) fig.remove();
|
|
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
|
}
|
|
@@ -5227,7 +5371,7 @@
|
|
|
5227
5371
|
* @returns {string}
|
|
5228
5372
|
*/
|
|
5229
5373
|
getHTML() {
|
|
5230
|
-
const raw = this.context.layoutInfo.editable.innerHTML.
|
|
5374
|
+
const raw = this.context.layoutInfo.editable.innerHTML.replaceAll("", "");
|
|
5231
5375
|
return this.context.invoke("clipboard.resolveImages", raw) ?? raw;
|
|
5232
5376
|
}
|
|
5233
5377
|
/**
|
|
@@ -5272,7 +5416,7 @@
|
|
|
5272
5416
|
* @returns {boolean}
|
|
5273
5417
|
*/
|
|
5274
5418
|
isEmpty() {
|
|
5275
|
-
const text = (this.context.layoutInfo.editable.innerText || "").trim().
|
|
5419
|
+
const text = (this.context.layoutInfo.editable.innerText || "").trim().replaceAll("\xA0", "");
|
|
5276
5420
|
const hasMedia = !!this.context.layoutInfo.editable.querySelector("img, video, iframe, table");
|
|
5277
5421
|
return !text && !hasMedia;
|
|
5278
5422
|
}
|
|
@@ -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 = globalThis.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.dataset.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
|
/**
|
|
@@ -5450,7 +5608,7 @@
|
|
|
5450
5608
|
* @param {boolean} [openInNewTab=false]
|
|
5451
5609
|
*/
|
|
5452
5610
|
insertLink(url, text, openInNewTab = false) {
|
|
5453
|
-
const sel =
|
|
5611
|
+
const sel = globalThis.getSelection();
|
|
5454
5612
|
if (!sel || sel.rangeCount === 0) return;
|
|
5455
5613
|
const safeUrl = sanitiseUrl(url);
|
|
5456
5614
|
if (!safeUrl) return;
|
|
@@ -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
|
}
|
|
@@ -5513,7 +5671,7 @@
|
|
|
5513
5671
|
this.afterCommand();
|
|
5514
5672
|
}
|
|
5515
5673
|
_getClosestAnchor() {
|
|
5516
|
-
const sel =
|
|
5674
|
+
const sel = globalThis.getSelection();
|
|
5517
5675
|
if (!sel || sel.rangeCount === 0) return null;
|
|
5518
5676
|
let node = sel.getRangeAt(0).startContainer;
|
|
5519
5677
|
while (node) {
|
|
@@ -5528,7 +5686,7 @@
|
|
|
5528
5686
|
* @returns {string}
|
|
5529
5687
|
*/
|
|
5530
5688
|
_escapeAttr(str) {
|
|
5531
|
-
return String(str ?? "").
|
|
5689
|
+
return String(str ?? "").replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
5532
5690
|
}
|
|
5533
5691
|
};
|
|
5534
5692
|
//#endregion
|
|
@@ -5643,7 +5801,7 @@
|
|
|
5643
5801
|
this._refreshRaf = null;
|
|
5644
5802
|
this._disposers.forEach((d) => d());
|
|
5645
5803
|
this._disposers = [];
|
|
5646
|
-
if (this.el && this.el.parentNode) this.el.
|
|
5804
|
+
if (this.el && this.el.parentNode) this.el.remove();
|
|
5647
5805
|
this.el = null;
|
|
5648
5806
|
}
|
|
5649
5807
|
_buildButtons() {
|
|
@@ -5711,8 +5869,8 @@
|
|
|
5711
5869
|
let isOpen = false;
|
|
5712
5870
|
const setHighlight = (rows, cols) => {
|
|
5713
5871
|
cells.forEach((cell) => {
|
|
5714
|
-
const r = +cell.
|
|
5715
|
-
const c = +cell.
|
|
5872
|
+
const r = +cell.dataset.row;
|
|
5873
|
+
const c = +cell.dataset.col;
|
|
5716
5874
|
cell.classList.toggle("active", r <= rows && c <= cols);
|
|
5717
5875
|
});
|
|
5718
5876
|
label.textContent = rows && cols ? `${rows} × ${cols}` : this.context.locale.toolbar.insertTableLabel || "Insert Table";
|
|
@@ -5726,8 +5884,8 @@
|
|
|
5726
5884
|
const ph = popup.offsetHeight;
|
|
5727
5885
|
let left = rect.left;
|
|
5728
5886
|
let top = rect.bottom + 4;
|
|
5729
|
-
if (left + pw >
|
|
5730
|
-
if (top + ph >
|
|
5887
|
+
if (left + pw > globalThis.innerWidth - 8) left = Math.max(8, globalThis.innerWidth - pw - 8);
|
|
5888
|
+
if (top + ph > globalThis.innerHeight - 8) top = rect.top - ph - 4;
|
|
5731
5889
|
popup.style.left = `${left}px`;
|
|
5732
5890
|
popup.style.top = `${top}px`;
|
|
5733
5891
|
popup.style.visibility = "";
|
|
@@ -5745,16 +5903,16 @@
|
|
|
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
|
-
setHighlight(+cell.
|
|
5908
|
+
setHighlight(+cell.dataset.row, +cell.dataset.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
|
-
const rows = +cell.
|
|
5757
|
-
const cols = +cell.
|
|
5914
|
+
const rows = +cell.dataset.row;
|
|
5915
|
+
const cols = +cell.dataset.col;
|
|
5758
5916
|
closePopup();
|
|
5759
5917
|
this.context.invoke("editor.focus");
|
|
5760
5918
|
def.action(this.context, rows, cols);
|
|
@@ -5763,7 +5921,7 @@
|
|
|
5763
5921
|
if (isOpen) closePopup();
|
|
5764
5922
|
});
|
|
5765
5923
|
this._disposers.push(d1, d2, d3, d4, d5, () => {
|
|
5766
|
-
if (popup.parentNode) popup.
|
|
5924
|
+
if (popup.parentNode) popup.remove();
|
|
5767
5925
|
});
|
|
5768
5926
|
wrap.appendChild(btn);
|
|
5769
5927
|
document.body.appendChild(popup);
|
|
@@ -5853,13 +6011,13 @@
|
|
|
5853
6011
|
/** @type {Range|null} saved selection range before popup opens */
|
|
5854
6012
|
let savedRange = null;
|
|
5855
6013
|
const saveSelection = () => {
|
|
5856
|
-
const sel =
|
|
6014
|
+
const sel = globalThis.getSelection();
|
|
5857
6015
|
savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
5858
6016
|
};
|
|
5859
6017
|
const restoreSelection = () => {
|
|
5860
6018
|
if (!savedRange) return;
|
|
5861
6019
|
try {
|
|
5862
|
-
const sel =
|
|
6020
|
+
const sel = globalThis.getSelection();
|
|
5863
6021
|
if (!sel) return;
|
|
5864
6022
|
sel.removeAllRanges();
|
|
5865
6023
|
sel.addRange(savedRange);
|
|
@@ -5874,7 +6032,7 @@
|
|
|
5874
6032
|
const rect = arrowBtn.getBoundingClientRect();
|
|
5875
6033
|
const popupMinW = 184;
|
|
5876
6034
|
let left = rect.left;
|
|
5877
|
-
if (left + popupMinW >
|
|
6035
|
+
if (left + popupMinW > globalThis.innerWidth) left = rect.right - popupMinW;
|
|
5878
6036
|
popup.style.top = `${rect.bottom + 4}px`;
|
|
5879
6037
|
popup.style.left = `${Math.max(4, left)}px`;
|
|
5880
6038
|
popup.style.display = "block";
|
|
@@ -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();
|
|
@@ -5931,9 +6095,9 @@
|
|
|
5931
6095
|
passive: true,
|
|
5932
6096
|
capture: true
|
|
5933
6097
|
});
|
|
5934
|
-
|
|
5935
|
-
this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () =>
|
|
5936
|
-
if (popup.parentNode) popup.
|
|
6098
|
+
globalThis.addEventListener("resize", onScrollResize, { passive: true });
|
|
6099
|
+
this._disposers.push(d1, d2, d2b, d3, d3b, d4, d5, d6, () => document.removeEventListener("scroll", onScrollResize, { capture: true }), () => globalThis.removeEventListener("resize", onScrollResize), () => {
|
|
6100
|
+
if (popup.parentNode) popup.remove();
|
|
5937
6101
|
});
|
|
5938
6102
|
this._colorPickerClosers.push(closePopup);
|
|
5939
6103
|
this._disposers.push(() => {
|
|
@@ -5977,7 +6141,7 @@
|
|
|
5977
6141
|
/** @type {Range|null} */
|
|
5978
6142
|
let _savedRange = null;
|
|
5979
6143
|
const dMousedown = on(select, "mousedown", () => {
|
|
5980
|
-
const sel =
|
|
6144
|
+
const sel = globalThis.getSelection();
|
|
5981
6145
|
_savedRange = sel && sel.rangeCount ? sel.getRangeAt(0).cloneRange() : null;
|
|
5982
6146
|
});
|
|
5983
6147
|
const disposer = on(select, "change", (e) => {
|
|
@@ -5986,7 +6150,7 @@
|
|
|
5986
6150
|
if (!value || selectedOpt.disabled) return;
|
|
5987
6151
|
this.context.invoke("editor.focus");
|
|
5988
6152
|
if (_savedRange) try {
|
|
5989
|
-
const sel =
|
|
6153
|
+
const sel = globalThis.getSelection();
|
|
5990
6154
|
if (sel) {
|
|
5991
6155
|
sel.removeAllRanges();
|
|
5992
6156
|
sel.addRange(_savedRange);
|
|
@@ -6051,17 +6215,25 @@
|
|
|
6051
6215
|
if (!this.el) return;
|
|
6052
6216
|
const btnMap = this._btnMap || /* @__PURE__ */ new Map();
|
|
6053
6217
|
this.el.querySelectorAll("button[data-btn]").forEach((btn) => {
|
|
6054
|
-
const def = btnMap.get(
|
|
6218
|
+
const def = btnMap.get(
|
|
6219
|
+
/** @type {HTMLElement} */
|
|
6220
|
+
btn.dataset.btn
|
|
6221
|
+
);
|
|
6055
6222
|
if (def && typeof def.isActive === "function") btn.classList.toggle("active", !!def.isActive(this.context));
|
|
6056
|
-
if (def && typeof def.isDisabled === "function")
|
|
6223
|
+
if (def && typeof def.isDisabled === "function")
|
|
6224
|
+
/** @type {HTMLButtonElement} */ btn.disabled = !!def.isDisabled(this.context);
|
|
6057
6225
|
});
|
|
6058
6226
|
this.el.querySelectorAll("select[data-btn]").forEach((select) => {
|
|
6059
|
-
const def = btnMap.get(
|
|
6227
|
+
const def = btnMap.get(
|
|
6228
|
+
/** @type {HTMLElement} */
|
|
6229
|
+
select.dataset.btn
|
|
6230
|
+
);
|
|
6060
6231
|
if (!def || typeof def.getValue !== "function") return;
|
|
6061
6232
|
let raw = (def.getValue(this.context) || "").replace(/["']/g, "").trim();
|
|
6062
6233
|
if (!raw) raw = this.options.defaultFontFamily || this.options.fontFamilies && this.options.fontFamilies[0] || "";
|
|
6063
|
-
const
|
|
6064
|
-
|
|
6234
|
+
const sel = select;
|
|
6235
|
+
const matched = Array.from(sel.options).find((opt) => opt.value && opt.value.toLowerCase() === raw.toLowerCase());
|
|
6236
|
+
sel.value = matched ? matched.value : "";
|
|
6065
6237
|
});
|
|
6066
6238
|
}
|
|
6067
6239
|
/**
|
|
@@ -6181,7 +6353,7 @@
|
|
|
6181
6353
|
this._dragDisposers.forEach((d) => d());
|
|
6182
6354
|
this._dragDisposers = null;
|
|
6183
6355
|
}
|
|
6184
|
-
|
|
6356
|
+
this.el?.remove();
|
|
6185
6357
|
this.el = null;
|
|
6186
6358
|
}
|
|
6187
6359
|
_bindResize(handle) {
|
|
@@ -6245,7 +6417,7 @@
|
|
|
6245
6417
|
if (!this._wordCountEl || !this._charCountEl) return;
|
|
6246
6418
|
const text = this.context.layoutInfo.editable.textContent || "";
|
|
6247
6419
|
const words = _countWords(text);
|
|
6248
|
-
const chars = text.
|
|
6420
|
+
const chars = text.replaceAll("\n", "").length;
|
|
6249
6421
|
const maxWords = this.options.maxWords || 0;
|
|
6250
6422
|
const maxChars = this.options.maxChars || 0;
|
|
6251
6423
|
const LS = this.context.locale.statusbar;
|
|
@@ -6267,7 +6439,7 @@
|
|
|
6267
6439
|
* @returns {number}
|
|
6268
6440
|
*/
|
|
6269
6441
|
getCharCount() {
|
|
6270
|
-
return (this.context.layoutInfo.editable.innerText || "").
|
|
6442
|
+
return (this.context.layoutInfo.editable.innerText || "").replaceAll("\n", "").length;
|
|
6271
6443
|
}
|
|
6272
6444
|
};
|
|
6273
6445
|
//#endregion
|
|
@@ -6360,7 +6532,7 @@
|
|
|
6360
6532
|
if (el.querySelector("a, strong, em, b, i, ul, ol, li, table, img, blockquote, pre, code, h1, h2, h3, h4, h5, h6")) continue;
|
|
6361
6533
|
const parent = el.parentNode;
|
|
6362
6534
|
while (el.firstChild) parent.insertBefore(el.firstChild, el);
|
|
6363
|
-
|
|
6535
|
+
el.remove();
|
|
6364
6536
|
}
|
|
6365
6537
|
doc.querySelectorAll("*").forEach((el) => {
|
|
6366
6538
|
el.removeAttribute("class");
|
|
@@ -6402,7 +6574,7 @@
|
|
|
6402
6574
|
this._forcePlain = !!val;
|
|
6403
6575
|
}
|
|
6404
6576
|
_onPaste(event) {
|
|
6405
|
-
const clipboardData = event.clipboardData ||
|
|
6577
|
+
const clipboardData = event.clipboardData || globalThis.clipboardData;
|
|
6406
6578
|
if (!clipboardData) return;
|
|
6407
6579
|
const forcePlain = this._forcePlain;
|
|
6408
6580
|
this._forcePlain = false;
|
|
@@ -6446,7 +6618,6 @@
|
|
|
6446
6618
|
if (this.options.pasteStripAttributes) html = this._stripAttributes(html);
|
|
6447
6619
|
execCommand("insertHTML", html);
|
|
6448
6620
|
this.context.invoke("editor.afterCommand");
|
|
6449
|
-
return;
|
|
6450
6621
|
}
|
|
6451
6622
|
}
|
|
6452
6623
|
_onDragover(event) {
|
|
@@ -6478,17 +6649,17 @@
|
|
|
6478
6649
|
this.options.onImageUpload(files);
|
|
6479
6650
|
return;
|
|
6480
6651
|
}
|
|
6481
|
-
const UNSUPPORTED = [
|
|
6652
|
+
const UNSUPPORTED = new Set([
|
|
6482
6653
|
"image/tiff",
|
|
6483
6654
|
"image/x-tiff",
|
|
6484
6655
|
"image/bmp",
|
|
6485
6656
|
"image/x-bmp",
|
|
6486
6657
|
"image/x-ms-bmp"
|
|
6487
|
-
];
|
|
6658
|
+
]);
|
|
6488
6659
|
const maxBytes = (this.options.maxImageSize || 5) * 1024 * 1024;
|
|
6489
6660
|
files.forEach((file) => {
|
|
6490
6661
|
if (!file || !file.type.startsWith("image/")) return;
|
|
6491
|
-
if (UNSUPPORTED.
|
|
6662
|
+
if (UNSUPPORTED.has(file.type)) {
|
|
6492
6663
|
const message = `Image format "${file.type}" is not supported for display in web browsers. Please convert to PNG, JPEG, or WebP first.`;
|
|
6493
6664
|
this.context.triggerEvent("imageError", {
|
|
6494
6665
|
file,
|
|
@@ -6540,10 +6711,10 @@
|
|
|
6540
6711
|
*/
|
|
6541
6712
|
_dataUrlToBlob(dataUrl) {
|
|
6542
6713
|
const [header, b64] = dataUrl.split(",");
|
|
6543
|
-
const mime =
|
|
6714
|
+
const mime = /:(.*?);/.exec(header)?.[1] ?? "image/png";
|
|
6544
6715
|
const binary = atob(b64);
|
|
6545
6716
|
const arr = new Uint8Array(binary.length);
|
|
6546
|
-
for (let i = 0; i < binary.length; i++) arr[i] = binary.
|
|
6717
|
+
for (let i = 0; i < binary.length; i++) arr[i] = binary.codePointAt(i);
|
|
6547
6718
|
return new Blob([arr], { type: mime });
|
|
6548
6719
|
}
|
|
6549
6720
|
/**
|
|
@@ -6611,7 +6782,7 @@
|
|
|
6611
6782
|
}
|
|
6612
6783
|
}
|
|
6613
6784
|
if (!range) return;
|
|
6614
|
-
const sel =
|
|
6785
|
+
const sel = globalThis.getSelection();
|
|
6615
6786
|
if (sel) {
|
|
6616
6787
|
sel.removeAllRanges();
|
|
6617
6788
|
sel.addRange(range);
|
|
@@ -6623,7 +6794,7 @@
|
|
|
6623
6794
|
* @returns {string}
|
|
6624
6795
|
*/
|
|
6625
6796
|
_escapeHTML(str) {
|
|
6626
|
-
return str.
|
|
6797
|
+
return str.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'");
|
|
6627
6798
|
}
|
|
6628
6799
|
};
|
|
6629
6800
|
//#endregion
|
|
@@ -6660,7 +6831,7 @@
|
|
|
6660
6831
|
_update() {
|
|
6661
6832
|
const editable = this.context.layoutInfo.editable;
|
|
6662
6833
|
const isFocused = document.activeElement === editable;
|
|
6663
|
-
const isEmpty = !(editable.textContent.
|
|
6834
|
+
const isEmpty = !(editable.textContent.replaceAll("", "").trim().length > 0) && !editable.querySelector("img, table, hr, .an-video-wrapper");
|
|
6664
6835
|
editable.classList.toggle("an-placeholder", isEmpty && !isFocused);
|
|
6665
6836
|
}
|
|
6666
6837
|
};
|
|
@@ -6688,7 +6859,7 @@
|
|
|
6688
6859
|
destroy() {
|
|
6689
6860
|
this._disposers.forEach((d) => d());
|
|
6690
6861
|
this._disposers = [];
|
|
6691
|
-
|
|
6862
|
+
this._textarea?.remove();
|
|
6692
6863
|
this._textarea = null;
|
|
6693
6864
|
}
|
|
6694
6865
|
toggle() {
|
|
@@ -6719,7 +6890,7 @@
|
|
|
6719
6890
|
if (!this._active || !this._textarea) return;
|
|
6720
6891
|
const { editable } = this.context.layoutInfo;
|
|
6721
6892
|
editable.innerHTML = sanitiseHTML(this._textarea.value, { allowIframes: true });
|
|
6722
|
-
this._textarea.
|
|
6893
|
+
this._textarea.remove();
|
|
6723
6894
|
this._textarea = null;
|
|
6724
6895
|
editable.style.display = "";
|
|
6725
6896
|
this._active = false;
|
|
@@ -6743,7 +6914,7 @@
|
|
|
6743
6914
|
}).split("\n").map((line) => {
|
|
6744
6915
|
const stripped = line.trim();
|
|
6745
6916
|
if (!stripped) return "";
|
|
6746
|
-
if (
|
|
6917
|
+
if (stripped.startsWith("</")) indent = Math.max(0, indent - 1);
|
|
6747
6918
|
const out = " ".repeat(indent) + stripped;
|
|
6748
6919
|
if (/^<[^/!][^>]*[^/]>/.test(stripped) && !INLINE_RE.test(stripped) && !/^<(br|hr|img|input|link|meta)/.test(stripped)) indent++;
|
|
6749
6920
|
return out;
|
|
@@ -6832,7 +7003,7 @@
|
|
|
6832
7003
|
destroy() {
|
|
6833
7004
|
this._disposers.forEach((d) => d());
|
|
6834
7005
|
this._disposers = [];
|
|
6835
|
-
if (this._dialog && this._dialog.parentNode) this._dialog.
|
|
7006
|
+
if (this._dialog && this._dialog.parentNode) this._dialog.remove();
|
|
6836
7007
|
this._dialog = null;
|
|
6837
7008
|
}
|
|
6838
7009
|
/**
|
|
@@ -6855,8 +7026,13 @@
|
|
|
6855
7026
|
"aria-label": L.ariaLabel
|
|
6856
7027
|
});
|
|
6857
7028
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7029
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7030
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7031
|
+
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
7032
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
6859
7033
|
title.textContent = L.title;
|
|
7034
|
+
header.appendChild(iconEl);
|
|
7035
|
+
header.appendChild(title);
|
|
6860
7036
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
6861
7037
|
urlLabel.textContent = L.url;
|
|
6862
7038
|
const urlInput = createElement("input", {
|
|
@@ -6901,8 +7077,9 @@
|
|
|
6901
7077
|
cancelBtn.textContent = L.cancelBtn;
|
|
6902
7078
|
btnRow.appendChild(insertBtn);
|
|
6903
7079
|
btnRow.appendChild(cancelBtn);
|
|
6904
|
-
box.append(
|
|
7080
|
+
box.append(header, urlLabel, urlInput, textLabel, textInput, tabLabel, btnRow);
|
|
6905
7081
|
overlay.appendChild(box);
|
|
7082
|
+
makeDraggable(header, box);
|
|
6906
7083
|
const d1 = on(insertBtn, "click", () => this._onInsert());
|
|
6907
7084
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
6908
7085
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -6924,7 +7101,7 @@
|
|
|
6924
7101
|
return overlay;
|
|
6925
7102
|
}
|
|
6926
7103
|
_prefill() {
|
|
6927
|
-
const sel =
|
|
7104
|
+
const sel = globalThis.getSelection();
|
|
6928
7105
|
let anchor = null;
|
|
6929
7106
|
if (sel && sel.rangeCount > 0) {
|
|
6930
7107
|
let node = sel.getRangeAt(0).startContainer;
|
|
@@ -7011,7 +7188,7 @@
|
|
|
7011
7188
|
destroy() {
|
|
7012
7189
|
this._disposers.forEach((d) => d());
|
|
7013
7190
|
this._disposers = [];
|
|
7014
|
-
if (this._dialog && this._dialog.parentNode) this._dialog.
|
|
7191
|
+
if (this._dialog && this._dialog.parentNode) this._dialog.remove();
|
|
7015
7192
|
this._dialog = null;
|
|
7016
7193
|
}
|
|
7017
7194
|
show() {
|
|
@@ -7032,8 +7209,13 @@
|
|
|
7032
7209
|
"aria-label": L.ariaLabel
|
|
7033
7210
|
});
|
|
7034
7211
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7212
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7213
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7214
|
+
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
7215
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
7036
7216
|
title.textContent = L.title;
|
|
7217
|
+
header.appendChild(iconEl);
|
|
7218
|
+
header.appendChild(title);
|
|
7037
7219
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
7038
7220
|
urlLabel.textContent = L.imageUrl;
|
|
7039
7221
|
const urlInput = createElement("input", {
|
|
@@ -7052,7 +7234,7 @@
|
|
|
7052
7234
|
autocomplete: "off"
|
|
7053
7235
|
});
|
|
7054
7236
|
this._altInput = altInput;
|
|
7055
|
-
box.append(
|
|
7237
|
+
box.append(header, urlLabel, urlInput, altLabel, altInput);
|
|
7056
7238
|
const alignLabel = createElement("label", { class: "an-label" });
|
|
7057
7239
|
alignLabel.textContent = L.alignment;
|
|
7058
7240
|
const alignRow = createElement("div", { class: "an-align-row" });
|
|
@@ -7121,6 +7303,7 @@
|
|
|
7121
7303
|
btnRow.appendChild(cancelBtn);
|
|
7122
7304
|
box.append(btnRow);
|
|
7123
7305
|
overlay.appendChild(box);
|
|
7306
|
+
makeDraggable(header, box);
|
|
7124
7307
|
const d1 = on(insertBtn, "click", () => this._onInsert());
|
|
7125
7308
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
7126
7309
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -7237,7 +7420,7 @@
|
|
|
7237
7420
|
destroy() {
|
|
7238
7421
|
this._disposers.forEach((d) => d());
|
|
7239
7422
|
this._disposers = [];
|
|
7240
|
-
if (this._dialog && this._dialog.parentNode) this._dialog.
|
|
7423
|
+
if (this._dialog && this._dialog.parentNode) this._dialog.remove();
|
|
7241
7424
|
this._dialog = null;
|
|
7242
7425
|
}
|
|
7243
7426
|
show() {
|
|
@@ -7258,8 +7441,13 @@
|
|
|
7258
7441
|
"aria-label": L.ariaLabel
|
|
7259
7442
|
});
|
|
7260
7443
|
const box = createElement("div", { class: "an-dialog-box" });
|
|
7444
|
+
const header = createElement("div", { class: "an-dialog-header" });
|
|
7445
|
+
const iconEl = createElement("span", { class: "an-dialog-icon" });
|
|
7446
|
+
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
7447
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
7262
7448
|
title.textContent = L.title;
|
|
7449
|
+
header.appendChild(iconEl);
|
|
7450
|
+
header.appendChild(title);
|
|
7263
7451
|
const urlLabel = createElement("label", { class: "an-label" });
|
|
7264
7452
|
urlLabel.textContent = L.videoUrl;
|
|
7265
7453
|
const urlInput = createElement("input", {
|
|
@@ -7295,8 +7483,9 @@
|
|
|
7295
7483
|
cancelBtn.textContent = L.cancelBtn;
|
|
7296
7484
|
btnRow.appendChild(insertBtn);
|
|
7297
7485
|
btnRow.appendChild(cancelBtn);
|
|
7298
|
-
box.append(
|
|
7486
|
+
box.append(header, urlLabel, urlInput, hintEl, widthLabel, widthInput, btnRow);
|
|
7299
7487
|
overlay.appendChild(box);
|
|
7488
|
+
makeDraggable(header, box);
|
|
7300
7489
|
const d0 = on(urlInput, "input", () => {
|
|
7301
7490
|
const info = this._parseVideoUrl(urlInput.value.trim());
|
|
7302
7491
|
hintEl.textContent = info ? this.context.locale.videoDialog.detected(info.type) : urlInput.value ? this.context.locale.videoDialog.unknownFormat : "";
|
|
@@ -7317,7 +7506,7 @@
|
|
|
7317
7506
|
}
|
|
7318
7507
|
_onInsert() {
|
|
7319
7508
|
const rawUrl = this._urlInput.value.trim();
|
|
7320
|
-
const width = Math.max(80, parseInt(this._widthInput.value, 10) || 560);
|
|
7509
|
+
const width = Math.max(80, Number.parseInt(this._widthInput.value, 10) || 560);
|
|
7321
7510
|
if (!rawUrl) {
|
|
7322
7511
|
this._urlInput.focus();
|
|
7323
7512
|
return;
|
|
@@ -7360,22 +7549,22 @@
|
|
|
7360
7549
|
} catch {
|
|
7361
7550
|
return null;
|
|
7362
7551
|
}
|
|
7363
|
-
const ytWatch =
|
|
7552
|
+
const ytWatch = /(?:youtube\.com\/watch\?(?:.*&)?v=|youtube\.com\/embed\/)([a-zA-Z0-9_-]{11})/.exec(url);
|
|
7364
7553
|
if (ytWatch) return {
|
|
7365
7554
|
type: "YouTube",
|
|
7366
7555
|
embedUrl: `https://www.youtube.com/embed/${ytWatch[1]}`
|
|
7367
7556
|
};
|
|
7368
|
-
const ytShort =
|
|
7557
|
+
const ytShort = /youtu\.be\/([a-zA-Z0-9_-]{11})/.exec(url);
|
|
7369
7558
|
if (ytShort) return {
|
|
7370
7559
|
type: "YouTube",
|
|
7371
7560
|
embedUrl: `https://www.youtube.com/embed/${ytShort[1]}`
|
|
7372
7561
|
};
|
|
7373
|
-
const ytShorts =
|
|
7562
|
+
const ytShorts = /youtube\.com\/shorts\/([a-zA-Z0-9_-]{11})/.exec(url);
|
|
7374
7563
|
if (ytShorts) return {
|
|
7375
7564
|
type: "YouTube Shorts",
|
|
7376
7565
|
embedUrl: `https://www.youtube.com/embed/${ytShorts[1]}`
|
|
7377
7566
|
};
|
|
7378
|
-
const vimeo =
|
|
7567
|
+
const vimeo = /vimeo\.com\/(\d+)/.exec(url);
|
|
7379
7568
|
if (vimeo) return {
|
|
7380
7569
|
type: "Vimeo",
|
|
7381
7570
|
embedUrl: `https://player.vimeo.com/video/${vimeo[1]}`
|
|
@@ -7399,7 +7588,7 @@
|
|
|
7399
7588
|
const iframeTitle = `${info.type} video player`;
|
|
7400
7589
|
return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><iframe src="${info.embedUrl}" width="${width}" height="${height}" title="${iframeTitle}" frameborder="0" allowfullscreen allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" style="display:block;max-width:100%"></iframe><div class="an-video-shield"></div></div>`;
|
|
7401
7590
|
}
|
|
7402
|
-
if (info && info.type === "Direct video") return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${info.embedUrl.
|
|
7591
|
+
if (info && info.type === "Direct video") return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${info.embedUrl.replaceAll("\"", "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
|
|
7403
7592
|
const safeSrc = (() => {
|
|
7404
7593
|
try {
|
|
7405
7594
|
const p = new URL(url);
|
|
@@ -7410,7 +7599,7 @@
|
|
|
7410
7599
|
}
|
|
7411
7600
|
})();
|
|
7412
7601
|
if (!safeSrc) return null;
|
|
7413
|
-
return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${safeSrc.
|
|
7602
|
+
return `<div class="an-video-wrapper" style="position:relative;display:block;width:${width}px;max-width:100%"><video src="${safeSrc.replaceAll("\"", "%22")}" width="${width}" height="${height}" controls style="display:block;max-width:100%"></video><div class="an-video-shield"></div></div>`;
|
|
7414
7603
|
}
|
|
7415
7604
|
};
|
|
7416
7605
|
//#endregion
|
|
@@ -7479,9 +7668,9 @@
|
|
|
7479
7668
|
};
|
|
7480
7669
|
this._disposers.push(on(editable, "click", (e) => this._onEditorClick(e)), on(editable, "contextmenu", (e) => {
|
|
7481
7670
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
7482
|
-
const img = e.target
|
|
7671
|
+
const img = e.target?.closest("img");
|
|
7483
7672
|
if (img) this._select(img);
|
|
7484
|
-
}), on(document, "click", (e) => this._onDocClick(e)), on(
|
|
7673
|
+
}), on(document, "click", (e) => this._onDocClick(e)), on(globalThis, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(globalThis, "resize", onWindowResize, { passive: true }), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }));
|
|
7485
7674
|
return this;
|
|
7486
7675
|
}
|
|
7487
7676
|
destroy() {
|
|
@@ -7496,7 +7685,7 @@
|
|
|
7496
7685
|
this._positionRaf = null;
|
|
7497
7686
|
}
|
|
7498
7687
|
this._deselect();
|
|
7499
|
-
if (this._overlay && this._overlay.parentNode) this._overlay.
|
|
7688
|
+
if (this._overlay && this._overlay.parentNode) this._overlay.remove();
|
|
7500
7689
|
this._overlay = null;
|
|
7501
7690
|
}
|
|
7502
7691
|
/** @returns {HTMLImageElement|null} */
|
|
@@ -7698,7 +7887,7 @@
|
|
|
7698
7887
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
7699
7888
|
const wrapper = this._findWrapper(e.target);
|
|
7700
7889
|
if (wrapper) this._select(wrapper);
|
|
7701
|
-
}), on(document, "click", (e) => this._onDocClick(e)), on(
|
|
7890
|
+
}), on(document, "click", (e) => this._onDocClick(e)), on(globalThis, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(globalThis, "resize", onWindowResize), on(editable, "scroll", () => this._updateOverlayPosition(), { passive: true }), on(editable, "dragstart", (e) => {
|
|
7702
7891
|
if (e.target instanceof Element && e.target.closest(".an-video-wrapper")) e.preventDefault();
|
|
7703
7892
|
}));
|
|
7704
7893
|
return this;
|
|
@@ -7715,7 +7904,7 @@
|
|
|
7715
7904
|
this._positionRaf = null;
|
|
7716
7905
|
}
|
|
7717
7906
|
this._deselect();
|
|
7718
|
-
|
|
7907
|
+
this._overlay?.remove();
|
|
7719
7908
|
this._overlay = null;
|
|
7720
7909
|
}
|
|
7721
7910
|
/** @returns {HTMLElement|null} */
|
|
@@ -7736,7 +7925,7 @@
|
|
|
7736
7925
|
*/
|
|
7737
7926
|
_findWrapper(el) {
|
|
7738
7927
|
if (!el || !(el instanceof Element)) return null;
|
|
7739
|
-
if (el.classList
|
|
7928
|
+
if (el.classList?.contains("an-video-wrapper")) return el;
|
|
7740
7929
|
const w = el.closest(".an-video-wrapper");
|
|
7741
7930
|
if (w) return w;
|
|
7742
7931
|
return null;
|
|
@@ -7769,7 +7958,7 @@
|
|
|
7769
7958
|
_onDocClick(e) {
|
|
7770
7959
|
if (!this._activeWrapper) return;
|
|
7771
7960
|
if (this._activeWrapper.contains(e.target)) return;
|
|
7772
|
-
if (this._overlay
|
|
7961
|
+
if (this._overlay?.contains(e.target)) return;
|
|
7773
7962
|
if (e.target.closest(".an-contextmenu")) return;
|
|
7774
7963
|
this._deselect();
|
|
7775
7964
|
}
|
|
@@ -7889,19 +8078,19 @@
|
|
|
7889
8078
|
document.body.appendChild(this._el);
|
|
7890
8079
|
const editable = this.context.layoutInfo.editable;
|
|
7891
8080
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
7892
|
-
const anchor = e.target
|
|
8081
|
+
const anchor = e.target?.closest("a[href]");
|
|
7893
8082
|
if (anchor && editable.contains(anchor)) this._scheduleShow(anchor);
|
|
7894
8083
|
}), on(editable, "mouseout", (e) => {
|
|
7895
8084
|
const to = e.relatedTarget;
|
|
7896
8085
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
7897
|
-
}));
|
|
8086
|
+
}), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
|
|
7898
8087
|
return this;
|
|
7899
8088
|
}
|
|
7900
8089
|
destroy() {
|
|
7901
8090
|
this._clearTimers();
|
|
7902
8091
|
this._disposers.forEach((d) => d());
|
|
7903
8092
|
this._disposers = [];
|
|
7904
|
-
if (this._el && this._el.parentNode) this._el.
|
|
8093
|
+
if (this._el && this._el.parentNode) this._el.remove();
|
|
7905
8094
|
this._el = null;
|
|
7906
8095
|
}
|
|
7907
8096
|
_buildTooltip() {
|
|
@@ -7986,8 +8175,8 @@
|
|
|
7986
8175
|
const margin = 6;
|
|
7987
8176
|
let top = rect.bottom + margin;
|
|
7988
8177
|
let left = rect.left;
|
|
7989
|
-
if (top + tipH >
|
|
7990
|
-
if (left + tipW >
|
|
8178
|
+
if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
|
|
8179
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
7991
8180
|
if (left < margin) left = margin;
|
|
7992
8181
|
this._el.style.top = `${top}px`;
|
|
7993
8182
|
this._el.style.left = `${left}px`;
|
|
@@ -8002,12 +8191,12 @@
|
|
|
8002
8191
|
}
|
|
8003
8192
|
}
|
|
8004
8193
|
_openLink() {
|
|
8005
|
-
const url = this._activeAnchor
|
|
8006
|
-
if (url)
|
|
8194
|
+
const url = this._activeAnchor?.getAttribute("href");
|
|
8195
|
+
if (url) globalThis.open(url, "_blank", "noopener,noreferrer");
|
|
8007
8196
|
this._hide();
|
|
8008
8197
|
}
|
|
8009
8198
|
_copyLink() {
|
|
8010
|
-
const url = this._activeAnchor
|
|
8199
|
+
const url = this._activeAnchor?.getAttribute("href");
|
|
8011
8200
|
if (url) navigator.clipboard.writeText(url).catch(() => {
|
|
8012
8201
|
const ta = document.createElement("textarea");
|
|
8013
8202
|
ta.value = url;
|
|
@@ -8016,7 +8205,7 @@
|
|
|
8016
8205
|
document.body.appendChild(ta);
|
|
8017
8206
|
ta.select();
|
|
8018
8207
|
document.execCommand("copy");
|
|
8019
|
-
|
|
8208
|
+
ta.remove();
|
|
8020
8209
|
});
|
|
8021
8210
|
if (this._copyBtn) {
|
|
8022
8211
|
this._copyBtn.classList.add("an-link-tooltip-btn--copied");
|
|
@@ -8027,7 +8216,7 @@
|
|
|
8027
8216
|
const anchor = this._activeAnchor;
|
|
8028
8217
|
if (!anchor) return;
|
|
8029
8218
|
this._hide();
|
|
8030
|
-
const sel =
|
|
8219
|
+
const sel = globalThis.getSelection();
|
|
8031
8220
|
const range = document.createRange();
|
|
8032
8221
|
range.selectNodeContents(anchor);
|
|
8033
8222
|
sel.removeAllRanges();
|
|
@@ -8038,7 +8227,7 @@
|
|
|
8038
8227
|
const anchor = this._activeAnchor;
|
|
8039
8228
|
if (!anchor) return;
|
|
8040
8229
|
this._hide();
|
|
8041
|
-
const sel =
|
|
8230
|
+
const sel = globalThis.getSelection();
|
|
8042
8231
|
const range = document.createRange();
|
|
8043
8232
|
range.selectNode(anchor);
|
|
8044
8233
|
sel.removeAllRanges();
|
|
@@ -8079,21 +8268,22 @@
|
|
|
8079
8268
|
const editable = this.context.layoutInfo.editable;
|
|
8080
8269
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8081
8270
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8082
|
-
const img = e.target
|
|
8271
|
+
const img = e.target?.closest("img");
|
|
8083
8272
|
if (img && editable.contains(img) && !img.closest("a[href]")) this._scheduleShow(img);
|
|
8084
8273
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
8085
8274
|
const to = e.relatedTarget;
|
|
8086
8275
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
8087
8276
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8088
|
-
|
|
8089
|
-
|
|
8277
|
+
const et = e.target;
|
|
8278
|
+
if (this._activeImg && !this._activeImg.contains(et) && !this._el.contains(et)) this._hide();
|
|
8279
|
+
}), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
|
|
8090
8280
|
return this;
|
|
8091
8281
|
}
|
|
8092
8282
|
destroy() {
|
|
8093
8283
|
this._clearTimers();
|
|
8094
8284
|
this._disposers.forEach((d) => d());
|
|
8095
8285
|
this._disposers = [];
|
|
8096
|
-
|
|
8286
|
+
this._el?.remove();
|
|
8097
8287
|
this._el = null;
|
|
8098
8288
|
}
|
|
8099
8289
|
_buildTooltip() {
|
|
@@ -8170,7 +8360,7 @@
|
|
|
8170
8360
|
clearTimeout(this._hideTimer);
|
|
8171
8361
|
this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$3);
|
|
8172
8362
|
}
|
|
8173
|
-
_show(
|
|
8363
|
+
_show(_img) {
|
|
8174
8364
|
this._el.style.display = "flex";
|
|
8175
8365
|
requestAnimationFrame(() => {
|
|
8176
8366
|
if (this._activeImg) this._positionNear(this._activeImg);
|
|
@@ -8194,8 +8384,8 @@
|
|
|
8194
8384
|
const margin = 6;
|
|
8195
8385
|
let top = rect.bottom + margin;
|
|
8196
8386
|
let left = rect.left + (rect.width - tipW) / 2;
|
|
8197
|
-
if (top + tipH >
|
|
8198
|
-
if (left + tipW >
|
|
8387
|
+
if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
|
|
8388
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
8199
8389
|
if (left < margin) left = margin;
|
|
8200
8390
|
this._el.style.top = `${top}px`;
|
|
8201
8391
|
this._el.style.left = `${left}px`;
|
|
@@ -8251,8 +8441,8 @@
|
|
|
8251
8441
|
const img = this._activeImg;
|
|
8252
8442
|
if (!img) return;
|
|
8253
8443
|
const current = img.style.transform || "";
|
|
8254
|
-
const match =
|
|
8255
|
-
const next = ((match ? parseFloat(match[1]) : 0) + delta + 360) % 360;
|
|
8444
|
+
const match = /rotate\((-?[\d.]+)deg\)/.exec(current);
|
|
8445
|
+
const next = ((match ? Number.parseFloat(match[1]) : 0) + delta + 360) % 360;
|
|
8256
8446
|
const cleaned = current.replace(/rotate\(-?[\d.]+deg\)/, "").trim();
|
|
8257
8447
|
img.style.transform = cleaned ? `${cleaned} rotate(${next}deg)` : next === 0 ? "" : `rotate(${next}deg)`;
|
|
8258
8448
|
this.context.invoke("editor.afterCommand");
|
|
@@ -8267,8 +8457,8 @@
|
|
|
8267
8457
|
this._hide();
|
|
8268
8458
|
this.context.invoke("imageResizer.deselect");
|
|
8269
8459
|
const figure = img.closest("figure.an-figure");
|
|
8270
|
-
if (figure
|
|
8271
|
-
else
|
|
8460
|
+
if (figure) figure.remove();
|
|
8461
|
+
else img.remove();
|
|
8272
8462
|
this.context.invoke("editor.afterCommand");
|
|
8273
8463
|
}
|
|
8274
8464
|
_crop() {
|
|
@@ -8287,7 +8477,7 @@
|
|
|
8287
8477
|
this._hide();
|
|
8288
8478
|
const range = document.createRange();
|
|
8289
8479
|
range.selectNodeContents(cap);
|
|
8290
|
-
const sel =
|
|
8480
|
+
const sel = globalThis.getSelection();
|
|
8291
8481
|
if (sel) {
|
|
8292
8482
|
sel.removeAllRanges();
|
|
8293
8483
|
sel.addRange(range);
|
|
@@ -8321,7 +8511,7 @@
|
|
|
8321
8511
|
figure.appendChild(figcaption);
|
|
8322
8512
|
const range = document.createRange();
|
|
8323
8513
|
range.selectNodeContents(figcaption);
|
|
8324
|
-
const sel =
|
|
8514
|
+
const sel = globalThis.getSelection();
|
|
8325
8515
|
if (sel) {
|
|
8326
8516
|
sel.removeAllRanges();
|
|
8327
8517
|
sel.addRange(range);
|
|
@@ -8362,14 +8552,15 @@
|
|
|
8362
8552
|
const editable = this.context.layoutInfo.editable;
|
|
8363
8553
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8364
8554
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8365
|
-
const wrapper = e.target
|
|
8555
|
+
const wrapper = e.target?.closest(".an-video-wrapper");
|
|
8366
8556
|
if (wrapper && editable.contains(wrapper)) this._scheduleShow(wrapper);
|
|
8367
8557
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
8368
8558
|
const to = e.relatedTarget;
|
|
8369
8559
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
8370
8560
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8371
|
-
|
|
8372
|
-
|
|
8561
|
+
const target = e.target;
|
|
8562
|
+
if (this._activeWrapper && !this._activeWrapper.contains(target) && !this._el.contains(target)) this._hide();
|
|
8563
|
+
}), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
|
|
8373
8564
|
return this;
|
|
8374
8565
|
}
|
|
8375
8566
|
destroy() {
|
|
@@ -8377,7 +8568,7 @@
|
|
|
8377
8568
|
this._clearTimers();
|
|
8378
8569
|
this._disposers.forEach((d) => d());
|
|
8379
8570
|
this._disposers = [];
|
|
8380
|
-
|
|
8571
|
+
this._el?.remove();
|
|
8381
8572
|
this._el = null;
|
|
8382
8573
|
}
|
|
8383
8574
|
_buildTooltip() {
|
|
@@ -8449,7 +8640,7 @@
|
|
|
8449
8640
|
if (this._hideTimer) return;
|
|
8450
8641
|
this._hideTimer = setTimeout(() => this._hide(), HIDE_DELAY$2);
|
|
8451
8642
|
}
|
|
8452
|
-
_show(
|
|
8643
|
+
_show(_wrapper) {
|
|
8453
8644
|
this._el.style.display = "flex";
|
|
8454
8645
|
requestAnimationFrame(() => {
|
|
8455
8646
|
if (this._activeWrapper) this._positionNear(this._activeWrapper);
|
|
@@ -8474,8 +8665,8 @@
|
|
|
8474
8665
|
const margin = 6;
|
|
8475
8666
|
let top = rect.bottom + margin;
|
|
8476
8667
|
let left = rect.left + (rect.width - tipW) / 2;
|
|
8477
|
-
if (top + tipH >
|
|
8478
|
-
if (left + tipW >
|
|
8668
|
+
if (top + tipH > globalThis.innerHeight - margin) top = rect.top - tipH - margin;
|
|
8669
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
8479
8670
|
if (left < margin) left = margin;
|
|
8480
8671
|
this._el.style.top = `${top}px`;
|
|
8481
8672
|
this._el.style.left = `${left}px`;
|
|
@@ -8529,7 +8720,7 @@
|
|
|
8529
8720
|
if (!wrapper) return;
|
|
8530
8721
|
this._hide();
|
|
8531
8722
|
this.context.invoke("videoResizer.deselect");
|
|
8532
|
-
|
|
8723
|
+
wrapper.remove();
|
|
8533
8724
|
this.context.invoke("editor.afterCommand");
|
|
8534
8725
|
}
|
|
8535
8726
|
_togglePreview() {
|
|
@@ -8673,8 +8864,35 @@
|
|
|
8673
8864
|
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
8865
|
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
8866
|
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
|
|
8867
|
+
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>`,
|
|
8868
|
+
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
8869
|
};
|
|
8870
|
+
var SHADE_PRESETS = [
|
|
8871
|
+
"#000000",
|
|
8872
|
+
"#434343",
|
|
8873
|
+
"#666666",
|
|
8874
|
+
"#999999",
|
|
8875
|
+
"#b7b7b7",
|
|
8876
|
+
"#cccccc",
|
|
8877
|
+
"#efefef",
|
|
8878
|
+
"#ffffff",
|
|
8879
|
+
"#ff0000",
|
|
8880
|
+
"#ff9900",
|
|
8881
|
+
"#ffff00",
|
|
8882
|
+
"#00ff00",
|
|
8883
|
+
"#00ffff",
|
|
8884
|
+
"#4a86e8",
|
|
8885
|
+
"#9900ff",
|
|
8886
|
+
"#ff00ff",
|
|
8887
|
+
"#f4cccc",
|
|
8888
|
+
"#fce5cd",
|
|
8889
|
+
"#fff2cc",
|
|
8890
|
+
"#d9ead3",
|
|
8891
|
+
"#d0e0e3",
|
|
8892
|
+
"#c9daf8",
|
|
8893
|
+
"#d9d2e9",
|
|
8894
|
+
"#ead1dc"
|
|
8895
|
+
];
|
|
8678
8896
|
var TableTooltip = class {
|
|
8679
8897
|
/** @param {import('../Context.js').Context} context */
|
|
8680
8898
|
constructor(context) {
|
|
@@ -8689,6 +8907,9 @@
|
|
|
8689
8907
|
this._sizeApply = null;
|
|
8690
8908
|
this._sizeTitleEl = null;
|
|
8691
8909
|
this._sizeInputEl = null;
|
|
8910
|
+
this._shadePopover = null;
|
|
8911
|
+
this._shadeTitleEl = null;
|
|
8912
|
+
this._shadeColorStrip = null;
|
|
8692
8913
|
this._selectMode = false;
|
|
8693
8914
|
this._selectedCells = [];
|
|
8694
8915
|
this._selectStart = null;
|
|
@@ -8701,6 +8922,8 @@
|
|
|
8701
8922
|
document.body.appendChild(this._el);
|
|
8702
8923
|
this._sizePopover = this._buildSizePopover();
|
|
8703
8924
|
document.body.appendChild(this._sizePopover);
|
|
8925
|
+
this._shadePopover = this._buildCellShadePopover();
|
|
8926
|
+
document.body.appendChild(this._shadePopover);
|
|
8704
8927
|
const editable = this.context.layoutInfo.editable;
|
|
8705
8928
|
this._editable = editable;
|
|
8706
8929
|
const onSelMousedown = (e) => {
|
|
@@ -8727,20 +8950,24 @@
|
|
|
8727
8950
|
this._disposers.push(on(editable, "mousedown", onSelMousedown), on(editable, "mousemove", onSelMousemove), on(document, "mouseup", onSelMouseup));
|
|
8728
8951
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
8729
8952
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
8730
|
-
const table = e.target
|
|
8953
|
+
const table = e.target?.closest("table");
|
|
8731
8954
|
if (table && editable.contains(table)) {
|
|
8732
|
-
const cell = e.target
|
|
8733
|
-
if (cell)
|
|
8955
|
+
const cell = e.target?.closest("td, th");
|
|
8956
|
+
if (cell) {
|
|
8957
|
+
this._activeCell = cell;
|
|
8958
|
+
this._syncShadeStrip();
|
|
8959
|
+
}
|
|
8734
8960
|
this._scheduleShow(table);
|
|
8735
8961
|
}
|
|
8736
8962
|
}, { passive: true }), on(editable, "mouseout", (e) => {
|
|
8737
8963
|
if (this._selectMode) return;
|
|
8738
8964
|
const to = e.relatedTarget;
|
|
8739
|
-
if (!to || !editable.contains(to) && !this._el.contains(to) && !
|
|
8965
|
+
if (!to || !editable.contains(to) && !this._el.contains(to) && !this._sizePopover?.contains(to)) this._scheduleHide();
|
|
8740
8966
|
}, { passive: true }), on(document, "click", (e) => {
|
|
8741
|
-
|
|
8742
|
-
if (this.
|
|
8743
|
-
|
|
8967
|
+
const et = e.target;
|
|
8968
|
+
if (this._selectMode && this._activeTable?.contains(et)) return;
|
|
8969
|
+
if (this._activeTable && !this._activeTable.contains(et) && !this._el.contains(et) && !this._sizePopover?.contains(et)) this._hide();
|
|
8970
|
+
}), on(document, "selectionchange", () => this._syncShadeStrip()), on(globalThis, "scroll", () => this._hide(), { passive: true }), on(globalThis, "resize", () => this._hide(), { passive: true }));
|
|
8744
8971
|
this._initResize();
|
|
8745
8972
|
return this;
|
|
8746
8973
|
}
|
|
@@ -8866,10 +9093,12 @@
|
|
|
8866
9093
|
this._clearTimers();
|
|
8867
9094
|
this._disposers.forEach((d) => d());
|
|
8868
9095
|
this._disposers = [];
|
|
8869
|
-
if (this._el && this._el.parentNode) this._el.
|
|
9096
|
+
if (this._el && this._el.parentNode) this._el.remove();
|
|
8870
9097
|
this._el = null;
|
|
8871
|
-
if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.
|
|
9098
|
+
if (this._sizePopover && this._sizePopover.parentNode) this._sizePopover.remove();
|
|
8872
9099
|
this._sizePopover = null;
|
|
9100
|
+
if (this._shadePopover && this._shadePopover.parentNode) this._shadePopover.remove();
|
|
9101
|
+
this._shadePopover = null;
|
|
8873
9102
|
}
|
|
8874
9103
|
_buildTooltip() {
|
|
8875
9104
|
const L = this.context.locale.tooltips.table;
|
|
@@ -8897,6 +9126,24 @@
|
|
|
8897
9126
|
el.appendChild(this._makeBtn(ICONS$2.mergeCells, L.mergeCells, () => this._mergeCells()));
|
|
8898
9127
|
el.appendChild(this._makeBtn(ICONS$2.unmergeCells, L.unmergeCells, () => this._unmergeCells()));
|
|
8899
9128
|
el.appendChild(this._sep());
|
|
9129
|
+
const shadeBtn = createElement("button", {
|
|
9130
|
+
type: "button",
|
|
9131
|
+
class: "an-link-tooltip-btn an-link-tooltip-btn--shade",
|
|
9132
|
+
title: L.cellBackground
|
|
9133
|
+
});
|
|
9134
|
+
const shadeSvgWrap = createElement("span", { class: "an-bubble-btn-svg" });
|
|
9135
|
+
shadeSvgWrap.innerHTML = ICONS$2.cellShade;
|
|
9136
|
+
const shadeStrip = createElement("span", { class: "an-link-tooltip-color-strip" });
|
|
9137
|
+
shadeBtn.appendChild(shadeSvgWrap);
|
|
9138
|
+
shadeBtn.appendChild(shadeStrip);
|
|
9139
|
+
this._shadeColorStrip = shadeStrip;
|
|
9140
|
+
this._disposers.push(on(shadeBtn, "click", (e) => {
|
|
9141
|
+
e.preventDefault();
|
|
9142
|
+
e.stopPropagation();
|
|
9143
|
+
this._openCellShadePopover();
|
|
9144
|
+
}));
|
|
9145
|
+
el.appendChild(shadeBtn);
|
|
9146
|
+
el.appendChild(this._sep());
|
|
8900
9147
|
el.appendChild(this._makeBtn(ICONS$2.colWidth, L.columnWidth, () => this._openSizePopover("col")));
|
|
8901
9148
|
el.appendChild(this._makeBtn(ICONS$2.rowHeight, L.rowHeight, () => this._openSizePopover("row")));
|
|
8902
9149
|
el.appendChild(this._makeBtn(ICONS$2.tableBorder, L.tableBorderWidth, () => this._openSizePopover("border")));
|
|
@@ -8905,6 +9152,7 @@
|
|
|
8905
9152
|
this._disposers.push(on(el, "mouseenter", () => this._clearTimers()), on(el, "mouseleave", () => {
|
|
8906
9153
|
if (this._selectMode) return;
|
|
8907
9154
|
if (this._sizePopover && this._sizePopover.style.display !== "none") return;
|
|
9155
|
+
if (this._shadePopover && this._shadePopover.style.display !== "none") return;
|
|
8908
9156
|
this._scheduleHide();
|
|
8909
9157
|
}));
|
|
8910
9158
|
return el;
|
|
@@ -8951,10 +9199,16 @@
|
|
|
8951
9199
|
_show() {
|
|
8952
9200
|
if (!this._activeTable) return;
|
|
8953
9201
|
this._el.style.display = "flex";
|
|
9202
|
+
this._syncShadeStrip();
|
|
8954
9203
|
requestAnimationFrame(() => {
|
|
8955
9204
|
if (this._activeTable) this._positionNear(this._activeTable);
|
|
8956
9205
|
});
|
|
8957
9206
|
}
|
|
9207
|
+
_syncShadeStrip() {
|
|
9208
|
+
if (!this._shadeColorStrip || !this._el || this._el.style.display === "none") return;
|
|
9209
|
+
const cell = this._getCell();
|
|
9210
|
+
this._shadeColorStrip.style.background = cell?.style.backgroundColor || "transparent";
|
|
9211
|
+
}
|
|
8958
9212
|
_hide() {
|
|
8959
9213
|
this._el.style.display = "none";
|
|
8960
9214
|
this._activeTable = null;
|
|
@@ -8983,20 +9237,20 @@
|
|
|
8983
9237
|
let left = rect.left + (rect.width - tipW) / 2;
|
|
8984
9238
|
let top = rect.top - tipH - margin;
|
|
8985
9239
|
if (top < margin) top = rect.bottom + margin;
|
|
8986
|
-
if (left + tipW >
|
|
9240
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
8987
9241
|
if (left < margin) left = margin;
|
|
8988
9242
|
this._el.style.left = `${left}px`;
|
|
8989
9243
|
this._el.style.top = `${top}px`;
|
|
8990
9244
|
}
|
|
8991
9245
|
_getCell() {
|
|
8992
|
-
const sel =
|
|
9246
|
+
const sel = globalThis.getSelection();
|
|
8993
9247
|
if (sel && sel.rangeCount) {
|
|
8994
9248
|
let container = sel.getRangeAt(0).commonAncestorContainer;
|
|
8995
9249
|
if (container.nodeType === 3) container = container.parentElement;
|
|
8996
|
-
const cellFromSel = container
|
|
8997
|
-
if (cellFromSel && this._activeTable
|
|
9250
|
+
const cellFromSel = container?.closest("td, th");
|
|
9251
|
+
if (cellFromSel && this._activeTable?.contains(cellFromSel)) return cellFromSel;
|
|
8998
9252
|
}
|
|
8999
|
-
return this._activeCell || this._activeTable
|
|
9253
|
+
return this._activeCell || this._activeTable?.querySelector("td, th");
|
|
9000
9254
|
}
|
|
9001
9255
|
_toggleSelectMode() {
|
|
9002
9256
|
this._selectMode = !this._selectMode;
|
|
@@ -9095,11 +9349,12 @@
|
|
|
9095
9349
|
const table = cells[0].closest("table");
|
|
9096
9350
|
if (!table) return;
|
|
9097
9351
|
const allRows = Array.from(table.querySelectorAll("tr"));
|
|
9098
|
-
const
|
|
9352
|
+
const selectedRows = [...new Set(cells.map((c) => c.closest("tr")).filter(Boolean))];
|
|
9353
|
+
const refRow = selectedRows.reduce((best, r) => {
|
|
9099
9354
|
const bi = allRows.indexOf(best);
|
|
9100
9355
|
const ri = allRows.indexOf(r);
|
|
9101
9356
|
return position === "above" ? ri < bi ? r : best : ri > bi ? r : best;
|
|
9102
|
-
});
|
|
9357
|
+
}, selectedRows[0]);
|
|
9103
9358
|
const colCount = Array.from(refRow.cells).reduce((sum, c) => sum + (c.colSpan || 1), 0);
|
|
9104
9359
|
const newRow = document.createElement("tr");
|
|
9105
9360
|
const refCells = Array.from(refRow.cells);
|
|
@@ -9142,7 +9397,7 @@
|
|
|
9142
9397
|
if (selectedRows.filter((r) => r.closest("tbody")).length >= totalBodyRows) return;
|
|
9143
9398
|
this._activeCell = null;
|
|
9144
9399
|
this._clearSelection();
|
|
9145
|
-
selectedRows.forEach((r) => r.
|
|
9400
|
+
selectedRows.forEach((r) => r.remove());
|
|
9146
9401
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
9147
9402
|
this.context.invoke("editor.afterCommand");
|
|
9148
9403
|
}
|
|
@@ -9164,7 +9419,7 @@
|
|
|
9164
9419
|
});
|
|
9165
9420
|
this._activeCell = null;
|
|
9166
9421
|
this._clearSelection();
|
|
9167
|
-
cellsToDelete.forEach((c) => c.
|
|
9422
|
+
cellsToDelete.forEach((c) => c.remove());
|
|
9168
9423
|
requestAnimationFrame(() => this._positionNear(this._activeTable));
|
|
9169
9424
|
this.context.invoke("editor.afterCommand");
|
|
9170
9425
|
}
|
|
@@ -9175,7 +9430,7 @@
|
|
|
9175
9430
|
if (!table) return;
|
|
9176
9431
|
let selected = this._getSelectedCells().filter((c) => table.contains(c));
|
|
9177
9432
|
if (selected.length < 2) {
|
|
9178
|
-
const sel =
|
|
9433
|
+
const sel = globalThis.getSelection();
|
|
9179
9434
|
if (!sel || sel.rangeCount === 0) return;
|
|
9180
9435
|
const range = sel.getRangeAt(0);
|
|
9181
9436
|
selected = Array.from(table.querySelectorAll("td, th")).filter((c) => {
|
|
@@ -9217,7 +9472,7 @@
|
|
|
9217
9472
|
first.rowSpan = maxR - minR + 1;
|
|
9218
9473
|
first.style.verticalAlign = "middle";
|
|
9219
9474
|
first.innerHTML = rectCells.map((c) => c.innerHTML).join("");
|
|
9220
|
-
rectCells.slice(1).forEach((c) => c.
|
|
9475
|
+
rectCells.slice(1).forEach((c) => c.remove());
|
|
9221
9476
|
this._clearSelection();
|
|
9222
9477
|
this.context.invoke("editor.afterCommand");
|
|
9223
9478
|
}
|
|
@@ -9225,7 +9480,7 @@
|
|
|
9225
9480
|
const table = this._activeTable;
|
|
9226
9481
|
if (!table) return;
|
|
9227
9482
|
this._hide();
|
|
9228
|
-
if (table.parentNode) table.
|
|
9483
|
+
if (table.parentNode) table.remove();
|
|
9229
9484
|
this.context.invoke("editor.afterCommand");
|
|
9230
9485
|
}
|
|
9231
9486
|
_unmergeCells() {
|
|
@@ -9314,20 +9569,22 @@
|
|
|
9314
9569
|
this._sizeInputEl = inputEl;
|
|
9315
9570
|
this._sizeApply = null;
|
|
9316
9571
|
const d1 = on(applyBtn, "click", () => {
|
|
9317
|
-
const val = parseInt(this._sizeInputEl.value, 10);
|
|
9572
|
+
const val = Number.parseInt(this._sizeInputEl.value, 10);
|
|
9318
9573
|
if (val > 0 && typeof this._sizeApply === "function") this._sizeApply(val);
|
|
9319
9574
|
this._hideSizePopover();
|
|
9320
9575
|
});
|
|
9321
9576
|
const d2 = on(cancelBtn, "click", () => this._hideSizePopover());
|
|
9322
9577
|
const d3 = on(inputEl, "keydown", (e) => {
|
|
9323
|
-
|
|
9578
|
+
const ke = e;
|
|
9579
|
+
if (ke.key === "Enter") {
|
|
9324
9580
|
e.preventDefault();
|
|
9325
9581
|
applyBtn.click();
|
|
9326
9582
|
}
|
|
9327
|
-
if (
|
|
9583
|
+
if (ke.key === "Escape") this._hideSizePopover();
|
|
9328
9584
|
});
|
|
9329
9585
|
const d4 = on(document, "click", (e) => {
|
|
9330
|
-
|
|
9586
|
+
const et = e.target;
|
|
9587
|
+
if (this._sizePopover && this._sizePopover.style.display !== "none" && !this._sizePopover.contains(et) && !this._el.contains(et)) this._hideSizePopover();
|
|
9331
9588
|
});
|
|
9332
9589
|
const d5 = on(popover, "mouseenter", () => this._clearTimers());
|
|
9333
9590
|
const d6 = on(popover, "mouseleave", () => this._scheduleHide());
|
|
@@ -9341,11 +9598,11 @@
|
|
|
9341
9598
|
const table = cell.closest("table");
|
|
9342
9599
|
if (!table) return;
|
|
9343
9600
|
const firstCell = table.querySelector("td, th");
|
|
9344
|
-
const currentPx = firstCell ? parseInt(firstCell.style.borderWidth, 10) || parseInt(
|
|
9601
|
+
const currentPx = firstCell ? Number.parseInt(firstCell.style.borderWidth, 10) || Number.parseInt(globalThis.getComputedStyle(firstCell).borderWidth, 10) || 1 : 1;
|
|
9345
9602
|
this._sizeTitleEl.textContent = this.context.locale.tooltips.table.tableBorderWidthPx;
|
|
9346
9603
|
this._sizeInputEl.min = "0";
|
|
9347
9604
|
this._sizeInputEl.max = "10";
|
|
9348
|
-
this._sizeInputEl.value = currentPx;
|
|
9605
|
+
this._sizeInputEl.value = String(currentPx);
|
|
9349
9606
|
this._sizeApply = (val) => {
|
|
9350
9607
|
const cells = Array.from(table.querySelectorAll("td, th"));
|
|
9351
9608
|
if (val === 0) cells.forEach((c) => {
|
|
@@ -9400,8 +9657,8 @@
|
|
|
9400
9657
|
const ph = this._sizePopover.offsetHeight || 110;
|
|
9401
9658
|
let left = tipRect.left;
|
|
9402
9659
|
let top = tipRect.bottom + 6;
|
|
9403
|
-
if (left + pw >
|
|
9404
|
-
if (top + ph >
|
|
9660
|
+
if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
|
|
9661
|
+
if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
|
|
9405
9662
|
this._sizePopover.style.left = `${left}px`;
|
|
9406
9663
|
this._sizePopover.style.top = `${top}px`;
|
|
9407
9664
|
if (this._sizeInputEl) {
|
|
@@ -9414,6 +9671,84 @@
|
|
|
9414
9671
|
if (this._sizePopover) this._sizePopover.style.display = "none";
|
|
9415
9672
|
this._sizeApply = null;
|
|
9416
9673
|
}
|
|
9674
|
+
_buildCellShadePopover() {
|
|
9675
|
+
const pop = createElement("div", { class: "an-cell-shade-popover" });
|
|
9676
|
+
pop.style.display = "none";
|
|
9677
|
+
const title = createElement("div", { class: "an-size-popover-title" });
|
|
9678
|
+
pop.appendChild(title);
|
|
9679
|
+
this._shadeTitleEl = title;
|
|
9680
|
+
const palette = createElement("div", { class: "an-context-color-palette" });
|
|
9681
|
+
SHADE_PRESETS.forEach((color) => {
|
|
9682
|
+
const sw = createElement("div", {
|
|
9683
|
+
class: "an-context-color-swatch",
|
|
9684
|
+
title: color
|
|
9685
|
+
});
|
|
9686
|
+
sw.style.background = color;
|
|
9687
|
+
this._disposers.push(on(sw, "click", (e) => {
|
|
9688
|
+
e.stopPropagation();
|
|
9689
|
+
this._applyCellShade(color);
|
|
9690
|
+
}));
|
|
9691
|
+
palette.appendChild(sw);
|
|
9692
|
+
});
|
|
9693
|
+
pop.appendChild(palette);
|
|
9694
|
+
const noShadeRow = createElement("div", { class: "an-context-color-custom" });
|
|
9695
|
+
const noShadeBtn = createElement("button", {
|
|
9696
|
+
type: "button",
|
|
9697
|
+
class: "an-shade-no-color"
|
|
9698
|
+
});
|
|
9699
|
+
this._disposers.push(on(noShadeBtn, "click", () => this._applyCellShade("")));
|
|
9700
|
+
noShadeRow.appendChild(noShadeBtn);
|
|
9701
|
+
pop.appendChild(noShadeRow);
|
|
9702
|
+
this._shadeNoBtn = noShadeBtn;
|
|
9703
|
+
const customRow = createElement("div", { class: "an-context-color-custom" });
|
|
9704
|
+
const colorInput = createElement("input", {
|
|
9705
|
+
type: "color",
|
|
9706
|
+
class: "an-shade-color-input",
|
|
9707
|
+
value: "#ffffff"
|
|
9708
|
+
});
|
|
9709
|
+
const customLabel = createElement("span");
|
|
9710
|
+
customLabel.textContent = "Custom…";
|
|
9711
|
+
this._disposers.push(on(colorInput, "change", () => this._applyCellShade(colorInput.value)));
|
|
9712
|
+
customRow.appendChild(colorInput);
|
|
9713
|
+
customRow.appendChild(customLabel);
|
|
9714
|
+
pop.appendChild(customRow);
|
|
9715
|
+
this._disposers.push(on(pop, "mousedown", (e) => e.preventDefault()), on(pop, "mouseenter", () => this._clearTimers()), on(pop, "mouseleave", () => this._scheduleHide()));
|
|
9716
|
+
this._disposers.push(on(document, "click", (e) => {
|
|
9717
|
+
const et = e.target;
|
|
9718
|
+
if (this._shadePopover && this._shadePopover.style.display !== "none" && !this._shadePopover.contains(et) && !this._el?.contains(et)) this._hideCellShadePopover();
|
|
9719
|
+
}));
|
|
9720
|
+
return pop;
|
|
9721
|
+
}
|
|
9722
|
+
_openCellShadePopover() {
|
|
9723
|
+
if (!this._shadePopover) return;
|
|
9724
|
+
const L = this.context.locale.tooltips.table;
|
|
9725
|
+
if (this._shadeTitleEl) this._shadeTitleEl.textContent = L.cellBackground;
|
|
9726
|
+
if (this._shadeNoBtn) this._shadeNoBtn.textContent = L.noShading;
|
|
9727
|
+
this._shadePopover.style.display = "block";
|
|
9728
|
+
requestAnimationFrame(() => {
|
|
9729
|
+
if (!this._shadePopover || !this._el) return;
|
|
9730
|
+
const pw = this._shadePopover.offsetWidth || 170;
|
|
9731
|
+
const ph = this._shadePopover.offsetHeight || 120;
|
|
9732
|
+
const tipRect = this._el.getBoundingClientRect();
|
|
9733
|
+
let left = tipRect.left;
|
|
9734
|
+
let top = tipRect.bottom + 6;
|
|
9735
|
+
if (left + pw > globalThis.innerWidth - 8) left = globalThis.innerWidth - pw - 8;
|
|
9736
|
+
if (top + ph > globalThis.innerHeight - 8) top = tipRect.top - ph - 6;
|
|
9737
|
+
this._shadePopover.style.left = `${Math.max(8, left)}px`;
|
|
9738
|
+
this._shadePopover.style.top = `${Math.max(8, top)}px`;
|
|
9739
|
+
});
|
|
9740
|
+
}
|
|
9741
|
+
_hideCellShadePopover() {
|
|
9742
|
+
if (this._shadePopover) this._shadePopover.style.display = "none";
|
|
9743
|
+
}
|
|
9744
|
+
_applyCellShade(color) {
|
|
9745
|
+
(this._selectMode ? this._selectedCells : [this._getCell()]).forEach((cell) => {
|
|
9746
|
+
if (cell) cell.style.backgroundColor = color;
|
|
9747
|
+
});
|
|
9748
|
+
if (this._shadeColorStrip) this._shadeColorStrip.style.background = color || "transparent";
|
|
9749
|
+
this._hideCellShadePopover();
|
|
9750
|
+
this.context.invoke("editor.afterCommand");
|
|
9751
|
+
}
|
|
9417
9752
|
};
|
|
9418
9753
|
//#endregion
|
|
9419
9754
|
//#region src/js/module/CodeTooltip.js
|
|
@@ -9446,13 +9781,14 @@
|
|
|
9446
9781
|
const editable = this.context.layoutInfo.editable;
|
|
9447
9782
|
this._disposers.push(on(editable, "mouseover", (e) => {
|
|
9448
9783
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
9449
|
-
const pre = e.target
|
|
9784
|
+
const pre = e.target?.closest("pre");
|
|
9450
9785
|
if (pre && editable.contains(pre)) this._scheduleShow(pre);
|
|
9451
9786
|
}), on(editable, "mouseout", (e) => {
|
|
9452
9787
|
const to = e.relatedTarget;
|
|
9453
9788
|
if (!to || !editable.contains(to) && !this._el.contains(to)) this._scheduleHide();
|
|
9454
9789
|
}), on(document, "click", (e) => {
|
|
9455
|
-
|
|
9790
|
+
const et = e.target;
|
|
9791
|
+
if (this._activePre && !this._activePre.contains(et) && !this._el.contains(et)) this._hide();
|
|
9456
9792
|
}));
|
|
9457
9793
|
return this;
|
|
9458
9794
|
}
|
|
@@ -9460,7 +9796,7 @@
|
|
|
9460
9796
|
this._clearTimers();
|
|
9461
9797
|
this._disposers.forEach((d) => d());
|
|
9462
9798
|
this._disposers = [];
|
|
9463
|
-
|
|
9799
|
+
this._el?.remove();
|
|
9464
9800
|
this._el = null;
|
|
9465
9801
|
}
|
|
9466
9802
|
_buildTooltip() {
|
|
@@ -9487,6 +9823,7 @@
|
|
|
9487
9823
|
["python", "Python"],
|
|
9488
9824
|
["html", "HTML"],
|
|
9489
9825
|
["css", "CSS"],
|
|
9826
|
+
["scss", "SCSS"],
|
|
9490
9827
|
["json", "JSON"],
|
|
9491
9828
|
["xml", "XML"],
|
|
9492
9829
|
["bash", "Bash / Shell"],
|
|
@@ -9585,21 +9922,21 @@
|
|
|
9585
9922
|
let top = rect.top - tipH - margin;
|
|
9586
9923
|
let left = rect.left + (rect.width - tipW) / 2;
|
|
9587
9924
|
if (top < margin) top = rect.bottom + margin;
|
|
9588
|
-
if (left + tipW >
|
|
9925
|
+
if (left + tipW > globalThis.innerWidth - margin) left = globalThis.innerWidth - tipW - margin;
|
|
9589
9926
|
if (left < margin) left = margin;
|
|
9590
9927
|
this._el.style.top = `${top}px`;
|
|
9591
9928
|
this._el.style.left = `${left}px`;
|
|
9592
9929
|
}
|
|
9593
9930
|
_syncWrapBtn() {
|
|
9594
9931
|
if (!this._activePre || !this._wrapBtn) return;
|
|
9595
|
-
const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") ||
|
|
9932
|
+
const wrapped = (this._activePre.style.whiteSpace || "").includes("pre-wrap") || globalThis.getComputedStyle(this._activePre).whiteSpace === "pre-wrap";
|
|
9596
9933
|
this._wrapBtn.classList.toggle("active", wrapped);
|
|
9597
9934
|
this._wrapBtn.title = wrapped ? this.context.locale.tooltips.code.disableWordWrap : this.context.locale.tooltips.code.enableWordWrap;
|
|
9598
9935
|
}
|
|
9599
9936
|
_syncLangSelect() {
|
|
9600
9937
|
if (!this._activePre || !this._langSelect) return;
|
|
9601
9938
|
const codeEl = this._activePre.querySelector("code");
|
|
9602
|
-
const fromAttr = this._activePre.
|
|
9939
|
+
const fromAttr = this._activePre.dataset.language || "";
|
|
9603
9940
|
const fromClass = codeEl ? (_LANG_CLASS_RE.exec(codeEl.className) || [])[1] || "" : "";
|
|
9604
9941
|
this._langSelect.value = fromAttr || fromClass || "";
|
|
9605
9942
|
}
|
|
@@ -9618,7 +9955,7 @@
|
|
|
9618
9955
|
document.execCommand("copy");
|
|
9619
9956
|
this._flashCopied();
|
|
9620
9957
|
} catch (_) {}
|
|
9621
|
-
|
|
9958
|
+
ta.remove();
|
|
9622
9959
|
}
|
|
9623
9960
|
}
|
|
9624
9961
|
_flashCopied() {
|
|
@@ -9642,10 +9979,26 @@
|
|
|
9642
9979
|
this.context.invoke("editor.afterCommand");
|
|
9643
9980
|
this._positionNear(pre);
|
|
9644
9981
|
}
|
|
9982
|
+
/**
|
|
9983
|
+
* Applies a language to a given <pre> element: sets classes, data-language,
|
|
9984
|
+
* and triggers Prism highlighting. Called by the auto-detect flow.
|
|
9985
|
+
* @param {HTMLElement} pre
|
|
9986
|
+
* @param {string} lang - Prism language identifier, e.g. 'javascript'
|
|
9987
|
+
*/
|
|
9988
|
+
applyLanguage(pre, lang) {
|
|
9989
|
+
if (!pre || !lang) return;
|
|
9990
|
+
const savedPre = this._activePre;
|
|
9991
|
+
this._activePre = pre;
|
|
9992
|
+
if (this._langSelect) this._langSelect.value = lang;
|
|
9993
|
+
this._onLangChange();
|
|
9994
|
+
if (this._langSelect) this._langSelect.value = lang;
|
|
9995
|
+
this._activePre = savedPre || pre;
|
|
9996
|
+
}
|
|
9645
9997
|
_onLangChange() {
|
|
9646
9998
|
const pre = this._activePre;
|
|
9647
9999
|
if (!pre) return;
|
|
9648
10000
|
const lang = this._langSelect.value;
|
|
10001
|
+
const _w = globalThis;
|
|
9649
10002
|
let codeEl = pre.querySelector("code");
|
|
9650
10003
|
if (!codeEl) {
|
|
9651
10004
|
codeEl = document.createElement("code");
|
|
@@ -9655,16 +10008,16 @@
|
|
|
9655
10008
|
}
|
|
9656
10009
|
codeEl.className = lang ? `language-${lang}` : "";
|
|
9657
10010
|
pre.className = lang ? `language-${lang}` : "";
|
|
9658
|
-
if (lang) pre.
|
|
9659
|
-
else pre.
|
|
10011
|
+
if (lang) pre.dataset.language = lang;
|
|
10012
|
+
else delete pre.dataset.language;
|
|
9660
10013
|
const applyPrism = () => {
|
|
9661
10014
|
codeEl.querySelectorAll("br").forEach((br) => br.replaceWith("\n"));
|
|
9662
|
-
|
|
10015
|
+
_w.Prism.highlightElement(codeEl);
|
|
9663
10016
|
this.context.invoke("editor.afterCommand");
|
|
9664
10017
|
};
|
|
9665
10018
|
if (lang) {
|
|
9666
|
-
if (
|
|
9667
|
-
if (
|
|
10019
|
+
if (_w.Prism !== void 0) {
|
|
10020
|
+
if (_w.Prism.languages[lang]) {
|
|
9668
10021
|
applyPrism();
|
|
9669
10022
|
return;
|
|
9670
10023
|
}
|
|
@@ -9672,7 +10025,7 @@
|
|
|
9672
10025
|
return;
|
|
9673
10026
|
} else if (this._prismScript) {
|
|
9674
10027
|
this._prismScript.addEventListener("load", () => {
|
|
9675
|
-
if (
|
|
10028
|
+
if (_w.Prism.languages[lang]) applyPrism();
|
|
9676
10029
|
else this._loadPrismComponent(lang, applyPrism);
|
|
9677
10030
|
}, { once: true });
|
|
9678
10031
|
return;
|
|
@@ -9685,7 +10038,8 @@
|
|
|
9685
10038
|
* Called once at initialize time. Fire-and-forget; errors are silent.
|
|
9686
10039
|
*/
|
|
9687
10040
|
_ensurePrism() {
|
|
9688
|
-
|
|
10041
|
+
const _w = globalThis;
|
|
10042
|
+
if (!this.context.options.codeHighlight || _w.Prism) return;
|
|
9689
10043
|
const cdn = this.context.options.codeHighlightCDN;
|
|
9690
10044
|
const themeHref = `${cdn}/themes/prism-tomorrow.min.css`;
|
|
9691
10045
|
const scriptSrc = `${cdn}/prism.min.js`;
|
|
@@ -9697,7 +10051,7 @@
|
|
|
9697
10051
|
}
|
|
9698
10052
|
const existingScript = document.querySelector(`script[src="${scriptSrc}"]`);
|
|
9699
10053
|
if (existingScript) {
|
|
9700
|
-
this._prismScript =
|
|
10054
|
+
this._prismScript = _w.Prism ? null : existingScript;
|
|
9701
10055
|
return;
|
|
9702
10056
|
}
|
|
9703
10057
|
const script = document.createElement("script");
|
|
@@ -9717,10 +10071,11 @@
|
|
|
9717
10071
|
* @param {Function} cb – called once the grammar is ready
|
|
9718
10072
|
*/
|
|
9719
10073
|
_loadPrismComponent(lang, cb) {
|
|
10074
|
+
const _w = globalThis;
|
|
9720
10075
|
const src = `${this.context.options.codeHighlightCDN}/components/prism-${lang}.min.js`;
|
|
9721
10076
|
if (document.querySelector(`script[src="${src}"]`)) {
|
|
9722
10077
|
const poll = setInterval(() => {
|
|
9723
|
-
if (
|
|
10078
|
+
if (_w.Prism?.languages[lang]) {
|
|
9724
10079
|
clearInterval(poll);
|
|
9725
10080
|
cb();
|
|
9726
10081
|
}
|
|
@@ -9751,7 +10106,7 @@
|
|
|
9751
10106
|
const pre = this._activePre;
|
|
9752
10107
|
if (!pre) return;
|
|
9753
10108
|
this._hide();
|
|
9754
|
-
|
|
10109
|
+
pre.remove();
|
|
9755
10110
|
this.context.invoke("editor.afterCommand");
|
|
9756
10111
|
}
|
|
9757
10112
|
};
|
|
@@ -12106,7 +12461,7 @@
|
|
|
12106
12461
|
destroy() {
|
|
12107
12462
|
this._disposers.forEach((d) => d());
|
|
12108
12463
|
this._disposers = [];
|
|
12109
|
-
if (this._dialog && this._dialog.parentNode) this._dialog.
|
|
12464
|
+
if (this._dialog && this._dialog.parentNode) this._dialog.remove();
|
|
12110
12465
|
this._dialog = null;
|
|
12111
12466
|
}
|
|
12112
12467
|
show() {
|
|
@@ -12133,15 +12488,19 @@
|
|
|
12133
12488
|
});
|
|
12134
12489
|
const box = createElement("div", { class: "an-dialog-box an-emoji-box" });
|
|
12135
12490
|
const titleRow = createElement("div", { class: "an-icon-title-row" });
|
|
12491
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
12492
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
12493
|
+
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
12494
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
12137
12495
|
title.textContent = L.title;
|
|
12496
|
+
titleGroup.append(iconEl, title);
|
|
12138
12497
|
const closeBtn = createElement("button", {
|
|
12139
12498
|
type: "button",
|
|
12140
12499
|
class: "an-icon-close",
|
|
12141
12500
|
"aria-label": L.close
|
|
12142
12501
|
});
|
|
12143
12502
|
closeBtn.innerHTML = "×";
|
|
12144
|
-
titleRow.append(
|
|
12503
|
+
titleRow.append(titleGroup, closeBtn);
|
|
12145
12504
|
const searchInput = createElement("input", {
|
|
12146
12505
|
type: "search",
|
|
12147
12506
|
class: "an-input an-icon-search",
|
|
@@ -12163,7 +12522,7 @@
|
|
|
12163
12522
|
class: "an-icon-cat",
|
|
12164
12523
|
"data-cat": id
|
|
12165
12524
|
});
|
|
12166
|
-
tab.textContent = L.categories
|
|
12525
|
+
tab.textContent = L.categories?.[id] || label;
|
|
12167
12526
|
catBar.appendChild(tab);
|
|
12168
12527
|
});
|
|
12169
12528
|
this._catBar = catBar;
|
|
@@ -12190,6 +12549,7 @@
|
|
|
12190
12549
|
btnRow.appendChild(cancelBtn);
|
|
12191
12550
|
box.append(titleRow, searchInput, catBar, grid, btnRow);
|
|
12192
12551
|
overlay.appendChild(box);
|
|
12552
|
+
makeDraggable(titleRow, box);
|
|
12193
12553
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
12194
12554
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
12195
12555
|
const d3 = on(overlay, "click", (e) => {
|
|
@@ -12197,7 +12557,7 @@
|
|
|
12197
12557
|
});
|
|
12198
12558
|
const d4 = on(searchInput, "input", () => this._filterEmojis(searchInput.value, this._activeCat));
|
|
12199
12559
|
const d5 = on(catBar, "click", (e) => {
|
|
12200
|
-
const tab = e.target
|
|
12560
|
+
const tab = e.target?.closest("[data-cat]");
|
|
12201
12561
|
if (tab) {
|
|
12202
12562
|
this._activeCat = tab.dataset.cat;
|
|
12203
12563
|
this._updateCatTabs();
|
|
@@ -12205,7 +12565,7 @@
|
|
|
12205
12565
|
}
|
|
12206
12566
|
});
|
|
12207
12567
|
const d6 = on(grid, "click", (e) => {
|
|
12208
|
-
const cell = e.target
|
|
12568
|
+
const cell = e.target?.closest(".an-emoji-cell");
|
|
12209
12569
|
if (cell) this._onEmojiClick(cell.dataset.char);
|
|
12210
12570
|
});
|
|
12211
12571
|
this._disposers.push(d1, d2, d3, d4, d5, d6);
|
|
@@ -12213,17 +12573,22 @@
|
|
|
12213
12573
|
}
|
|
12214
12574
|
_updateCatTabs() {
|
|
12215
12575
|
this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
|
|
12216
|
-
tab.classList.toggle(
|
|
12576
|
+
tab.classList.toggle(
|
|
12577
|
+
"active",
|
|
12578
|
+
/** @type {HTMLElement} */
|
|
12579
|
+
tab.dataset.cat === this._activeCat
|
|
12580
|
+
);
|
|
12217
12581
|
});
|
|
12218
12582
|
}
|
|
12219
12583
|
_filterEmojis(query, cat) {
|
|
12220
12584
|
const q = (query || "").trim().toLowerCase();
|
|
12221
12585
|
let count = 0;
|
|
12222
12586
|
this._grid.querySelectorAll(".an-emoji-cell").forEach((cell) => {
|
|
12223
|
-
const
|
|
12224
|
-
const
|
|
12587
|
+
const hCell = cell;
|
|
12588
|
+
const matchCat = !cat || cat === "all" || hCell.dataset.cat === cat;
|
|
12589
|
+
const matchQuery = !q || hCell.dataset.keywords.includes(q) || hCell.dataset.char === q;
|
|
12225
12590
|
const visible = matchCat && matchQuery;
|
|
12226
|
-
|
|
12591
|
+
hCell.style.display = visible ? "" : "none";
|
|
12227
12592
|
if (visible) count++;
|
|
12228
12593
|
});
|
|
12229
12594
|
let empty = this._grid.querySelector(".an-icon-empty");
|
|
@@ -12232,13 +12597,13 @@
|
|
|
12232
12597
|
empty.textContent = "No emojis found";
|
|
12233
12598
|
this._grid.appendChild(empty);
|
|
12234
12599
|
}
|
|
12235
|
-
empty.style.display = count > 0 ? "none" : "";
|
|
12600
|
+
/** @type {HTMLElement} */ empty.style.display = count > 0 ? "none" : "";
|
|
12236
12601
|
}
|
|
12237
12602
|
_onEmojiClick(char) {
|
|
12238
12603
|
const savedRange = this._savedRange;
|
|
12239
12604
|
const editable = this.context.layoutInfo.editable;
|
|
12240
12605
|
if (savedRange) savedRange.select();
|
|
12241
|
-
const sel =
|
|
12606
|
+
const sel = globalThis.getSelection();
|
|
12242
12607
|
let range = sel && sel.rangeCount > 0 ? sel.getRangeAt(0) : null;
|
|
12243
12608
|
if (!range) {
|
|
12244
12609
|
range = document.createRange();
|
|
@@ -12246,9 +12611,9 @@
|
|
|
12246
12611
|
range.collapse(false);
|
|
12247
12612
|
}
|
|
12248
12613
|
const _sc = range.startContainer;
|
|
12249
|
-
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest
|
|
12614
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
|
|
12250
12615
|
range.deleteContents();
|
|
12251
|
-
if (_tdAnchor
|
|
12616
|
+
if (_tdAnchor?.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
12252
12617
|
range.setStart(_tdAnchor, 0);
|
|
12253
12618
|
range.collapse(true);
|
|
12254
12619
|
}
|
|
@@ -12268,7 +12633,7 @@
|
|
|
12268
12633
|
if (this._dialog) {
|
|
12269
12634
|
this._dialog.style.display = "flex";
|
|
12270
12635
|
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
12271
|
-
setTimeout(() => this._searchInput
|
|
12636
|
+
setTimeout(() => this._searchInput?.focus(), 50);
|
|
12272
12637
|
}
|
|
12273
12638
|
}
|
|
12274
12639
|
_close() {
|
|
@@ -12575,7 +12940,7 @@
|
|
|
12575
12940
|
destroy() {
|
|
12576
12941
|
this._disposers.forEach((d) => d());
|
|
12577
12942
|
this._disposers = [];
|
|
12578
|
-
|
|
12943
|
+
this._dialog?.remove();
|
|
12579
12944
|
this._dialog = null;
|
|
12580
12945
|
}
|
|
12581
12946
|
show() {
|
|
@@ -12606,15 +12971,19 @@
|
|
|
12606
12971
|
});
|
|
12607
12972
|
const box = createElement("div", { class: "an-dialog-box an-icon-box" });
|
|
12608
12973
|
const titleRow = createElement("div", { class: "an-icon-title-row" });
|
|
12974
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
12975
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
12976
|
+
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
12977
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
12610
12978
|
title.textContent = L.title;
|
|
12979
|
+
titleGroup.append(iconEl, title);
|
|
12611
12980
|
const closeBtn = createElement("button", {
|
|
12612
12981
|
type: "button",
|
|
12613
12982
|
class: "an-icon-close",
|
|
12614
12983
|
"aria-label": L.close
|
|
12615
12984
|
});
|
|
12616
12985
|
closeBtn.innerHTML = "×";
|
|
12617
|
-
titleRow.append(
|
|
12986
|
+
titleRow.append(titleGroup, closeBtn);
|
|
12618
12987
|
const searchInput = createElement("input", {
|
|
12619
12988
|
type: "search",
|
|
12620
12989
|
class: "an-input an-icon-search",
|
|
@@ -12636,7 +13005,7 @@
|
|
|
12636
13005
|
class: "an-icon-cat",
|
|
12637
13006
|
"data-cat": id
|
|
12638
13007
|
});
|
|
12639
|
-
tab.textContent = L.categories
|
|
13008
|
+
tab.textContent = L.categories?.[id] || label;
|
|
12640
13009
|
catBar.appendChild(tab);
|
|
12641
13010
|
});
|
|
12642
13011
|
this._catBar = catBar;
|
|
@@ -12729,6 +13098,7 @@
|
|
|
12729
13098
|
this._insertBtn = insertBtn;
|
|
12730
13099
|
box.append(titleRow, searchInput, catBar, grid, optRow, preview, btnRow);
|
|
12731
13100
|
overlay.appendChild(box);
|
|
13101
|
+
makeDraggable(titleRow, box);
|
|
12732
13102
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
12733
13103
|
const d2 = on(cancelBtn, "click", () => this._close());
|
|
12734
13104
|
const d3 = on(insertBtn, "click", () => this._onInsert());
|
|
@@ -12737,7 +13107,7 @@
|
|
|
12737
13107
|
});
|
|
12738
13108
|
const d5 = on(searchInput, "input", () => this._filterIcons(searchInput.value, this._activeCat));
|
|
12739
13109
|
const d6 = on(catBar, "click", (e) => {
|
|
12740
|
-
const tab = e.target
|
|
13110
|
+
const tab = e.target?.closest("[data-cat]");
|
|
12741
13111
|
if (tab) {
|
|
12742
13112
|
this._activeCat = tab.dataset.cat;
|
|
12743
13113
|
this._updateCatTabs();
|
|
@@ -12745,7 +13115,7 @@
|
|
|
12745
13115
|
}
|
|
12746
13116
|
});
|
|
12747
13117
|
const d7 = on(grid, "click", (e) => {
|
|
12748
|
-
const cell = e.target
|
|
13118
|
+
const cell = e.target?.closest(".an-icon-cell");
|
|
12749
13119
|
if (cell) this._selectIcon(cell.dataset.name);
|
|
12750
13120
|
});
|
|
12751
13121
|
const d8 = on(styleSelect, "change", () => this._updatePreview(this._selectedIcon));
|
|
@@ -12757,19 +13127,24 @@
|
|
|
12757
13127
|
}
|
|
12758
13128
|
_updateCatTabs() {
|
|
12759
13129
|
this._catBar.querySelectorAll(".an-icon-cat").forEach((tab) => {
|
|
12760
|
-
tab.classList.toggle(
|
|
13130
|
+
tab.classList.toggle(
|
|
13131
|
+
"active",
|
|
13132
|
+
/** @type {HTMLElement} */
|
|
13133
|
+
tab.dataset.cat === this._activeCat
|
|
13134
|
+
);
|
|
12761
13135
|
});
|
|
12762
13136
|
}
|
|
12763
13137
|
_filterIcons(query, cat) {
|
|
12764
13138
|
const q = (query || "").trim().toLowerCase();
|
|
12765
13139
|
let visibleCount = 0;
|
|
12766
13140
|
this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
|
|
12767
|
-
const
|
|
12768
|
-
const
|
|
13141
|
+
const hCell = cell;
|
|
13142
|
+
const name = hCell.dataset.name;
|
|
13143
|
+
const cellCat = hCell.dataset.cat;
|
|
12769
13144
|
const matchesCat = !cat || cat === "all" || cellCat === cat;
|
|
12770
13145
|
const matchesQuery = !q || name.includes(q);
|
|
12771
13146
|
const visible = matchesCat && matchesQuery;
|
|
12772
|
-
|
|
13147
|
+
hCell.style.display = visible ? "" : "none";
|
|
12773
13148
|
if (visible) visibleCount++;
|
|
12774
13149
|
});
|
|
12775
13150
|
let empty = this._grid.querySelector(".an-icon-empty");
|
|
@@ -12778,12 +13153,16 @@
|
|
|
12778
13153
|
empty.textContent = "No icons found";
|
|
12779
13154
|
this._grid.appendChild(empty);
|
|
12780
13155
|
}
|
|
12781
|
-
empty.style.display = visibleCount > 0 ? "none" : "";
|
|
13156
|
+
/** @type {HTMLElement} */ empty.style.display = visibleCount > 0 ? "none" : "";
|
|
12782
13157
|
}
|
|
12783
13158
|
_selectIcon(name) {
|
|
12784
13159
|
this._selectedIcon = name;
|
|
12785
13160
|
this._grid.querySelectorAll(".an-icon-cell").forEach((cell) => {
|
|
12786
|
-
cell.classList.toggle(
|
|
13161
|
+
cell.classList.toggle(
|
|
13162
|
+
"active",
|
|
13163
|
+
/** @type {HTMLElement} */
|
|
13164
|
+
cell.dataset.name === name
|
|
13165
|
+
);
|
|
12787
13166
|
});
|
|
12788
13167
|
this._insertBtn.removeAttribute("disabled");
|
|
12789
13168
|
this._updatePreview(name);
|
|
@@ -12794,17 +13173,17 @@
|
|
|
12794
13173
|
this._preview.innerHTML = "<span class=\"an-icon-preview-hint\">Select an icon</span>";
|
|
12795
13174
|
return;
|
|
12796
13175
|
}
|
|
12797
|
-
const cls = this._styleSelect
|
|
12798
|
-
const size = this._sizeSelect
|
|
12799
|
-
const color =
|
|
13176
|
+
const cls = this._styleSelect?.value || "fa-solid";
|
|
13177
|
+
const size = this._sizeSelect?.value || "1em";
|
|
13178
|
+
const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
|
|
12800
13179
|
const styleAttr = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean).join(";");
|
|
12801
13180
|
this._preview.innerHTML = `<i class="${cls} fa-${name}" aria-hidden="true"${styleAttr ? ` style="${styleAttr}"` : ""}></i><div class="an-icon-preview-name">${cls} fa-${name}</div>`;
|
|
12802
13181
|
}
|
|
12803
13182
|
_onInsert() {
|
|
12804
13183
|
if (!this._selectedIcon) return;
|
|
12805
|
-
const cls = this._styleSelect
|
|
12806
|
-
const size = this._sizeSelect
|
|
12807
|
-
const color =
|
|
13184
|
+
const cls = this._styleSelect?.value || "fa-solid";
|
|
13185
|
+
const size = this._sizeSelect?.value || "";
|
|
13186
|
+
const color = this._useColorCb?.checked ?? false ? this._colorInput?.value ?? "" : "";
|
|
12808
13187
|
const styleParts = [size ? `font-size:${size}` : "", color ? `color:${color}` : ""].filter(Boolean);
|
|
12809
13188
|
const iconEl = document.createElement("i");
|
|
12810
13189
|
iconEl.className = `${cls} fa-${this._selectedIcon}`;
|
|
@@ -12814,15 +13193,15 @@
|
|
|
12814
13193
|
const savedRange = this._savedRange;
|
|
12815
13194
|
const editable = this.context.layoutInfo.editable;
|
|
12816
13195
|
if (savedRange) savedRange.select();
|
|
12817
|
-
const sel =
|
|
12818
|
-
let range = sel
|
|
13196
|
+
const sel = globalThis.getSelection();
|
|
13197
|
+
let range = (sel?.rangeCount ?? 0) > 0 ? sel.getRangeAt(0) : null;
|
|
12819
13198
|
if (!range) {
|
|
12820
13199
|
range = document.createRange();
|
|
12821
13200
|
range.selectNodeContents(editable);
|
|
12822
13201
|
range.collapse(false);
|
|
12823
13202
|
}
|
|
12824
13203
|
const _sc = range.startContainer;
|
|
12825
|
-
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest
|
|
13204
|
+
const _tdAnchor = (_sc.nodeType === 1 ? _sc : _sc.parentElement)?.closest("td, th");
|
|
12826
13205
|
range.deleteContents();
|
|
12827
13206
|
if (_tdAnchor && _tdAnchor.isConnected && !_tdAnchor.contains(range.startContainer)) {
|
|
12828
13207
|
range.setStart(_tdAnchor, 0);
|
|
@@ -12854,7 +13233,7 @@
|
|
|
12854
13233
|
if (this._dialog) {
|
|
12855
13234
|
this._dialog.style.display = "flex";
|
|
12856
13235
|
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
12857
|
-
setTimeout(() => this._searchInput
|
|
13236
|
+
setTimeout(() => this._searchInput?.focus(), 50);
|
|
12858
13237
|
}
|
|
12859
13238
|
}
|
|
12860
13239
|
_close() {
|
|
@@ -12955,7 +13334,7 @@
|
|
|
12955
13334
|
icon: ICONS.paste,
|
|
12956
13335
|
action: (ctx) => {
|
|
12957
13336
|
if (!navigator.clipboard) return;
|
|
12958
|
-
const editable = ctx.layoutInfo
|
|
13337
|
+
const editable = ctx.layoutInfo?.editable;
|
|
12959
13338
|
if (!editable) return;
|
|
12960
13339
|
const doInsert = (html, text) => {
|
|
12961
13340
|
editable.focus();
|
|
@@ -13080,29 +13459,29 @@
|
|
|
13080
13459
|
this.el.style.display = "none";
|
|
13081
13460
|
document.body.appendChild(this.el);
|
|
13082
13461
|
this._renderItems(this._items);
|
|
13083
|
-
const editable = this.context.layoutInfo
|
|
13462
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13084
13463
|
if (editable) this._disposers.push(on(editable, "contextmenu", (e) => this._onContextMenu(e)));
|
|
13085
13464
|
this._disposers.push(on(document, "click", (e) => this._maybeHide(e)));
|
|
13086
13465
|
this._disposers.push(on(document, "keydown", (e) => {
|
|
13087
13466
|
if (e.key === "Escape") this.hide();
|
|
13088
13467
|
}));
|
|
13089
|
-
this._disposers.push(on(
|
|
13468
|
+
this._disposers.push(on(globalThis, "scroll", () => this.hide(), { passive: true }));
|
|
13090
13469
|
return this;
|
|
13091
13470
|
}
|
|
13092
13471
|
destroy() {
|
|
13093
13472
|
this._menuDisposers.forEach((d) => {
|
|
13094
13473
|
try {
|
|
13095
13474
|
d();
|
|
13096
|
-
} catch (
|
|
13475
|
+
} catch (_e) {}
|
|
13097
13476
|
});
|
|
13098
13477
|
this._menuDisposers = [];
|
|
13099
13478
|
this._disposers.forEach((d) => {
|
|
13100
13479
|
try {
|
|
13101
13480
|
d();
|
|
13102
|
-
} catch (
|
|
13481
|
+
} catch (_e) {}
|
|
13103
13482
|
});
|
|
13104
13483
|
this._disposers = [];
|
|
13105
|
-
if (this.el
|
|
13484
|
+
if (this.el) this.el.remove();
|
|
13106
13485
|
this.el = null;
|
|
13107
13486
|
}
|
|
13108
13487
|
_renderItems(items) {
|
|
@@ -13130,8 +13509,8 @@
|
|
|
13130
13509
|
backBtn.appendChild(createElement("span", { class: "an-context-label" }, [backLabel]));
|
|
13131
13510
|
const off = on(backBtn, "click", (e) => {
|
|
13132
13511
|
e.stopPropagation();
|
|
13133
|
-
const curLeft = parseFloat(this.el.style.left);
|
|
13134
|
-
const curTop = parseFloat(this.el.style.top);
|
|
13512
|
+
const curLeft = Number.parseFloat(this.el.style.left);
|
|
13513
|
+
const curTop = Number.parseFloat(this.el.style.top);
|
|
13135
13514
|
this._renderItems(it.navigate());
|
|
13136
13515
|
this._reposition(curLeft, curTop);
|
|
13137
13516
|
});
|
|
@@ -13174,8 +13553,8 @@
|
|
|
13174
13553
|
btn.appendChild(chevron);
|
|
13175
13554
|
const off = on(btn, "click", (e) => {
|
|
13176
13555
|
e.stopPropagation();
|
|
13177
|
-
const curLeft = parseFloat(this.el.style.left);
|
|
13178
|
-
const curTop = parseFloat(this.el.style.top);
|
|
13556
|
+
const curLeft = Number.parseFloat(this.el.style.left);
|
|
13557
|
+
const curTop = Number.parseFloat(this.el.style.top);
|
|
13179
13558
|
this._renderItems(it.navigate());
|
|
13180
13559
|
this._reposition(curLeft, curTop);
|
|
13181
13560
|
});
|
|
@@ -13290,20 +13669,20 @@
|
|
|
13290
13669
|
});
|
|
13291
13670
|
this._menuDisposers.push(offHeader);
|
|
13292
13671
|
const offMove = on(gridEl, "mousemove", (e) => {
|
|
13293
|
-
const cell = e.target
|
|
13672
|
+
const cell = e.target?.closest("[data-row]");
|
|
13294
13673
|
if (!cell) return;
|
|
13295
13674
|
setHighlight(+cell.dataset.row, +cell.dataset.col);
|
|
13296
13675
|
});
|
|
13297
13676
|
const offLeave = on(gridEl, "mouseleave", () => setHighlight(0, 0));
|
|
13298
13677
|
const offClick = on(gridEl, "click", (e) => {
|
|
13299
|
-
const cell = e.target
|
|
13678
|
+
const cell = e.target?.closest("[data-row]");
|
|
13300
13679
|
if (!cell) return;
|
|
13301
13680
|
const rows = +cell.dataset.row;
|
|
13302
13681
|
const cols = +cell.dataset.col;
|
|
13303
|
-
const editable = this.context.layoutInfo
|
|
13682
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13304
13683
|
if (editable && this._savedRange) {
|
|
13305
13684
|
editable.focus();
|
|
13306
|
-
const sel =
|
|
13685
|
+
const sel = globalThis.getSelection();
|
|
13307
13686
|
sel.removeAllRanges();
|
|
13308
13687
|
sel.addRange(this._savedRange.cloneRange());
|
|
13309
13688
|
}
|
|
@@ -13321,7 +13700,7 @@
|
|
|
13321
13700
|
class: "an-context-item",
|
|
13322
13701
|
"data-name": it.name || ""
|
|
13323
13702
|
});
|
|
13324
|
-
if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) btn.disabled = true;
|
|
13703
|
+
if (typeof it.disabled === "function" ? it.disabled(this.context) : !!it.disabled) /** @type {HTMLButtonElement} */ btn.disabled = true;
|
|
13325
13704
|
if (it.icon) {
|
|
13326
13705
|
const iconSpan = createElement("span", {
|
|
13327
13706
|
class: "an-context-icon",
|
|
@@ -13345,15 +13724,15 @@
|
|
|
13345
13724
|
});
|
|
13346
13725
|
}
|
|
13347
13726
|
_onContextMenu(event) {
|
|
13348
|
-
const editable = this.context.layoutInfo
|
|
13727
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13349
13728
|
if (!editable) return;
|
|
13350
13729
|
if (!editable.contains(event.target)) return;
|
|
13351
13730
|
if (this.context.layoutInfo.container.classList.contains("an-disabled")) return;
|
|
13352
13731
|
event.preventDefault();
|
|
13353
|
-
const winSel =
|
|
13732
|
+
const winSel = globalThis.getSelection();
|
|
13354
13733
|
this._savedRange = winSel && winSel.rangeCount > 0 ? winSel.getRangeAt(0).cloneRange() : null;
|
|
13355
13734
|
this._renderItems(this._items);
|
|
13356
|
-
|
|
13735
|
+
const openX = event.clientX;
|
|
13357
13736
|
let openY = event.clientY;
|
|
13358
13737
|
if (this._savedRange && !this._savedRange.collapsed) try {
|
|
13359
13738
|
const selRect = this._savedRange.getBoundingClientRect();
|
|
@@ -13382,9 +13761,9 @@
|
|
|
13382
13761
|
const h = this.el.offsetHeight;
|
|
13383
13762
|
let left = rx;
|
|
13384
13763
|
let top = ry;
|
|
13385
|
-
if (left + w >
|
|
13764
|
+
if (left + w > globalThis.innerWidth - 8) left = globalThis.innerWidth - w - 8;
|
|
13386
13765
|
if (left < 8) left = 8;
|
|
13387
|
-
if (top + h >
|
|
13766
|
+
if (top + h > globalThis.innerHeight - 8) top = globalThis.innerHeight - h - 8;
|
|
13388
13767
|
if (top < 8) top = 8;
|
|
13389
13768
|
this.el.style.left = `${left}px`;
|
|
13390
13769
|
this.el.style.top = `${top}px`;
|
|
@@ -13405,17 +13784,17 @@
|
|
|
13405
13784
|
let node = range.startContainer;
|
|
13406
13785
|
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
13407
13786
|
if (!node) return type === "foreColor" ? "#000000" : "transparent";
|
|
13408
|
-
const cs =
|
|
13787
|
+
const cs = globalThis.getComputedStyle(node);
|
|
13409
13788
|
if (type === "foreColor") return cs.color || "#000000";
|
|
13410
13789
|
const bg = cs.backgroundColor;
|
|
13411
13790
|
return !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
13412
13791
|
}
|
|
13413
13792
|
/** Restore selection, apply a color command, then hide the menu. */
|
|
13414
13793
|
_applyColor(type, color) {
|
|
13415
|
-
const editable = this.context.layoutInfo
|
|
13794
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13416
13795
|
if (!editable || !this._savedRange) return;
|
|
13417
13796
|
editable.focus();
|
|
13418
|
-
const sel =
|
|
13797
|
+
const sel = globalThis.getSelection();
|
|
13419
13798
|
sel.removeAllRanges();
|
|
13420
13799
|
sel.addRange(this._savedRange.cloneRange());
|
|
13421
13800
|
document.execCommand(type, false, color);
|
|
@@ -13430,15 +13809,15 @@
|
|
|
13430
13809
|
copyFormat() {
|
|
13431
13810
|
const range = this._savedRange;
|
|
13432
13811
|
if (!range) return;
|
|
13433
|
-
const editable = this.context.layoutInfo
|
|
13812
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13434
13813
|
let node = range.startContainer;
|
|
13435
13814
|
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
13436
13815
|
if (!node || !editable || !editable.contains(node)) return;
|
|
13437
|
-
const cs =
|
|
13816
|
+
const cs = globalThis.getComputedStyle(node);
|
|
13438
13817
|
const explicitFontFamily = this._findExplicitStyle(node, editable, "fontFamily");
|
|
13439
13818
|
const explicitFontSize = this._findExplicitStyle(node, editable, "fontSize");
|
|
13440
13819
|
this._copiedFormat = {
|
|
13441
|
-
bold: parseInt(cs.fontWeight, 10) >= 700,
|
|
13820
|
+
bold: Number.parseInt(cs.fontWeight, 10) >= 700,
|
|
13442
13821
|
italic: cs.fontStyle === "italic" || cs.fontStyle === "oblique",
|
|
13443
13822
|
underline: (cs.textDecorationLine || "").includes("underline"),
|
|
13444
13823
|
strikethrough: (cs.textDecorationLine || "").includes("line-through"),
|
|
@@ -13469,10 +13848,10 @@
|
|
|
13469
13848
|
pasteFormat() {
|
|
13470
13849
|
if (!this._copiedFormat || !this._savedRange) return;
|
|
13471
13850
|
const fmt = this._copiedFormat;
|
|
13472
|
-
const editable = this.context.layoutInfo
|
|
13851
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13473
13852
|
if (!editable) return;
|
|
13474
13853
|
editable.focus();
|
|
13475
|
-
const sel =
|
|
13854
|
+
const sel = globalThis.getSelection();
|
|
13476
13855
|
sel.removeAllRanges();
|
|
13477
13856
|
sel.addRange(this._savedRange.cloneRange());
|
|
13478
13857
|
document.execCommand("removeFormat");
|
|
@@ -13489,14 +13868,14 @@
|
|
|
13489
13868
|
const preExisting = new Set(editable.querySelectorAll("font[size=\"7\"]"));
|
|
13490
13869
|
document.execCommand("fontSize", false, "7");
|
|
13491
13870
|
editable.querySelectorAll("font[size=\"7\"]").forEach((el) => {
|
|
13492
|
-
if (!preExisting.has(el)) el.
|
|
13871
|
+
if (!preExisting.has(el)) /** @type {HTMLElement} */ el.dataset.anTmp = marker;
|
|
13493
13872
|
});
|
|
13494
13873
|
editable.querySelectorAll(`[data-an-tmp="${marker}"]`).forEach((el) => {
|
|
13495
13874
|
const span = document.createElement("span");
|
|
13496
13875
|
span.style.fontSize = fmt.fontSize;
|
|
13497
13876
|
el.parentNode.insertBefore(span, el);
|
|
13498
13877
|
while (el.firstChild) span.appendChild(el.firstChild);
|
|
13499
|
-
el.
|
|
13878
|
+
el.remove();
|
|
13500
13879
|
});
|
|
13501
13880
|
}
|
|
13502
13881
|
this.context.invoke("editor.afterCommand");
|
|
@@ -13504,10 +13883,10 @@
|
|
|
13504
13883
|
/** Strip all inline formatting from the saved selection. */
|
|
13505
13884
|
removeFormat() {
|
|
13506
13885
|
if (!this._savedRange) return;
|
|
13507
|
-
const editable = this.context.layoutInfo
|
|
13886
|
+
const editable = this.context.layoutInfo?.editable;
|
|
13508
13887
|
if (!editable) return;
|
|
13509
13888
|
editable.focus();
|
|
13510
|
-
const sel =
|
|
13889
|
+
const sel = globalThis.getSelection();
|
|
13511
13890
|
sel.removeAllRanges();
|
|
13512
13891
|
sel.addRange(this._savedRange.cloneRange());
|
|
13513
13892
|
document.execCommand("removeFormat");
|
|
@@ -13519,7 +13898,7 @@
|
|
|
13519
13898
|
while (el = iter.nextNode()) {
|
|
13520
13899
|
if (!editable.contains(el) || el === editable) continue;
|
|
13521
13900
|
try {
|
|
13522
|
-
if (range.intersectsNode(el)) el.removeAttribute("style");
|
|
13901
|
+
if (range.intersectsNode(el)) /** @type {Element} */ el.removeAttribute("style");
|
|
13523
13902
|
} catch {}
|
|
13524
13903
|
}
|
|
13525
13904
|
this.context.invoke("editor.afterCommand");
|
|
@@ -13621,14 +14000,14 @@
|
|
|
13621
14000
|
destroy() {
|
|
13622
14001
|
this._disposers.forEach((d) => d());
|
|
13623
14002
|
this._disposers = [];
|
|
13624
|
-
|
|
14003
|
+
this._dialog?.remove();
|
|
13625
14004
|
this._dialog = null;
|
|
13626
14005
|
}
|
|
13627
14006
|
show() {
|
|
13628
14007
|
if (this._dialog) {
|
|
13629
14008
|
this._dialog.style.display = "flex";
|
|
13630
14009
|
this._removeTrap = trapFocus(this._dialog, () => this._close());
|
|
13631
|
-
setTimeout(() => this._closeBtn
|
|
14010
|
+
setTimeout(() => this._closeBtn?.focus(), 50);
|
|
13632
14011
|
}
|
|
13633
14012
|
}
|
|
13634
14013
|
_close() {
|
|
@@ -13734,7 +14113,7 @@
|
|
|
13734
14113
|
this._clearHighlights();
|
|
13735
14114
|
this._disposers.forEach((d) => d());
|
|
13736
14115
|
this._disposers = [];
|
|
13737
|
-
if (this._dialog && this._dialog.parentNode) this._dialog.
|
|
14116
|
+
if (this._dialog && this._dialog.parentNode) this._dialog.remove();
|
|
13738
14117
|
this._dialog = null;
|
|
13739
14118
|
}
|
|
13740
14119
|
/**
|
|
@@ -13781,8 +14160,8 @@
|
|
|
13781
14160
|
const replaceActions = this._dialog.querySelector(".an-fr-replace-actions");
|
|
13782
14161
|
const title = this._dialog.querySelector(".an-dialog-title");
|
|
13783
14162
|
const isReplace = this._mode === "replace";
|
|
13784
|
-
if (replaceRow) replaceRow.style.display = isReplace ? "" : "none";
|
|
13785
|
-
if (replaceActions) replaceActions.style.display = isReplace ? "" : "none";
|
|
14163
|
+
if (replaceRow) /** @type {HTMLElement} */ replaceRow.style.display = isReplace ? "" : "none";
|
|
14164
|
+
if (replaceActions) /** @type {HTMLElement} */ replaceActions.style.display = isReplace ? "" : "none";
|
|
13786
14165
|
if (title) title.textContent = isReplace ? this.context.locale.findReplace.findReplaceTitle : this.context.locale.findReplace.findTitle;
|
|
13787
14166
|
}
|
|
13788
14167
|
_buildDialog() {
|
|
@@ -13793,106 +14172,123 @@
|
|
|
13793
14172
|
"aria-modal": "true",
|
|
13794
14173
|
"aria-label": L.findReplaceTitle
|
|
13795
14174
|
});
|
|
13796
|
-
const box = createElement("div", { class: "an-dialog-box" });
|
|
13797
|
-
const
|
|
14175
|
+
const box = createElement("div", { class: "an-dialog-box an-fr-box" });
|
|
14176
|
+
const header = createElement("div", { class: "an-fr-header" });
|
|
14177
|
+
const titleGroup = createElement("div", { class: "an-dialog-title-group" });
|
|
14178
|
+
const iconEl = createElement("span", { class: "an-dialog-icon an-dialog-icon--sm" });
|
|
14179
|
+
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
14180
|
const title = createElement("h3", { class: "an-dialog-title" });
|
|
13799
14181
|
title.textContent = L.findTitle;
|
|
14182
|
+
titleGroup.append(iconEl, title);
|
|
13800
14183
|
const closeBtn = createElement("button", {
|
|
13801
14184
|
type: "button",
|
|
13802
14185
|
class: "an-icon-close",
|
|
13803
|
-
|
|
14186
|
+
title: L.close,
|
|
14187
|
+
"aria-label": L.close
|
|
13804
14188
|
});
|
|
13805
|
-
closeBtn.
|
|
14189
|
+
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
14190
|
this._closeBtn = closeBtn;
|
|
13807
|
-
|
|
13808
|
-
box.appendChild(
|
|
13809
|
-
const
|
|
14191
|
+
header.append(titleGroup, closeBtn);
|
|
14192
|
+
box.appendChild(header);
|
|
14193
|
+
const searchBar = createElement("div", { class: "an-fr-search-bar" });
|
|
13810
14194
|
const findInput = createElement("input", {
|
|
13811
14195
|
type: "text",
|
|
13812
|
-
class: "an-input",
|
|
14196
|
+
class: "an-input an-fr-input",
|
|
13813
14197
|
placeholder: L.findPlaceholder,
|
|
13814
14198
|
"aria-label": L.searchAriaLabel
|
|
13815
14199
|
});
|
|
13816
14200
|
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
14201
|
const caseCheckbox = createElement("input", {
|
|
13822
14202
|
type: "checkbox",
|
|
13823
|
-
|
|
14203
|
+
style: "display:none",
|
|
14204
|
+
"aria-hidden": "true"
|
|
13824
14205
|
});
|
|
13825
14206
|
this._caseCheckbox = caseCheckbox;
|
|
13826
|
-
|
|
13827
|
-
|
|
13828
|
-
|
|
13829
|
-
|
|
13830
|
-
|
|
13831
|
-
|
|
14207
|
+
const caseBtn = createElement("button", {
|
|
14208
|
+
type: "button",
|
|
14209
|
+
class: "an-fr-icon-btn",
|
|
14210
|
+
title: "Case sensitive",
|
|
14211
|
+
"aria-label": "Case sensitive"
|
|
14212
|
+
});
|
|
14213
|
+
caseBtn.textContent = "Aa";
|
|
13832
14214
|
const prevBtn = createElement("button", {
|
|
13833
14215
|
type: "button",
|
|
13834
|
-
class: "an-btn"
|
|
14216
|
+
class: "an-fr-icon-btn",
|
|
14217
|
+
title: "Previous (Shift+Enter)",
|
|
14218
|
+
"aria-label": "Previous"
|
|
13835
14219
|
});
|
|
13836
|
-
prevBtn.
|
|
14220
|
+
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
14221
|
const nextBtn = createElement("button", {
|
|
13838
14222
|
type: "button",
|
|
13839
|
-
class: "an-
|
|
14223
|
+
class: "an-fr-icon-btn",
|
|
14224
|
+
title: "Next (Enter)",
|
|
14225
|
+
"aria-label": "Next"
|
|
13840
14226
|
});
|
|
13841
|
-
nextBtn.
|
|
13842
|
-
|
|
13843
|
-
|
|
14227
|
+
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>`;
|
|
14228
|
+
const counter = createElement("span", { class: "an-fr-counter" });
|
|
14229
|
+
this._counterEl = counter;
|
|
14230
|
+
searchBar.append(findInput, caseCheckbox, caseBtn, prevBtn, nextBtn, counter);
|
|
14231
|
+
box.appendChild(searchBar);
|
|
13844
14232
|
const replaceRow = createElement("div", { class: "an-fr-replace-row" });
|
|
13845
14233
|
replaceRow.style.display = "none";
|
|
13846
14234
|
const replaceInput = createElement("input", {
|
|
13847
14235
|
type: "text",
|
|
13848
|
-
class: "an-input",
|
|
14236
|
+
class: "an-input an-fr-input",
|
|
13849
14237
|
placeholder: L.replacePlaceholder,
|
|
13850
14238
|
"aria-label": L.replaceAriaLabel
|
|
13851
14239
|
});
|
|
13852
14240
|
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
14241
|
const replaceBtn = createElement("button", {
|
|
13858
14242
|
type: "button",
|
|
13859
|
-
class: "an-btn"
|
|
14243
|
+
class: "an-btn an-fr-replace-btn"
|
|
13860
14244
|
});
|
|
13861
14245
|
replaceBtn.textContent = L.replaceBtn;
|
|
13862
14246
|
const replaceAllBtn = createElement("button", {
|
|
13863
14247
|
type: "button",
|
|
13864
|
-
class: "an-btn an-btn-primary"
|
|
14248
|
+
class: "an-btn an-btn-primary an-fr-replace-btn"
|
|
13865
14249
|
});
|
|
13866
14250
|
replaceAllBtn.textContent = L.replaceAllBtn;
|
|
13867
|
-
|
|
14251
|
+
replaceRow.append(replaceInput, replaceBtn, replaceAllBtn);
|
|
14252
|
+
box.appendChild(replaceRow);
|
|
14253
|
+
const replaceActions = createElement("div", { class: "an-fr-replace-actions" });
|
|
14254
|
+
replaceActions.style.display = "none";
|
|
13868
14255
|
box.appendChild(replaceActions);
|
|
13869
14256
|
overlay.appendChild(box);
|
|
14257
|
+
makeDraggable(header, box);
|
|
13870
14258
|
const d1 = on(closeBtn, "click", () => this._close());
|
|
13871
14259
|
const d2 = on(overlay, "click", (e) => {
|
|
13872
14260
|
if (e.target === overlay) this._close();
|
|
13873
14261
|
});
|
|
13874
14262
|
const d3 = on(findInput, "input", () => this._onSearch());
|
|
13875
|
-
const d4 = on(
|
|
14263
|
+
const d4 = on(caseBtn, "click", () => {
|
|
14264
|
+
this._caseSensitive = !this._caseSensitive;
|
|
14265
|
+
caseCheckbox.checked = this._caseSensitive;
|
|
14266
|
+
caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
|
|
14267
|
+
this._onSearch();
|
|
14268
|
+
});
|
|
14269
|
+
const d5 = on(caseCheckbox, "change", () => {
|
|
13876
14270
|
this._caseSensitive = caseCheckbox.checked;
|
|
14271
|
+
caseBtn.classList.toggle("an-fr-icon-btn--active", this._caseSensitive);
|
|
13877
14272
|
this._onSearch();
|
|
13878
14273
|
});
|
|
13879
|
-
const
|
|
13880
|
-
const
|
|
13881
|
-
const
|
|
13882
|
-
const
|
|
13883
|
-
const
|
|
13884
|
-
|
|
14274
|
+
const d6 = on(nextBtn, "click", () => this._next());
|
|
14275
|
+
const d7 = on(prevBtn, "click", () => this._prev());
|
|
14276
|
+
const d8 = on(replaceBtn, "click", () => this._replace());
|
|
14277
|
+
const d9 = on(replaceAllBtn, "click", () => this._replaceAll());
|
|
14278
|
+
const d10 = on(findInput, "keydown", (e) => {
|
|
14279
|
+
const ke = e;
|
|
14280
|
+
if (ke.key === "Enter") {
|
|
13885
14281
|
e.preventDefault();
|
|
13886
|
-
|
|
14282
|
+
ke.shiftKey ? this._prev() : this._next();
|
|
13887
14283
|
}
|
|
13888
14284
|
});
|
|
13889
|
-
const
|
|
14285
|
+
const d11 = on(replaceInput, "keydown", (e) => {
|
|
13890
14286
|
if (e.key === "Enter") {
|
|
13891
14287
|
e.preventDefault();
|
|
13892
14288
|
this._replace();
|
|
13893
14289
|
}
|
|
13894
14290
|
});
|
|
13895
|
-
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10);
|
|
14291
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11);
|
|
13896
14292
|
return overlay;
|
|
13897
14293
|
}
|
|
13898
14294
|
_onSearch() {
|
|
@@ -13960,15 +14356,19 @@
|
|
|
13960
14356
|
const re = this._queryRegex;
|
|
13961
14357
|
const MAX_RESULTS = 500;
|
|
13962
14358
|
const walker = document.createTreeWalker(root, 4);
|
|
13963
|
-
let node;
|
|
13964
|
-
while (
|
|
14359
|
+
let node = walker.nextNode();
|
|
14360
|
+
while (node && results.length < MAX_RESULTS) {
|
|
13965
14361
|
re.lastIndex = 0;
|
|
13966
14362
|
let m;
|
|
13967
|
-
while ((m = re.exec(
|
|
14363
|
+
while ((m = re.exec(
|
|
14364
|
+
/** @type {Text} */
|
|
14365
|
+
node.textContent
|
|
14366
|
+
)) !== null && results.length < MAX_RESULTS) results.push({
|
|
13968
14367
|
node,
|
|
13969
14368
|
start: m.index,
|
|
13970
14369
|
end: m.index + m[0].length
|
|
13971
14370
|
});
|
|
14371
|
+
node = walker.nextNode();
|
|
13972
14372
|
}
|
|
13973
14373
|
return results;
|
|
13974
14374
|
}
|
|
@@ -14003,7 +14403,7 @@
|
|
|
14003
14403
|
const parent = match.mark.parentNode;
|
|
14004
14404
|
const textNode = document.createTextNode(replacement);
|
|
14005
14405
|
parent.insertBefore(textNode, match.mark);
|
|
14006
|
-
|
|
14406
|
+
match.mark.remove();
|
|
14007
14407
|
parent.normalize();
|
|
14008
14408
|
this.context.invoke("editor.afterCommand");
|
|
14009
14409
|
const savedIndex = this._currentIndex;
|
|
@@ -14021,7 +14421,7 @@
|
|
|
14021
14421
|
if (!mark || !mark.parentNode) return;
|
|
14022
14422
|
const textNode = document.createTextNode(replacement);
|
|
14023
14423
|
mark.parentNode.insertBefore(textNode, mark);
|
|
14024
|
-
mark.
|
|
14424
|
+
mark.remove();
|
|
14025
14425
|
});
|
|
14026
14426
|
if (this.context.layoutInfo.editable) this.context.layoutInfo.editable.normalize();
|
|
14027
14427
|
this._matches = [];
|
|
@@ -14040,7 +14440,7 @@
|
|
|
14040
14440
|
const parent = mark.parentNode;
|
|
14041
14441
|
if (!parent) return;
|
|
14042
14442
|
while (mark.firstChild) parent.insertBefore(mark.firstChild, mark);
|
|
14043
|
-
|
|
14443
|
+
mark.remove();
|
|
14044
14444
|
});
|
|
14045
14445
|
editable.normalize();
|
|
14046
14446
|
this._matches = [];
|
|
@@ -14091,7 +14491,7 @@
|
|
|
14091
14491
|
resolve(null);
|
|
14092
14492
|
}
|
|
14093
14493
|
};
|
|
14094
|
-
if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(
|
|
14494
|
+
if (img.src.startsWith("data:") || img.src.startsWith("blob:") || img.src.startsWith(globalThis.location.origin)) {
|
|
14095
14495
|
tryDraw(img);
|
|
14096
14496
|
return;
|
|
14097
14497
|
}
|
|
@@ -14253,9 +14653,10 @@
|
|
|
14253
14653
|
}), on(h, "touchstart", (e) => {
|
|
14254
14654
|
e.preventDefault();
|
|
14255
14655
|
e.stopPropagation();
|
|
14656
|
+
const te = e;
|
|
14256
14657
|
this._startHandleDrag({
|
|
14257
|
-
clientX:
|
|
14258
|
-
clientY:
|
|
14658
|
+
clientX: te.touches[0].clientX,
|
|
14659
|
+
clientY: te.touches[0].clientY
|
|
14259
14660
|
}, id);
|
|
14260
14661
|
}, { passive: false }));
|
|
14261
14662
|
this._handles[id] = h;
|
|
@@ -14272,9 +14673,10 @@
|
|
|
14272
14673
|
if (e.target !== cropBox && e.target !== grid) return;
|
|
14273
14674
|
e.preventDefault();
|
|
14274
14675
|
e.stopPropagation();
|
|
14676
|
+
const te2 = e;
|
|
14275
14677
|
this._startBoxMove({
|
|
14276
|
-
clientX:
|
|
14277
|
-
clientY:
|
|
14678
|
+
clientX: te2.touches[0].clientX,
|
|
14679
|
+
clientY: te2.touches[0].clientY
|
|
14278
14680
|
});
|
|
14279
14681
|
}, { passive: false }));
|
|
14280
14682
|
const infoEl = document.createElement("div");
|
|
@@ -14347,8 +14749,8 @@
|
|
|
14347
14749
|
this._cropBox.style.top = `${y}px`;
|
|
14348
14750
|
this._cropBox.style.width = `${w}px`;
|
|
14349
14751
|
this._cropBox.style.height = `${h}px`;
|
|
14350
|
-
const vw =
|
|
14351
|
-
const vh =
|
|
14752
|
+
const vw = globalThis.innerWidth;
|
|
14753
|
+
const vh = globalThis.innerHeight;
|
|
14352
14754
|
this._scrim.style.clipPath = [
|
|
14353
14755
|
`polygon(`,
|
|
14354
14756
|
`0 0, ${vw}px 0, ${vw}px ${vh}px, 0 ${vh}px, 0 0,`,
|
|
@@ -14372,7 +14774,7 @@
|
|
|
14372
14774
|
}
|
|
14373
14775
|
const margin = 8;
|
|
14374
14776
|
let tbTop = y + h + margin;
|
|
14375
|
-
if (tbTop + 40 >
|
|
14777
|
+
if (tbTop + 40 > globalThis.innerHeight - margin) tbTop = y - 40 - margin;
|
|
14376
14778
|
this._toolbar.style.left = `${x}px`;
|
|
14377
14779
|
this._toolbar.style.top = `${tbTop}px`;
|
|
14378
14780
|
}
|
|
@@ -14436,9 +14838,10 @@
|
|
|
14436
14838
|
_attachDocDrag(onMove) {
|
|
14437
14839
|
const onTouchMove = (e) => {
|
|
14438
14840
|
e.preventDefault();
|
|
14841
|
+
const te3 = e;
|
|
14439
14842
|
onMove({
|
|
14440
|
-
clientX:
|
|
14441
|
-
clientY:
|
|
14843
|
+
clientX: te3.touches[0].clientX,
|
|
14844
|
+
clientY: te3.touches[0].clientY
|
|
14442
14845
|
});
|
|
14443
14846
|
};
|
|
14444
14847
|
const cleanup = () => {
|
|
@@ -14498,7 +14901,7 @@
|
|
|
14498
14901
|
this._close(false);
|
|
14499
14902
|
return;
|
|
14500
14903
|
}
|
|
14501
|
-
const fmt =
|
|
14904
|
+
const fmt = /^data:image\/(jpe?g)/i.exec(img.src) ? "image/jpeg" : "image/png";
|
|
14502
14905
|
const quality = fmt === "image/jpeg" ? .92 : void 0;
|
|
14503
14906
|
const newSrc = canvas.toDataURL(fmt, quality);
|
|
14504
14907
|
this._close(false);
|
|
@@ -14538,7 +14941,7 @@
|
|
|
14538
14941
|
banner.textContent = msg;
|
|
14539
14942
|
document.body.appendChild(banner);
|
|
14540
14943
|
setTimeout(() => {
|
|
14541
|
-
|
|
14944
|
+
banner.remove();
|
|
14542
14945
|
}, 4e3);
|
|
14543
14946
|
}
|
|
14544
14947
|
/**
|
|
@@ -14553,7 +14956,7 @@
|
|
|
14553
14956
|
this._cropBox,
|
|
14554
14957
|
this._toolbar
|
|
14555
14958
|
].forEach((el) => {
|
|
14556
|
-
|
|
14959
|
+
el?.remove();
|
|
14557
14960
|
});
|
|
14558
14961
|
this._scrim = null;
|
|
14559
14962
|
this._cropBox = null;
|
|
@@ -14574,7 +14977,7 @@
|
|
|
14574
14977
|
*
|
|
14575
14978
|
* Activated when both `autoSave` and `autoSaveRestore` options are true.
|
|
14576
14979
|
* On initialize it checks localStorage for a draft that is within the
|
|
14577
|
-
* `autoSaveRestoreTimeout` day
|
|
14980
|
+
* `autoSaveRestoreTimeout` day globalThis. If one is found a dismissible banner
|
|
14578
14981
|
* is prepended to the editor container.
|
|
14579
14982
|
*/
|
|
14580
14983
|
var AutoSaveRestore = class {
|
|
@@ -14660,7 +15063,7 @@
|
|
|
14660
15063
|
this._removeBanner();
|
|
14661
15064
|
}
|
|
14662
15065
|
_removeBanner() {
|
|
14663
|
-
|
|
15066
|
+
this._banner?.remove();
|
|
14664
15067
|
this._banner = null;
|
|
14665
15068
|
}
|
|
14666
15069
|
};
|
|
@@ -14723,8 +15126,8 @@
|
|
|
14723
15126
|
* @returns {{ text: string, range: Range, lineNode: Node } | null}
|
|
14724
15127
|
*/
|
|
14725
15128
|
_getLineContext() {
|
|
14726
|
-
const sel =
|
|
14727
|
-
if (!sel
|
|
15129
|
+
const sel = globalThis.getSelection();
|
|
15130
|
+
if (!sel?.rangeCount) return null;
|
|
14728
15131
|
const range = sel.getRangeAt(0);
|
|
14729
15132
|
if (!range.collapsed) return null;
|
|
14730
15133
|
const editable = this.context.layoutInfo.editable;
|
|
@@ -14743,7 +15146,7 @@
|
|
|
14743
15146
|
}
|
|
14744
15147
|
_isBlock(node) {
|
|
14745
15148
|
if (node.nodeType !== Node.ELEMENT_NODE) return false;
|
|
14746
|
-
const display =
|
|
15149
|
+
const display = globalThis.getComputedStyle(node).display;
|
|
14747
15150
|
return display === "block" || display === "list-item" || display === "table-cell";
|
|
14748
15151
|
}
|
|
14749
15152
|
/** Applies block rule on Space key. Returns true if a rule fired. */
|
|
@@ -14774,7 +15177,7 @@
|
|
|
14774
15177
|
}
|
|
14775
15178
|
];
|
|
14776
15179
|
for (const { re, handler } of blockPatterns) {
|
|
14777
|
-
const m =
|
|
15180
|
+
const m = re.exec(text);
|
|
14778
15181
|
if (m) {
|
|
14779
15182
|
handler(m);
|
|
14780
15183
|
return true;
|
|
@@ -14798,8 +15201,8 @@
|
|
|
14798
15201
|
return false;
|
|
14799
15202
|
}
|
|
14800
15203
|
_selectLineAndDelete() {
|
|
14801
|
-
const sel =
|
|
14802
|
-
if (!sel
|
|
15204
|
+
const sel = globalThis.getSelection();
|
|
15205
|
+
if (!sel?.rangeCount) return;
|
|
14803
15206
|
const range = sel.getRangeAt(0);
|
|
14804
15207
|
const startRange = document.createRange();
|
|
14805
15208
|
startRange.setStart(range.startContainer.parentNode || range.startContainer, 0);
|
|
@@ -14837,8 +15240,8 @@
|
|
|
14837
15240
|
this.context.triggerEvent("change", this.context.getHTML());
|
|
14838
15241
|
}
|
|
14839
15242
|
_onInput() {
|
|
14840
|
-
const sel =
|
|
14841
|
-
if (!sel
|
|
15243
|
+
const sel = globalThis.getSelection();
|
|
15244
|
+
if (!sel?.rangeCount) return;
|
|
14842
15245
|
const range = sel.getRangeAt(0);
|
|
14843
15246
|
if (!range.collapsed) return;
|
|
14844
15247
|
if (!this.context.layoutInfo.editable.contains(range.startContainer)) return;
|
|
@@ -14866,7 +15269,7 @@
|
|
|
14866
15269
|
];
|
|
14867
15270
|
const upToCursor = text.slice(0, offset);
|
|
14868
15271
|
for (const { re, tag } of inlineRules) {
|
|
14869
|
-
const m =
|
|
15272
|
+
const m = re.exec(upToCursor);
|
|
14870
15273
|
if (!m) continue;
|
|
14871
15274
|
const matchStart = upToCursor.length - m[0].length;
|
|
14872
15275
|
const matchEnd = offset;
|
|
@@ -14877,11 +15280,8 @@
|
|
|
14877
15280
|
el.textContent = innerText;
|
|
14878
15281
|
const beforeNode = document.createTextNode(before);
|
|
14879
15282
|
const afterNode = document.createTextNode("" + after);
|
|
14880
|
-
|
|
14881
|
-
|
|
14882
|
-
parent.insertBefore(el, node);
|
|
14883
|
-
parent.insertBefore(afterNode, node);
|
|
14884
|
-
parent.removeChild(node);
|
|
15283
|
+
/** @type {ChildNode} */ node.before(beforeNode, el, afterNode);
|
|
15284
|
+
/** @type {ChildNode} */ node.remove();
|
|
14885
15285
|
const newRange = document.createRange();
|
|
14886
15286
|
newRange.setStart(afterNode, 1);
|
|
14887
15287
|
newRange.collapse(true);
|
|
@@ -14952,12 +15352,12 @@
|
|
|
14952
15352
|
strikethrough: (ctx) => ctx.invoke("editor.strikethrough"),
|
|
14953
15353
|
link: (ctx) => ctx.invoke("linkDialog.show"),
|
|
14954
15354
|
removeFormat: (ctx) => {
|
|
14955
|
-
const editable = ctx.layoutInfo
|
|
15355
|
+
const editable = ctx.layoutInfo?.editable;
|
|
14956
15356
|
if (!editable) return;
|
|
14957
15357
|
editable.focus();
|
|
14958
15358
|
document.execCommand("removeFormat");
|
|
14959
|
-
const sel =
|
|
14960
|
-
if (sel
|
|
15359
|
+
const sel = globalThis.getSelection();
|
|
15360
|
+
if (sel?.rangeCount > 0 && !sel.getRangeAt(0).collapsed) {
|
|
14961
15361
|
const range = sel.getRangeAt(0);
|
|
14962
15362
|
const ancestor = range.commonAncestorContainer;
|
|
14963
15363
|
const root = ancestor.nodeType === 1 ? ancestor : ancestor.parentElement;
|
|
@@ -15015,13 +15415,15 @@
|
|
|
15015
15415
|
const d6 = this.context.on("contextMenu:hide", () => {
|
|
15016
15416
|
this._contextMenuOpen = false;
|
|
15017
15417
|
});
|
|
15018
|
-
|
|
15418
|
+
const d7 = on(globalThis, "scroll", () => this._hide(), { passive: true });
|
|
15419
|
+
const d8 = on(globalThis, "resize", () => this._hide(), { passive: true });
|
|
15420
|
+
this._disposers.push(d1, d2, d3, d4, d5, d6, d7, d8);
|
|
15019
15421
|
return this;
|
|
15020
15422
|
}
|
|
15021
15423
|
destroy() {
|
|
15022
|
-
|
|
15424
|
+
this._el?.remove();
|
|
15023
15425
|
this._el = null;
|
|
15024
|
-
|
|
15426
|
+
this._picker?.remove();
|
|
15025
15427
|
this._picker = null;
|
|
15026
15428
|
this._disposers.forEach((d) => d());
|
|
15027
15429
|
this._disposers = [];
|
|
@@ -15129,20 +15531,22 @@
|
|
|
15129
15531
|
picker.appendChild(customRow);
|
|
15130
15532
|
document.body.appendChild(picker);
|
|
15131
15533
|
this._picker = picker;
|
|
15132
|
-
|
|
15133
|
-
|
|
15134
|
-
|
|
15534
|
+
const pickerAny = picker;
|
|
15535
|
+
pickerAny._paletteEl = palette;
|
|
15536
|
+
pickerAny._noColorBtn = noColorBtn;
|
|
15537
|
+
pickerAny._colorInput = colorInput;
|
|
15135
15538
|
}
|
|
15136
15539
|
_openColorPicker(type, anchorBtn) {
|
|
15137
|
-
const sel =
|
|
15138
|
-
if (sel
|
|
15540
|
+
const sel = globalThis.getSelection();
|
|
15541
|
+
if (sel?.rangeCount > 0) this._savedRange = sel.getRangeAt(0).cloneRange();
|
|
15139
15542
|
this._pickerType = type;
|
|
15140
|
-
const
|
|
15141
|
-
const
|
|
15543
|
+
const pickerAny = this._picker;
|
|
15544
|
+
const palette = pickerAny._paletteEl;
|
|
15545
|
+
const noColorBtn = pickerAny._noColorBtn;
|
|
15142
15546
|
if (type === "hiliteColor") {
|
|
15143
15547
|
if (!palette.contains(noColorBtn)) palette.appendChild(noColorBtn);
|
|
15144
|
-
} else if (palette.contains(noColorBtn))
|
|
15145
|
-
this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15548
|
+
} else if (palette.contains(noColorBtn)) noColorBtn.remove();
|
|
15549
|
+
/** @type {any} */ this._picker._colorInput.value = type === "foreColor" ? "#000000" : "#ffff00";
|
|
15146
15550
|
this._picker.style.display = "block";
|
|
15147
15551
|
const pw = this._picker.offsetWidth;
|
|
15148
15552
|
const ph = this._picker.offsetHeight;
|
|
@@ -15150,7 +15554,7 @@
|
|
|
15150
15554
|
let top = toolbarRect.top - ph - 6;
|
|
15151
15555
|
if (top < 8) top = toolbarRect.bottom + 6;
|
|
15152
15556
|
let left = anchorBtn.getBoundingClientRect().left;
|
|
15153
|
-
left = Math.max(8, Math.min(left,
|
|
15557
|
+
left = Math.max(8, Math.min(left, globalThis.innerWidth - pw - 8));
|
|
15154
15558
|
this._picker.style.left = `${left}px`;
|
|
15155
15559
|
this._picker.style.top = `${top}px`;
|
|
15156
15560
|
}
|
|
@@ -15160,10 +15564,10 @@
|
|
|
15160
15564
|
}
|
|
15161
15565
|
/** Restore the saved selection, apply execCommand, update the color strip, then close the picker. */
|
|
15162
15566
|
_applyColor(type, color) {
|
|
15163
|
-
const editable = this.context.layoutInfo
|
|
15567
|
+
const editable = this.context.layoutInfo?.editable;
|
|
15164
15568
|
if (!editable || !this._savedRange) return;
|
|
15165
15569
|
editable.focus();
|
|
15166
|
-
const sel =
|
|
15570
|
+
const sel = globalThis.getSelection();
|
|
15167
15571
|
sel.removeAllRanges();
|
|
15168
15572
|
try {
|
|
15169
15573
|
sel.addRange(this._savedRange.cloneRange());
|
|
@@ -15174,9 +15578,8 @@
|
|
|
15174
15578
|
if (!document.execCommand(cmd, false, color) && cmd === "hiliteColor") document.execCommand("backColor", false, color);
|
|
15175
15579
|
this.context.invoke("editor.afterCommand");
|
|
15176
15580
|
const name = type === "hiliteColor" ? "hiliteColor" : "foreColor";
|
|
15177
|
-
const
|
|
15178
|
-
|
|
15179
|
-
if (strip) strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15581
|
+
const strip = (this._el?.querySelector(`[data-name="${name}"]`))?.querySelector(".an-bubble-color-strip");
|
|
15582
|
+
if (strip) /** @type {HTMLElement} */ strip.style.background = color === "transparent" ? "transparent" : color;
|
|
15180
15583
|
this._closeColorPicker();
|
|
15181
15584
|
this._syncActive();
|
|
15182
15585
|
}
|
|
@@ -15190,8 +15593,16 @@
|
|
|
15190
15593
|
const gap = 8;
|
|
15191
15594
|
let left = rect.left + rect.width / 2 - bw / 2;
|
|
15192
15595
|
let top = rect.top - bh - gap;
|
|
15193
|
-
left = Math.max(8, Math.min(left,
|
|
15596
|
+
left = Math.max(8, Math.min(left, globalThis.innerWidth - bw - 8));
|
|
15194
15597
|
if (top < 8) top = rect.bottom + gap;
|
|
15598
|
+
const tableTooltipEl = document.querySelector(".an-table-tooltip");
|
|
15599
|
+
if (tableTooltipEl && tableTooltipEl.style.display !== "none") {
|
|
15600
|
+
const ttRect = tableTooltipEl.getBoundingClientRect();
|
|
15601
|
+
if (top < ttRect.bottom + gap && top + bh > ttRect.top - gap) {
|
|
15602
|
+
top = rect.bottom + gap;
|
|
15603
|
+
if (top + bh > globalThis.innerHeight - 8) top = ttRect.bottom + gap;
|
|
15604
|
+
}
|
|
15605
|
+
}
|
|
15195
15606
|
el.style.top = `${top}px`;
|
|
15196
15607
|
el.style.left = `${left}px`;
|
|
15197
15608
|
el.style.visibility = "";
|
|
@@ -15215,20 +15626,18 @@
|
|
|
15215
15626
|
/** Read the current selection's color and update the color-strip indicators. */
|
|
15216
15627
|
_syncColorStrips() {
|
|
15217
15628
|
if (!this._el) return;
|
|
15218
|
-
const sel =
|
|
15629
|
+
const sel = globalThis.getSelection();
|
|
15219
15630
|
if (!sel || !sel.rangeCount) return;
|
|
15220
15631
|
let node = sel.getRangeAt(0).startContainer;
|
|
15221
15632
|
if (node.nodeType === Node.TEXT_NODE) node = node.parentElement;
|
|
15222
15633
|
if (!node) return;
|
|
15223
|
-
const cs =
|
|
15224
|
-
const
|
|
15225
|
-
|
|
15226
|
-
|
|
15227
|
-
const hiliteBtn = this._el.querySelector("[data-name=\"hiliteColor\"]");
|
|
15228
|
-
const hiliteStrip = hiliteBtn && hiliteBtn.querySelector(".an-bubble-color-strip");
|
|
15634
|
+
const cs = globalThis.getComputedStyle(node);
|
|
15635
|
+
const foreStrip = this._el.querySelector("[data-name=\"foreColor\"]")?.querySelector(".an-bubble-color-strip");
|
|
15636
|
+
if (foreStrip) /** @type {HTMLElement} */ foreStrip.style.background = cs.color || "#000000";
|
|
15637
|
+
const hiliteStrip = this._el.querySelector("[data-name=\"hiliteColor\"]")?.querySelector(".an-bubble-color-strip");
|
|
15229
15638
|
if (hiliteStrip) {
|
|
15230
15639
|
const bg = cs.backgroundColor;
|
|
15231
|
-
hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15640
|
+
/** @type {HTMLElement} */ hiliteStrip.style.background = !bg || bg === "rgba(0, 0, 0, 0)" || bg === "transparent" ? "transparent" : bg;
|
|
15232
15641
|
}
|
|
15233
15642
|
}
|
|
15234
15643
|
_onSelectionChange() {
|
|
@@ -15236,7 +15645,7 @@
|
|
|
15236
15645
|
this._rafId = requestAnimationFrame(() => {
|
|
15237
15646
|
if (this._contextMenuOpen) return;
|
|
15238
15647
|
if (this._picker && this._picker.style.display !== "none") return;
|
|
15239
|
-
const sel =
|
|
15648
|
+
const sel = globalThis.getSelection();
|
|
15240
15649
|
if (!sel || sel.isCollapsed || !sel.rangeCount) {
|
|
15241
15650
|
this._hide();
|
|
15242
15651
|
return;
|
|
@@ -15259,8 +15668,8 @@
|
|
|
15259
15668
|
}
|
|
15260
15669
|
_onMousedown(e) {
|
|
15261
15670
|
if (!this._visible) return;
|
|
15262
|
-
if (this._el
|
|
15263
|
-
if (this._picker
|
|
15671
|
+
if (this._el?.contains(e.target)) return;
|
|
15672
|
+
if (this._picker?.contains(e.target)) return;
|
|
15264
15673
|
if (this.context.layoutInfo.editable.contains(e.target)) return;
|
|
15265
15674
|
this._hide();
|
|
15266
15675
|
}
|
|
@@ -15345,7 +15754,7 @@
|
|
|
15345
15754
|
}
|
|
15346
15755
|
destroy() {
|
|
15347
15756
|
clearTimeout(this._debounceTimer);
|
|
15348
|
-
|
|
15757
|
+
this._dropdown?.remove();
|
|
15349
15758
|
this._dropdown = null;
|
|
15350
15759
|
this._disposers.forEach((d) => d());
|
|
15351
15760
|
this._disposers = [];
|
|
@@ -15356,11 +15765,11 @@
|
|
|
15356
15765
|
el.setAttribute("role", "listbox");
|
|
15357
15766
|
el.addEventListener("mousedown", (e) => e.preventDefault());
|
|
15358
15767
|
el.addEventListener("click", (e) => {
|
|
15359
|
-
const item = e.target
|
|
15768
|
+
const item = e.target?.closest(".an-mention-item");
|
|
15360
15769
|
if (item) this._select(+item.dataset.index);
|
|
15361
15770
|
});
|
|
15362
15771
|
el.addEventListener("mousemove", (e) => {
|
|
15363
|
-
const item = e.target
|
|
15772
|
+
const item = e.target?.closest(".an-mention-item");
|
|
15364
15773
|
if (item) this._highlightItem(+item.dataset.index);
|
|
15365
15774
|
});
|
|
15366
15775
|
document.body.appendChild(el);
|
|
@@ -15375,7 +15784,7 @@
|
|
|
15375
15784
|
const li = document.createElement("div");
|
|
15376
15785
|
li.className = "an-mention-item";
|
|
15377
15786
|
li.setAttribute("role", "option");
|
|
15378
|
-
li.dataset.index = i;
|
|
15787
|
+
li.dataset.index = String(i);
|
|
15379
15788
|
if (item.avatar) {
|
|
15380
15789
|
const img = document.createElement("img");
|
|
15381
15790
|
img.src = item.avatar;
|
|
@@ -15409,8 +15818,8 @@
|
|
|
15409
15818
|
const ddw = dd.offsetWidth;
|
|
15410
15819
|
let top = rect.bottom + 4;
|
|
15411
15820
|
let left = rect.left;
|
|
15412
|
-
if (rect.bottom + ddh + 8 >
|
|
15413
|
-
left = Math.max(8, Math.min(left,
|
|
15821
|
+
if (rect.bottom + ddh + 8 > globalThis.innerHeight) top = rect.top - ddh - 4;
|
|
15822
|
+
left = Math.max(8, Math.min(left, globalThis.innerWidth - ddw - 8));
|
|
15414
15823
|
dd.style.top = `${top}px`;
|
|
15415
15824
|
dd.style.left = `${left}px`;
|
|
15416
15825
|
dd.style.visibility = "";
|
|
@@ -15434,7 +15843,7 @@
|
|
|
15434
15843
|
* collapsed range is reliable at this point but often empty inside async callbacks.
|
|
15435
15844
|
*/
|
|
15436
15845
|
_captureCaretRect() {
|
|
15437
|
-
if (this._triggerNode
|
|
15846
|
+
if (this._triggerNode?.isConnected) try {
|
|
15438
15847
|
const r = document.createRange();
|
|
15439
15848
|
const end = Math.min(this._triggerOffset + 1, this._triggerNode.textContent.length);
|
|
15440
15849
|
r.setStart(this._triggerNode, this._triggerOffset);
|
|
@@ -15445,13 +15854,13 @@
|
|
|
15445
15854
|
return;
|
|
15446
15855
|
}
|
|
15447
15856
|
} catch (_) {}
|
|
15448
|
-
const sel =
|
|
15857
|
+
const sel = globalThis.getSelection();
|
|
15449
15858
|
if (!sel || !sel.rangeCount) return;
|
|
15450
15859
|
const rects = sel.getRangeAt(0).getClientRects();
|
|
15451
15860
|
if (rects.length > 0) this._caretRect = rects[rects.length - 1];
|
|
15452
15861
|
}
|
|
15453
15862
|
_getQueryAtCursor() {
|
|
15454
|
-
const sel =
|
|
15863
|
+
const sel = globalThis.getSelection();
|
|
15455
15864
|
if (!sel || !sel.rangeCount) return null;
|
|
15456
15865
|
const range = sel.getRangeAt(0);
|
|
15457
15866
|
if (!range.collapsed) return null;
|
|
@@ -15508,7 +15917,7 @@
|
|
|
15508
15917
|
}
|
|
15509
15918
|
_onDocClick(e) {
|
|
15510
15919
|
if (!this._open) return;
|
|
15511
|
-
if (this._dropdown
|
|
15920
|
+
if (this._dropdown?.contains(e.target)) return;
|
|
15512
15921
|
this._hideDropdown();
|
|
15513
15922
|
}
|
|
15514
15923
|
_select(index) {
|
|
@@ -15517,7 +15926,7 @@
|
|
|
15517
15926
|
if (this._triggerNode) {
|
|
15518
15927
|
const node = this._triggerNode;
|
|
15519
15928
|
node.textContent = node.textContent.slice(0, this._triggerOffset) + node.textContent.slice(this._triggerOffset + this._cfg.trigger.length + this._query.length);
|
|
15520
|
-
const sel =
|
|
15929
|
+
const sel = globalThis.getSelection();
|
|
15521
15930
|
const range = document.createRange();
|
|
15522
15931
|
range.setStart(node, this._triggerOffset);
|
|
15523
15932
|
range.collapse(true);
|
|
@@ -15625,7 +16034,7 @@
|
|
|
15625
16034
|
/**
|
|
15626
16035
|
* Registers and initialises a custom module on this instance.
|
|
15627
16036
|
* @param {string} name
|
|
15628
|
-
* @param {
|
|
16037
|
+
* @param {new (ctx: this) => any} ModuleClass
|
|
15629
16038
|
* @returns {this}
|
|
15630
16039
|
*/
|
|
15631
16040
|
registerModule(name, ModuleClass) {
|
|
@@ -15878,23 +16287,27 @@
|
|
|
15878
16287
|
a.style.display = "none";
|
|
15879
16288
|
document.body.appendChild(a);
|
|
15880
16289
|
a.click();
|
|
15881
|
-
|
|
16290
|
+
a.remove();
|
|
15882
16291
|
URL.revokeObjectURL(url);
|
|
15883
16292
|
}
|
|
15884
16293
|
/**
|
|
15885
|
-
* Opens the editor content in a new
|
|
16294
|
+
* Opens the editor content in a new globalThis and triggers the browser print dialog.
|
|
15886
16295
|
* @param {string} [title='']
|
|
15887
16296
|
*/
|
|
15888
16297
|
print(title = "") {
|
|
15889
16298
|
const content = this.getHTML();
|
|
15890
|
-
const
|
|
15891
|
-
const
|
|
15892
|
-
|
|
15893
|
-
w
|
|
15894
|
-
w
|
|
15895
|
-
|
|
16299
|
+
const markup = `<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><title>${(title || "").replace(/[<>&"']/g, (c) => `&#${c.charCodeAt(0)};`)}</title><style>body{font-family:system-ui,-apple-system,"Segoe UI",Roboto,Arial,sans-serif;font-size:14px;line-height:1.6;padding:20mm;color:#111827;}ul.an-checklist{list-style:none;padding-left:0;}ul.an-checklist li{padding-left:24px;position:relative;margin:2px 0;}ul.an-checklist li input[type="checkbox"]{position:absolute;left:0;top:3px;}code{background:#f3f4f6;border-radius:3px;padding:.1em .35em;font-family:monospace;}pre{background:#f3f4f6;padding:.75em 1em;border-radius:4px;overflow-x:auto;}table{border-collapse:collapse;}td,th{border:1px solid #d1d5db;padding:4px 8px;}</style></head><body>${content}</body></html>`;
|
|
16300
|
+
const blob = new Blob([markup], { type: "text/html" });
|
|
16301
|
+
const url = URL.createObjectURL(blob);
|
|
16302
|
+
const w = globalThis.open(url, "_blank");
|
|
16303
|
+
if (!w) {
|
|
16304
|
+
URL.revokeObjectURL(url);
|
|
16305
|
+
return;
|
|
16306
|
+
}
|
|
16307
|
+
w.addEventListener("load", () => {
|
|
15896
16308
|
w.print();
|
|
15897
|
-
|
|
16309
|
+
URL.revokeObjectURL(url);
|
|
16310
|
+
});
|
|
15898
16311
|
}
|
|
15899
16312
|
/**
|
|
15900
16313
|
* Sets whether the editor is disabled (readonly).
|
|
@@ -15932,10 +16345,12 @@
|
|
|
15932
16345
|
this._disposers.forEach((d) => d());
|
|
15933
16346
|
this._disposers = [];
|
|
15934
16347
|
const container = this.layoutInfo.container;
|
|
15935
|
-
|
|
16348
|
+
const wasDark = container?.classList.contains("an-theme-dark");
|
|
16349
|
+
if (container?.parentNode) {
|
|
15936
16350
|
this.targetEl.style.display = "";
|
|
15937
|
-
container.
|
|
16351
|
+
container.remove();
|
|
15938
16352
|
}
|
|
16353
|
+
if (wasDark && !document.querySelector(".an-container.an-theme-dark")) document.body.classList.remove("an-theme-dark");
|
|
15939
16354
|
if (typeof this.options.onDestroy === "function") this.options.onDestroy(this);
|
|
15940
16355
|
this._alive = false;
|
|
15941
16356
|
this._listeners.clear();
|
|
@@ -15944,7 +16359,8 @@
|
|
|
15944
16359
|
* Syncs editor HTML back into the original textarea/input for form submission.
|
|
15945
16360
|
*/
|
|
15946
16361
|
_syncToTarget() {
|
|
15947
|
-
if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
|
|
16362
|
+
if (this.targetEl.tagName === "TEXTAREA" || this.targetEl.tagName === "INPUT")
|
|
16363
|
+
/** @type {HTMLInputElement} */ this.targetEl.value = this.getHTML();
|
|
15948
16364
|
}
|
|
15949
16365
|
};
|
|
15950
16366
|
//#endregion
|
|
@@ -15954,7 +16370,7 @@
|
|
|
15954
16370
|
* Inspired by Summernote's env.js
|
|
15955
16371
|
*/
|
|
15956
16372
|
var userAgent = navigator.userAgent;
|
|
15957
|
-
/Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in
|
|
16373
|
+
/Chrome\//.test(userAgent), /Firefox\//.test(userAgent), /^((?!chrome|android).)*safari/i.test(userAgent), /Edg\//.test(userAgent), /Macintosh/.test(userAgent), /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent), "ontouchstart" in globalThis || navigator.maxTouchPoints, /Macintosh/.test(userAgent);
|
|
15958
16374
|
//#endregion
|
|
15959
16375
|
//#region src/js/index.js
|
|
15960
16376
|
var _originalDefaults = { ...defaultOptions };
|