This is an automated email from the ASF dual-hosted git repository.

gerben pushed a commit to branch simpler-matcher-creation
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git

commit 92531b495cb28b6ad2f8ba8f09ca6e0324eaee57
Merge: d896d01 cc99947
Author: Gerben <[email protected]>
AuthorDate: Sun Jun 20 17:02:53 2021 +0200

    Merge branch 'master' into simpler-matcher-creation

 .eslintignore                                      |    1 +
 .eslintrc.js                                       |   82 +-
 .gitignore                                         |    5 +
 .mocharc.js                                        |    7 +-
 .travis.yml                                        |   23 +-
 Makefile                                           |    1 +
 README.md                                          |  116 +-
 babel-register.js                                  |    5 +-
 babel.config.js                                    |   33 +-
 lerna.json                                         |    8 +-
 package.json                                       |   44 +-
 packages/apache-annotator/.npmignore               |    5 +
 packages/apache-annotator/package.json             |   28 +
 .../src/types.ts => apache-annotator/src/dom.ts}   |   15 +-
 .../types.ts => apache-annotator/src/selector.ts}  |   16 +-
 packages/{dom => apache-annotator}/tsconfig.json   |    1 +
 packages/dom/.npmignore                            |    7 +-
 packages/dom/@types/optimal-select/index.d.ts      |    9 +
 packages/dom/README.md                             |    3 -
 packages/dom/package.json                          |   23 +-
 packages/dom/src/css.ts                            |   83 +-
 packages/dom/src/highlight-range.ts                |   37 +-
 packages/dom/src/index.ts                          |    1 +
 packages/dom/src/normalize-range.ts                |  165 ++
 packages/dom/src/{scope.ts => owner-document.ts}   |   27 +-
 packages/dom/src/range/cartesian.ts                |  113 +-
 packages/dom/src/range/match.ts                    |  105 +-
 packages/dom/src/text-node-chunker.ts              |  170 ++
 packages/dom/src/text-position/describe.ts         |   72 +
 .../dom-seek.d.ts => text-position/index.ts}       |    8 +-
 packages/dom/src/text-position/match.ts            |   68 +
 packages/dom/src/text-quote/describe.ts            |  221 +-
 packages/dom/src/text-quote/match.ts               |  117 +-
 .../dom/src/{types/cartesian.d.ts => to-range.ts}  |   28 +-
 packages/dom/test/css/describe.test.ts             |   58 +
 packages/dom/test/css/match-cases.ts               |   56 +
 packages/dom/test/css/match.test.ts                |   59 +
 .../test/highlight-range/highlight-range.test.ts   |    8 +-
 packages/dom/test/range/cartesian.test.ts          |   66 +-
 packages/dom/test/text-position/describe.test.ts   |   56 +
 packages/dom/test/text-position/match-cases.ts     |  143 ++
 .../{text-quote => text-position}/match.test.ts    |  134 +-
 packages/dom/test/text-quote/describe-cases.ts     |  316 ++-
 packages/dom/test/text-quote/describe.test.ts      |   79 +-
 packages/dom/test/text-quote/match-cases.ts        |   58 +-
 packages/dom/test/text-quote/match.test.ts         |   50 +-
 packages/dom/tsconfig.json                         |    2 +-
 packages/selector/.npmignore                       |    7 +-
 packages/selector/README.md                        |    3 -
 packages/selector/package.json                     |   18 +-
 packages/selector/src/index.ts                     |   10 +-
 packages/selector/src/text/chunker.ts              |  157 ++
 packages/selector/src/text/code-point-seeker.ts    |  196 ++
 .../selector/src/text/describe-text-position.ts    |   61 +
 packages/selector/src/text/describe-text-quote.ts  |  298 +++
 .../src/types.ts => selector/src/text/index.ts}    |   10 +-
 packages/selector/src/text/match-text-position.ts  |   76 +
 packages/selector/src/text/match-text-quote.ts     |  208 ++
 packages/selector/src/text/seeker.ts               |  415 ++++
 packages/selector/src/types.ts                     |   61 +
 test/data-model.test.ts                            |    5 +-
 tsconfig.base.json                                 |    7 +-
 tsconfig.json                                      |   15 +-
 tsconfig.test.json                                 |    8 +
 typedoc.json                                       |    5 +
 web/demo/index.html                                |   99 -
 web/index.html                                     |   79 +-
 web/{demo => }/index.js                            |   59 +-
 web/test/index.html                                |   24 -
 web/webpack.config.js                              |   20 +-
 yarn.lock                                          | 2328 ++++++++++----------
 71 files changed, 4846 insertions(+), 2055 deletions(-)

