modern-text 1.4.2 → 1.4.3
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/dist/index.cjs +568 -80
- package/dist/index.d.cts +82 -17
- package/dist/index.d.mts +82 -17
- package/dist/index.d.ts +82 -17
- package/dist/index.js +75 -5
- package/dist/index.mjs +569 -82
- package/package.json +9 -8
package/dist/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const modernPath2d = require('modern-path2d');
|
|
4
4
|
const modernIdoc = require('modern-idoc');
|
|
5
5
|
const modernFont = require('modern-font');
|
|
6
|
+
const diff = require('diff');
|
|
6
7
|
|
|
7
8
|
function parseColor(ctx, source, box) {
|
|
8
9
|
if (typeof source === "string" && source.startsWith("linear-gradient")) {
|
|
@@ -577,7 +578,7 @@ class Measurer {
|
|
|
577
578
|
"paddingRight",
|
|
578
579
|
"paddingBottom"
|
|
579
580
|
]);
|
|
580
|
-
|
|
581
|
+
_toDOMStyle(style) {
|
|
581
582
|
const _style = {};
|
|
582
583
|
for (const key in style) {
|
|
583
584
|
const value = style[key];
|
|
@@ -599,8 +600,7 @@ class Measurer {
|
|
|
599
600
|
* </ul>
|
|
600
601
|
* </section>
|
|
601
602
|
*/
|
|
602
|
-
|
|
603
|
-
const documentFragment = document.createDocumentFragment();
|
|
603
|
+
createDOM(paragraphs, rootStyle) {
|
|
604
604
|
const dom = document.createElement("section");
|
|
605
605
|
const style = { ...rootStyle };
|
|
606
606
|
const isHorizontal = rootStyle.writingMode.includes("horizontal");
|
|
@@ -630,7 +630,7 @@ class Measurer {
|
|
|
630
630
|
}
|
|
631
631
|
const isFlex = Boolean(style.justifyContent || style.alignItems);
|
|
632
632
|
Object.assign(dom.style, {
|
|
633
|
-
...this.
|
|
633
|
+
...this._toDOMStyle({
|
|
634
634
|
...style,
|
|
635
635
|
boxSizing: style.boxSizing ?? "border-box",
|
|
636
636
|
display: style.display ?? (isFlex ? "inline-flex" : void 0),
|
|
@@ -638,9 +638,7 @@ class Measurer {
|
|
|
638
638
|
height: style.height ?? "max-content"
|
|
639
639
|
}),
|
|
640
640
|
whiteSpace: "pre-wrap",
|
|
641
|
-
wordBreak: "break-all"
|
|
642
|
-
position: "fixed",
|
|
643
|
-
visibility: "hidden"
|
|
641
|
+
wordBreak: "break-all"
|
|
644
642
|
});
|
|
645
643
|
const ul = document.createElement("ul");
|
|
646
644
|
Object.assign(ul.style, {
|
|
@@ -655,13 +653,13 @@ class Measurer {
|
|
|
655
653
|
const li = document.createElement("li");
|
|
656
654
|
Object.assign(li.style, {
|
|
657
655
|
verticalAlign: "inherit",
|
|
658
|
-
...this.
|
|
656
|
+
...this._toDOMStyle(paragraph.style)
|
|
659
657
|
});
|
|
660
658
|
paragraph.fragments.forEach((fragment) => {
|
|
661
659
|
const span = document.createElement("span");
|
|
662
660
|
Object.assign(span.style, {
|
|
663
661
|
verticalAlign: "inherit",
|
|
664
|
-
...this.
|
|
662
|
+
...this._toDOMStyle(fragment.style)
|
|
665
663
|
});
|
|
666
664
|
span.appendChild(document.createTextNode(fragment.content));
|
|
667
665
|
li.appendChild(span);
|
|
@@ -669,14 +667,9 @@ class Measurer {
|
|
|
669
667
|
ul.appendChild(li);
|
|
670
668
|
});
|
|
671
669
|
dom.appendChild(ul);
|
|
672
|
-
|
|
673
|
-
document.body.appendChild(documentFragment);
|
|
674
|
-
return {
|
|
675
|
-
dom,
|
|
676
|
-
destory: () => dom.parentNode?.removeChild(dom)
|
|
677
|
-
};
|
|
670
|
+
return dom;
|
|
678
671
|
}
|
|
679
|
-
|
|
672
|
+
measureDOMText(text) {
|
|
680
673
|
const range = document.createRange();
|
|
681
674
|
range.selectNodeContents(text);
|
|
682
675
|
const data = text.data ?? "";
|
|
@@ -705,12 +698,12 @@ class Measurer {
|
|
|
705
698
|
return void 0;
|
|
706
699
|
}).filter(Boolean);
|
|
707
700
|
}
|
|
708
|
-
|
|
701
|
+
measureDOM(dom) {
|
|
709
702
|
const paragraphs = [];
|
|
710
703
|
const fragments = [];
|
|
711
704
|
const characters = [];
|
|
712
|
-
dom.querySelectorAll("li").forEach((
|
|
713
|
-
const pBox =
|
|
705
|
+
dom.querySelectorAll("li").forEach((pDOM, paragraphIndex) => {
|
|
706
|
+
const pBox = pDOM.getBoundingClientRect();
|
|
714
707
|
paragraphs.push({
|
|
715
708
|
paragraphIndex,
|
|
716
709
|
left: pBox.left,
|
|
@@ -718,8 +711,8 @@ class Measurer {
|
|
|
718
711
|
width: pBox.width,
|
|
719
712
|
height: pBox.height
|
|
720
713
|
});
|
|
721
|
-
|
|
722
|
-
const fBox =
|
|
714
|
+
pDOM.querySelectorAll(":scope > *").forEach((fDOM, fragmentIndex) => {
|
|
715
|
+
const fBox = fDOM.getBoundingClientRect();
|
|
723
716
|
fragments.push({
|
|
724
717
|
paragraphIndex,
|
|
725
718
|
fragmentIndex,
|
|
@@ -729,8 +722,8 @@ class Measurer {
|
|
|
729
722
|
height: fBox.height
|
|
730
723
|
});
|
|
731
724
|
let characterIndex = 0;
|
|
732
|
-
if (!
|
|
733
|
-
this.
|
|
725
|
+
if (!fDOM.children.length && fDOM.firstChild instanceof window.Text) {
|
|
726
|
+
this.measureDOMText(fDOM.firstChild).forEach((char) => {
|
|
734
727
|
characters.push({
|
|
735
728
|
...char,
|
|
736
729
|
newParagraphIndex: -1,
|
|
@@ -742,9 +735,9 @@ class Measurer {
|
|
|
742
735
|
});
|
|
743
736
|
});
|
|
744
737
|
} else {
|
|
745
|
-
|
|
746
|
-
if (
|
|
747
|
-
this.
|
|
738
|
+
fDOM.querySelectorAll(":scope > *").forEach((cDOM) => {
|
|
739
|
+
if (cDOM.firstChild instanceof window.Text) {
|
|
740
|
+
this.measureDOMText(cDOM.firstChild).forEach((char) => {
|
|
748
741
|
characters.push({
|
|
749
742
|
...char,
|
|
750
743
|
newParagraphIndex: -1,
|
|
@@ -766,9 +759,9 @@ class Measurer {
|
|
|
766
759
|
characters
|
|
767
760
|
};
|
|
768
761
|
}
|
|
769
|
-
|
|
762
|
+
measureParagraphDOM(paragraphs, dom) {
|
|
770
763
|
const rect = dom.getBoundingClientRect();
|
|
771
|
-
const measured = this.
|
|
764
|
+
const measured = this.measureDOM(dom);
|
|
772
765
|
measured.paragraphs.forEach((p) => {
|
|
773
766
|
const _p = paragraphs[p.paragraphIndex];
|
|
774
767
|
_p.lineBox.left = p.left - rect.left;
|
|
@@ -821,9 +814,15 @@ class Measurer {
|
|
|
821
814
|
measure(paragraphs, rootStyle, dom) {
|
|
822
815
|
let destory;
|
|
823
816
|
if (!dom) {
|
|
824
|
-
|
|
817
|
+
dom = this.createDOM(paragraphs, rootStyle);
|
|
818
|
+
Object.assign(dom.style, {
|
|
819
|
+
position: "fixed",
|
|
820
|
+
visibility: "hidden"
|
|
821
|
+
});
|
|
822
|
+
document.body.appendChild(dom);
|
|
823
|
+
destory = () => dom?.parentNode?.removeChild(dom);
|
|
825
824
|
}
|
|
826
|
-
const result = this.
|
|
825
|
+
const result = this.measureParagraphDOM(paragraphs, dom);
|
|
827
826
|
destory?.();
|
|
828
827
|
return result;
|
|
829
828
|
}
|
|
@@ -1633,7 +1632,7 @@ class Text extends EventEmitter {
|
|
|
1633
1632
|
content;
|
|
1634
1633
|
style;
|
|
1635
1634
|
effects;
|
|
1636
|
-
|
|
1635
|
+
measureDOM;
|
|
1637
1636
|
needsUpdate = true;
|
|
1638
1637
|
computedStyle = { ...textDefaultStyle };
|
|
1639
1638
|
paragraphs = [];
|
|
@@ -1656,10 +1655,13 @@ class Text extends EventEmitter {
|
|
|
1656
1655
|
}
|
|
1657
1656
|
constructor(options = {}) {
|
|
1658
1657
|
super();
|
|
1658
|
+
this.set(options);
|
|
1659
|
+
}
|
|
1660
|
+
set(options = {}) {
|
|
1659
1661
|
this.debug = options.debug ?? false;
|
|
1660
|
-
this.content = options.content ?? "";
|
|
1662
|
+
this.content = modernIdoc.normalizeTextContent(options.content ?? "");
|
|
1661
1663
|
this.style = options.style ?? {};
|
|
1662
|
-
this.
|
|
1664
|
+
this.measureDOM = options.measureDOM;
|
|
1663
1665
|
this.effects = options.effects;
|
|
1664
1666
|
this.fonts = options.fonts;
|
|
1665
1667
|
this.use(background()).use(outline()).use(listStyle()).use(textDecoration()).use(highlight()).use(render());
|
|
@@ -1687,56 +1689,27 @@ class Text extends EventEmitter {
|
|
|
1687
1689
|
}
|
|
1688
1690
|
updateParagraphs() {
|
|
1689
1691
|
this.computedStyle = { ...textDefaultStyle, ...this.style };
|
|
1690
|
-
|
|
1692
|
+
const { content, computedStyle: style } = this;
|
|
1691
1693
|
const paragraphs = [];
|
|
1692
|
-
|
|
1693
|
-
const
|
|
1694
|
-
paragraph
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
if (typeof p === "string") {
|
|
1700
|
-
const paragraph = new Paragraph({}, style);
|
|
1701
|
-
paragraph.addFragment(p);
|
|
1702
|
-
paragraphs.push(paragraph);
|
|
1703
|
-
} else if (Array.isArray(p)) {
|
|
1704
|
-
const paragraph = new Paragraph({}, style);
|
|
1705
|
-
p.forEach((f) => {
|
|
1706
|
-
if (typeof f === "string") {
|
|
1707
|
-
paragraph.addFragment(f);
|
|
1708
|
-
} else {
|
|
1709
|
-
const { content: content2, ...fStyle } = f;
|
|
1710
|
-
if (content2 !== void 0) {
|
|
1711
|
-
paragraph.addFragment(content2, fStyle);
|
|
1712
|
-
}
|
|
1713
|
-
}
|
|
1714
|
-
});
|
|
1715
|
-
paragraphs.push(paragraph);
|
|
1716
|
-
} else if ("fragments" in p) {
|
|
1717
|
-
const { fragments, ...pStyle } = p;
|
|
1718
|
-
const paragraph = new Paragraph(pStyle, style);
|
|
1719
|
-
fragments.forEach((f) => {
|
|
1720
|
-
const { content: content2, ...fStyle } = f;
|
|
1721
|
-
if (content2 !== void 0) {
|
|
1722
|
-
paragraph.addFragment(content2, fStyle);
|
|
1723
|
-
}
|
|
1724
|
-
});
|
|
1725
|
-
paragraphs.push(paragraph);
|
|
1726
|
-
} else if ("content" in p) {
|
|
1727
|
-
const { content: pData, ...pStyle } = p;
|
|
1728
|
-
if (pData !== void 0) {
|
|
1729
|
-
const paragraph = new Paragraph(pStyle, style);
|
|
1730
|
-
paragraph.addFragment(pData);
|
|
1731
|
-
paragraphs.push(paragraph);
|
|
1732
|
-
}
|
|
1694
|
+
content.forEach((p) => {
|
|
1695
|
+
const { fragments, ...pStyle } = p;
|
|
1696
|
+
const paragraph = new Paragraph(pStyle, style);
|
|
1697
|
+
fragments.forEach((f) => {
|
|
1698
|
+
const { content: content2, ...fStyle } = f;
|
|
1699
|
+
if (content2 !== void 0) {
|
|
1700
|
+
paragraph.addFragment(content2, fStyle);
|
|
1733
1701
|
}
|
|
1734
|
-
}
|
|
1735
|
-
|
|
1702
|
+
});
|
|
1703
|
+
paragraphs.push(paragraph);
|
|
1704
|
+
});
|
|
1736
1705
|
this.paragraphs = paragraphs;
|
|
1737
1706
|
return this;
|
|
1738
1707
|
}
|
|
1739
|
-
|
|
1708
|
+
createDOM() {
|
|
1709
|
+
this.updateParagraphs();
|
|
1710
|
+
return this.measurer.createDOM(this.paragraphs, this.computedStyle);
|
|
1711
|
+
}
|
|
1712
|
+
measure(dom = this.measureDOM) {
|
|
1740
1713
|
const old = {
|
|
1741
1714
|
paragraphs: this.paragraphs,
|
|
1742
1715
|
lineBox: this.lineBox,
|
|
@@ -1806,8 +1779,8 @@ class Text extends EventEmitter {
|
|
|
1806
1779
|
this.needsUpdate = true;
|
|
1807
1780
|
return this;
|
|
1808
1781
|
}
|
|
1809
|
-
update() {
|
|
1810
|
-
const result = this.measure();
|
|
1782
|
+
update(dom = this.measureDOM) {
|
|
1783
|
+
const result = this.measure(dom);
|
|
1811
1784
|
for (const key in result) {
|
|
1812
1785
|
this[key] = result[key];
|
|
1813
1786
|
}
|
|
@@ -1841,6 +1814,7 @@ class Text extends EventEmitter {
|
|
|
1841
1814
|
}
|
|
1842
1815
|
});
|
|
1843
1816
|
this.emit("render", { text: this, view, pixelRatio });
|
|
1817
|
+
options.onContext?.(ctx);
|
|
1844
1818
|
}
|
|
1845
1819
|
}
|
|
1846
1820
|
|
|
@@ -1864,11 +1838,525 @@ function renderText(options, load) {
|
|
|
1864
1838
|
return text.render(options);
|
|
1865
1839
|
}
|
|
1866
1840
|
|
|
1841
|
+
function normalizeStyle(style) {
|
|
1842
|
+
const newStyle = {};
|
|
1843
|
+
for (const key in style) {
|
|
1844
|
+
if (key !== "id" && style[key] !== void 0 && style[key] !== "") {
|
|
1845
|
+
newStyle[key] = style[key];
|
|
1846
|
+
}
|
|
1847
|
+
}
|
|
1848
|
+
return newStyle;
|
|
1849
|
+
}
|
|
1850
|
+
function contentsToCharStyles(contents) {
|
|
1851
|
+
return contents.flatMap((p) => {
|
|
1852
|
+
const res = p.fragments.flatMap((f) => {
|
|
1853
|
+
const { content, ...style } = f;
|
|
1854
|
+
return Array.from(modernIdoc.normalizeCRLF(content)).map(() => ({ ...style }));
|
|
1855
|
+
});
|
|
1856
|
+
if (modernIdoc.isCRLF(modernIdoc.normalizeCRLF(p.fragments[p.fragments.length - 1]?.content ?? ""))) {
|
|
1857
|
+
return res;
|
|
1858
|
+
}
|
|
1859
|
+
return [...res, {}];
|
|
1860
|
+
});
|
|
1861
|
+
}
|
|
1862
|
+
function isEqualStyle(style1, style2) {
|
|
1863
|
+
const keys1 = Object.keys(style1);
|
|
1864
|
+
const keys2 = Object.keys(style2);
|
|
1865
|
+
const keys = Array.from(/* @__PURE__ */ new Set([...keys1, ...keys2]));
|
|
1866
|
+
return !keys.length || keys.every((key) => style1[key] === style2[key]);
|
|
1867
|
+
}
|
|
1868
|
+
const emojiRE = /[#*0-9]\uFE0F?\u20E3|[\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23ED-\u23EF\u23F1\u23F2\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB\u25FC\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692\u2694-\u2697\u2699\u269B\u269C\u26A0\u26A7\u26AA\u26B0\u26B1\u26BD\u26BE\u26C4\u26C8\u26CF\u26D1\u26E9\u26F0-\u26F5\u26F7\u26F8\u26FA\u2702\u2708\u2709\u270F\u2712\u2714\u2716\u271D\u2721\u2733\u2734\u2744\u2747\u2757\u2763\u27A1\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B55\u3030\u303D\u3297\u3299]\uFE0F?|[\u261D\u270C\u270D](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\u270A\u270B](?:\uD83C[\uDFFB-\uDFFF])?|[\u23E9-\u23EC\u23F0\u23F3\u25FD\u2693\u26A1\u26AB\u26C5\u26CE\u26D4\u26EA\u26FD\u2705\u2728\u274C\u274E\u2753-\u2755\u2795-\u2797\u27B0\u27BF\u2B50]|\u26D3\uFE0F?(?:\u200D\uD83D\uDCA5)?|\u26F9(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\u2764\uFE0F?(?:\u200D(?:\uD83D\uDD25|\uD83E\uDE79))?|\uD83C(?:[\uDC04\uDD70\uDD71\uDD7E\uDD7F\uDE02\uDE37\uDF21\uDF24-\uDF2C\uDF36\uDF7D\uDF96\uDF97\uDF99-\uDF9B\uDF9E\uDF9F\uDFCD\uDFCE\uDFD4-\uDFDF\uDFF5\uDFF7]\uFE0F?|[\uDF85\uDFC2\uDFC7](?:\uD83C[\uDFFB-\uDFFF])?|[\uDFC4\uDFCA](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDFCB\uDFCC](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDCCF\uDD8E\uDD91-\uDD9A\uDE01\uDE1A\uDE2F\uDE32-\uDE36\uDE38-\uDE3A\uDE50\uDE51\uDF00-\uDF20\uDF2D-\uDF35\uDF37-\uDF43\uDF45-\uDF4A\uDF4C-\uDF7C\uDF7E-\uDF84\uDF86-\uDF93\uDFA0-\uDFC1\uDFC5\uDFC6\uDFC8\uDFC9\uDFCF-\uDFD3\uDFE0-\uDFF0\uDFF8-\uDFFF]|\uDDE6\uD83C[\uDDE8-\uDDEC\uDDEE\uDDF1\uDDF2\uDDF4\uDDF6-\uDDFA\uDDFC\uDDFD\uDDFF]|\uDDE7\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEF\uDDF1-\uDDF4\uDDF6-\uDDF9\uDDFB\uDDFC\uDDFE\uDDFF]|\uDDE8\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDEE\uDDF0-\uDDF7\uDDFA-\uDDFF]|\uDDE9\uD83C[\uDDEA\uDDEC\uDDEF\uDDF0\uDDF2\uDDF4\uDDFF]|\uDDEA\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDED\uDDF7-\uDDFA]|\uDDEB\uD83C[\uDDEE-\uDDF0\uDDF2\uDDF4\uDDF7]|\uDDEC\uD83C[\uDDE6\uDDE7\uDDE9-\uDDEE\uDDF1-\uDDF3\uDDF5-\uDDFA\uDDFC\uDDFE]|\uDDED\uD83C[\uDDF0\uDDF2\uDDF3\uDDF7\uDDF9\uDDFA]|\uDDEE\uD83C[\uDDE8-\uDDEA\uDDF1-\uDDF4\uDDF6-\uDDF9]|\uDDEF\uD83C[\uDDEA\uDDF2\uDDF4\uDDF5]|\uDDF0\uD83C[\uDDEA\uDDEC-\uDDEE\uDDF2\uDDF3\uDDF5\uDDF7\uDDFC\uDDFE\uDDFF]|\uDDF1\uD83C[\uDDE6-\uDDE8\uDDEE\uDDF0\uDDF7-\uDDFB\uDDFE]|\uDDF2\uD83C[\uDDE6\uDDE8-\uDDED\uDDF0-\uDDFF]|\uDDF3\uD83C[\uDDE6\uDDE8\uDDEA-\uDDEC\uDDEE\uDDF1\uDDF4\uDDF5\uDDF7\uDDFA\uDDFF]|\uDDF4\uD83C\uDDF2|\uDDF5\uD83C[\uDDE6\uDDEA-\uDDED\uDDF0-\uDDF3\uDDF7-\uDDF9\uDDFC\uDDFE]|\uDDF6\uD83C\uDDE6|\uDDF7\uD83C[\uDDEA\uDDF4\uDDF8\uDDFA\uDDFC]|\uDDF8\uD83C[\uDDE6-\uDDEA\uDDEC-\uDDF4\uDDF7-\uDDF9\uDDFB\uDDFD-\uDDFF]|\uDDF9\uD83C[\uDDE6\uDDE8\uDDE9\uDDEB-\uDDED\uDDEF-\uDDF4\uDDF7\uDDF9\uDDFB\uDDFC\uDDFF]|\uDDFA\uD83C[\uDDE6\uDDEC\uDDF2\uDDF3\uDDF8\uDDFE\uDDFF]|\uDDFB\uD83C[\uDDE6\uDDE8\uDDEA\uDDEC\uDDEE\uDDF3\uDDFA]|\uDDFC\uD83C[\uDDEB\uDDF8]|\uDDFD\uD83C\uDDF0|\uDDFE\uD83C[\uDDEA\uDDF9]|\uDDFF\uD83C[\uDDE6\uDDF2\uDDFC]|\uDF44(?:\u200D\uD83D\uDFEB)?|\uDF4B(?:\u200D\uD83D\uDFE9)?|\uDFC3(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDFF3\uFE0F?(?:\u200D(?:\u26A7\uFE0F?|\uD83C\uDF08))?|\uDFF4(?:\u200D\u2620\uFE0F?|\uDB40\uDC67\uDB40\uDC62\uDB40(?:\uDC65\uDB40\uDC6E\uDB40\uDC67|\uDC73\uDB40\uDC63\uDB40\uDC74|\uDC77\uDB40\uDC6C\uDB40\uDC73)\uDB40\uDC7F)?)|\uD83D(?:[\uDC3F\uDCFD\uDD49\uDD4A\uDD6F\uDD70\uDD73\uDD76-\uDD79\uDD87\uDD8A-\uDD8D\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA\uDECB\uDECD-\uDECF\uDEE0-\uDEE5\uDEE9\uDEF0\uDEF3]\uFE0F?|[\uDC42\uDC43\uDC46-\uDC50\uDC66\uDC67\uDC6B-\uDC6D\uDC72\uDC74-\uDC76\uDC78\uDC7C\uDC83\uDC85\uDC8F\uDC91\uDCAA\uDD7A\uDD95\uDD96\uDE4C\uDE4F\uDEC0\uDECC](?:\uD83C[\uDFFB-\uDFFF])?|[\uDC6E\uDC70\uDC71\uDC73\uDC77\uDC81\uDC82\uDC86\uDC87\uDE45-\uDE47\uDE4B\uDE4D\uDE4E\uDEA3\uDEB4\uDEB5](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD74\uDD90](?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?|[\uDC00-\uDC07\uDC09-\uDC14\uDC16-\uDC25\uDC27-\uDC3A\uDC3C-\uDC3E\uDC40\uDC44\uDC45\uDC51-\uDC65\uDC6A\uDC79-\uDC7B\uDC7D-\uDC80\uDC84\uDC88-\uDC8E\uDC90\uDC92-\uDCA9\uDCAB-\uDCFC\uDCFF-\uDD3D\uDD4B-\uDD4E\uDD50-\uDD67\uDDA4\uDDFB-\uDE2D\uDE2F-\uDE34\uDE37-\uDE41\uDE43\uDE44\uDE48-\uDE4A\uDE80-\uDEA2\uDEA4-\uDEB3\uDEB7-\uDEBF\uDEC1-\uDEC5\uDED0-\uDED2\uDED5-\uDED7\uDEDC-\uDEDF\uDEEB\uDEEC\uDEF4-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uDC08(?:\u200D\u2B1B)?|\uDC15(?:\u200D\uD83E\uDDBA)?|\uDC26(?:\u200D(?:\u2B1B|\uD83D\uDD25))?|\uDC3B(?:\u200D\u2744\uFE0F?)?|\uDC41\uFE0F?(?:\u200D\uD83D\uDDE8\uFE0F?)?|\uDC68(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDC68\uDC69]\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?)|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?\uDC68\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D\uDC68\uD83C[\uDFFB-\uDFFE])))?))?|\uDC69(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:\uDC8B\u200D\uD83D)?[\uDC68\uDC69]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D(?:[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?|\uDC69\u200D\uD83D(?:\uDC66(?:\u200D\uD83D\uDC66)?|\uDC67(?:\u200D\uD83D[\uDC66\uDC67])?))|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFC-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFD-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFD\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D\uD83D(?:[\uDC68\uDC69]|\uDC8B\u200D\uD83D[\uDC68\uDC69])\uD83C[\uDFFB-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83D[\uDC68\uDC69]\uD83C[\uDFFB-\uDFFE])))?))?|\uDC6F(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDD75(?:\uD83C[\uDFFB-\uDFFF]|\uFE0F)?(?:\u200D[\u2640\u2642]\uFE0F?)?|\uDE2E(?:\u200D\uD83D\uDCA8)?|\uDE35(?:\u200D\uD83D\uDCAB)?|\uDE36(?:\u200D\uD83C\uDF2B\uFE0F?)?|\uDE42(?:\u200D[\u2194\u2195]\uFE0F?)?|\uDEB6(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?)|\uD83E(?:[\uDD0C\uDD0F\uDD18-\uDD1F\uDD30-\uDD34\uDD36\uDD77\uDDB5\uDDB6\uDDBB\uDDD2\uDDD3\uDDD5\uDEC3-\uDEC5\uDEF0\uDEF2-\uDEF8](?:\uD83C[\uDFFB-\uDFFF])?|[\uDD26\uDD35\uDD37-\uDD39\uDD3D\uDD3E\uDDB8\uDDB9\uDDCD\uDDCF\uDDD4\uDDD6-\uDDDD](?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDDDE\uDDDF](?:\u200D[\u2640\u2642]\uFE0F?)?|[\uDD0D\uDD0E\uDD10-\uDD17\uDD20-\uDD25\uDD27-\uDD2F\uDD3A\uDD3F-\uDD45\uDD47-\uDD76\uDD78-\uDDB4\uDDB7\uDDBA\uDDBC-\uDDCC\uDDD0\uDDE0-\uDDFF\uDE70-\uDE7C\uDE80-\uDE89\uDE8F-\uDEC2\uDEC6\uDECE-\uDEDC\uDEDF-\uDEE9]|\uDD3C(?:\u200D[\u2640\u2642]\uFE0F?|\uD83C[\uDFFB-\uDFFF])?|\uDDCE(?:\uD83C[\uDFFB-\uDFFF])?(?:\u200D(?:[\u2640\u2642]\uFE0F?(?:\u200D\u27A1\uFE0F?)?|\u27A1\uFE0F?))?|\uDDD1(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1|\uDDD1\u200D\uD83E\uDDD2(?:\u200D\uD83E\uDDD2)?|\uDDD2(?:\u200D\uD83E\uDDD2)?))|\uD83C(?:\uDFFB(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFC-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFC(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFD-\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFD(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFE(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFD\uDFFF]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?|\uDFFF(?:\u200D(?:[\u2695\u2696\u2708]\uFE0F?|\u2764\uFE0F?\u200D(?:\uD83D\uDC8B\u200D)?\uD83E\uDDD1\uD83C[\uDFFB-\uDFFE]|\uD83C[\uDF3E\uDF73\uDF7C\uDF84\uDF93\uDFA4\uDFA8\uDFEB\uDFED]|\uD83D[\uDCBB\uDCBC\uDD27\uDD2C\uDE80\uDE92]|\uD83E(?:[\uDDAF\uDDBC\uDDBD](?:\u200D\u27A1\uFE0F?)?|[\uDDB0-\uDDB3]|\uDD1D\u200D\uD83E\uDDD1\uD83C[\uDFFB-\uDFFF])))?))?|\uDEF1(?:\uD83C(?:\uDFFB(?:\u200D\uD83E\uDEF2\uD83C[\uDFFC-\uDFFF])?|\uDFFC(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFD-\uDFFF])?|\uDFFD(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB\uDFFC\uDFFE\uDFFF])?|\uDFFE(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFD\uDFFF])?|\uDFFF(?:\u200D\uD83E\uDEF2\uD83C[\uDFFB-\uDFFE])?))?)/g;
|
|
1869
|
+
function parseHTML(html) {
|
|
1870
|
+
const template = document.createElement("template");
|
|
1871
|
+
template.innerHTML = html;
|
|
1872
|
+
return template.content.cloneNode(true);
|
|
1873
|
+
}
|
|
1874
|
+
class TextEditor extends HTMLElement {
|
|
1875
|
+
static observedAttributes = [
|
|
1876
|
+
"left",
|
|
1877
|
+
"top",
|
|
1878
|
+
"width",
|
|
1879
|
+
"height",
|
|
1880
|
+
"is-vertical"
|
|
1881
|
+
];
|
|
1882
|
+
static register() {
|
|
1883
|
+
customElements.define("text-editor", this);
|
|
1884
|
+
}
|
|
1885
|
+
left = 0;
|
|
1886
|
+
top = 0;
|
|
1887
|
+
width = 0;
|
|
1888
|
+
height = 0;
|
|
1889
|
+
pixelRatio = 2;
|
|
1890
|
+
text = new Text();
|
|
1891
|
+
composition = false;
|
|
1892
|
+
selection = [0, 0];
|
|
1893
|
+
prevSelection = [0, 0];
|
|
1894
|
+
_oldText = "";
|
|
1895
|
+
$preview;
|
|
1896
|
+
$textInput;
|
|
1897
|
+
$cursor;
|
|
1898
|
+
$cursorInput;
|
|
1899
|
+
get selectionMinMax() {
|
|
1900
|
+
return {
|
|
1901
|
+
min: Math.min(...this.selection),
|
|
1902
|
+
max: Math.max(...this.selection)
|
|
1903
|
+
};
|
|
1904
|
+
}
|
|
1905
|
+
get selectedCharacters() {
|
|
1906
|
+
const { min, max } = this.selectionMinMax;
|
|
1907
|
+
return this.selectableCharacters.filter((_char, index) => {
|
|
1908
|
+
return index >= min && index < max;
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1911
|
+
get selectableCharacters() {
|
|
1912
|
+
const paragraphs = [];
|
|
1913
|
+
this.text?.paragraphs.forEach((p, paragraphIndex) => {
|
|
1914
|
+
p.fragments.forEach((f, fragmentIndex) => {
|
|
1915
|
+
f.characters.forEach((c) => {
|
|
1916
|
+
if (!paragraphs[paragraphIndex])
|
|
1917
|
+
paragraphs[paragraphIndex] = [];
|
|
1918
|
+
if (!paragraphs[paragraphIndex][fragmentIndex])
|
|
1919
|
+
paragraphs[paragraphIndex][fragmentIndex] = [];
|
|
1920
|
+
paragraphs[paragraphIndex][fragmentIndex].push(c);
|
|
1921
|
+
});
|
|
1922
|
+
});
|
|
1923
|
+
});
|
|
1924
|
+
const toSelectableCharacter = (c) => {
|
|
1925
|
+
return {
|
|
1926
|
+
color: c.computedStyle.color,
|
|
1927
|
+
left: c.inlineBox.left - this.text.boundingBox.left,
|
|
1928
|
+
top: c.inlineBox.top - this.text.boundingBox.top,
|
|
1929
|
+
width: c.inlineBox.width,
|
|
1930
|
+
height: c.inlineBox.height,
|
|
1931
|
+
content: c.content
|
|
1932
|
+
};
|
|
1933
|
+
};
|
|
1934
|
+
const map = [];
|
|
1935
|
+
let pos = 0;
|
|
1936
|
+
paragraphs.forEach((p) => {
|
|
1937
|
+
if (p.length === 1 && p[0].length === 1 && modernIdoc.isCRLF(p[0][0].content)) {
|
|
1938
|
+
const c = p[0][0];
|
|
1939
|
+
map[pos] = {
|
|
1940
|
+
...toSelectableCharacter(c),
|
|
1941
|
+
isCrlf: true
|
|
1942
|
+
};
|
|
1943
|
+
} else {
|
|
1944
|
+
p.forEach((f) => {
|
|
1945
|
+
f.forEach((c) => {
|
|
1946
|
+
map[pos] = {
|
|
1947
|
+
...toSelectableCharacter(c)
|
|
1948
|
+
};
|
|
1949
|
+
pos++;
|
|
1950
|
+
if (!modernIdoc.isCRLF(c.content)) {
|
|
1951
|
+
map[pos] = {
|
|
1952
|
+
...toSelectableCharacter(c),
|
|
1953
|
+
content: " ",
|
|
1954
|
+
isLastSelected: true
|
|
1955
|
+
};
|
|
1956
|
+
}
|
|
1957
|
+
});
|
|
1958
|
+
});
|
|
1959
|
+
}
|
|
1960
|
+
pos++;
|
|
1961
|
+
});
|
|
1962
|
+
if (map[0]) {
|
|
1963
|
+
map[0].isFirst = true;
|
|
1964
|
+
}
|
|
1965
|
+
if (map[map.length - 1]) {
|
|
1966
|
+
map[map.length - 1].isLast = true;
|
|
1967
|
+
}
|
|
1968
|
+
return map;
|
|
1969
|
+
}
|
|
1970
|
+
get cursorPosition() {
|
|
1971
|
+
let left = 0;
|
|
1972
|
+
let top = 0;
|
|
1973
|
+
const char = this.selectableCharacters[this.selectionMinMax.min];
|
|
1974
|
+
if (char?.isLastSelected) {
|
|
1975
|
+
if (this.text.isVertical) {
|
|
1976
|
+
top += char?.height ?? 0;
|
|
1977
|
+
} else {
|
|
1978
|
+
left += char?.width ?? 0;
|
|
1979
|
+
}
|
|
1980
|
+
}
|
|
1981
|
+
left += char?.left ?? 0;
|
|
1982
|
+
top += char?.top ?? 0;
|
|
1983
|
+
return {
|
|
1984
|
+
color: char?.color,
|
|
1985
|
+
left,
|
|
1986
|
+
top,
|
|
1987
|
+
height: char?.height ?? 0,
|
|
1988
|
+
width: char?.width ?? 0
|
|
1989
|
+
};
|
|
1990
|
+
}
|
|
1991
|
+
constructor() {
|
|
1992
|
+
super();
|
|
1993
|
+
const shadowRoot = this.attachShadow({ mode: "open" });
|
|
1994
|
+
shadowRoot.appendChild(
|
|
1995
|
+
parseHTML(`
|
|
1996
|
+
<style>
|
|
1997
|
+
:host {
|
|
1998
|
+
position: absolute;
|
|
1999
|
+
width: 0;
|
|
2000
|
+
height: 0;
|
|
2001
|
+
--color: 0, 0, 0;
|
|
2002
|
+
}
|
|
2003
|
+
|
|
2004
|
+
.preview {
|
|
2005
|
+
position: absolute;
|
|
2006
|
+
left: 0;
|
|
2007
|
+
top: 0;
|
|
2008
|
+
width: 100%;
|
|
2009
|
+
height: 100%;
|
|
2010
|
+
--selection-color: rgba(var(--color, 0, 0, 0), 0.4);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
.text-input {
|
|
2014
|
+
position: absolute;
|
|
2015
|
+
z-index: -9999;
|
|
2016
|
+
opacity: 0;
|
|
2017
|
+
caret-color: transparent;
|
|
2018
|
+
left: 0;
|
|
2019
|
+
top: 0;
|
|
2020
|
+
width: 100%;
|
|
2021
|
+
height: 100%;
|
|
2022
|
+
padding: 0;
|
|
2023
|
+
border: 0;
|
|
2024
|
+
}
|
|
2025
|
+
|
|
2026
|
+
.cursor {
|
|
2027
|
+
position: absolute;
|
|
2028
|
+
left: 0;
|
|
2029
|
+
top: 0;
|
|
2030
|
+
}
|
|
2031
|
+
|
|
2032
|
+
.cursor.blink {
|
|
2033
|
+
animation: cursor-blink 1s steps(2, start) infinite;
|
|
2034
|
+
}
|
|
2035
|
+
|
|
2036
|
+
@keyframes cursor-blink {
|
|
2037
|
+
100% {
|
|
2038
|
+
display: none;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
|
|
2042
|
+
.cursor-input {
|
|
2043
|
+
position: absolute;
|
|
2044
|
+
cursor: text;
|
|
2045
|
+
left: 0;
|
|
2046
|
+
top: 0;
|
|
2047
|
+
width: 100%;
|
|
2048
|
+
height: 100%;
|
|
2049
|
+
outline: 0;
|
|
2050
|
+
}
|
|
2051
|
+
</style>
|
|
2052
|
+
|
|
2053
|
+
<canvas class="preview"></canvas>
|
|
2054
|
+
|
|
2055
|
+
<textarea class="text-input"></textarea>
|
|
2056
|
+
|
|
2057
|
+
<div class="cursor blink"></div>
|
|
2058
|
+
|
|
2059
|
+
<div
|
|
2060
|
+
class="cursor-input"
|
|
2061
|
+
autofocus
|
|
2062
|
+
contenteditable="true"
|
|
2063
|
+
></div>
|
|
2064
|
+
`)
|
|
2065
|
+
);
|
|
2066
|
+
this.$preview = shadowRoot.querySelector(".preview");
|
|
2067
|
+
this.$textInput = shadowRoot.querySelector(".text-input");
|
|
2068
|
+
this.$cursor = shadowRoot.querySelector(".cursor");
|
|
2069
|
+
this.$cursorInput = shadowRoot.querySelector(".cursor-input");
|
|
2070
|
+
this.$textInput.addEventListener("compositionstart", () => this.composition = true);
|
|
2071
|
+
this.$textInput.addEventListener("compositionend", () => this.composition = false);
|
|
2072
|
+
this.$textInput.addEventListener("keydown", this.onKeydown.bind(this));
|
|
2073
|
+
this.$textInput.addEventListener("input", this.onInput.bind(this));
|
|
2074
|
+
this.$textInput.addEventListener("blur", this.onBlur.bind(this));
|
|
2075
|
+
this.$cursorInput.addEventListener("keydown", (e) => e.preventDefault());
|
|
2076
|
+
this.$cursorInput.addEventListener("focus", this.onFocus.bind(this));
|
|
2077
|
+
this.$cursorInput.addEventListener("mousedown", this.onMousedown.bind(this));
|
|
2078
|
+
}
|
|
2079
|
+
update(options) {
|
|
2080
|
+
this.text.set(options);
|
|
2081
|
+
this.setTextInput(this.getTextValue());
|
|
2082
|
+
this.render();
|
|
2083
|
+
}
|
|
2084
|
+
getTextValue() {
|
|
2085
|
+
return modernIdoc.textContentToString(
|
|
2086
|
+
this.getContentValue(
|
|
2087
|
+
this.text.content
|
|
2088
|
+
)
|
|
2089
|
+
);
|
|
2090
|
+
}
|
|
2091
|
+
getContentValue(content, newString = modernIdoc.textContentToString(content), oldString = newString) {
|
|
2092
|
+
newString = modernIdoc.normalizeCRLF(newString);
|
|
2093
|
+
newString = newString.replace(emojiRE, (emoji) => {
|
|
2094
|
+
if (Array.from(emoji).length > 1) {
|
|
2095
|
+
return "?";
|
|
2096
|
+
}
|
|
2097
|
+
return emoji;
|
|
2098
|
+
});
|
|
2099
|
+
oldString = modernIdoc.normalizeCRLF(oldString);
|
|
2100
|
+
const oldStyles = contentsToCharStyles(content);
|
|
2101
|
+
const styles = [];
|
|
2102
|
+
let styleIndex = 0;
|
|
2103
|
+
let oldStyleIndex = 0;
|
|
2104
|
+
let prevOldStyle = {};
|
|
2105
|
+
const changes = diff.diffChars(oldString, newString);
|
|
2106
|
+
changes.forEach((change) => {
|
|
2107
|
+
const chars = Array.from(change.value);
|
|
2108
|
+
if (change.removed) {
|
|
2109
|
+
oldStyleIndex += chars.length;
|
|
2110
|
+
} else {
|
|
2111
|
+
chars.forEach(() => {
|
|
2112
|
+
if (change.added) {
|
|
2113
|
+
styles[styleIndex] = { ...prevOldStyle };
|
|
2114
|
+
} else {
|
|
2115
|
+
prevOldStyle = normalizeStyle(oldStyles[oldStyleIndex]);
|
|
2116
|
+
styles[styleIndex] = { ...prevOldStyle };
|
|
2117
|
+
oldStyleIndex++;
|
|
2118
|
+
}
|
|
2119
|
+
styleIndex++;
|
|
2120
|
+
});
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
let charIndex = 0;
|
|
2124
|
+
const newContents = [];
|
|
2125
|
+
modernIdoc.normalizeTextContent(newString).forEach((p) => {
|
|
2126
|
+
let newParagraph = { fragments: [] };
|
|
2127
|
+
let newFragment;
|
|
2128
|
+
p.fragments.forEach((f) => {
|
|
2129
|
+
Array.from(f.content).forEach((char) => {
|
|
2130
|
+
const style = styles[charIndex] ?? {};
|
|
2131
|
+
if (newFragment) {
|
|
2132
|
+
const { content: _, ..._style } = newFragment;
|
|
2133
|
+
if (isEqualStyle(style, _style)) {
|
|
2134
|
+
newFragment.content += char;
|
|
2135
|
+
} else {
|
|
2136
|
+
newParagraph.fragments.push(newFragment);
|
|
2137
|
+
newFragment = { ...style, content: char };
|
|
2138
|
+
}
|
|
2139
|
+
} else {
|
|
2140
|
+
newFragment = { ...style, content: char };
|
|
2141
|
+
}
|
|
2142
|
+
charIndex++;
|
|
2143
|
+
});
|
|
2144
|
+
});
|
|
2145
|
+
if (!modernIdoc.isCRLF(p.fragments[p.fragments.length - 1].content)) {
|
|
2146
|
+
charIndex++;
|
|
2147
|
+
}
|
|
2148
|
+
if (newFragment) {
|
|
2149
|
+
newParagraph.fragments.push(newFragment);
|
|
2150
|
+
}
|
|
2151
|
+
if (newParagraph.fragments.length) {
|
|
2152
|
+
newContents.push(newParagraph);
|
|
2153
|
+
newParagraph = { fragments: [] };
|
|
2154
|
+
}
|
|
2155
|
+
});
|
|
2156
|
+
return newContents;
|
|
2157
|
+
}
|
|
2158
|
+
setTextInput(newText) {
|
|
2159
|
+
this.$textInput.value = newText;
|
|
2160
|
+
this._oldText = newText;
|
|
2161
|
+
}
|
|
2162
|
+
onInput() {
|
|
2163
|
+
const newText = this.$textInput.value;
|
|
2164
|
+
this.text.content = this.getContentValue(
|
|
2165
|
+
this.text.content,
|
|
2166
|
+
newText,
|
|
2167
|
+
this._oldText
|
|
2168
|
+
);
|
|
2169
|
+
this._oldText = newText;
|
|
2170
|
+
this.updateSelection();
|
|
2171
|
+
this.render();
|
|
2172
|
+
}
|
|
2173
|
+
_timer;
|
|
2174
|
+
onKeydown(e) {
|
|
2175
|
+
switch (e.key) {
|
|
2176
|
+
}
|
|
2177
|
+
this.updateSelection();
|
|
2178
|
+
this.render();
|
|
2179
|
+
setTimeout(() => {
|
|
2180
|
+
this.updateSelection();
|
|
2181
|
+
this.render();
|
|
2182
|
+
}, 0);
|
|
2183
|
+
setTimeout(() => {
|
|
2184
|
+
this.updateSelection();
|
|
2185
|
+
this.render();
|
|
2186
|
+
}, 100);
|
|
2187
|
+
}
|
|
2188
|
+
onFocus(e) {
|
|
2189
|
+
e.preventDefault();
|
|
2190
|
+
this.$cursorInput.blur();
|
|
2191
|
+
this.$textInput?.focus();
|
|
2192
|
+
}
|
|
2193
|
+
onBlur() {
|
|
2194
|
+
}
|
|
2195
|
+
findNearest(options) {
|
|
2196
|
+
const { x, y, xWeight = 1, yWeight = 1 } = options;
|
|
2197
|
+
const char = this.selectableCharacters.reduce(
|
|
2198
|
+
(prev, current, index) => {
|
|
2199
|
+
const diff = Math.abs(current.left + current.width / 2 - x) * xWeight + Math.abs(current.top + current.height / 2 - y) * yWeight;
|
|
2200
|
+
if (diff < prev.diff) {
|
|
2201
|
+
return {
|
|
2202
|
+
diff,
|
|
2203
|
+
index,
|
|
2204
|
+
value: current
|
|
2205
|
+
};
|
|
2206
|
+
}
|
|
2207
|
+
return prev;
|
|
2208
|
+
},
|
|
2209
|
+
{
|
|
2210
|
+
diff: Number.MAX_SAFE_INTEGER,
|
|
2211
|
+
index: -1,
|
|
2212
|
+
value: void 0
|
|
2213
|
+
}
|
|
2214
|
+
);
|
|
2215
|
+
if (char?.value) {
|
|
2216
|
+
const middleX = char.value.left + char.value.width / 2;
|
|
2217
|
+
if (x > middleX && !char.value.isCrlf && !char.value.isLastSelected) {
|
|
2218
|
+
return char.index + 1;
|
|
2219
|
+
}
|
|
2220
|
+
return char.index;
|
|
2221
|
+
}
|
|
2222
|
+
return -1;
|
|
2223
|
+
}
|
|
2224
|
+
updateSelection() {
|
|
2225
|
+
if (this.composition) {
|
|
2226
|
+
this.selection = this.prevSelection;
|
|
2227
|
+
} else {
|
|
2228
|
+
let count = 0;
|
|
2229
|
+
const _selection = [];
|
|
2230
|
+
this.selectableCharacters.forEach((char, index) => {
|
|
2231
|
+
if (count <= this.$textInput.selectionStart) {
|
|
2232
|
+
_selection[0] = index;
|
|
2233
|
+
} else if (count <= this.$textInput.selectionEnd) {
|
|
2234
|
+
_selection[1] = index;
|
|
2235
|
+
}
|
|
2236
|
+
count += char.content.length;
|
|
2237
|
+
});
|
|
2238
|
+
this.selection = _selection;
|
|
2239
|
+
this.prevSelection = this.selection;
|
|
2240
|
+
}
|
|
2241
|
+
}
|
|
2242
|
+
updateDOMSelection() {
|
|
2243
|
+
let start = 0;
|
|
2244
|
+
let end = 0;
|
|
2245
|
+
this.selectableCharacters.forEach((char, index) => {
|
|
2246
|
+
if (index < this.selectionMinMax.min) {
|
|
2247
|
+
start += char.content.length;
|
|
2248
|
+
end = start;
|
|
2249
|
+
} else if (index < this.selectionMinMax.max) {
|
|
2250
|
+
end += char.content.length;
|
|
2251
|
+
}
|
|
2252
|
+
});
|
|
2253
|
+
this.$textInput.selectionStart = start;
|
|
2254
|
+
this.$textInput.selectionEnd = end;
|
|
2255
|
+
}
|
|
2256
|
+
onMousedown(e) {
|
|
2257
|
+
const index = this.findNearest({ x: e.offsetX, y: e.offsetY });
|
|
2258
|
+
this.selection = [index, index];
|
|
2259
|
+
this.updateDOMSelection();
|
|
2260
|
+
this.render();
|
|
2261
|
+
const onMousemove = (e2) => {
|
|
2262
|
+
this.selection[1] = this.findNearest({ x: e2.offsetX, y: e2.offsetY });
|
|
2263
|
+
this.updateDOMSelection();
|
|
2264
|
+
this.render();
|
|
2265
|
+
};
|
|
2266
|
+
const onMouseup = () => {
|
|
2267
|
+
document.removeEventListener("mousemove", onMousemove);
|
|
2268
|
+
document.removeEventListener("mouseup", onMouseup);
|
|
2269
|
+
};
|
|
2270
|
+
document.addEventListener("mousemove", onMousemove);
|
|
2271
|
+
document.addEventListener("mouseup", onMouseup);
|
|
2272
|
+
}
|
|
2273
|
+
render() {
|
|
2274
|
+
this.text.update();
|
|
2275
|
+
this.width = this.text.boundingBox.width;
|
|
2276
|
+
this.height = this.text.boundingBox.height;
|
|
2277
|
+
const isVertical = this.text.isVertical;
|
|
2278
|
+
this.style.left = `${this.left}px`;
|
|
2279
|
+
this.style.top = `${this.top}px`;
|
|
2280
|
+
this.style.width = `${this.width}px`;
|
|
2281
|
+
this.style.height = `${this.height}px`;
|
|
2282
|
+
let ctx;
|
|
2283
|
+
this.text.render({
|
|
2284
|
+
pixelRatio: this.pixelRatio,
|
|
2285
|
+
view: this.$preview,
|
|
2286
|
+
onContext: (_ctx) => ctx = _ctx
|
|
2287
|
+
});
|
|
2288
|
+
const selectedCharacters = this.selectedCharacters;
|
|
2289
|
+
if (ctx) {
|
|
2290
|
+
const boxesGroups = {};
|
|
2291
|
+
selectedCharacters.forEach((char) => {
|
|
2292
|
+
if (char.isLastSelected) {
|
|
2293
|
+
return;
|
|
2294
|
+
}
|
|
2295
|
+
const key = isVertical ? char.left : char.top;
|
|
2296
|
+
if (!boxesGroups[key]) {
|
|
2297
|
+
boxesGroups[key] = [];
|
|
2298
|
+
}
|
|
2299
|
+
boxesGroups[key].push({
|
|
2300
|
+
x: char.left,
|
|
2301
|
+
y: char.top,
|
|
2302
|
+
w: char.width,
|
|
2303
|
+
h: char.height
|
|
2304
|
+
});
|
|
2305
|
+
});
|
|
2306
|
+
ctx.fillStyle = getComputedStyle(this.$preview).getPropertyValue("--selection-color");
|
|
2307
|
+
Object.values(boxesGroups).forEach((boxes) => {
|
|
2308
|
+
const min = {
|
|
2309
|
+
x: Math.min(...boxes.map((v) => v.x)),
|
|
2310
|
+
y: Math.min(...boxes.map((v) => v.y))
|
|
2311
|
+
};
|
|
2312
|
+
const max = {
|
|
2313
|
+
x: Math.max(...boxes.map((v) => v.x + v.w)),
|
|
2314
|
+
y: Math.max(...boxes.map((v) => v.y + v.h))
|
|
2315
|
+
};
|
|
2316
|
+
ctx.fillRect(
|
|
2317
|
+
min.x + this.text.boundingBox.left,
|
|
2318
|
+
min.y + this.text.boundingBox.top,
|
|
2319
|
+
max.x - min.x,
|
|
2320
|
+
max.y - min.y
|
|
2321
|
+
);
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
if (selectedCharacters.length === 0) {
|
|
2325
|
+
this.$cursor.style.visibility = "visible";
|
|
2326
|
+
const cursorPosition = this.cursorPosition;
|
|
2327
|
+
this.$cursor.style.backgroundColor = cursorPosition.color ?? "rgba(var(--color)";
|
|
2328
|
+
this.$cursor.style.left = `${cursorPosition.left}px`;
|
|
2329
|
+
this.$cursor.style.top = `${cursorPosition.top}px`;
|
|
2330
|
+
this.$cursor.style.height = isVertical ? "1px" : `${cursorPosition.height}px`;
|
|
2331
|
+
this.$cursor.style.width = isVertical ? `${cursorPosition.width}px` : "1px";
|
|
2332
|
+
} else {
|
|
2333
|
+
this.$cursor.style.visibility = "hidden";
|
|
2334
|
+
}
|
|
2335
|
+
this.$cursor.classList.remove("blink");
|
|
2336
|
+
if (this._timer) {
|
|
2337
|
+
clearTimeout(this._timer);
|
|
2338
|
+
}
|
|
2339
|
+
this._timer = setTimeout(() => this.$cursor.classList.add("blink"), 500);
|
|
2340
|
+
}
|
|
2341
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
2342
|
+
switch (name) {
|
|
2343
|
+
case "left":
|
|
2344
|
+
case "top":
|
|
2345
|
+
case "width":
|
|
2346
|
+
case "height":
|
|
2347
|
+
this[name] = newValue;
|
|
2348
|
+
this.render();
|
|
2349
|
+
break;
|
|
2350
|
+
}
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
|
|
1867
2354
|
exports.Character = Character;
|
|
1868
2355
|
exports.Fragment = Fragment;
|
|
1869
2356
|
exports.Measurer = Measurer;
|
|
1870
2357
|
exports.Paragraph = Paragraph;
|
|
1871
2358
|
exports.Text = Text;
|
|
2359
|
+
exports.TextEditor = TextEditor;
|
|
1872
2360
|
exports.background = background;
|
|
1873
2361
|
exports.createSVGLoader = createSVGLoader;
|
|
1874
2362
|
exports.createSVGParser = createSVGParser;
|