UNPKG

label-studio

Version:

Data Labeling Tool that is backend agnostic and can be embedded into your applications

205 lines (163 loc) 5.92 kB
import insertAfter from "insert-after"; // work directly with the html tree function documentForward(node) { if (node.firstChild) return node.firstChild; while (!node.nextSibling) { node = node.parentNode; if (!node) return null; } return node.nextSibling; } function isTextNode(node) { const TEXT_NODE = 3; return node.nodeType === TEXT_NODE; } function firstLeaf(node) { while (node.hasChildNodes()) node = node.firstChild; return node; } /* Find the last leaf node. */ function lastLeaf(node) { while (node.hasChildNodes()) node = node.lastChild; return node; } function getNextNode(node) { if (node.firstChild) return node.firstChild; while (node) { if (node.nextSibling) return node.nextSibling; node = node.parentNode; } } function getNodesInRange(range) { var start = range.startContainer; var end = range.endContainer; var commonAncestor = range.commonAncestorContainer; var nodes = []; var node; // walk parent nodes from start to common ancestor for (node = start.parentNode; node; node = node.parentNode) { nodes.push(node); if (node === commonAncestor) break; } nodes.reverse(); // walk children and siblings from start until end is found for (node = start; node; node = getNextNode(node)) { nodes.push(node); if (node === end) break; } return nodes; } function documentReverse(node) { if (node.lastChild) return node.lastChild; while (!node.previousSibling) { node = node.parentNode; if (!node) return null; } return node.previousSibling; } function splitText(node, offset) { let tail = node.cloneNode(false); tail.deleteData(0, offset); node.deleteData(offset, node.length - offset); return insertAfter(tail, node); } function normalizeBoundaries(range) { let { startContainer, startOffset, endContainer, endOffset } = range; let node, next, last, start, end; // Move the start container to the last leaf before any sibling boundary, // guaranteeing that any children of the container are within the range. if (startContainer.childNodes.length && startOffset > 0) { startContainer = lastLeaf(startContainer.childNodes[startOffset - 1]); startOffset = startContainer.length || startContainer.childNodes.length; } // Move the end container to the first leaf after any sibling boundary, // guaranteeing that any children of the container are within the range. if (endOffset < endContainer.childNodes.length) { endContainer = firstLeaf(endContainer.childNodes[endOffset]); endOffset = 0; } // Any TextNode in the traversal is valid unless excluded by the offset. function isTextNodeInRange(node) { if (!isTextNode(node)) return false; if (node === startContainer && startOffset > 0) return false; if (node === endContainer && endOffset === 0) return false; return true; } // Find the start TextNode. // The guarantees above provide that a document order traversal visits every // Node in the Range before visiting the last leaf of the end container. node = startContainer; next = node => (node === last ? null : documentForward(node)); last = lastLeaf(endContainer); while (node && !isTextNodeInRange(node)) node = next(node); start = node; // Find the end TextNode. // Similarly, a reverse document order traversal visits every Node in the // Range before visiting the first leaf of the start container. node = endContainer; next = node => (node === last ? null : documentReverse(node)); last = firstLeaf(startContainer); while (node && !isTextNodeInRange(node)) node = next(node); end = node; range.setStart(start, 0); range.setEnd(end, end.length); } function highlightRange(normedRange, cssClass, cssStyle, labels) { if (typeof cssClass === "undefined" || cssClass === null) { cssClass = "htx-annotation"; } const allNodes = getNodesInRange(normedRange._range); const textNodes = allNodes.filter(n => isTextNode(n)); var white = /^\s*$/; var nodes = textNodes; // normedRange.textNodes(), let nlen = nodes.length; if (nlen > 1 && nodes[nodes.length - 1].length !== normedRange._range.endOffset) nlen = nlen - 1; const results = []; for (var i = 0, len = nlen; i < len; i++) { var node = nodes[i]; if (!white.test(node.nodeValue)) { var hl = window.document.createElement("span"); hl.style.backgroundColor = cssStyle.backgroundColor; hl.addEventListener("click", function() { normedRange.onClickRegion(); }); hl.addEventListener("mouseover", function() { this.style.cursor = "pointer"; }); hl.className = cssClass; node.parentNode.replaceChild(hl, node); hl.appendChild(node); results.push(hl); } } // if (labels && labels.length !== 0) { // var dateSpan = document.createElement('sup'); // dateSpan.style.userSelect="none"; // dateSpan.style.fontSize="12px"; // dateSpan.innerHTML = "[" + labels.join(" ") + "]"; // var lastSpan = results[results.length - 1]; // lastSpan.appendChild(dateSpan); // } return results; } function splitBoundaries(range) { let { startContainer, startOffset, endContainer, endOffset } = range; if (isTextNode(endContainer)) { if (endOffset > 0 && endOffset < endContainer.length) { endContainer = splitText(endContainer, endOffset); range.setEnd(endContainer, 0); } } if (isTextNode(startContainer)) { if (startOffset > 0 && startOffset < startContainer.length) { if (startContainer === endContainer) { startContainer = splitText(startContainer, startOffset); range.setEnd(startContainer, endOffset - startOffset); } else { startContainer = splitText(startContainer, startOffset); } range.setStart(startContainer, 0); } } } export { highlightRange, splitBoundaries, normalizeBoundaries };