This is an automated email from the ASF dual-hosted git repository. gerben pushed a commit to branch match-any-selector in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 27650e59ed276e3f270ecdba33596a2565c6106d Author: Gerben <[email protected]> AuthorDate: Wed Nov 23 22:06:26 2022 +0100 Add convenience matcher functions to dom package - createAnySelectorMatcher mimics the signature of existing matcher creators. - matchSelector makes the common use case easy. --- packages/dom/src/index.ts | 1 + packages/dom/src/match.ts | 101 ++++++++++++++++++++++++++++++++++++++++++++++ web/index.js | 28 +------------ 3 files changed, 104 insertions(+), 26 deletions(-) diff --git a/packages/dom/src/index.ts b/packages/dom/src/index.ts index 6969ea9..5073811 100644 --- a/packages/dom/src/index.ts +++ b/packages/dom/src/index.ts @@ -21,6 +21,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +export * from './match.js'; export * from './css.js'; export * from './range/index.js'; export * from './text-quote/index.js'; diff --git a/packages/dom/src/match.ts b/packages/dom/src/match.ts new file mode 100644 index 0000000..d0038f8 --- /dev/null +++ b/packages/dom/src/match.ts @@ -0,0 +1,101 @@ +/** + * @license + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + * + * SPDX-FileCopyrightText: The Apache Software Foundation + * SPDX-License-Identifier: Apache-2.0 + */ + +import { asArray, OneOrMore } from "@apache-annotator/annotation"; +import type { + CssSelector, + TextQuoteSelector, + TextPositionSelector, + RangeSelector, + Matcher, + Refinable, +} from "@apache-annotator/selector"; +import { makeRefinable } from "@apache-annotator/selector"; +import { createCssSelectorMatcher } from "./css"; +import { makeCreateRangeSelectorMatcher } from "./range"; +import { createTextPositionSelectorMatcher } from "./text-position"; +import { createTextQuoteSelectorMatcher } from "./text-quote"; + +export const supportedSelectorTypes = [ + "CssSelector", + "TextQuoteSelector", + "TextPositionSelector", + "RangeSelector", +]; + +export type SupportedSelector = Refinable< + | CssSelector + | TextQuoteSelector + | TextPositionSelector + | RangeSelector<SupportedSelector> +>; + +export type DomScope = Node | Range; +export type DomMatch = Element | Range; +export type DomMatcher = Matcher<DomScope, DomMatch>; + +const createMatcher: ( + selector: SupportedSelector +) => DomMatcher = makeRefinable<SupportedSelector, DomScope, DomMatch>( + (selector: SupportedSelector) => { + if (selector.type === "CssSelector") + return createCssSelectorMatcher(selector); + if (selector.type === "TextQuoteSelector") + return createTextQuoteSelectorMatcher(selector); + if (selector.type === "TextPositionSelector") + return createTextPositionSelectorMatcher(selector); + if (selector.type === "RangeSelector") + return makeCreateRangeSelectorMatcher( + // @ts-ignore (needless type error; bug in TypeScript?) + createMatcher + )(selector); + throw new Error(`Unsupported selector type: ${(selector as any)?.type}`); + } +); + +export function createAnySelectorMatcher( + oneOrMoreSelectors: OneOrMore<SupportedSelector> +): DomMatcher { + const selectors = asArray(oneOrMoreSelectors); + // Use the first selector we understand. (“Multiple Selectors SHOULD select the same content”) + // TODO Take the more precise one; retry with others if the first fails; perhaps combine e.g. Position+Quote for speedup. + const selector = selectors.find( + (selector) => + selector.type && supportedSelectorTypes.includes(selector.type) + ); + if (!selector) throw new Error(`Unsupported selector type: ${asArray(selectors).map(s => s.type)}`); + const matcher = createMatcher(selector as SupportedSelector) + return matcher; +} + +export async function matchSelector( + selectors: OneOrMore<SupportedSelector>, + scope: DomScope = window.document +) { + const matchGenerator = createAnySelectorMatcher(selectors)(scope); + const matches: DomMatch[] = []; + for await (const match of matchGenerator) { + matches.push(match); + } + return matches; +} diff --git a/web/index.js b/web/index.js index 7917798..bdaa31c 100644 --- a/web/index.js +++ b/web/index.js @@ -24,14 +24,11 @@ /* global info, module, source, target, form */ import { - makeCreateRangeSelectorMatcher, - createTextQuoteSelectorMatcher, + matchSelector, describeTextQuote, - createTextPositionSelectorMatcher, describeTextPosition, highlightText, } from '@apache-annotator/dom'; -import { makeRefinable } from '@apache-annotator/selector'; const EXAMPLE_SELECTORS = [ { @@ -99,29 +96,8 @@ function cleanup() { info.innerText = ''; } -const createMatcher = makeRefinable((selector) => { - const innerCreateMatcher = { - TextQuoteSelector: createTextQuoteSelectorMatcher, - TextPositionSelector: createTextPositionSelectorMatcher, - RangeSelector: makeCreateRangeSelectorMatcher(createMatcher), - }[selector.type]; - - if (!innerCreateMatcher) { - throw new Error(`Unsupported selector type: ${selector.type}`); - } - - return innerCreateMatcher(selector); -}); - async function anchor(selector) { - const matchAll = createMatcher(selector); - const ranges = []; - - // First collect all matches, and only then highlight them; to avoid - // modifying the DOM while the matcher is running. - for await (const range of matchAll(target)) { - ranges.push(range); - } + const ranges = matchSelector(selector, target); for (const range of ranges) { const removeHighlight = highlightText(range);
