mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-31 14:33:54 -04:00
EPUB CFI: Handle identifiers pointing to content inside a scrollable element
This commit is contained in:
parent
645d7e95c7
commit
383fe346dd
@ -63,6 +63,18 @@ get_current_time = (target) -> # {{{
|
||||
fstr(ans)
|
||||
# }}}
|
||||
|
||||
viewport_to_document = (x, y, doc) -> # {{{
|
||||
win = doc.defaultView
|
||||
x += win.scrollX
|
||||
y += win.scrollY
|
||||
if doc != window.document
|
||||
# We are in a frame
|
||||
node = win.frameElement
|
||||
rect = node.getBoundingClientRect()
|
||||
return viewport_to_document(rect.left, rect.top, node.ownerDocument)
|
||||
return [x + win.scrollX, y + win.scrollY]
|
||||
# }}}
|
||||
|
||||
# Equivalent for caretRangeFromPoint for non WebKit browsers {{{
|
||||
range_has_point = (range, x, y) ->
|
||||
for rect in range.getClientRects()
|
||||
@ -297,7 +309,9 @@ class CanonicalFragmentIdentifier
|
||||
next = false
|
||||
while true
|
||||
nn = node.nextSibling
|
||||
if nn.nodeType in [3, 4, 5, 6] # Text node, entity, cdata
|
||||
if not nn
|
||||
break
|
||||
if nn.nodeType in [3, 4, 5, 6] and nn.nodeValue?.length # Text node, entity, cdata
|
||||
next = nn
|
||||
break
|
||||
if not next
|
||||
@ -387,6 +401,7 @@ class CanonicalFragmentIdentifier
|
||||
nwin = ndoc.defaultView
|
||||
x = null
|
||||
y = null
|
||||
range = null
|
||||
|
||||
if typeof(r.offset) == "number"
|
||||
# Character offset
|
||||
@ -421,29 +436,62 @@ class CanonicalFragmentIdentifier
|
||||
if rects?.length
|
||||
break
|
||||
|
||||
|
||||
if not rects?.length
|
||||
log("Could not find caret position: rects: #{ rects } offset: #{ r.offset }")
|
||||
return null
|
||||
|
||||
rect = rects[0]
|
||||
x = (a*rect.left + (1-a)*rect.right)
|
||||
y = (rect.top + rect.bottom)/2
|
||||
else
|
||||
x = node.offsetLeft - nwin.scrollX
|
||||
y = node.offsetTop - nwin.scrollY
|
||||
if typeof(r.x) == "number" and node.offsetWidth
|
||||
x += (r.x*node.offsetWidth)/100
|
||||
y += (r.y*node.offsetHeight)/100
|
||||
[x, y] = [r.x, r.y]
|
||||
|
||||
until ndoc == doc
|
||||
node = nwin.frameElement
|
||||
{x:x, y:y, node:r.node, time:r.time, range:range, a:a}
|
||||
|
||||
# }}}
|
||||
|
||||
scroll_to: (cfi, callback=false, doc=window?.document) -> # {{{
|
||||
point = this.point(cfi, doc)
|
||||
if not point
|
||||
log("No point found for cfi: #{ cfi }")
|
||||
return
|
||||
if typeof point.time == 'number'
|
||||
this.set_current_time(point.node, point.time)
|
||||
|
||||
if point.range != null
|
||||
r = point.range
|
||||
node = r.startContainer
|
||||
ndoc = node.ownerDocument
|
||||
nwin = ndoc.defaultView
|
||||
x += node.offsetLeft - nwin.scrollX
|
||||
y += node.offsetTop - nwin.scrollY
|
||||
span = ndoc.createElement('span')
|
||||
span.setAttribute('style', 'border-width: 0; padding: 0; margin: 0')
|
||||
r.surroundContents(span)
|
||||
span.scrollIntoView()
|
||||
fn = ->
|
||||
rect = span.getBoundingClientRect()
|
||||
x = (point.a*rect.left + (1-point.a)*rect.right)
|
||||
y = (rect.top + rect.bottom)/2
|
||||
[x, y] = viewport_to_document(x, y, ndoc)
|
||||
span.outerHTML = span.innerHTML
|
||||
if callback
|
||||
callback(x, y)
|
||||
else
|
||||
node = point.node
|
||||
nwin = node.ownerDocument.defaultView
|
||||
node.scrollIntoView()
|
||||
|
||||
{x:x, y:y, node:r.node, time:r.time}
|
||||
fn = ->
|
||||
rect = node.getBoundingClientRect()
|
||||
[x, y] = viewport_to_document(rect.left, rect.top, node.ownerDocument)
|
||||
if typeof(point.x) == 'number' and node.offsetWidth
|
||||
x += (r.x*node.offsetWidth)/100
|
||||
if typeof(point.y) == 'number' and node.offsetHeight
|
||||
y += (r.y*node.offsetHeight)/100
|
||||
scrollTo(x, y)
|
||||
if callback
|
||||
callback(x, y)
|
||||
|
||||
setTimeout(fn, 10)
|
||||
|
||||
null
|
||||
# }}}
|
||||
|
||||
if window?
|
||||
|
@ -21,30 +21,29 @@ viewport_left = (node) ->
|
||||
|
||||
show_cfi = (dont_seek) ->
|
||||
if window.current_cfi
|
||||
pos = window.cfi.point(window.current_cfi)
|
||||
if pos
|
||||
fn = (x, y) ->
|
||||
ms = $("#marker")
|
||||
ms.offset({left:pos.x-1, top:pos.y-30})
|
||||
ms.css('visibility', 'visible')
|
||||
if not dont_seek
|
||||
if typeof pos.time == "number"
|
||||
window.cfi.set_current_time(pos.node, pos.time)
|
||||
scrollTo(0, pos.y - 30)
|
||||
# This strange sequence is needed to get it to work in Chrome
|
||||
# when called from the onload handler
|
||||
ms.offset({left:x-1, top:y-30})
|
||||
ms.offset()
|
||||
ms.offset({left:x-1, top:y-30})
|
||||
|
||||
|
||||
window.cfi.scroll_to(window.current_cfi, fn)
|
||||
null
|
||||
|
||||
# Set this to true to have the browser reload the page with the current cfi
|
||||
RELOAD = false
|
||||
|
||||
mark_and_reload = (evt) ->
|
||||
window.current_cfi = window.cfi.at(evt.clientX, evt.clientY)
|
||||
if not RELOAD
|
||||
show_cfi(true)
|
||||
if window.current_cfi
|
||||
fn = () ->
|
||||
newloc = window.location.href.replace(/#.*$/, '') + "#epubcfi(#{ window.current_cfi })"
|
||||
epubcfi = "#epubcfi(#{ window.current_cfi })"
|
||||
newloc = window.location.href.replace(/#.*$/, '') + epubcfi
|
||||
window.location.replace(newloc)
|
||||
if RELOAD
|
||||
window.location.reload()
|
||||
document.getElementById('current-cfi').innerHTML = window.current_cfi
|
||||
window.location.reload()
|
||||
|
||||
setTimeout(fn, 1)
|
||||
null
|
||||
@ -58,7 +57,7 @@ window.onload = ->
|
||||
r = location.hash.match(/#epubcfi\((.+)\)$/)
|
||||
if r
|
||||
window.current_cfi = r[1]
|
||||
setTimeout(show_cfi, 1)
|
||||
document.getElementById('current-cfi').innerHTML = window.current_cfi
|
||||
setTimeout(show_cfi, 100)
|
||||
null
|
||||
|
||||
|
||||
|
@ -5,11 +5,61 @@
|
||||
<script type="text/javascript" src="cfi.coffee"></script>
|
||||
<script type="text/javascript" src="jquery.js"></script>
|
||||
<script type="text/javascript" src="cfi-test.coffee"></script>
|
||||
<style type="text/css">
|
||||
body { font-family: sans-serif }
|
||||
|
||||
h2 {
|
||||
border-top: solid 2px black;
|
||||
margin-top: 4ex;
|
||||
}
|
||||
|
||||
#container {
|
||||
max-width: 30em;
|
||||
margin-right: auto;
|
||||
margin-left: 2em;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
#current-cfi {
|
||||
font-family: monospace;
|
||||
border: solid 1px blue;
|
||||
padding: 1em;
|
||||
}
|
||||
#overflow {
|
||||
max-height: 100px;
|
||||
overflow: scroll;
|
||||
border: solid 1px black;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1 id="first-h1" style="border: solid 1px red">Testing CFI functionality</h1>
|
||||
<img id="marker" style="position: absolute; visibility: hidden;" src="marker.png" alt="Marker" />
|
||||
<p>0123</p>
|
||||
<div id="container">
|
||||
<h1 id="first-h1">Testing EPUB CFI</h1>
|
||||
<div>Current CFI: <span id="current-cfi">None</span></div>
|
||||
<h2>A div with scrollbars</h2>
|
||||
<div id="overflow"> But I must explain to you how all this mistaken
|
||||
idea of denouncing pleasure and praising pain was born and I
|
||||
will give you a complete account of the system, and expound the
|
||||
actual teachings of the great explorer of the truth, the
|
||||
master-builder of human happiness. No one rejects, dislikes, or
|
||||
avoids pleasure itself, because it is pleasure, but because
|
||||
those who do not know how to pursue pleasure rationally
|
||||
encounter consequences that are extremely painful. Nor again is
|
||||
there anyone who <b>loves</b> or pursues or desires to obtain pain of
|
||||
itself, because it is pain, but because occasionally
|
||||
circumstances occur in which toil and pain can procure him some
|
||||
great pleasure. To take a trivial example, which of us ever
|
||||
undertakes laborious physical exercise, except to obtain some
|
||||
advantage from it? But who has any right to find fault with a
|
||||
man who chooses to enjoy a pleasure that has no annoying
|
||||
consequences, or one who avoids a pain that produces no
|
||||
resultant pleasure? On the other hand, we denounce with
|
||||
righteous indignation and dislike men who are so beguiled and
|
||||
demoralized by the charms of pleasure of the moment, so blinded
|
||||
by desire, that they cannot foresee
|
||||
</div>
|
||||
</div>
|
||||
<img id="marker" style="position: absolute; visibility: hidden; z-index:10" src="marker.png" alt="Marker" />
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user