UNPKG

on-codemerge

Version:

A WYSIWYG editor for on-codemerge is a user-friendly interface that allows users to edit and view their code in real time, exactly as it will appear in the final product

202 lines (201 loc) 7.3 kB
/*! on-codemerge v1.3.1 @author Pavel Kuzmin @license MIT @homepage https://s00d.github.io/on-codemerge/ @repository git+https://github.com/s00d/on-codemerge.git Copyright (c) 2026 Pavel Kuzmin - Built on 2026-07-02T13:39:17.947Z */ var d = Object.defineProperty; var u = (l, e, t) => e in l ? d(l, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : l[e] = t; var c = (l, e, t) => u(l, typeof e != "symbol" ? e + "" : e, t); import { DomUtils as p } from "./DomUtils.mjs"; import { StyleManager as f } from "./StyleManager.mjs"; import { isFormatDebugEnabled as y, formatDebugMapToString as g } from "./FormatDebugMap.mjs"; import { FormatDebugTracer as T } from "./FormatDebugTracer.mjs"; class R { constructor(e, t, r) { c(this, "domUtils"); c(this, "styleManager"); c(this, "shadowRoot", null); c(this, "debugTracer", new T()); this.container = e, this.domUtils = new p(e), this.styleManager = new f(), this.shadowRoot = t || null, (r ?? y()) && this.enableFormatDebug(!0); } enableFormatDebug(e) { this.debugTracer.setEnabled(e), this.domUtils.setTracer(e ? this.debugTracer : null); } isFormatDebugEnabled() { return this.debugTracer.isEnabled(); } getLastDebugMap() { return this.debugTracer.getLastMap(); } toggleStyle(e) { const t = this.getSelection(); if (!t || t.rangeCount === 0) return; if (this.styleManager.isBlockStyle(e)) { this.toggleBlockStyle(e, t); return; } this.debugTracer.begin(e, this.container, t); const r = this.domUtils.getSelectedRoot(t, !1, e); if (!r || r.length === 0) { this.debugTracer.setAction("noop"); const n = this.debugTracer.end(this.container); n && this.debugTracer.logToConsole(n, g); return; } let o = "apply"; r.forEach((n) => { n.nodeType === Node.TEXT_NODE && (n = this.wrapTextNodeInSpan(n)); const a = n; this.styleManager.has(a, e) ? (o = "remove", this.domUtils.removeStyleFromNode( n, e, this.styleManager.remove.bind(this.styleManager) )) : this.domUtils.applyStyleToNode( n, e, this.styleManager.set.bind(this.styleManager) ); }), this.debugTracer.setAction(o); const s = this.debugTracer.end(this.container); s && this.debugTracer.logToConsole(s, g); } toggleBlockStyle(e, t) { this.debugTracer.begin(e, this.container, t); const r = t.getRangeAt(0), o = this.domUtils.findBlockElementsInRange(r); if (this.debugTracer.step( "findBlockElements", o.map((i) => i.tagName), o.length === 0 ? "no_blocks_found" : "blocks_in_range" ), o.length === 0) { this.debugTracer.setAction("noop"); const i = this.debugTracer.end(this.container); i && this.debugTracer.logToConsole(i, g); return; } const s = o.every((i) => this.styleManager.has(i, e)), n = s ? "remove" : "apply"; o.forEach((i) => { s ? this.styleManager.remove(i, e) : this.styleManager.set(i, e); }), this.debugTracer.setNodesToModify(o, this.container), this.debugTracer.setAction(n); const a = this.debugTracer.end(this.container); a && this.debugTracer.logToConsole(a, g); } setColor(e) { this.applyStyleToSelectedNodes((t) => { t.style.color = e; }); } setBackgroundColor(e) { this.applyStyleToSelectedNodes((t) => { t.style.backgroundColor = e; }); } setFont(e, t, r) { this.applyStyleToSelectedNodes((o) => { o.style.fontFamily = e, o.style.fontSize = t, o.style.lineHeight = r; }); } clearFont() { this.applyStyleToSelectedNodes((e) => { e.style.removeProperty("font-family"), e.style.removeProperty("font-size"), e.style.removeProperty("line-height"); }); } applyBlock(e = "span") { this.applyStyleToSelectedNodes((t) => { const r = document.createElement(e); r.textContent = t.textContent, r.classList.add("format-text-block"), t.parentNode && t.parentNode.replaceChild(r, t); }); } clearBlock() { this.applyStyleToSelectedNodes((e) => { const t = document.createElement("span"); t.textContent = e.textContent, e.style.removeProperty("font-family"), e.style.removeProperty("font-size"), e.style.removeProperty("line-height"), e.style.removeProperty("format-text-block"), e.parentNode && e.parentNode.replaceChild(t, e); }); } applyStyleToSelectedNodes(e) { const t = this.getSelection(); if (!t) return; const r = this.domUtils.getSelectedRoot(t, !1); r && r.forEach((o) => { o.nodeType === Node.ELEMENT_NODE && o !== this.container && e(o); }); } wrapTextNodeInSpan(e) { var r; const t = document.createElement("span"); return (r = e.parentNode) == null || r.replaceChild(t, e), t.appendChild(e), t; } getSelection() { if (this.shadowRoot) { if ("getSelection" in this.shadowRoot) { const o = this.shadowRoot.getSelection(); if (o && o.rangeCount > 0) return o; } const e = window.getSelection(); if (!e || e.rangeCount === 0) return null; const r = e.getRangeAt(0).commonAncestorContainer; if (this.shadowRoot.contains(r) || r.nodeType === Node.ELEMENT_NODE && this.shadowRoot.contains(r)) return e; { const o = this.shadowRoot.activeElement; if (o && o === this.container) { const s = this.container; let n = 1; return { get rangeCount() { return n; }, getRangeAt: (i) => { const h = document.createRange(); return h.selectNodeContents(s), h; }, removeAllRanges: () => { n = 0; }, addRange: (i) => { i && s.contains(i.commonAncestorContainer) && (n = 1); }, toString: () => s.textContent || "" }; } return null; } } else return window.getSelection(); } hasClass(e) { const t = this.getSelection(); if (!t || t.rangeCount === 0) return !1; if (this.styleManager.isBlockStyle(e)) { const s = t.getRangeAt(0), n = this.domUtils.findBlockElementsInRange(s); return n.length > 0 && n.every((a) => this.styleManager.has(a, e)); } const r = t.getRangeAt(0); if (r.collapsed) { const s = this.domUtils.getSelectedRoot(t, !0, e); return (s == null ? void 0 : s.some((n) => n.nodeType === Node.ELEMENT_NODE ? this.styleManager.has(n, e) : !1)) ?? !1; } const o = this.domUtils.collectTextNodesInRangeForRead(r).filter((s) => { var n; return (n = s.textContent) == null ? void 0 : n.trim(); }); return o.length === 0 ? !1 : o.every((s) => { let n = s.parentElement; for (; n && n !== this.container; ) { if (this.styleManager.has(n, e)) return !0; n = n.parentElement; } return !1; }); } getStyle(e) { const t = this.getSelection(); if (!t || t.rangeCount === 0) return null; const r = this.domUtils.getSelectedRoot(t, !0); if (!r) return null; for (const o of r) return o.nodeType === Node.ELEMENT_NODE ? o.style[e] ?? null : null; } } export { R as TextFormatter };