Commit 66db778f authored by Randall Leeds's avatar Randall Leeds

Fuzzy anchoring on unrendered PDF pages

Also change the PDF anchoring to leverage the HTML anchoring by
allowing the options to be passed into the module-level anchor
functions in the pdf and html modules so that the pdf module can
be only responsible for finding the appropriate page and then
delegating.
parent 0d49f282
......@@ -16,11 +16,7 @@
# :return: A Promise that resolves to a Range on success.
# :rtype: Promise
####
exports.anchor = (selectors) ->
options =
root: document.body
ignoreSelector: '[class^="annotator-"]'
exports.anchor = (selectors, options = {}) ->
# Selectors
fragment = null
position = null
......
......@@ -3,10 +3,8 @@ seek = require('dom-seek')
Annotator = require('annotator')
xpathRange = Annotator.Range
{
TextPositionAnchor
TextQuoteAnchor
} = require('./types')
html = require('./html')
{TextPositionAnchor, TextQuoteAnchor} = require('./types')
getSiblingIndex = (node) ->
......@@ -77,11 +75,7 @@ findPage = (offset) ->
# :return: A Promise that resolves to a Range on success.
# :rtype: Promise
####
exports.anchor = (selectors) ->
options =
root: document.getElementById('viewer')
ignoreSelector: '[class^="annotator-"]'
exports.anchor = (selectors, options = {}) ->
# Selectors
position = null
quote = null
......@@ -104,37 +98,37 @@ exports.anchor = (selectors) ->
else
return range
anchorByPosition = (page, anchor) ->
renderingState = page.renderingState
renderingDone = page.textLayer?.renderingDone
if renderingState is RenderingStates.FINISHED and renderingDone
root = page.textLayer.textLayerDiv
selector = anchor.toSelector()
return html.anchor([selector], {root})
else
div = page.div ? page.el
placeholder = div.getElementsByClassName('annotator-placeholder')[0]
unless placeholder?
placeholder = document.createElement('span')
placeholder.classList.add('annotator-placeholder')
placeholder.textContent = 'Loading annotations…'
div.appendChild(placeholder)
range = document.createRange()
range.setStartBefore(placeholder)
range.setEndAfter(placeholder)
return range
if position?
promise = promise.catch ->
findPage(position.start)
.then(({index, offset, textContent}) ->
return findPage(position.start)
.then ({index, offset, textContent}) ->
page = getPage(index)
start = position.start - offset
end = position.end - offset
length = end - start
assertQuote(textContent.substr(start, length))
renderingState = page.renderingState
renderingDone = page.textLayer?.renderingDone
if renderingState is RenderingStates.FINISHED and renderingDone
root = page.textLayer.textLayerDiv
Promise.resolve(TextPositionAnchor.fromSelector({start, end}, {root}))
.then((a) -> Promise.resolve(a.toRange({root})))
else
div = page.div ? page.el
placeholder = div.getElementsByClassName('annotator-placeholder')[0]
unless placeholder?
placeholder = document.createElement('span')
placeholder.classList.add('annotator-placeholder')
placeholder.textContent = 'Loading annotations…'
div.appendChild(placeholder)
range = document.createRange()
range.setStartBefore(placeholder)
range.setEndAfter(placeholder)
return range
)
anchor = new TextPositionAnchor(start, end)
return anchorByPosition(page, anchor)
if quote?
promise = promise.catch ->
......@@ -142,30 +136,26 @@ exports.anchor = (selectors) ->
pageSearches = for pageIndex in [0...pagesCount]
page = getPage(pageIndex)
continue unless page.textLayer?.renderingDone
content = getPageTextContent(pageIndex)
offset = getPageOffset(pageIndex)
Promise.all([content, offset, page]).then((results) ->
Promise.all([content, offset, page]).then (results) ->
[content, offset, page] = results
quoteOptions = {root: page.textLayer.textLayerDiv}
pageOptions = {root: {textContent: content}}
if position?
# XXX: must be on one page
start = position.start - offset
end = position.end - offset
quoteOptions.position = {start, end}
return TextQuoteAnchor
.fromSelector(quote, quoteOptions)
.toRange(quoteOptions)
).catch(-> null)
return Promise.all(pageSearches).then((results) ->
pageOptions.position = {start, end}
anchor = new TextQuoteAnchor.fromSelector(quote, pageOptions)
return Promise.resolve(anchor)
.then((a) -> return a.toPositionAnchor(pageOptions))
.then((a) -> return anchorByPosition(page, a))
pageSearches = (p.catch(-> null) for p in pageSearches)
return Promise.all(pageSearches).then (results) ->
for result in results when result?
return result
throw new Error('quote not found')
)
return promise
......
......@@ -211,6 +211,18 @@ class TextQuoteAnchor extends Anchor
return new TextQuoteAnchor(exact, prefix, suffix, start, end)
toRange: (options = {}) ->
return this.toPositionAnchor(options).toRange()
toSelector: ->
selector = {
type: 'TextQuoteSelector'
exact: @quote
}
if @prefix? then selector.prefix = @prefix
if @suffix? then selector.suffix = @suffix
return selector
toPositionAnchor: (options = {}) ->
root = options.root or document.body
dmp = new DiffMatchPatch()
......@@ -245,16 +257,7 @@ class TextQuoteAnchor extends Anchor
dmp.Match_Distance = 64
{start, end} = slices.reduce(foldSlices, {start, end, loc})
return new TextPositionAnchor(start, end).toRange(options)
toSelector: ->
selector = {
type: 'TextQuoteSelector'
exact: @quote
}
if @prefix? then selector.prefix = @prefix
if @suffix? then selector.suffix = @suffix
return selector
return new TextPositionAnchor(start, end)
exports.Anchor = Anchor
exports.FragmentAnchor = FragmentAnchor
......
......@@ -155,8 +155,9 @@ module.exports = class Guest extends Annotator
deadHighlights = []
_anchor = (target) ->
options = {ignoreSelector: '[class^="annotator-"]'}
return new Promise(raf)
.then(-> anchoring.anchor(target.selector))
.then(-> anchoring.anchor(target.selector, options))
.then((range) -> {annotation, target, range})
.catch(-> {annotation, target})
......
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