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
3 lines (2 loc) • 8.34 kB
JavaScript
/*! 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 */
"use strict";var g=Object.defineProperty;var y=(d,t,e)=>t in d?g(d,t,{enumerable:!0,configurable:!0,writable:!0,value:e}):d[t]=e;var C=(d,t,e)=>y(d,typeof t!="symbol"?t+"":t,e);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const u=require("./FormatDebugMap.cjs");class x{constructor(t){C(this,"tracer",null);this.container=t}setTracer(t){this.tracer=t}getContainer(){return this.container}getSelectedRoot(t,e=!1,i){const s=t.getRangeAt(0);return!s.collapsed||this.hasContentInRange(s)?this.getDeepestNodes(s,e,i):e?this.getCollapsedReadNodes(t):this.handleCollapsedRange(t,s)}findBlockElementsInRange(t){const e=new Set,i=document.createTreeWalker(this.container,NodeFilter.SHOW_ELEMENT,{acceptNode:s=>t.intersectsNode(s)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT});for(;i.nextNode();){const s=i.currentNode;this.isBlockNode(s)&&e.add(s)}return e.size===0&&t.intersectsNode(this.container)&&Array.from(this.container.childNodes).some(n=>{var r;return n.nodeType===Node.TEXT_NODE&&((r=n.textContent)==null?void 0:r.trim())})&&!this.container.querySelector("div, p, li, h1, h2, h3, h4, h5, h6")?[]:Array.from(e)}getDeepestNodes(t,e,i){var a,l,h,N,p,m,T,E;const s=[];if(!(this.container.contains(t.commonAncestorContainer)||this.container.getRootNode().contains(t.commonAncestorContainer)))return(a=this.tracer)==null||a.step("getDeepestNodes",[],"outside_container"),[];e||((l=this.tracer)==null||l.step("splitBoundaryNodes",{startOffset:t.startOffset,endOffset:t.endOffset}),this.splitBoundaryNodes(t));const r=this.collectTextNodesInRange(t);(h=this.tracer)==null||h.step("collectTextNodes",r.map(f=>{var o;return{path:u.getNodePath(f,this.container),text:(o=f.textContent)==null?void 0:o.slice(0,30)}}));for(const f of r){if(!((N=f.textContent)!=null&&N.trim()))continue;const o=this.findFormattingParent(f);if(o){const c=this.isNodeFullySelected(o,t);(p=this.tracer)==null||p.step("formattingParent",{path:u.getNodePath(o,this.container),tag:o.tagName},c?"fully_selected":"partial_selection")}if(o&&this.isNodeFullySelected(o,t)&&!s.includes(o)){s.push(o);continue}if(!e&&o&&i&&this.styleCommandOnElement(o,i)&&!this.isNodeFullySelected(o,t)){const c=this.isolatePartialFormattedSegment(o,t);(m=this.tracer)==null||m.step("isolatePartialFormattedSegment",c?u.getNodePath(c,this.container):null,"partial_in_formatted_parent"),c&&!s.includes(c)&&s.push(c);continue}if(e&&o&&!s.includes(o)){s.push(o);continue}if(e)o&&!s.includes(o)&&s.push(o);else{const c=this.createSpanIfNeeded(f,t);c&&((T=this.tracer)==null||T.step("createSpanIfNeeded",u.getNodePath(c,this.container),c===o?"reuse_parent":"new_span")),c&&!s.includes(c)&&s.push(c)}}return(E=this.tracer)==null||E.setNodesToModify(s,this.container),s}collectTextNodesInRange(t){const e=[],i=document.createTreeWalker(t.commonAncestorContainer,NodeFilter.SHOW_TEXT,{acceptNode:n=>t.intersectsNode(n)?NodeFilter.FILTER_ACCEPT:NodeFilter.FILTER_REJECT}),s=t.startContainer;if(s.nodeType===Node.TEXT_NODE)t.intersectsNode(s)&&e.push(s);else if(s.nodeType===Node.ELEMENT_NODE){const n=s.childNodes[t.startOffset];(n==null?void 0:n.nodeType)===Node.TEXT_NODE&&t.intersectsNode(n)&&e.push(n)}for(i.currentNode=s;i.nextNode();){const n=i.currentNode;if(!t.intersectsNode(n)||t.endContainer===n&&t.endOffset===0)break;if(n===t.endContainer&&t.endContainer.nodeType===Node.TEXT_NODE){t.endOffset>0&&!e.includes(n)&&e.push(n);break}e.includes(n)||e.push(n)}return e.length===0&&t.startContainer.nodeType===Node.TEXT_NODE&&t.intersectsNode(t.startContainer)&&e.push(t.startContainer),e}splitBoundaryNodes(t){const e=t.endContainer,i=t.endOffset;e.nodeType===Node.TEXT_NODE&&i>0&&i<e.length&&e.splitText(i);const s=t.startContainer,n=t.startOffset;if(s.nodeType===Node.TEXT_NODE&&n>0&&n<s.length){const r=s.splitText(n);e===s&&t.setEnd(r,i-n),t.setStart(r,0)}}isolatePartialFormattedSegment(t,e){var r,a;const i=document.createElement("span");this.copyFormattingAttributes(t,i);const s=e.extractContents();return i.appendChild(s),e.startContainer===t&&e.startOffset===0||e.startContainer===t.firstChild&&e.startOffset===0&&t.firstChild===t.childNodes[0]?(r=t.parentNode)==null||r.insertBefore(i,t):(a=t.parentNode)==null||a.insertBefore(i,t.nextSibling),this.unwrapIfEmpty(t),i}styleCommandOnElement(t,e){const s={bold:"format-bold",italic:"format-italic",underline:"format-underline",strikethrough:"format-strikethrough",superscript:"format-superscript",subscript:"format-subscript"}[e];return!!(s&&t.classList.contains(s)||e==="bold"&&t.tagName==="B"||e==="italic"&&t.tagName==="I"||e==="underline"&&t.tagName==="U")}collectTextNodesInRangeForRead(t){return this.collectTextNodesInRange(t)}isNodeFullySelected(t,e){var r;if(!((r=t.textContent)!=null&&r.trim()))return!1;const i=document.createTreeWalker(t,NodeFilter.SHOW_TEXT);let s,n=!1;for(;s=i.nextNode();){const a=s.textContent;if(!(a!=null&&a.trim()))continue;if(n=!0,!e.intersectsNode(s))return!1;const l=a.length,h=s===e.startContainer?e.startOffset:0,N=s===e.endContainer?e.endOffset:l;if(h>0||N<l)return!1}return n}findFormattingParent(t){let e=t.parentElement;for(;e&&e!==this.container;){if(this.isFormattingElement(e))return e;e=e.parentElement}return null}isFormattingElement(t){if(this.isTableNode(t)||this.isBlockNode(t))return!1;const e=t.tagName;return["B","I","U","STRIKE","S","SUB","SUP"].includes(e)||e==="A"&&t.hasAttribute("href")?!0:e==="SPAN"?t.classList.length>0||!!t.getAttribute("style")||Array.from(t.classList).some(i=>i.startsWith("format-")):Array.from(t.classList).some(i=>i.startsWith("format-"))}getCollapsedReadNodes(t){const e=t.anchorNode;if(!e)return[];if(!(this.container.contains(e)||this.container.getRootNode().contains(e)))return[];const s=[];let n=e.nodeType===Node.TEXT_NODE?e.parentElement:e;for(;n&&n!==this.container;)this.isFormattingElement(n)&&s.push(n),n=n.parentElement;return s}handleCollapsedRange(t,e){var a,l;const i=t.anchorNode;if(!i)return[];if(!(this.container.contains(i)||this.container.getRootNode().contains(i)))return[];const n=this.findFormattingParent(i);if(n)return(a=this.tracer)==null||a.step("handleCollapsedRange",u.getNodePath(n,this.container),"reuse_formatting_parent"),[n];const r=document.createElement("span");return r.textContent="",e.insertNode(r),e.setStart(r,1),e.setEnd(r,1),t.removeAllRanges(),t.addRange(e),(l=this.tracer)==null||l.step("handleCollapsedRange",u.getNodePath(r,this.container),"insert_zwsp_span"),[r]}hasContentInRange(t){return t.toString().trim().length>0||t.cloneContents().childNodes.length>0}createSpanIfNeeded(t,e){if(t.nodeType!==Node.TEXT_NODE||!t.textContent)return null;const i=t.textContent,s=t===e.startContainer?e.startOffset:0,n=t===e.endContainer?e.endOffset:i.length;if(n===0||s===i.length||s===n)return null;const r=this.findFormattingParent(t);if(s===0&&n===i.length&&r&&this.isNodeFullySelected(r,e))return r;const a=document.createElement("span"),l=i.substring(s,n);return a.textContent=l,this.replaceNodeWithSpan(t,a,s,n),a}copyFormattingAttributes(t,e){Array.from(t.classList).forEach(i=>e.classList.add(i)),t.style.cssText&&(e.style.cssText=t.style.cssText)}replaceNodeWithSpan(t,e,i,s){const n=t.textContent,r=t.parentNode;if(!r||!n)return;const a=document.createTextNode(n.substring(0,i)),l=document.createTextNode(n.substring(s));r.insertBefore(a,t),r.insertBefore(e,t),r.insertBefore(l,t),r.removeChild(t)}isTableNode(t){return t?!!(t.closest("table")||t.closest("tr")||t.closest("td")||t.closest("th")):!1}isBlockNode(t){return t?t.classList.contains("block-content")?!0:["DIV","P","LI","H1","H2","H3","H4","H5","H6"].includes(t.tagName):!1}applyStyleToNode(t,e,i){if(t.nodeType===Node.ELEMENT_NODE){const s=t;(!this.isBlockNode(s)||this.isFormattingElement(s))&&i(s,e)}}removeStyleFromNode(t,e,i){if(t.nodeType!==Node.ELEMENT_NODE)return;const s=t;!this.isFormattingElement(s)&&!s.classList.length||(i(s,e),this.unwrapIfEmpty(s))}unwrapIfEmpty(t){if(!t.getAttribute("style")&&t.classList.length===0&&t.tagName==="SPAN"){const e=t.parentNode;for(;t.firstChild;)e==null||e.insertBefore(t.firstChild,t);e==null||e.removeChild(t)}}}exports.DomUtils=x;