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/02/13 15:47:22 UTC

[incubator-annotator] branch add-highlight-range created (now 18eb1d5)

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

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


      at 18eb1d5  Add highlightRange to dom package

This branch includes the following new commits:

     new 18eb1d5  Add highlightRange to dom package

The 1 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/01: Add highlightRange to dom package

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

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

commit 18eb1d5043787aa62e91217cc5664e3f7b4029a1
Author: Gerben <ge...@treora.com>
AuthorDate: Thu Feb 13 16:42:32 2020 +0100

    Add highlightRange to dom package
    
    Code is copied from https://www.npmjs.com/package/dom-highlight-range v3.0.1
---
 packages/dom/src/highlight-range.js | 106 ++++++++++++++++++++++++++++++++++++
 packages/dom/src/index.js           |   1 +
 2 files changed, 107 insertions(+)

diff --git a/packages/dom/src/highlight-range.js b/packages/dom/src/highlight-range.js
new file mode 100644
index 0000000..09e22d5
--- /dev/null
+++ b/packages/dom/src/highlight-range.js
@@ -0,0 +1,106 @@
+// Wrap each text node in a given DOM Range with a <mark> or other element.
+// Breaks start and/or end node if needed.
+// Returns a function that cleans up the created highlight (not a perfect undo: split text nodes are
+// not merged again).
+//
+// Parameters:
+// - range: a DOM Range object. Note that as highlighting modifies the DOM, the range may be
+//   unusable afterwards
+// - tagName: the element used to wrap text nodes. Defaults to 'mark'.
+// - attributes: an Object defining any attributes to be set on the wrapper elements.
+export function highlightRange(range, tagName = 'mark', attributes = {}) {
+  if (range.collapsed) return;
+
+  // First put all nodes in an array (splits start and end nodes if needed)
+  const nodes = textNodesInRange(range);
+
+  // Highlight each node
+  const highlightElements = [];
+  for (const node of nodes) {
+    const highlightElement = wrapNodeInHighlight(node, tagName, attributes);
+    highlightElements.push(highlightElement);
+  }
+
+  // Return a function that cleans up the highlightElements.
+  function removeHighlights() {
+    // Remove each of the created highlightElements.
+    for (const highlightIdx in highlightElements) {
+      removeHighlight(highlightElements[highlightIdx]);
+    }
+  }
+  return removeHighlights;
+}
+
+// Return an array of the text nodes in the range. Split the start and end nodes if required.
+function textNodesInRange(range) {
+  // If the start or end node is a text node and only partly in the range, split it.
+  if (range.startContainer.nodeType === Node.TEXT_NODE && range.startOffset > 0) {
+    const endOffset = range.endOffset; // (this may get lost when the splitting the node)
+    const createdNode = range.startContainer.splitText(range.startOffset);
+    if (range.endContainer === range.startContainer) {
+      // If the end was in the same container, it will now be in the newly created node.
+      range.setEnd(createdNode, endOffset - range.startOffset);
+    }
+    range.setStart(createdNode, 0);
+  }
+  if (
+    range.endContainer.nodeType === Node.TEXT_NODE
+    && range.endOffset < range.endContainer.length
+  ) {
+    range.endContainer.splitText(range.endOffset);
+  }
+
+  // Collect the text nodes.
+  const walker = range.startContainer.ownerDocument.createTreeWalker(
+    range.commonAncestorContainer,
+    NodeFilter.SHOW_TEXT,
+    node => range.intersectsNode(node) ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_REJECT,
+  );
+  walker.currentNode = range.startContainer;
+
+  // // Optimise by skipping nodes that are explicitly outside the range.
+  // const NodeTypesWithCharacterOffset = [
+  //  Node.TEXT_NODE,
+  //  Node.PROCESSING_INSTRUCTION_NODE,
+  //  Node.COMMENT_NODE,
+  // ];
+  // if (!NodeTypesWithCharacterOffset.includes(range.startContainer.nodeType)) {
+  //   if (range.startOffset < range.startContainer.childNodes.length) {
+  //     walker.currentNode = range.startContainer.childNodes[range.startOffset];
+  //   } else {
+  //     walker.nextSibling(); // TODO verify this is correct.
+  //   }
+  // }
+
+  const nodes = [];
+  if (walker.currentNode.nodeType === Node.TEXT_NODE)
+    nodes.push(walker.currentNode);
+  while (walker.nextNode() && range.comparePoint(walker.currentNode, 0) !== 1)
+    nodes.push(walker.currentNode);
+  return nodes;
+}
+
+// Replace [node] with <tagName ...attributes>[node]</tagName>
+function wrapNodeInHighlight(node, tagName, attributes) {
+  const highlightElement = node.ownerDocument.createElement(tagName);
+  Object.keys(attributes).forEach(key => {
+    highlightElement.setAttribute(key, attributes[key]);
+  });
+  const tempRange = node.ownerDocument.createRange();
+  tempRange.selectNode(node);
+  tempRange.surroundContents(highlightElement);
+  return highlightElement;
+}
+
+// Remove a highlight element created with wrapNodeInHighlight.
+function removeHighlight(highlightElement) {
+  if (highlightElement.childNodes.length === 1) {
+    highlightElement.parentNode.replaceChild(highlightElement.firstChild, highlightElement);
+  } else {
+    // If the highlight somehow contains multiple nodes now, move them all.
+    while (highlightElement.firstChild) {
+      highlightElement.parentNode.insertBefore(highlightElement.firstChild, highlightElement);
+    }
+    highlightElement.remove();
+  }
+}
diff --git a/packages/dom/src/index.js b/packages/dom/src/index.js
index e54a806..3d7ca58 100644
--- a/packages/dom/src/index.js
+++ b/packages/dom/src/index.js
@@ -21,3 +21,4 @@
 export * from './css';
 export * from './range';
 export * from './text-quote';
+export * from './highlight-range';