From 5fb8a6d833ca83df9ce91ab28c0b0e55dfb7b767 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 4 Jan 2012 21:50:52 +0530 Subject: [PATCH] EPUB CFI basically works. Now for the more comprehensive testing. --- src/calibre/ebooks/oeb/display/cfi.coffee | 57 +++++++++-------- .../ebooks/oeb/display/test/cfi-test.coffee | 58 +++++++++++++++--- .../ebooks/oeb/display/test/index.html | 2 + .../ebooks/oeb/display/test/marker.png | Bin 0 -> 751 bytes src/calibre/utils/coffeescript.py | 5 +- 5 files changed, 87 insertions(+), 35 deletions(-) create mode 100644 src/calibre/ebooks/oeb/display/test/marker.png diff --git a/src/calibre/ebooks/oeb/display/cfi.coffee b/src/calibre/ebooks/oeb/display/cfi.coffee index dea5b2fa4b..e76886575d 100644 --- a/src/calibre/ebooks/oeb/display/cfi.coffee +++ b/src/calibre/ebooks/oeb/display/cfi.coffee @@ -58,16 +58,6 @@ get_current_time = (target) -> # {{{ fstr(ans) # }}} -set_current_time = (target, val) -> # {{{ - if target.currentTime == undefined - return - if target.readyState == 4 or target.readyState == "complete" - target.currentTime = val - else - fn = -> target.currentTime = val - target.addEventListener("canplay", fn, false) - -#}}} class CanonicalFragmentIdentifier @@ -76,13 +66,23 @@ class CanonicalFragmentIdentifier constructor: () -> + set_current_time: (target, val) -> # {{{ + if target.currentTime == undefined + return + if target.readyState == 4 or target.readyState == "complete" + target.currentTime = val + else + fn = -> target.currentTime = val + target.addEventListener("canplay", fn, false) + #}}} + encode: (doc, node, offset, tail) -> # {{{ cfi = tail or "" # Handle the offset, if any switch node.nodeType when 1 # Element node - if typeoff(offset) == 'number' + if typeof(offset) == 'number' node = node.childNodes.item(offset) when 3, 4, 5, 6 # Text/entity/CDATA node offset or= 0 @@ -136,7 +136,7 @@ class CanonicalFragmentIdentifier node = doc until cfi.length < 1 or error - if ( (r = cfi.match(simple_node_regex)) is not null ) # Path step + if (r = cfi.match(simple_node_regex)) # Path step target = parseInt(r[1]) assertion = r[2] if assertion @@ -318,22 +318,31 @@ class CanonicalFragmentIdentifier try_list = [{start:0, end:0, a:0.5}, {start:0, end:1, a:1}, {start:-1, end:0, a:0}] else try_list = [{start:0, end:0, a:0.5}, {start:-1, end:0, a:0}, {start:0, end:1, a:1}] - k = 0 a = null rects = null node_len = node.nodeValue.length - until rects or rects.length or k >= try_list.length - t = try_list[k++] - start_offset = r.offset + t.start - end_offset = r.offset + t.end - a = t.a - if start_offset < 0 or end_offset >= node_len - continue - range.setStart(node, start_offset) - range.setEnd(node, end_offset) - rects = range.getClientRects() + offset = r.offset + for i in [0, 1] + # Try reducing the offset by 1 if we get no match as if it refers to the position after the + # last character we wont get a match with getClientRects + offset = r.offset - i + if offset < 0 + offset = 0 + k = 0 + until rects?.length or k >= try_list.length + t = try_list[k++] + start_offset = offset + t.start + end_offset = offset + t.end + a = t.a + if start_offset < 0 or end_offset >= node_len + continue + range.setStart(node, start_offset) + range.setEnd(node, end_offset) + rects = range.getClientRects() + if rects?.length + break - if not rects or not rects.length + if not rects?.length log("Could not find caret position: rects: #{ rects } offset: #{ r.offset }") return null diff --git a/src/calibre/ebooks/oeb/display/test/cfi-test.coffee b/src/calibre/ebooks/oeb/display/test/cfi-test.coffee index 056d24b396..e371bab4df 100644 --- a/src/calibre/ebooks/oeb/display/test/cfi-test.coffee +++ b/src/calibre/ebooks/oeb/display/test/cfi-test.coffee @@ -6,19 +6,59 @@ Released under the GPLv3 License ### +log = (error) -> + if error + if window?.console?.log + window.console.log(error) + else if process?.stdout?.write + process.stdout.write(error + '\n') + viewport_top = (node) -> $(node).offset().top - window.pageYOffset viewport_left = (node) -> $(node).offset().left - window.pageXOffset -window.onload = -> - h1 = document.getElementsByTagName('h1')[0] - x = h1.scrollLeft + 150 - y = viewport_top(h1) + h1.offsetHeight/2 - e = document.elementFromPoint x, y - if e.getAttribute('id') != 'first-h1' - alert 'Failed to find top h1' - return - alert window.cfi.at x, y +show_cfi = (dont_seek) -> + if window.current_cfi + pos = window.cfi.point(window.current_cfi) + ms = document.getElementById("marker").style + if pos + ms.visibility = "visible" + ms.top = (pos.y - 30) + window.scrollY + "px" + ms.left = (pos.x - 1) + window.scrollX + "px" + if not dont_seek + if typeof pos.time == "number" + window.cfi.set_current_time(pos.node, pos.time) + scrollTo(0, pos.y - 30) + null + +RELOAD = true + +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 })" + window.location.replace(newloc) + if RELOAD + window.location.reload() + + setTimeout(fn, 1) + null + +window.onload = -> + window.onscroll = show_cfi + window.onresize = show_cfi + document.onclick = mark_and_reload + for iframe in document.getElementsByTagName("iframe") + iframe.contentWindow.onscroll = show_cfi + r = location.hash.match(/#epubcfi\((.+)\)$/) + if r + window.current_cfi = r[1] + setTimeout(show_cfi, 1) + null + diff --git a/src/calibre/ebooks/oeb/display/test/index.html b/src/calibre/ebooks/oeb/display/test/index.html index 1b93bb9739..4ab7dca502 100644 --- a/src/calibre/ebooks/oeb/display/test/index.html +++ b/src/calibre/ebooks/oeb/display/test/index.html @@ -8,6 +8,8 @@

