UNPKG

pangu

Version:

Opinionated paranoid text spacing: automatically inserts whitespace between CJK (Chinese, Japanese, Korean) and ANS (alphabetical letters, numerical digits and symbols)

64 lines (63 loc) • 2.77 kB
//#region src/browser/dom/dom-walker.ts var DomWalker = class { static ignoredClass = "no-pangu-spacing"; static ignoredTags = /^(code|pre|script|style|textarea|iframe|input)$/i; static blockTags = /^(div|p|h1|h2|h3|h4|h5|h6)$/i; static spaceLikeTags = /^(br|hr|i|img|pangu)$/i; static spaceSensitiveTags = /^(a|del|i|pre|s|strike|u)$/i; static collectTextNodes(root, reverse = false) { const nodes = []; if (!root || root instanceof DocumentFragment) return nodes; const walker = this.createTextWalker(root); while (walker.nextNode()) nodes.push(walker.currentNode); return reverse ? nodes.reverse() : nodes; } static findAdjacentTextNode(textNode, direction) { let root = textNode; while (root.parentNode && root !== document.body && !this.blockTags.test(root.nodeName)) root = root.parentNode; const walker = this.createTextWalker(root); walker.currentNode = textNode; return direction === "previous" ? walker.previousNode() : walker.nextNode(); } static findBoundaryNode(textNode, edge) { let node = textNode; while (node.parentNode && !this.spaceSensitiveTags.test(node.nodeName) && (edge === "first" ? this.isFirstTextChild(node.parentNode, node) : this.isLastTextChild(node.parentNode, node))) node = node.parentNode; return node; } static isFirstTextChild(parentNode, targetNode) { const { childNodes } = parentNode; for (const childNode of childNodes) if (childNode.nodeType !== Node.COMMENT_NODE && childNode.textContent) return childNode === targetNode; return false; } static isLastTextChild(parentNode, targetNode) { const { childNodes } = parentNode; for (let i = childNodes.length - 1; i > -1; i--) { const childNode = childNodes[i]; if (childNode.nodeType !== Node.COMMENT_NODE && childNode.textContent) return childNode === targetNode; } return false; } static createTextWalker(root) { return document.createTreeWalker(root, NodeFilter.SHOW_TEXT, { acceptNode: (node) => { if (!node.nodeValue || !/\S/.test(node.nodeValue)) return NodeFilter.FILTER_REJECT; return this.isIgnoredNode(node) ? NodeFilter.FILTER_REJECT : NodeFilter.FILTER_ACCEPT; } }); } static isIgnoredNode(node) { let currentNode = node; while (currentNode) { if (currentNode instanceof Element && this.isIgnoredElement(currentNode)) return true; currentNode = currentNode.parentNode; } return false; } static isIgnoredElement(element) { return this.ignoredTags.test(element.nodeName) || this.isContentEditable(element) || element.classList.contains(this.ignoredClass); } static isContentEditable(node) { return node instanceof HTMLElement && (node.isContentEditable || node.getAttribute("g_editable") === "true"); } }; //#endregion export { DomWalker }; //# sourceMappingURL=dom-walker.js.map