diff --cc packages/dom/src/range/match.ts
index 87f54b4,d6891c6..d0b6943
--- a/packages/dom/src/range/match.ts
+++ b/packages/dom/src/range/match.ts
@@@ -18,17 -18,86 +18,86 @@@
   * under the License.
   */
  
- import type { RangeSelector, Selector, MatcherCreator, Plugin } from 
'@annotator/selector';
- 
- import { ownerDocument } from '../scope';
- import type { DomMatcher, DomScope } from '../types';
- 
- import { product } from './cartesian';
+ import type {
+   Matcher,
++  Plugin,
+   RangeSelector,
+   Selector,
++  MatcherCreator,
+ } from '@apache-annotator/selector';
+ import { ownerDocument } from '../owner-document';
+ import { toRange } from '../to-range';
+ import { cartesian } from './cartesian';
  
+ /**
+  * Find the range(s) corresponding to the given {@link RangeSelector}.
+  *
+  * As a RangeSelector itself nests two further selectors, one needs to pass a
+  * `createMatcher` function that will be used to process those nested 
selectors.
+  *
+  * The function is curried, taking first the `createMatcher` function, then 
the
+  * selector, and then the scope.
+  *
+  * As there may be multiple matches for the start & end selectors, the 
resulting
+  * matcher will return an (async) iterable, that produces a match for each
+  * possible pair of matches of the nested selectors (except those where its 
end
+  * would precede its start). *(Note that this behaviour is a rather free
+  * interpretation of the Web Annotation Data Model spec, which is silent about
+  * the possibility of multiple matches for RangeSelectors)*
+  *
+  * @example
+  * By using a matcher for {@link TextQuoteSelector}s, one
+  * could create a matcher for text quotes with ellipsis to select a phrase
+  * “ipsum … amet,”:
+  * ```
+  * const selector = {
+  *   type: 'RangeSelector',
+  *   startSelector: {
+  *     type: 'TextQuoteSelector',
+  *     exact: 'ipsum ',
+  *   },
+  *   endSelector: {
+  *     type: 'TextQuoteSelector',
+  *     // Because the end of a RangeSelector is *exclusive*, we will present 
the
+  *     // latter part of the quote as the *prefix* so it will be part of the
+  *     // match.
+  *     exact: '',
+  *     prefix: ' amet,',
+  *   }
+  * };
+  * const createRangeSelectorMatcher =
+  *   makeCreateRangeSelectorMatcher(createTextQuoteMatcher);
+  * const match = createRangeSelectorMatcher(selector)(document.body);
+  * console.log(match)
+  * // ⇒ Range { startContainer: #text, startOffset: 6, endContainer: #text,
+  * //   endOffset: 27, … }
+  * ```
+  *
+  * @example
+  * To support RangeSelectors that might themselves contain RangeSelectors,
+  * recursion can be created by supplying the resulting matcher creator 
function
+  * as the `createMatcher` parameter:
+  * ```
+  * const createWhicheverMatcher = (selector) => {
+  *   const innerCreateMatcher = {
+  *     TextQuoteSelector: createTextQuoteSelectorMatcher,
+  *     TextPositionSelector: createTextPositionSelectorMatcher,
+  *     RangeSelector: makeCreateRangeSelectorMatcher(createWhicheverMatcher),
+  *   }[selector.type];
+  *   return innerCreateMatcher(selector);
+  * });
+  * ```
+  *
+  * @param createMatcher - The function used to process nested selectors.
+  * @returns A function that, given a RangeSelector `selector`, creates a 
{@link
+  * Matcher} function that can apply it to a given `scope`.
+  *
+  * @public
+  */
  export function makeCreateRangeSelectorMatcher(
-   createMatcher: <T extends Selector>(selector: T) => DomMatcher,
- ): (selector: RangeSelector) => DomMatcher {
-   return function createRangeSelectorMatcher(selector: RangeSelector) {
 -  createMatcher: <T extends Selector, TMatch extends Node | Range>(
 -    selector: T,
 -  ) => Matcher<Node | Range, TMatch>,
++  createMatcher: MatcherCreator<Node | Range, Node | Range>,
+ ): (selector: RangeSelector) => Matcher<Node | Range, Range> {
+   return function createRangeSelectorMatcher(selector) {
      const startMatcher = createMatcher(selector.startSelector);
      const endMatcher = createMatcher(selector.endSelector);
  
@@@ -51,17 -122,3 +122,17 @@@
      };
    };
  }
 +
