You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@annotator.apache.org by ge...@apache.org on 2020/07/23 13:42:55 UTC

[incubator-annotator] branch tweak-highlighter created (now 992e385)

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

gerben pushed a change to branch tweak-highlighter
in repository https://gitbox.apache.org/repos/asf/incubator-annotator.git.


      at 992e385  Create tests for highlighter

This branch includes the following new commits:

     new 226107b  nitpicking
     new 46cb99f  Fix document->doc
     new 1f43c02  Move test utils.ts one folder up
     new a8fe93e  Support highlighting empty ranges
     new 992e385  Create tests for highlighter

The 5 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[incubator-annotator] 01/05: nitpicking

Posted by ge...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 226107b9f7a514c11c72b6f4881fc17ed7384f0f
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Jun 25 22:47:16 2020 +0200

    nitpicking
    
    Replace replaceChild with replaceWith (or: In the function that invokes
    replaceChild, replace with replaceWith replaceChild)
---
 packages/dom/src/highlight-range.ts | 9 +++------
 1 file changed, 3 insertions(+), 6 deletions(-)

diff --git a/packages/dom/src/highlight-range.ts b/packages/dom/src/highlight-range.ts
index e18a6a4..043d739 100644
--- a/packages/dom/src/highlight-range.ts
+++ b/packages/dom/src/highlight-range.ts
@@ -46,8 +46,8 @@ export function highlightRange(
   // Return a function that cleans up the highlightElements.
   function removeHighlights() {
     // Remove each of the created highlightElements.
-    for (const highlightIdx in highlightElements) {
-      removeHighlight(highlightElements[highlightIdx]);
+    for (const highlightElement of highlightElements) {
+      removeHighlight(highlightElement);
     }
   }
   return removeHighlights;
@@ -132,10 +132,7 @@ function removeHighlight(highlightElement: HTMLElement) {
   // If it has somehow been removed already, there is nothing to be done.
   if (!highlightElement.parentNode) return;
   if (highlightElement.childNodes.length === 1) {
-    highlightElement.parentNode.replaceChild(
-      highlightElement.firstChild as ChildNode,
-      highlightElement,
-    );
+    highlightElement.replaceWith(highlightElement.firstChild as Node);
   } else {
     // If the highlight somehow contains multiple nodes now, move them all.
     while (highlightElement.firstChild) {


[incubator-annotator] 02/05: Fix document->doc

Posted by ge...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 46cb99facfaace607381b8629157e7e83507d28f
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Jul 23 12:24:00 2020 +0200

    Fix document->doc
---
 packages/dom/test/text-quote/describe.test.ts |  4 ++--
 packages/dom/test/text-quote/match.test.ts    | 12 ++++++------
 2 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/packages/dom/test/text-quote/describe.test.ts b/packages/dom/test/text-quote/describe.test.ts
index f962157..5b560ff 100644
--- a/packages/dom/test/text-quote/describe.test.ts
+++ b/packages/dom/test/text-quote/describe.test.ts
@@ -38,7 +38,7 @@ describe('describeTextQuote', () => {
   it('works with custom scope', async () => {
     const { html, range } = testCases['minimal prefix'];
     const doc = domParser.parseFromString(html, 'text/html');
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b/text()'), 15);
     scope.setEnd(evaluateXPath(doc, '//b/text()'), 30); // "not to annotate"
     const result = await describeTextQuote(hydrateRange(range, doc), scope);
@@ -53,7 +53,7 @@ describe('describeTextQuote', () => {
   it('strips part of the range outside the scope', async () => {
     const { html, range } = testCases['simple'];
     const doc = domParser.parseFromString(html, 'text/html');
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b/text()'), 6);
     scope.setEnd(evaluateXPath(doc, '//b/text()'), 17); // "ipsum dolor"
     const result = await describeTextQuote(hydrateRange(range, doc), scope);
diff --git a/packages/dom/test/text-quote/match.test.ts b/packages/dom/test/text-quote/match.test.ts
index 3148a3f..af6a5d2 100644
--- a/packages/dom/test/text-quote/match.test.ts
+++ b/packages/dom/test/text-quote/match.test.ts
@@ -117,7 +117,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const doc = domParser.parseFromString(html, 'text/html');
 
     // Use the substring ‘ipsum dolor amet’ as scope.
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b/text()'), 6);
     scope.setEnd(evaluateXPath(doc, '//b/text()'), 22);
     await testMatcher(doc, scope, selector, expected);
@@ -128,7 +128,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const doc = domParser.parseFromString(html, 'text/html');
 
     // Use the substring ‘sum dolor am’ as scope.
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//i/text()'), 2);
     scope.setEnd(evaluateXPath(doc, '//u/text()'), 2);
     await testMatcher(doc, scope, selector, expected);
@@ -138,7 +138,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const { html, selector, expected } = testCases['across elements'];
     const doc = domParser.parseFromString(html, 'text/html');
 
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b'), 1); // before the <i>
     scope.setEnd(evaluateXPath(doc, '//b'), 4); // before the " yada yada"
     await testMatcher(doc, scope, selector, expected);
@@ -148,7 +148,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const { html, selector } = testCases['simple'];
     const doc = domParser.parseFromString(html, 'text/html');
 
-    const scope = document.createRange();
+    const scope = doc.createRange();
     await testMatcher(doc, scope, selector, []);
   });
 
@@ -156,7 +156,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const { html, selector } = testCases['simple'];
     const doc = domParser.parseFromString(html, 'text/html');
 
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b/text()'), 0);
     scope.setEnd(evaluateXPath(doc, '//b/text()'), 19);
     await testMatcher(doc, scope, selector, []);
@@ -166,7 +166,7 @@ describe('createTextQuoteSelectorMatcher', () => {
     const { html, selector } = testCases['simple'];
     const doc = domParser.parseFromString(html, 'text/html');
 
-    const scope = document.createRange();
+    const scope = doc.createRange();
     scope.setStart(evaluateXPath(doc, '//b/text()'), 13);
     scope.setEnd(evaluateXPath(doc, '//b/text()'), 32);
     await testMatcher(doc, scope, selector, []);


[incubator-annotator] 05/05: Create tests for highlighter

Posted by ge...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 992e3855909b1c665e8579c6f5286529b0ab2d0b
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Jul 23 14:16:49 2020 +0200

    Create tests for highlighter
---
 .../test/highlight-range/highlight-range.test.ts   | 199 +++++++++++++++++++++
 1 file changed, 199 insertions(+)

diff --git a/packages/dom/test/highlight-range/highlight-range.test.ts b/packages/dom/test/highlight-range/highlight-range.test.ts
new file mode 100644
index 0000000..387fd7b
--- /dev/null
+++ b/packages/dom/test/highlight-range/highlight-range.test.ts
@@ -0,0 +1,199 @@
+/**
+ * @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 { assert } from 'chai';
+import { highlightRange } from '../../src/highlight-range';
+import { RangeInfo, hydrateRange, evaluateXPath } from '../utils';
+
+const domParser = new window.DOMParser();
+
+const testCases: {
+  [name: string]: {
+    inputHtml: string,
+    range: RangeInfo,
+    tagName?: string,
+    attributes?: Record<string, string>,
+    expectedHtml: string,
+  }
+} = {
+  'single text node': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    expectedHtml: '<b>lorem ipsum <mark>dolor am</mark>et yada yada</b>',
+  },
+  'across elements': {
+    inputHtml: '<b>lorem <i>ipsum</i> dolor <u>amet</u> yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()[2]',
+      startOffset: 1,
+      endContainerXPath: '//u/text()',
+      endOffset: 2,
+    },
+    expectedHtml: '<b>lorem <i>ipsum</i> <mark>dolor </mark><u><mark>am</mark>et</u> yada yada</b>',
+  },
+  'collapsed range': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 12,
+    },
+    expectedHtml: '<b>lorem ipsum <mark></mark>dolor amet yada yada</b>',
+  },
+  'custom tag name': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    tagName: 'span',
+      expectedHtml: '<b>lorem ipsum <span>dolor am</span>et yada yada</b>',
+  },
+  'custom attributes': {
+    inputHtml: '<b>lorem ipsum dolor amet yada yada</b>',
+    range: {
+      startContainerXPath: '//b/text()',
+      startOffset: 12,
+      endContainerXPath: '//b/text()',
+      endOffset: 20,
+    },
+    attributes: {
+        class: 'red',
+      },
+      expectedHtml: '<b>lorem ipsum <mark class="red">dolor am</mark>et yada yada</b>',
+  },
+  'overlapping highlight': {
+    // Starts off from the result of the 'single text node' case.
+    inputHtml: '<b>lorem ipsum <mark>dolor am</mark>et yada yada</b>',
+    range:  {
+      startContainerXPath: '//mark/text()',
+      startOffset: 6,
+      endContainerXPath: '//b/text()[2]',
+      endOffset: 7,
+    },
+    tagName: 'mark2',
+    expectedHtml: '<b>lorem ipsum <mark>dolor <mark2>am</mark2></mark><mark2>et yada</mark2> yada</b>',
+  },
+};
+
+describe('highlightRange', () => {
+  for (const [name, { inputHtml, range, tagName, attributes, expectedHtml }] of Object.entries(testCases)) {
+    it(`works for case: ${name}`, () => {
+      const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+      // Invoke highlightRange for the specified Range, and check the result.
+      const removeHighlights = highlightRange(hydrateRange(range, doc), tagName, attributes);
+      assert.equal(doc.body.innerHTML, expectedHtml);
+
+      // Remove the highlight again and check that we end up exactly how we started.
+      removeHighlights();
+      assert.equal(doc.body.innerHTML, inputHtml);
+    });
+  }
+
+  it('works on adjacent text nodes', () => {
+    const inputHtml = '<b>lorem ipsum dolor amet yada yada</b>';
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const textNode = evaluateXPath(doc, '//b/text()') as Text;
+    textNode.splitText(15); // after 'dol'
+
+    const range = doc.createRange();
+    range.setStart(evaluateXPath(doc, '//b/text()[1]'), 12); // before 'dolor am'
+    range.setEnd(evaluateXPath(doc, '//b/text()[2]'), 20 - 15); // after 'dolor am'
+
+    const removeHighlights = highlightRange(range);
+    const expectedHtml = '<b>lorem ipsum <mark>dol</mark><mark>or am</mark>et yada yada</b>';
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('also marks empty text nodes', () => {
+    const inputHtml = '<b>lorem ipsum dolor amet yada yada</b>';
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const textNode = evaluateXPath(doc, '//b/text()') as Text;
+    textNode.splitText(15);
+    textNode.splitText(15); // Split the node twice to create an empty text node.
+
+    const range = doc.createRange();
+    range.setStart(evaluateXPath(doc, '//b/text()[1]'), 12); // before 'dolor am'
+    range.setEnd(evaluateXPath(doc, '//b/text()[3]'), 20 - 15); // after 'dolor am'
+
+    const removeHighlights = highlightRange(range);
+    const expectedHtml = '<b>lorem ipsum <mark>dol</mark><mark></mark><mark>or am</mark>et yada yada</b>';
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('ignores a range that does not contain Text nodes', () => {
+    const inputHtml = `<b>Try highlighting this image: <img> — would that work?</b>`
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const range = document.createRange();
+    range.selectNode(evaluateXPath(doc, '//img'));
+
+    const removeHighlights = highlightRange(range);
+    assert.equal(doc.body.innerHTML, inputHtml);
+
+    removeHighlights();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('correctly removes multiple highlights (fifo order)', () => {
+    const { inputHtml, range } = testCases['single text node'];
+    const { range: range2, expectedHtml } = testCases['overlapping highlight'];
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const removeHighlights1 = highlightRange(hydrateRange(range, doc));
+    const removeHighlights2 = highlightRange(hydrateRange(range2, doc), 'mark2');
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights1();
+    removeHighlights2();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+
+  it('correctly removes multiple highlights (lifo order)', () => {
+    const { inputHtml, range } = testCases['single text node'];
+    const { range: range2, expectedHtml } = testCases['overlapping highlight'];
+    const doc = domParser.parseFromString(inputHtml, 'text/html');
+
+    const removeHighlights1 = highlightRange(hydrateRange(range, doc));
+    const removeHighlights2 = highlightRange(hydrateRange(range2, doc), 'mark2');
+    assert.equal(doc.body.innerHTML, expectedHtml);
+
+    removeHighlights2();
+    removeHighlights1();
+    assert.equal(doc.body.innerHTML, inputHtml);
+  });
+});


[incubator-annotator] 03/05: Move test utils.ts one folder up

Posted by ge...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 1f43c029ca9342ca9c6a47587c3683fd9df2c3e1
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Jul 23 12:29:17 2020 +0200

    Move test utils.ts one folder up
---
 packages/dom/test/text-quote/describe-cases.ts | 2 +-
 packages/dom/test/text-quote/describe.test.ts  | 2 +-
 packages/dom/test/text-quote/match-cases.ts    | 2 +-
 packages/dom/test/text-quote/match.test.ts     | 2 +-
 packages/dom/test/{text-quote => }/utils.ts    | 0
 5 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/packages/dom/test/text-quote/describe-cases.ts b/packages/dom/test/text-quote/describe-cases.ts
index 129804d..b0fe659 100644
--- a/packages/dom/test/text-quote/describe-cases.ts
+++ b/packages/dom/test/text-quote/describe-cases.ts
@@ -19,7 +19,7 @@
  */
 
 import { TextQuoteSelector } from "../../../selector/src";
-import { RangeInfo } from "./utils";
+import { RangeInfo } from "../utils";
 
 const testCases: {
   [name: string]: {
diff --git a/packages/dom/test/text-quote/describe.test.ts b/packages/dom/test/text-quote/describe.test.ts
index 5b560ff..22dfeeb 100644
--- a/packages/dom/test/text-quote/describe.test.ts
+++ b/packages/dom/test/text-quote/describe.test.ts
@@ -22,7 +22,7 @@ import { assert } from 'chai';
 import { describeTextQuote } from '../../src/text-quote/describe';
 import testCases from './describe-cases';
 import testMatchCases from './match-cases';
-import { hydrateRange, evaluateXPath } from './utils';
+import { hydrateRange, evaluateXPath } from '../utils';
 
 const domParser = new window.DOMParser();
 
diff --git a/packages/dom/test/text-quote/match-cases.ts b/packages/dom/test/text-quote/match-cases.ts
index 1748d1d..b4c44a5 100644
--- a/packages/dom/test/text-quote/match-cases.ts
+++ b/packages/dom/test/text-quote/match-cases.ts
@@ -19,7 +19,7 @@
  */
 
 import { TextQuoteSelector } from "../../../selector/src";
-import { RangeInfo } from "./utils";
+import { RangeInfo } from "../utils";
 
 const testCases: {
   [name: string]: {
diff --git a/packages/dom/test/text-quote/match.test.ts b/packages/dom/test/text-quote/match.test.ts
index af6a5d2..c4881aa 100644
--- a/packages/dom/test/text-quote/match.test.ts
+++ b/packages/dom/test/text-quote/match.test.ts
@@ -23,7 +23,7 @@ import { createTextQuoteSelectorMatcher } from '../../src/text-quote/match';
 import { TextQuoteSelector } from '../../../selector/src/types';
 import { DomScope } from '../../src/types';
 import testCases from './match-cases';
-import { evaluateXPath, RangeInfo } from './utils';
+import { evaluateXPath, RangeInfo } from '../utils';
 
 const domParser = new window.DOMParser();
 
diff --git a/packages/dom/test/text-quote/utils.ts b/packages/dom/test/utils.ts
similarity index 100%
rename from packages/dom/test/text-quote/utils.ts
rename to packages/dom/test/utils.ts


[incubator-annotator] 04/05: Support highlighting empty ranges

Posted by ge...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit a8fe93e147ddd484cd972fdaa6d3f4085788e300
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Jul 23 14:12:29 2020 +0200

    Support highlighting empty ranges
    
    Can be useful to mark a specific point in a text. And if ignoring
    collapsed ranges is desirable, the user can just check whether
    range.collapsed is true.
---
 packages/dom/src/highlight-range.ts | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/packages/dom/src/highlight-range.ts b/packages/dom/src/highlight-range.ts
index 043d739..90b3efa 100644
--- a/packages/dom/src/highlight-range.ts
+++ b/packages/dom/src/highlight-range.ts
@@ -55,9 +55,6 @@ export function highlightRange(
 
 // Return an array of the text nodes in the range. Split the start and end nodes if required.
 function textNodesInRange(range: Range): Text[] {
-  // If the range is empty, avoid creating and returning an empty text node.
-  if (range.collapsed) return [];
-
   // If the start or end node is a text node and only partly in the range, split it.
   if (
     isTextNode(range.startContainer) &&