web-highlighter
Version:
✨A no-runtime dependency lib for text highlighting & persistence on any website ✨🖍️
67 lines (56 loc) • 2.01 kB
text/typescript
import type { DomNode } from '@src//types';
import type HighlightSource from '@src/model/source/index';
import { ROOT_IDX } from '@src/util/const';
/**
* Because of supporting highlighting a same area (range overlapping),
* Highlighter will calculate which text-node and how much offset it actually be,
* based on the origin website dom node and the text offset.
*
* @param {Node} $parent element node in the origin website dom tree
* @param {number} offset text offset in the origin website dom tree
* @return {DomNode} DOM a dom info object
*/
export const getTextChildByOffset = ($parent: Node, offset: number): DomNode => {
const nodeStack: Node[] = [$parent];
let $curNode: Node = null;
let curOffset = 0;
let startOffset = 0;
while (($curNode = nodeStack.pop())) {
const children = $curNode.childNodes;
for (let i = children.length - 1; i >= 0; i--) {
nodeStack.push(children[i]);
}
if ($curNode.nodeType === 3) {
startOffset = offset - curOffset;
curOffset += $curNode.textContent.length;
if (curOffset >= offset) {
break;
}
}
}
if (!$curNode) {
$curNode = $parent;
}
return {
$node: $curNode,
offset: startOffset,
};
};
/**
* get start and end parent element from meta info
*
* @param {HighlightSource} hs
* @param {HTMLElement | Document} $root root element, default document
* @return {Object}
*/
export const queryElementNode = (hs: HighlightSource, $root: Document | HTMLElement): { start: Node; end: Node } => {
const start =
hs.startMeta.parentIndex === ROOT_IDX
? $root
: $root.getElementsByTagName(hs.startMeta.parentTagName)[hs.startMeta.parentIndex];
const end =
hs.endMeta.parentIndex === ROOT_IDX
? $root
: $root.getElementsByTagName(hs.endMeta.parentTagName)[hs.endMeta.parentIndex];
return { start, end };
};