From b71674436e89b6eaf9032018675889bf431c59e9 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 19 Nov 2013 14:07:50 +0530 Subject: [PATCH] Refactor preview panel JS into CS --- src/calibre/ebooks/oeb/display/utils.coffee | 11 +++ src/calibre/ebooks/oeb/polish/preview.coffee | 67 ++++++++++++++++++ src/calibre/gui2/tweak_book/preview.py | 74 +++----------------- 3 files changed, 88 insertions(+), 64 deletions(-) create mode 100644 src/calibre/ebooks/oeb/polish/preview.coffee diff --git a/src/calibre/ebooks/oeb/display/utils.coffee b/src/calibre/ebooks/oeb/display/utils.coffee index b90fbc55b6..5558b1ad0a 100644 --- a/src/calibre/ebooks/oeb/display/utils.coffee +++ b/src/calibre/ebooks/oeb/display/utils.coffee @@ -93,6 +93,17 @@ class CalibreUtils return this.viewport_to_document(r.left, 0, elem.ownerDocument)[0] # }}} + abstop: (elem) -> # {{{ + # The left edge of elem in document co-ords. Works in all + # circumstances, including column layout. Note that this will cause + # a relayout if the render tree is dirty. Also, because of a bug in the + # version of WebKit bundled with Qt 4.8, this does not always work, see + # https://bugs.launchpad.net/bugs/1132641 for a test case. + r = elem.getBoundingClientRect() + return this.viewport_to_document(r.top, 0, elem.ownerDocument)[0] + # }}} + + if window? window.calibre_utils = new CalibreUtils() diff --git a/src/calibre/ebooks/oeb/polish/preview.coffee b/src/calibre/ebooks/oeb/polish/preview.coffee new file mode 100644 index 0000000000..f218cc26bd --- /dev/null +++ b/src/calibre/ebooks/oeb/polish/preview.coffee @@ -0,0 +1,67 @@ +#!/usr/bin/env coffee +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +### + Copyright 2013, Kovid Goyal + Released under the GPLv3 License +### + + +if window?.calibre_utils + log = window.calibre_utils.log + +is_hidden = (elem) -> + while elem + if (elem.style && (elem.style.visibility == 'hidden' || elem.style.display == 'none')) + return true + elem = elem.parentNode + return false + +class PreviewIntegration + + ### + # Namespace to expose all the functions used for integration with the Tweak + # Book Preview Panel. + ### + + constructor: () -> + if not this instanceof arguments.callee + throw new Error('PreviewIntegration constructor called as function') + + go_to_line: (lnum) -> + for node in document.querySelectorAll('[data-lnum="' + lnum + '"]') + if is_hidden(node) + continue + top = window.calibre_utils.abstop(node) - (window.innerHeight / 2) + if (top < 0) + top = 0 + window.scrollTo(0, top) + return + + line_numbers: () -> + found_body = false + ans = [] + for node in document.getElementsByTagName('*') + if not found_body and node.tagName.toLowerCase() == "body" + found_body = true + if found_body + ans.push(node.getAttribute("data-lnum")) + return ans + + find_blocks: () => + for elem in document.body.getElementsByTagName('*') + style = window.getComputedStyle(elem) + if style.display in ['block', 'flex-box', 'box'] + elem.setAttribute('data-is-block', '1') + elem.onclick = this.onclick + + onload: () -> + window.document.body.addEventListener('click', window.calibre_preview_integration.onclick, true) + + onclick: (event) -> + event.preventDefault() + window.py_bridge.request_sync(event.target.getAttribute("data-lnum")) + +window.calibre_preview_integration = new PreviewIntegration() +window.onload = window.calibre_preview_integration.onload + diff --git a/src/calibre/gui2/tweak_book/preview.py b/src/calibre/gui2/tweak_book/preview.py index d1daa71c32..4df502dd81 100644 --- a/src/calibre/gui2/tweak_book/preview.py +++ b/src/calibre/gui2/tweak_book/preview.py @@ -19,7 +19,7 @@ from PyQt4.Qt import ( from PyQt4.QtWebKit import QWebView, QWebInspector, QWebPage from calibre import prints -from calibre.constants import iswindows +from calibre.constants import iswindows, DEBUG from calibre.ebooks.oeb.polish.parsing import parse from calibre.ebooks.oeb.base import serialize, OEB_DOCS from calibre.ptempfile import PersistentTemporaryDirectory @@ -220,66 +220,6 @@ class NetworkAccessManager(QNetworkAccessManager): # }}} -JS = ''' -function handle_click(event) { - event.preventDefault(); - window.py_bridge.request_sync(event.target.getAttribute("data-lnum")); -} - -function line_numbers() { - var elements = document.getElementsByTagName('*'), found_body = false, ans = [], node, i; - var found_body = false; - var ans = []; - for (i = 0; i < elements.length; i++) { - node = elements[i]; - if (!found_body && node.tagName.toLowerCase() === "body") { - found_body = true; - } - if (found_body) { - ans.push(node.getAttribute("data-lnum")); - } - } - return ans; -} - -function document_offset_top(obj) { - var curtop = 0; - if (obj.offsetParent) { - do { - curtop += obj.offsetTop; - } while (obj = obj.offsetParent); - return curtop; - } -} - -function is_hidden(elem) { - var p = elem; - while (p) { - if (p.style && (p.style.visibility === 'hidden' || p.style.display === 'none')) - return true; - p = p.parentNode; - } - return false; -} - -function go_to_line(lnum) { - var elements = document.querySelectorAll('[data-lnum="' + lnum + '"]'); - for (var i = 0; i < elements.length; i++) { - var node = elements[i]; - if (is_hidden(node)) continue; - var top = document_offset_top(node) - (window.innerHeight / 2); - if (top < 0) top = 0; - window.scrollTo(0, top); - return; - } -} - -window.onload = function() { - document.body.addEventListener('click', handle_click, true); -} - -''' - def uniq(vals): ''' Remove all duplicates from vals, while preserving order. ''' vals = vals or () @@ -321,10 +261,14 @@ class WebPage(QWebPage): prints('preview js:%s:%s:'%(unicode(source_id), lineno), unicode(msg)) def init_javascript(self): + if not hasattr(self, 'js'): + from calibre.utils.resources import compiled_coffeescript + self.js = compiled_coffeescript('ebooks.oeb.display.utils', dynamic=DEBUG) + self.js += compiled_coffeescript('ebooks.oeb.polish.preview', dynamic=DEBUG) self._line_numbers = None mf = self.mainFrame() mf.addToJavaScriptWindowObject("py_bridge", self) - mf.evaluateJavaScript(JS) + mf.evaluateJavaScript(self.js) @pyqtSlot(str) def request_sync(self, lnum): @@ -341,7 +285,8 @@ class WebPage(QWebPage): if not ok: ans = None return ans - self._line_numbers = sorted(uniq(filter(lambda x:x is not None, map(atoi, self.mainFrame().evaluateJavaScript('line_numbers()').toStringList())))) + self._line_numbers = sorted(uniq(filter(lambda x:x is not None, map(atoi, self.mainFrame().evaluateJavaScript( + 'window.calibre_preview_integration.line_numbers()').toStringList())))) return self._line_numbers def go_to_line(self, lnum): @@ -349,7 +294,8 @@ class WebPage(QWebPage): lnum = find_le(self.line_numbers, lnum) except ValueError: return - self.mainFrame().evaluateJavaScript('go_to_line(%d)' % lnum) + self.mainFrame().evaluateJavaScript( + 'window.calibre_preview_integration.go_to_line(%d)' % lnum) class WebView(QWebView):