Testing CFI functionality

+ +

0123

diff --git a/src/calibre/ebooks/oeb/display/test/marker.png b/src/calibre/ebooks/oeb/display/test/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..6dcc1fb7ba693b0af2e108a59e3e99c439bfdc55 GIT binary patch literal 751 zcmVvA>c ziyb~c5Rlt>S1qO$pR{q*&Xq}-IOlYj{(#*&8t@8Z^QX*Y45*O?UsO3>W%~=Xw^J? zB69JK_?I6^_rh!GsAUM!1-ankJ(2I%M9#lUzKLB9b|o+cuzbSe>kSMLsqdc<5dweN zHq1I(J%RV<-kyH(b5rE#p?xz+@f(+eonnB#gjRDkt*gveMj&-V)(Uw-cX9u!34Dg% zorHnNz(kNXRzd&=?cmThQqf5ml-Nt~{xTAtBX}4;6+tQ%690wBl?O>I^btoi*IzaCjXEG%C3AE{QDiXgtoC(!AMi_5P;K4-O$qU`lx~d8qZ%- zLgUR_$LW{XEvwc6v%`(h2TqATkBfkpBA_J3(0G!PppO}7Bv$mSn#IHnly=^;A)}3F z$KX0oT>8I@1zH##<4(r}YFMlg5tQJ}^)lM$5n!}8)9V$x8o=@nAJ7eO$270fmAMb3 z=Dx3~*jp5kAUf6@FC|{a{{-JPUd4K`m@44Nc55yg`HNi8%sdBbkua~nR{^)Vz#8p~ zuHsdto-u!5Ud0(?Wm2oG1v