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 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
- _styleToDomStyle(style) {
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
- createParagraphDom(paragraphs, rootStyle) {
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._styleToDomStyle({
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._styleToDomStyle(paragraph.style)
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._styleToDomStyle(fragment.style)
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
- documentFragment.appendChild(dom);
673
- document.body.appendChild(documentFragment);
674
- return {
675
- dom,
676
- destory: () => dom.parentNode?.removeChild(dom)
677
- };
670
+ return dom;
678
671
  }
679
- measureDomText(text) {
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
- measureDom(dom) {
701
+ measureDOM(dom) {
709
702
  const paragraphs = [];
710
703
  const fragments = [];
711
704
  const characters = [];
712
- dom.querySelectorAll("li").forEach((pDom, paragraphIndex) => {
713
- const pBox = pDom.getBoundingClientRect();
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
- pDom.querySelectorAll(":scope > *").forEach((fDom, fragmentIndex) => {
722
- const fBox = fDom.getBoundingClientRect();
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 (!fDom.children.length && fDom.firstChild instanceof window.Text) {
733
- this.measureDomText(fDom.firstChild).forEach((char) => {
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
- fDom.querySelectorAll(":scope > *").forEach((cDom) => {
746
- if (cDom.firstChild instanceof window.Text) {
747
- this.measureDomText(cDom.firstChild).forEach((char) => {
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
- measureParagraphDom(paragraphs, dom) {
762
+ measureParagraphDOM(paragraphs, dom) {
770
763
  const rect = dom.getBoundingClientRect();
771
- const measured = this.measureDom(dom);
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
- ({ dom, destory } = this.createParagraphDom(paragraphs, rootStyle));
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.measureParagraphDom(paragraphs, dom);
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
- measureDom;
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.measureDom = options.measureDom;
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
- let { content, computedStyle: style } = this;
1692
+ const { content, computedStyle: style } = this;
1691
1693
  const paragraphs = [];
1692
- if (typeof content === "string") {
1693
- const paragraph = new Paragraph({}, style);
1694
- paragraph.addFragment(content);
1695
- paragraphs.push(paragraph);
1696
- } else {
1697
- content = Array.isArray(content) ? content : [content];
1698
- for (const p of content) {
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
- measure(dom = this.measureDom) {
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;