blob: 150058f4fe5eef2955ce059d8256054f6267d2dd [file] [log] [blame]
/**
* @license
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
import { makeRefinable } from '@annotator/selector';
import { createRangeSelectorCreator } from '@annotator/range';
import { createTextQuoteSelector } from '@annotator/text';
const createSelector = makeRefinable(selector => {
const selectorCreator = {
TextQuoteSelector: createTextQuoteSelector,
RangeSelector: createRangeSelectorCreator(createSelector),
}[selector.type];
if (selectorCreator == null) {
throw new Error(`Unsupported selector type: ${selector.type}`);
}
return selectorCreator(selector);
});
/**
* Locate a selector.
* @param {Node} root node
* @param {Selector} selector
* @return {Range}
*/
export async function* search(root, selector) {
for (const node of nodeIterator(root)) {
if (!node.nodeValue) continue;
const matches = createSelector(selector)(node.nodeValue);
for await (let match of matches) {
const startIndex = match.index;
const endIndex = startIndex + match[0].length;
const range = document.createRange();
range.setStart(node, startIndex);
range.setEnd(node, endIndex);
yield range;
}
}
}
/**
* Iterate over the nodes of a sub-tree of the document.
* @param {Node} node
*/
function* nodeIterator(node) {
yield node;
for (const child of node.childNodes) {
yield* nodeIterator(child);
}
}