Commit 736991f3 authored by Robert Knight's avatar Robert Knight

Import fixes for BrowserRange#normalize() from Annotator 1.2.x

Replace the Range functions from the compiled version of Annotator 1.2.x
with the latest CoffeeScript source from the v1.2.x branch of Annotator,
plus the fix added in b3056d27

This includes fixes for BrowserRange#normalize from
https://github.com/openannotation/annotator/commit/9bec776511a717ef944b36ed7cca3d107bd95c88

In addition, this removes the dependency on the Annotator framework from
annotator/anchoring and the highlighter, getting us closer to a point
where we can remove the dependency of the annotation layer on Annotator.

Fixes #109
parent 136b3c91
seek = require('dom-seek') seek = require('dom-seek')
Annotator = require('annotator') xpathRange = require('./range')
xpathRange = Annotator.Range
html = require('./html') html = require('./html')
RenderingStates = require('../pdfjs-rendering-states') RenderingStates = require('../pdfjs-rendering-states')
......
This diff is collapsed.
...@@ -117,7 +117,7 @@ var expectedFailures = [ ...@@ -117,7 +117,7 @@ var expectedFailures = [
['Text between br tags, with <br/><p><br/></p> at end', {position: true, quote: true}], ['Text between br tags, with <br/><p><br/></p> at end', {position: true, quote: true}],
['Text between br tags, with <p><br/></p> at the start', {position: true, quote: true}], ['Text between br tags, with <p><br/></p> at the start', {position: true, quote: true}],
['Text between br tags, with <br/><p><br/></p> at the start', {position: true, quote: true}], ['Text between br tags, with <br/><p><br/></p> at the start', {position: true, quote: true}],
['No text node at the end and offset 0', {position: true, quote: true, range: true}], ['No text node at the end and offset 0', {position: true, quote: true}],
]; ];
describe('HTML anchoring', function () { describe('HTML anchoring', function () {
......
Annotator = require('annotator') xpathRange = require('./range')
xpathRange = Annotator.Range
# Helper function for throwing common errors # Helper function for throwing common errors
......
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# This is a modified copy of
# https://github.com/openannotation/annotator/blob/v1.2.x/src/util.coffee
$ = require('jquery')
{ simpleXPathJQuery, simpleXPathPure } = require('./xpath')
Util = {}
# Public: Flatten a nested array structure
#
# Returns an array
Util.flatten = (array) ->
flatten = (ary) ->
flat = []
for el in ary
flat = flat.concat(if el and $.isArray(el) then flatten(el) else el)
return flat
flatten(array)
# Public: Finds all text nodes within the elements in the current collection.
#
# Returns a new jQuery collection of text nodes.
Util.getTextNodes = (jq) ->
getTextNodes = (node) ->
if node and node.nodeType != Node.TEXT_NODE
nodes = []
# If not a comment then traverse children collecting text nodes.
# We traverse the child nodes manually rather than using the .childNodes
# property because IE9 does not update the .childNodes property after
# .splitText() is called on a child text node.
if node.nodeType != Node.COMMENT_NODE
# Start at the last child and walk backwards through siblings.
node = node.lastChild
while node
nodes.push getTextNodes(node)
node = node.previousSibling
# Finally reverse the array so that nodes are in the correct order.
return nodes.reverse()
else
return node
jq.map -> Util.flatten(getTextNodes(this))
# Public: determine the last text node inside or before the given node
Util.getLastTextNodeUpTo = (n) ->
switch n.nodeType
when Node.TEXT_NODE
return n # We have found our text node.
when Node.ELEMENT_NODE
# This is an element, we need to dig in
if n.lastChild? # Does it have children at all?
result = Util.getLastTextNodeUpTo n.lastChild
if result? then return result
else
# Not a text node, and not an element node.
# Could not find a text node in current node, go backwards
n = n.previousSibling
if n?
Util.getLastTextNodeUpTo n
else
null
# Public: determine the first text node in or after the given jQuery node.
Util.getFirstTextNodeNotBefore = (n) ->
switch n.nodeType
when Node.TEXT_NODE
return n # We have found our text node.
when Node.ELEMENT_NODE
# This is an element, we need to dig in
if n.firstChild? # Does it have children at all?
result = Util.getFirstTextNodeNotBefore n.firstChild
if result? then return result
else
# Not a text or an element node.
# Could not find a text node in current node, go forward
n = n.nextSibling
if n?
Util.getFirstTextNodeNotBefore n
else
null
Util.xpathFromNode = (el, relativeRoot) ->
try
result = simpleXPathJQuery.call el, relativeRoot
catch exception
console.log "jQuery-based XPath construction failed! Falling back to manual."
result = simpleXPathPure.call el, relativeRoot
result
Util.nodeFromXPath = (xp, root) ->
steps = xp.substring(1).split("/")
node = root
for step in steps
[name, idx] = step.split "["
idx = if idx? then parseInt (idx?.split "]")[0] else 1
node = findChild node, name.toLowerCase(), idx
node
module.exports = {
nodeFromXPath: Util.nodeFromXPath,
xpathFromNode: Util.xpathFromNode,
getTextNodes: Util.getTextNodes,
getFirstTextNodeNotBefore: Util.getFirstTextNodeNotBefore,
getLastTextNodeUpTo: Util.getLastTextNodeUpTo,
}
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# This is a modified copy of
# https://github.com/openannotation/annotator/blob/v1.2.x/src/xpath.coffee
$ = require('jquery')
# A simple XPath evaluator using jQuery which can evaluate queries of
simpleXPathJQuery = (relativeRoot) ->
jq = this.map ->
path = ''
elem = this
while elem?.nodeType == Node.ELEMENT_NODE and elem isnt relativeRoot
tagName = elem.tagName.replace(":", "\\:")
idx = $(elem.parentNode).children(tagName).index(elem) + 1
idx = "[#{idx}]"
path = "/" + elem.tagName.toLowerCase() + idx + path
elem = elem.parentNode
path
jq.get()
# A simple XPath evaluator using only standard DOM methods which can
# evaluate queries of the form /tag[index]/tag[index].
simpleXPathPure = (relativeRoot) ->
getPathSegment = (node) ->
name = getNodeName node
pos = getNodePosition node
"#{name}[#{pos}]"
rootNode = relativeRoot
getPathTo = (node) ->
xpath = '';
while node != rootNode
unless node?
throw new Error "Called getPathTo on a node which was not a descendant of @rootNode. " + rootNode
xpath = (getPathSegment node) + '/' + xpath
node = node.parentNode
xpath = '/' + xpath
xpath = xpath.replace /\/$/, ''
xpath
jq = this.map ->
path = getPathTo this
path
jq.get()
findChild = (node, type, index) ->
unless node.hasChildNodes()
throw new Error "XPath error: node has no children!"
children = node.childNodes
found = 0
for child in children
name = getNodeName child
if name is type
found += 1
if found is index
return child
throw new Error "XPath error: wanted child not found."
# Get the node name for use in generating an xpath expression.
getNodeName = (node) ->
nodeName = node.nodeName.toLowerCase()
switch nodeName
when "#text" then return "text()"
when "#comment" then return "comment()"
when "#cdata-section" then return "cdata-section()"
else return nodeName
# Get the index of the node as it appears in its parent's child list
getNodePosition = (node) ->
pos = 0
tmp = node
while tmp
if tmp.nodeName is node.nodeName
pos++
tmp = tmp.previousSibling
pos
module.exports = {
simpleXPathJQuery,
simpleXPathPure,
}
...@@ -10,6 +10,7 @@ adder = require('./adder') ...@@ -10,6 +10,7 @@ adder = require('./adder')
highlighter = require('./highlighter') highlighter = require('./highlighter')
rangeUtil = require('./range-util') rangeUtil = require('./range-util')
selections = require('./selections') selections = require('./selections')
xpathRange = require('./anchoring/range')
animationPromise = (fn) -> animationPromise = (fn) ->
return new Promise (resolve, reject) -> return new Promise (resolve, reject) ->
...@@ -214,7 +215,7 @@ module.exports = class Guest extends Annotator ...@@ -214,7 +215,7 @@ module.exports = class Guest extends Annotator
# Highlight the range for an anchor. # Highlight the range for an anchor.
return anchor unless anchor.range? return anchor unless anchor.range?
return animationPromise -> return animationPromise ->
range = Annotator.Range.sniff(anchor.range) range = xpathRange.sniff(anchor.range)
normedRange = range.normalize(root) normedRange = range.normalize(root)
highlights = highlighter.highlightRange(normedRange) highlights = highlighter.highlightRange(normedRange)
...@@ -438,4 +439,3 @@ module.exports = class Guest extends Annotator ...@@ -438,4 +439,3 @@ module.exports = class Guest extends Annotator
@element.removeClass(SHOW_HIGHLIGHTS_CLASS) @element.removeClass(SHOW_HIGHLIGHTS_CLASS)
@visibleHighlights = shouldShowHighlights @visibleHighlights = shouldShowHighlights
Annotator = require('annotator') $ = require('jquery')
$ = Annotator.$
# Public: Wraps the DOM Nodes within the provided range with a highlight # Public: Wraps the DOM Nodes within the provided range with a highlight
......
Annotator = require('annotator') Range = require('../anchoring/range')
$ = Annotator.$ $ = require('jquery')
highlighter = require('../highlighter') highlighter = require('../highlighter')
...@@ -8,7 +8,7 @@ describe "highlightRange", -> ...@@ -8,7 +8,7 @@ describe "highlightRange", ->
txt = document.createTextNode('test highlight span') txt = document.createTextNode('test highlight span')
el = document.createElement('span') el = document.createElement('span')
el.appendChild(txt) el.appendChild(txt)
r = new Annotator.Range.NormalizedRange({ r = new Range.NormalizedRange({
commonAncestor: el, commonAncestor: el,
start: txt, start: txt,
end: txt end: txt
...@@ -26,7 +26,7 @@ describe "highlightRange", -> ...@@ -26,7 +26,7 @@ describe "highlightRange", ->
el.appendChild(txt) el.appendChild(txt)
el.appendChild(blank) el.appendChild(blank)
el.appendChild(txt2) el.appendChild(txt2)
r = new Annotator.Range.NormalizedRange({ r = new Range.NormalizedRange({
commonAncestor: el, commonAncestor: el,
start: txt, start: txt,
end: txt2 end: txt2
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment