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')
Annotator = require('annotator')
xpathRange = Annotator.Range
xpathRange = require('./range')
html = require('./html')
RenderingStates = require('../pdfjs-rendering-states')
......
# 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/range.coffee
$ = require('jquery')
Util = require('./util')
Range = {}
# Public: Determines the type of Range of the provided object and returns
# a suitable Range instance.
#
# r - A range Object.
#
# Examples
#
# selection = window.getSelection()
# Range.sniff(selection.getRangeAt(0))
# # => Returns a BrowserRange instance.
#
# Returns a Range object or false.
Range.sniff = (r) ->
if r.commonAncestorContainer?
new Range.BrowserRange(r)
else if typeof r.start is "string"
new Range.SerializedRange(r)
else if r.start and typeof r.start is "object"
new Range.NormalizedRange(r)
else
console.error(_t("Could not sniff range type"))
false
# Public: Finds an Element Node using an XPath relative to the document root.
#
# If the document is served as application/xhtml+xml it will try and resolve
# any namespaces within the XPath.
#
# xpath - An XPath String to query.
#
# Examples
#
# node = Range.nodeFromXPath('/html/body/div/p[2]')
# if node
# # Do something with the node.
#
# Returns the Node if found otherwise null.
Range.nodeFromXPath = (xpath, root=document) ->
evaluateXPath = (xp, nsResolver=null) ->
try
document.evaluate('.' + xp, root, nsResolver, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue
catch exception
# There are cases when the evaluation fails, because the
# HTML documents contains nodes with invalid names,
# for example tags with equal signs in them, or something like that.
# In these cases, the XPath expressions will have these abominations,
# too, and then they can not be evaluated.
# In these cases, we get an XPathException, with error code 52.
# See http://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathException
# This does not necessarily make any sense, but this what we see
# happening.
console.log "XPath evaluation failed."
console.log "Trying fallback..."
# We have a an 'evaluator' for the really simple expressions that
# should work for the simple expressions we generate.
Util.nodeFromXPath(xp, root)
if not $.isXMLDoc document.documentElement
evaluateXPath xpath
else
# We're in an XML document, create a namespace resolver function to try
# and resolve any namespaces in the current document.
# https://developer.mozilla.org/en/DOM/document.createNSResolver
customResolver = document.createNSResolver(
if document.ownerDocument == null
document.documentElement
else
document.ownerDocument.documentElement
)
node = evaluateXPath xpath, customResolver
unless node
# If the previous search failed to find a node then we must try to
# provide a custom namespace resolver to take into account the default
# namespace. We also prefix all node names with a custom xhtml namespace
# eg. 'div' => 'xhtml:div'.
xpath = (for segment in xpath.split '/'
if segment and segment.indexOf(':') == -1
segment.replace(/^([a-z]+)/, 'xhtml:$1')
else segment
).join('/')
# Find the default document namespace.
namespace = document.lookupNamespaceURI null
# Try and resolve the namespace, first seeing if it is an xhtml node
# otherwise check the head attributes.
customResolver = (ns) ->
if ns == 'xhtml' then namespace
else document.documentElement.getAttribute('xmlns:' + ns)
node = evaluateXPath xpath, customResolver
node
class Range.RangeError extends Error
constructor: (@type, @message, @parent=null) ->
super(@message)
# Public: Creates a wrapper around a range object obtained from a DOMSelection.
class Range.BrowserRange
# Public: Creates an instance of BrowserRange.
#
# object - A range object obtained via DOMSelection#getRangeAt().
#
# Examples
#
# selection = window.getSelection()
# range = new Range.BrowserRange(selection.getRangeAt(0))
#
# Returns an instance of BrowserRange.
constructor: (obj) ->
@commonAncestorContainer = obj.commonAncestorContainer
@startContainer = obj.startContainer
@startOffset = obj.startOffset
@endContainer = obj.endContainer
@endOffset = obj.endOffset
# Public: normalize works around the fact that browsers don't generate
# ranges/selections in a consistent manner. Some (Safari) will create
# ranges that have (say) a textNode startContainer and elementNode
# endContainer. Others (Firefox) seem to only ever generate
# textNode/textNode or elementNode/elementNode pairs.
#
# Returns an instance of Range.NormalizedRange
normalize: (root) ->
if @tainted
console.error(_t("You may only call normalize() once on a BrowserRange!"))
return false
else
@tainted = true
r = {}
# Look at the start
if @startContainer.nodeType is Node.ELEMENT_NODE
# We are dealing with element nodes
r.start = Util.getFirstTextNodeNotBefore @startContainer.childNodes[@startOffset]
r.startOffset = 0
else
# We are dealing with simple text nodes
r.start = @startContainer
r.startOffset = @startOffset
# Look at the end
if @endContainer.nodeType is Node.ELEMENT_NODE
# Get specified node.
node = @endContainer.childNodes[@endOffset]
if node? # Does that node exist?
# Look for a text node either at the immediate beginning of node
n = node
while n? and (n.nodeType isnt Node.TEXT_NODE)
n = n.firstChild
if n? # Did we find a text node at the start of this element?
r.end = n
r.endOffset = 0
unless r.end?
# We need to find a text node in the previous sibling of the node at the
# given offset, if one exists, or in the previous sibling of its container.
if @endOffset
node = @endContainer.childNodes[@endOffset - 1]
else
node = @endContainer.previousSibling
r.end = Util.getLastTextNodeUpTo node
r.endOffset = r.end.nodeValue.length
else # We are dealing with simple text nodes
r.end = @endContainer
r.endOffset = @endOffset
# We have collected the initial data.
# Now let's start to slice & dice the text elements!
nr = {}
if r.startOffset > 0
# Do we really have to cut?
if !r.start.nextSibling || r.start.nodeValue.length > r.startOffset
# Yes. Cut.
nr.start = r.start.splitText(r.startOffset)
else
# Avoid splitting off zero-length pieces.
nr.start = r.start.nextSibling
else
nr.start = r.start
# is the whole selection inside one text element ?
if r.start is r.end
if nr.start.nodeValue.length > (r.endOffset - r.startOffset)
nr.start.splitText(r.endOffset - r.startOffset)
nr.end = nr.start
else # no, the end of the selection is in a separate text element
# does the end need to be cut?
if r.end.nodeValue.length > r.endOffset
r.end.splitText(r.endOffset)
nr.end = r.end
# Make sure the common ancestor is an element node.
nr.commonAncestor = @commonAncestorContainer
while nr.commonAncestor.nodeType isnt Node.ELEMENT_NODE
nr.commonAncestor = nr.commonAncestor.parentNode
new Range.NormalizedRange(nr)
# Public: Creates a range suitable for storage.
#
# root - A root Element from which to anchor the serialisation.
# ignoreSelector - A selector String of elements to ignore. For example
# elements injected by the annotator.
#
# Returns an instance of SerializedRange.
serialize: (root, ignoreSelector) ->
this.normalize(root).serialize(root, ignoreSelector)
# Public: A normalised range is most commonly used throughout the annotator.
# its the result of a deserialised SerializedRange or a BrowserRange with
# out browser inconsistencies.
class Range.NormalizedRange
# Public: Creates an instance of a NormalizedRange.
#
# This is usually created by calling the .normalize() method on one of the
# other Range classes rather than manually.
#
# obj - An Object literal. Should have the following properties.
# commonAncestor: A Element that encompasses both the start and end nodes
# start: The first TextNode in the range.
# end The last TextNode in the range.
#
# Returns an instance of NormalizedRange.
constructor: (obj) ->
@commonAncestor = obj.commonAncestor
@start = obj.start
@end = obj.end
# Public: For API consistency.
#
# Returns itself.
normalize: (root) ->
this
# Public: Limits the nodes within the NormalizedRange to those contained
# withing the bounds parameter. It returns an updated range with all
# properties updated. NOTE: Method returns null if all nodes fall outside
# of the bounds.
#
# bounds - An Element to limit the range to.
#
# Returns updated self or null.
limit: (bounds) ->
nodes = $.grep this.textNodes(), (node) ->
node.parentNode == bounds or $.contains(bounds, node.parentNode)
return null unless nodes.length
@start = nodes[0]
@end = nodes[nodes.length - 1]
startParents = $(@start).parents()
for parent in $(@end).parents()
if startParents.index(parent) != -1
@commonAncestor = parent
break
this
# Convert this range into an object consisting of two pairs of (xpath,
# character offset), which can be easily stored in a database.
#
# root - The root Element relative to which XPaths should be calculated
# ignoreSelector - A selector String of elements to ignore. For example
# elements injected by the annotator.
#
# Returns an instance of SerializedRange.
serialize: (root, ignoreSelector) ->
serialization = (node, isEnd) ->
if ignoreSelector
origParent = $(node).parents(":not(#{ignoreSelector})").eq(0)
else
origParent = $(node).parent()
xpath = Util.xpathFromNode(origParent, root)[0]
textNodes = Util.getTextNodes(origParent)
# Calculate real offset as the combined length of all the
# preceding textNode siblings. We include the length of the
# node if it's the end node.
nodes = textNodes.slice(0, textNodes.index(node))
offset = 0
for n in nodes
offset += n.nodeValue.length
if isEnd then [xpath, offset + node.nodeValue.length] else [xpath, offset]
start = serialization(@start)
end = serialization(@end, true)
new Range.SerializedRange({
# XPath strings
start: start[0]
end: end[0]
# Character offsets (integer)
startOffset: start[1]
endOffset: end[1]
})
# Public: Creates a concatenated String of the contents of all the text nodes
# within the range.
#
# Returns a String.
text: ->
(for node in this.textNodes()
node.nodeValue
).join ''
# Public: Fetches only the text nodes within th range.
#
# Returns an Array of TextNode instances.
textNodes: ->
textNodes = Util.getTextNodes($(this.commonAncestor))
[start, end] = [textNodes.index(this.start), textNodes.index(this.end)]
# Return the textNodes that fall between the start and end indexes.
$.makeArray textNodes[start..end]
# Public: Converts the Normalized range to a native browser range.
#
# See: https://developer.mozilla.org/en/DOM/range
#
# Examples
#
# selection = window.getSelection()
# selection.removeAllRanges()
# selection.addRange(normedRange.toRange())
#
# Returns a Range object.
toRange: ->
range = document.createRange()
range.setStartBefore(@start)
range.setEndAfter(@end)
range
# Public: A range suitable for storing in local storage or serializing to JSON.
class Range.SerializedRange
# Public: Creates a SerializedRange
#
# obj - The stored object. It should have the following properties.
# start: An xpath to the Element containing the first TextNode
# relative to the root Element.
# startOffset: The offset to the start of the selection from obj.start.
# end: An xpath to the Element containing the last TextNode
# relative to the root Element.
# startOffset: The offset to the end of the selection from obj.end.
#
# Returns an instance of SerializedRange
constructor: (obj) ->
@start = obj.start
@startOffset = obj.startOffset
@end = obj.end
@endOffset = obj.endOffset
# Public: Creates a NormalizedRange.
#
# root - The root Element from which the XPaths were generated.
#
# Returns a NormalizedRange instance.
normalize: (root) ->
range = {}
for p in ['start', 'end']
try
node = Range.nodeFromXPath(this[p], root)
catch e
throw new Range.RangeError(p, "Error while finding #{p} node: #{this[p]}: " + e, e)
if not node
throw new Range.RangeError(p, "Couldn't find #{p} node: #{this[p]}")
# Unfortunately, we *can't* guarantee only one textNode per
# elementNode, so we have to walk along the element's textNodes until
# the combined length of the textNodes to that point exceeds or
# matches the value of the offset.
length = 0
targetOffset = this[p + 'Offset']
# Range excludes its endpoint because it describes the boundary position.
# Target the string index of the last character inside the range.
if p is 'end' then targetOffset--
for tn in Util.getTextNodes($(node))
if (length + tn.nodeValue.length > targetOffset)
range[p + 'Container'] = tn
range[p + 'Offset'] = this[p + 'Offset'] - length
break
else
length += tn.nodeValue.length
# If we fall off the end of the for loop without having set
# 'startOffset'/'endOffset', the element has shorter content than when
# we annotated, so throw an error:
if not range[p + 'Offset']?
throw new Range.RangeError("#{p}offset", "Couldn't find offset #{this[p + 'Offset']} in element #{this[p]}")
# Here's an elegant next step...
#
# range.commonAncestorContainer = $(range.startContainer).parents().has(range.endContainer)[0]
#
# ...but unfortunately Node.contains() is broken in Safari 5.1.5 (7534.55.3)
# and presumably other earlier versions of WebKit. In particular, in a
# document like
#
# <p>Hello</p>
#
# the code
#
# p = document.getElementsByTagName('p')[0]
# p.contains(p.firstChild)
#
# returns `false`. Yay.
#
# So instead, we step through the parents from the bottom up and use
# Node.compareDocumentPosition() to decide when to set the
# commonAncestorContainer and bail out.
contains =
if not document.compareDocumentPosition?
# IE
(a, b) -> a.contains(b)
else
# Everyone else
(a, b) -> a.compareDocumentPosition(b) & 16
$(range.startContainer).parents().each ->
if contains(this, range.endContainer)
range.commonAncestorContainer = this
return false
new Range.BrowserRange(range).normalize(root)
# Public: Creates a range suitable for storage.
#
# root - A root Element from which to anchor the serialisation.
# ignoreSelector - A selector String of elements to ignore. For example
# elements injected by the annotator.
#
# Returns an instance of SerializedRange.
serialize: (root, ignoreSelector) ->
this.normalize(root).serialize(root, ignoreSelector)
# Public: Returns the range as an Object literal.
toObject: ->
{
start: @start
startOffset: @startOffset
end: @end
endOffset: @endOffset
}
module.exports = Range
......@@ -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 <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 () {
......
Annotator = require('annotator')
xpathRange = Annotator.Range
xpathRange = require('./range')
# 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')
highlighter = require('./highlighter')
rangeUtil = require('./range-util')
selections = require('./selections')
xpathRange = require('./anchoring/range')
animationPromise = (fn) ->
return new Promise (resolve, reject) ->
......@@ -214,7 +215,7 @@ module.exports = class Guest extends Annotator
# Highlight the range for an anchor.
return anchor unless anchor.range?
return animationPromise ->
range = Annotator.Range.sniff(anchor.range)
range = xpathRange.sniff(anchor.range)
normedRange = range.normalize(root)
highlights = highlighter.highlightRange(normedRange)
......@@ -438,4 +439,3 @@ module.exports = class Guest extends Annotator
@element.removeClass(SHOW_HIGHLIGHTS_CLASS)
@visibleHighlights = shouldShowHighlights
Annotator = require('annotator')
$ = Annotator.$
$ = require('jquery')
# Public: Wraps the DOM Nodes within the provided range with a highlight
......
Annotator = require('annotator')
$ = Annotator.$
Range = require('../anchoring/range')
$ = require('jquery')
highlighter = require('../highlighter')
......@@ -8,7 +8,7 @@ describe "highlightRange", ->
txt = document.createTextNode('test highlight span')
el = document.createElement('span')
el.appendChild(txt)
r = new Annotator.Range.NormalizedRange({
r = new Range.NormalizedRange({
commonAncestor: el,
start: txt,
end: txt
......@@ -26,7 +26,7 @@ describe "highlightRange", ->
el.appendChild(txt)
el.appendChild(blank)
el.appendChild(txt2)
r = new Annotator.Range.NormalizedRange({
r = new Range.NormalizedRange({
commonAncestor: el,
start: txt,
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