This is an automated email from the ASF dual-hosted git repository. gerben pushed a commit to branch import-dom-seek in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git
commit 5706821a20472b934fd7a524e2298bd7cb346b9c Author: Gerben <[email protected]> AuthorDate: Mon Nov 16 23:01:25 2020 +0100 Implement describe text position --- packages/dom/src/text-position/describe.ts | 74 ++++++++++++++++++++++++++++++ packages/dom/src/text-position/index.ts | 1 + 2 files changed, 75 insertions(+) diff --git a/packages/dom/src/text-position/describe.ts b/packages/dom/src/text-position/describe.ts new file mode 100644 index 0000000..8baff8d --- /dev/null +++ b/packages/dom/src/text-position/describe.ts @@ -0,0 +1,74 @@ +/** + * @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. + */ + +import type { TextPositionSelector } from '@annotator/selector'; +import { ownerDocument } from '../owner-document'; +import { Chunk, Chunker, ChunkRange, TextNodeChunker, PartialTextNode } from '../chunker'; +import { CodePointSeeker } from '../code-point-seeker'; +import { TextSeeker, NonEmptyChunker } from '../seek'; + +export async function describeTextPosition( + range: Range, + maybeScope?: Range, +): Promise<TextPositionSelector> { + // Default to search in the whole document. + let scope: Range; + if (maybeScope !== undefined) { + scope = maybeScope; + } else { + const document = ownerDocument(range); + scope = document.createRange(); + scope.selectNodeContents(document); + } + + // Take the part of the range that falls within the scope. + range = range.cloneRange(); + if (range.compareBoundaryPoints(Range.START_TO_START, scope) === -1) + range.setStart(scope.startContainer, scope.startOffset); + if (range.compareBoundaryPoints(Range.END_TO_END, scope) === 1) + range.setEnd(scope.endContainer, scope.endOffset); + + const textChunks = new TextNodeChunker(scope); + if (textChunks.currentChunk === null) + throw new RangeError('Range does not contain any Text nodes.'); + + return await abstractDescribeTextPosition( + textChunks.rangeToChunkRange(range), + textChunks as NonEmptyChunker<PartialTextNode>, + ); +} + +async function abstractDescribeTextPosition<TChunk extends Chunk<string>>( + target: ChunkRange<TChunk>, + scope: NonEmptyChunker<TChunk>, +): Promise<TextPositionSelector> { + const codeUnitSeeker = new TextSeeker(scope); + const codePointSeeker = new CodePointSeeker(codeUnitSeeker); + + codePointSeeker.seekToChunk(target.startChunk, target.startIndex); + const start = codePointSeeker.position; + codePointSeeker.seekToChunk(target.endChunk, target.endIndex); + const end = codePointSeeker.position; + return { + type: 'TextPositionSelector', + start, + end, + }; +} diff --git a/packages/dom/src/text-position/index.ts b/packages/dom/src/text-position/index.ts index 011e994..bb73732 100644 --- a/packages/dom/src/text-position/index.ts +++ b/packages/dom/src/text-position/index.ts @@ -18,4 +18,5 @@ * under the License. */ +export * from './describe'; export * from './match';