- export const supportRangeSelector: Plugin<DomScope, Range> = function 
supportRangeSelectorPlugin(
++export const supportRangeSelector: Plugin<Node | Range, Node | Range> = 
function supportRangeSelectorPlugin(
 +  next,
 +  recurse,
 +) {
 +  const createRangeSelectorMatcher = makeCreateRangeSelectorMatcher(recurse);
 +  return function (selector: Selector) {
 +    if (selector.type === 'RangeSelector') {
 +      return createRangeSelectorMatcher(selector as RangeSelector);
 +    } else {
 +      return next(selector);
 +    }
 +  };
 +};
diff --cc packages/selector/src/index.ts
index 2214711,e0a5e48..7c294ae
--- a/packages/selector/src/index.ts
+++ b/packages/selector/src/index.ts
@@@ -18,69 -18,47 +18,77 @@@
   * under the License.
   */
  
 -import type { Matcher, Selector } from './types';
 +import type { Matcher, Selector, SelectorType, MatcherCreator, Plugin } from 
'./types';
  
 -export type { Matcher, Selector } from './types';
 -export type {
 -  CssSelector,
 -  RangeSelector,
 -  TextPositionSelector,
 -  TextQuoteSelector,
 -} from './types';
+ export * from './text';
 +export * from './types';
 +
 +interface TypeToMatcherCreatorMap<TScope, TMatch> {
 +  // [K: SelectorType]: MatcherCreator<TScope, TMatch>; // Gives errors 
further down. TypeScript’s fault?
 +  [K: string]: MatcherCreator<TScope, TMatch> | undefined;
 +}
 +
 +export function composeMatcherCreator<TScope, TMatch extends TScope>(
 +  ...plugins: Array<Plugin<TScope, TMatch>>
 +): MatcherCreator<TScope, TMatch> {
 +  function innerMatcherCreator(selector: Selector): Matcher<TScope, TMatch> {
 +    throw new TypeError(`Unhandled selector. Selector type: 
${selector.type}`);
 +  }
 +
 +  function outerMatcherCreator(selector: Selector): Matcher<TScope, TMatch> {
 +    return composedMatcherCreator(selector);
 +  }
 +
 +  const composedMatcherCreator = plugins.reduceRight(
 +    (
 +      matcherCreator: MatcherCreator<TScope, TMatch>,
 +      plugin: Plugin<TScope, TMatch>
 +    ) => plugin(matcherCreator, outerMatcherCreator),
 +    innerMatcherCreator,
 +  );
 +
 +  return outerMatcherCreator;
 +}
 +
 +// A plugin with parameters (i.e. a function that returns a plugin)
 +// Invokes the matcher implementation corresponding to the selector’s type.
 +export function mapSelectorTypes<TScope, TMatch extends TScope>(
 +  typeToMatcherCreator: TypeToMatcherCreatorMap<TScope, TMatch>,
 +): Plugin<TScope, TMatch> {
 +  return function mapSelectorTypesPlugin(next, recurse): 
MatcherCreator<TScope, TMatch> {
 +    return function(selector: Selector): Matcher<TScope, TMatch> {
 +      const type = selector.type;
 +      if (type !== undefined) {
 +        const matcherCreator = typeToMatcherCreator[type];
 +        if (matcherCreator !== undefined)
 +          return matcherCreator(selector);
 +      }
 +      // Not a know selector type; continue down the plugin chain.
 +      return next(selector);
 +    }
 +  }
 +}
  
- // A plugin to support the Selector’s refinedBy field .
+ /**
 - * Wrap a matcher creation function so that it supports refinement of 
selection.
++ * A plugin to support the Selector’s refinedBy field.
+  *
+  * See {@link 
https://www.w3.org/TR/2017/REC-annotation-model-20170223/#refinement-of-selection
+  * | §4.2.9 Refinement of Selection} in the Web Annotation Data Model.
+  *
 - * @param matcherCreator - The function to wrap; it will be executed both for
 - * {@link Selector}s passed to the returned wrapper function, and for any
 - * refining Selector those might contain (and any refinement of that, etc.).
 - *
+  * @public
+  */
 -export function makeRefinable<
 -  // Any subtype of Selector can be made refinable; but note we limit the 
value
 -  // of refinedBy because it must also be accepted by matcherCreator.
 -  TSelector extends Selector & { refinedBy: TSelector },
 -  TScope,
 -  // To enable refinement, the implementation’s Match object must be usable 
as a
 -  // Scope object itself.
 -  TMatch extends TScope
 ->(
 -  matcherCreator: (selector: TSelector) => Matcher<TScope, TMatch>,
 -): (selector: TSelector) => Matcher<TScope, TMatch> {
 +export const supportRefinement: Plugin<any, any> =
 +  function supportRefinementPlugin<TScope, TMatch extends TScope>(
 +    next: MatcherCreator<TScope, TMatch>,
 +    recurse: MatcherCreator<TScope, TMatch>,
 +  ) {
    return function createMatcherWithRefinement(
 -    sourceSelector: TSelector,
 +    sourceSelector: Selector,
    ): Matcher<TScope, TMatch> {
 -    const matcher = matcherCreator(sourceSelector);
 +    const matcher = next(sourceSelector);
  
      if (sourceSelector.refinedBy) {
 -      const refiningSelector = createMatcherWithRefinement(
 +      const refiningSelector = recurse(
          sourceSelector.refinedBy,
        );
  
diff --cc packages/selector/src/types.ts
index 2cf0929,fd59dfb..fff5199
--- a/packages/selector/src/types.ts
+++ b/packages/selector/src/types.ts
@@@ -18,13 -18,34 +18,38 @@@
   * under the License.
   */
  
+ /**
+  * A {@link 
https://www.w3.org/TR/2017/REC-annotation-model-20170223/#selectors
+  * | Selector} object of the Web Annotation Data Model.
+  *
+  * Corresponds to RDF class {@link http://www.w3.org/ns/oa#Selector}
+  *
+  * @public
+  */
  export interface Selector {
+   /**
+    * A Selector can be refined by another Selector.
+    *
+    * See {@link 
https://www.w3.org/TR/2017/REC-annotation-model-20170223/#refinement-of-selection
+    * | §4.2.9 Refinement of Selection} in the Web Annotation Data Model.
+    *
+    * Corresponds to RDF property {@link http://www.w3.org/ns/oa#refinedBy}
+    */
 -  refinedBy?: Selector;
 +  refinedBy?: this;
++
 +  type?: SelectorType;
  }
  
 +export type SelectorType = string; // not enumerating known options: we allow 
extensibility.
 +
+ /**
+  * The {@link 
https://www.w3.org/TR/2017/REC-annotation-model-20170223/#css-selector
+  * | CssSelector} of the Web Annotation Data Model.
+  *
+  * Corresponds to RDF class {@link http://www.w3.org/ns/oa#CssSelector}
+  *
+  * @public
+  */
  export interface CssSelector extends Selector {
    type: 'CssSelector';
    value: string;
diff --cc web/index.js
index fd5050e,abb9a8b..d0c33f8
--- a/web/index.js
+++ b/web/index.js
@@@ -18,23 -18,17 +18,21 @@@
   * under the License.
   */
  
- /* global info, module, source, target */
- // declare const module; // TODO type?
- // declare const info: HTMLElement;
- // declare const source: HTMLElement;
- // declare const target: HTMLElement;
+ /* global info, module, source, target, form */
  
  import {
 -  makeCreateRangeSelectorMatcher,
    createTextQuoteSelectorMatcher,
    describeTextQuote,
 +  supportRangeSelector,
+   createTextPositionSelectorMatcher,
+   describeTextPosition,
    highlightRange,
- } from '@annotator/dom';
+ } from '@apache-annotator/dom';
 -import { makeRefinable } from '@apache-annotator/selector';
 +import {
 +  composeMatcherCreator,
 +  mapSelectorTypes,
 +  supportRefinement,
- } from '@annotator/selector';
++} from '@apache-annotator/selector';
  
  const EXAMPLE_SELECTORS = [
    {
@@@ -97,15 -93,22 +97,17 @@@ function cleanup() 
      removeHighlight();
    }
    target.normalize();
+   info.innerText = '';
  }
  
 -const createMatcher = makeRefinable((selector) => {
 -  const innerCreateMatcher = {
 +const createMatcher = composeMatcherCreator(
 +  supportRefinement, // this plugin must come first: it needs to access the 
result of the ones below.
 +  supportRangeSelector,
 +  mapSelectorTypes({
      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);

Reply via email to