mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Migrate editor coffee script to rapydscript
This commit is contained in:
parent
943f07b1fd
commit
00a9d0f275
@ -278,8 +278,16 @@ class RapydScript(Command): # {{{
|
|||||||
|
|
||||||
description = 'Compile RapydScript to JavaScript'
|
description = 'Compile RapydScript to JavaScript'
|
||||||
|
|
||||||
|
def add_options(self, parser):
|
||||||
|
parser.add_option('--only-module', default=None,
|
||||||
|
help='Only compile the specified module')
|
||||||
|
|
||||||
def run(self, opts):
|
def run(self, opts):
|
||||||
from calibre.utils.rapydscript import compile_srv
|
from calibre.utils.rapydscript import compile_srv, compile_editor
|
||||||
|
if opts.only_module:
|
||||||
|
locals()['compile_' + opts.only]()
|
||||||
|
else:
|
||||||
|
compile_editor()
|
||||||
compile_srv()
|
compile_srv()
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ from __future__ import absolute_import, division, print_function, unicode_litera
|
|||||||
# check if you can remove the restriction that prevents inspector dock from being undocked
|
# check if you can remove the restriction that prevents inspector dock from being undocked
|
||||||
# check syncing of position back and forth
|
# check syncing of position back and forth
|
||||||
# check all buttons in preview panel
|
# check all buttons in preview panel
|
||||||
# rewrite JS from coffeescript to rapydscript
|
|
||||||
# pass user stylesheet with css for split
|
# pass user stylesheet with css for split
|
||||||
|
|
||||||
import json
|
import json
|
||||||
@ -30,7 +29,9 @@ from PyQt5.QtWebEngineWidgets import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
from calibre.constants import FAKE_HOST, FAKE_PROTOCOL, __version__
|
from calibre.constants import (
|
||||||
|
FAKE_HOST, FAKE_PROTOCOL, __version__, is_running_from_develop
|
||||||
|
)
|
||||||
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
|
from calibre.ebooks.oeb.base import OEB_DOCS, XHTML_MIME, serialize
|
||||||
from calibre.ebooks.oeb.polish.parsing import parse
|
from calibre.ebooks.oeb.polish.parsing import parse
|
||||||
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, open_url, secure_webengine
|
from calibre.gui2 import NO_URL_FORMATTING, error_dialog, open_url, secure_webengine
|
||||||
@ -276,11 +277,12 @@ def create_profile():
|
|||||||
ans = QWebEngineProfile(QApplication.instance())
|
ans = QWebEngineProfile(QApplication.instance())
|
||||||
ua = 'calibre-editor-preview ' + __version__
|
ua = 'calibre-editor-preview ' + __version__
|
||||||
ans.setHttpUserAgent(ua)
|
ans.setHttpUserAgent(ua)
|
||||||
from calibre.utils.resources import compiled_coffeescript
|
if is_running_from_develop:
|
||||||
js = compiled_coffeescript('ebooks.oeb.display.utils', dynamic=False)
|
from calibre.utils.rapydscript import compile_editor
|
||||||
|
compile_editor()
|
||||||
|
js = P('editor.js', data=True, allow_user_override=False)
|
||||||
js += P('csscolorparser.js', data=True, allow_user_override=False)
|
js += P('csscolorparser.js', data=True, allow_user_override=False)
|
||||||
js += compiled_coffeescript('ebooks.oeb.polish.preview', dynamic=False)
|
insert_scripts(ans, create_script('editor.js', js))
|
||||||
insert_scripts(ans, create_script('editor-preview.js', js))
|
|
||||||
url_handler = UrlSchemeHandler(ans)
|
url_handler = UrlSchemeHandler(ans)
|
||||||
ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
|
ans.installUrlSchemeHandler(QByteArray(FAKE_PROTOCOL.encode('ascii')), url_handler)
|
||||||
s = ans.settings()
|
s = ans.settings()
|
||||||
|
@ -98,7 +98,7 @@ def module_cache_dir():
|
|||||||
return _cache_dir
|
return _cache_dir
|
||||||
|
|
||||||
|
|
||||||
def compile_pyj(data, filename='<stdin>', beautify=True, private_scope=True, libdir=None, omit_baselib=False):
|
def compile_pyj(data, filename='<stdin>', beautify=True, private_scope=True, libdir=None, omit_baselib=False, js_version=5):
|
||||||
if isinstance(data, bytes):
|
if isinstance(data, bytes):
|
||||||
data = data.decode('utf-8')
|
data = data.decode('utf-8')
|
||||||
c = compiler()
|
c = compiler()
|
||||||
@ -109,6 +109,7 @@ def compile_pyj(data, filename='<stdin>', beautify=True, private_scope=True, lib
|
|||||||
'libdir': libdir or default_lib_dir(),
|
'libdir': libdir or default_lib_dir(),
|
||||||
'basedir': getcwd() if not filename or filename == '<stdin>' else os.path.dirname(filename),
|
'basedir': getcwd() if not filename or filename == '<stdin>' else os.path.dirname(filename),
|
||||||
'filename': filename,
|
'filename': filename,
|
||||||
|
'js_version': js_version,
|
||||||
}
|
}
|
||||||
c.g.rs_source_code = data
|
c.g.rs_source_code = data
|
||||||
ok, result = c.eval(
|
ok, result = c.eval(
|
||||||
@ -156,12 +157,12 @@ def detect_external_compiler():
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def compile_fast(data, filename=None, beautify=True, private_scope=True, libdir=None, omit_baselib=False):
|
def compile_fast(data, filename=None, beautify=True, private_scope=True, libdir=None, omit_baselib=False, js_version=None):
|
||||||
global has_external_compiler
|
global has_external_compiler
|
||||||
if has_external_compiler is None:
|
if has_external_compiler is None:
|
||||||
has_external_compiler = detect_external_compiler()
|
has_external_compiler = detect_external_compiler()
|
||||||
if not has_external_compiler:
|
if not has_external_compiler:
|
||||||
return compile_pyj(data, filename or '<stdin>', beautify, private_scope, libdir, omit_baselib)
|
return compile_pyj(data, filename or '<stdin>', beautify, private_scope, libdir, omit_baselib, js_version or 6)
|
||||||
args = ['--cache-dir', module_cache_dir(), '--import-path', libdir or default_lib_dir()]
|
args = ['--cache-dir', module_cache_dir(), '--import-path', libdir or default_lib_dir()]
|
||||||
if not beautify:
|
if not beautify:
|
||||||
args.append('--uglify')
|
args.append('--uglify')
|
||||||
@ -169,6 +170,8 @@ def compile_fast(data, filename=None, beautify=True, private_scope=True, libdir=
|
|||||||
args.append('--bare')
|
args.append('--bare')
|
||||||
if omit_baselib:
|
if omit_baselib:
|
||||||
args.append('--omit-baselib')
|
args.append('--omit-baselib')
|
||||||
|
if js_version:
|
||||||
|
args.append('--js-version=' + str(js_version))
|
||||||
if not isinstance(data, bytes):
|
if not isinstance(data, bytes):
|
||||||
data = data.encode('utf-8')
|
data = data.encode('utf-8')
|
||||||
if filename:
|
if filename:
|
||||||
@ -199,6 +202,24 @@ def base_dir():
|
|||||||
return d(d(d(d(os.path.abspath(__file__)))))
|
return d(d(d(d(os.path.abspath(__file__)))))
|
||||||
|
|
||||||
|
|
||||||
|
def atomic_write(base, name, content):
|
||||||
|
name = os.path.join(base, name)
|
||||||
|
tname = name + '.tmp'
|
||||||
|
with lopen(tname, 'wb') as f:
|
||||||
|
f.write(content)
|
||||||
|
atomic_rename(tname, name)
|
||||||
|
|
||||||
|
|
||||||
|
def compile_editor():
|
||||||
|
base = base_dir()
|
||||||
|
rapydscript_dir = os.path.join(base, 'src', 'pyj')
|
||||||
|
fname = os.path.join(rapydscript_dir, 'editor.pyj')
|
||||||
|
with lopen(fname, 'rb') as f:
|
||||||
|
js = compile_fast(f.read(), fname, js_version=6)
|
||||||
|
base = os.path.join(base, 'resources')
|
||||||
|
atomic_write(base, 'editor.js', js)
|
||||||
|
|
||||||
|
|
||||||
def compile_srv():
|
def compile_srv():
|
||||||
base = base_dir()
|
base = base_dir()
|
||||||
iconf = os.path.join(base, 'imgsrc', 'srv', 'generate.py')
|
iconf = os.path.join(base, 'imgsrc', 'srv', 'generate.py')
|
||||||
@ -223,16 +244,8 @@ def compile_srv():
|
|||||||
html = f.read().replace(b'RESET_STYLES', reset, 1).replace(b'ICONS', icons, 1).replace(b'MAIN_JS', js, 1)
|
html = f.read().replace(b'RESET_STYLES', reset, 1).replace(b'ICONS', icons, 1).replace(b'MAIN_JS', js, 1)
|
||||||
|
|
||||||
manifest = create_manifest(html)
|
manifest = create_manifest(html)
|
||||||
|
atomic_write(base, 'index-generated.html', html)
|
||||||
def atomic_write(name, content):
|
atomic_write(base, 'calibre.appcache', manifest)
|
||||||
name = os.path.join(base, name)
|
|
||||||
tname = name + '.tmp'
|
|
||||||
with lopen(tname, 'wb') as f:
|
|
||||||
f.write(content)
|
|
||||||
atomic_rename(tname, name)
|
|
||||||
|
|
||||||
atomic_write('index-generated.html', html)
|
|
||||||
atomic_write('calibre.appcache', manifest)
|
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
@ -1,38 +1,35 @@
|
|||||||
#!/usr/bin/env coffee
|
# vim:fileencoding=utf-8
|
||||||
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
# globals: CSSRule
|
||||||
|
from __python__ import bound_methods, hash_literals
|
||||||
|
|
||||||
###
|
def is_hidden(elem):
|
||||||
Copyright 2013, Kovid Goyal <kovid at kovidgoyal.net>
|
while elem:
|
||||||
Released under the GPLv3 License
|
if (elem.style and (elem.style.visibility is 'hidden' or elem.style.display is 'none')):
|
||||||
###
|
return True
|
||||||
|
|
||||||
|
|
||||||
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
|
elem = elem.parentNode
|
||||||
return false
|
return False
|
||||||
|
|
||||||
is_block = (elem) ->
|
|
||||||
|
def is_block(elem):
|
||||||
style = window.getComputedStyle(elem)
|
style = window.getComputedStyle(elem)
|
||||||
return style.display in ['block', 'flex-box', 'box']
|
return style.display is 'block' or style.display is 'flex-box' or style.display is 'box'
|
||||||
|
|
||||||
in_table = (elem) ->
|
|
||||||
while elem
|
def in_table(elem):
|
||||||
if elem.tagName?.toLowerCase() == 'table'
|
while elem:
|
||||||
return true
|
if elem.tagName?.toLowerCase() is 'table':
|
||||||
|
return True
|
||||||
elem = elem.parentNode
|
elem = elem.parentNode
|
||||||
return false
|
return False
|
||||||
|
|
||||||
find_containing_block = (elem) ->
|
|
||||||
while elem and elem.getAttribute('data-is-block') != '1'
|
def find_containing_block(elem):
|
||||||
|
while elem and elem.getAttribute('data-is-block') != '1':
|
||||||
elem = elem.parentNode
|
elem = elem.parentNode
|
||||||
return elem
|
return elem
|
||||||
|
|
||||||
|
|
||||||
INHERITED_PROPS = { # {{{
|
INHERITED_PROPS = { # {{{
|
||||||
'azimuth': '2',
|
'azimuth': '2',
|
||||||
'border-collapse': '2',
|
'border-collapse': '2',
|
||||||
@ -140,158 +137,166 @@ INHERITED_PROPS = { # {{{
|
|||||||
'-ms-word-wrap': 'e'
|
'-ms-word-wrap': 'e'
|
||||||
} # }}}
|
} # }}}
|
||||||
|
|
||||||
get_sourceline_address = (node) ->
|
|
||||||
sourceline = parseInt(node.getAttribute('data-lnum'))
|
|
||||||
tags = []
|
|
||||||
for elem in document.querySelectorAll('[data-lnum="' + sourceline + '"]')
|
|
||||||
tags.push(elem.tagName.toLowerCase())
|
|
||||||
if elem is node
|
|
||||||
break
|
|
||||||
return [sourceline, tags]
|
|
||||||
|
|
||||||
get_color = (property, val) ->
|
def get_sourceline_address(node):
|
||||||
color = null
|
sourceline = parseInt(node.getAttribute('data-lnum'))
|
||||||
if property.indexOf('color') > -1
|
tags = v'[]'
|
||||||
try
|
for elem in document.querySelectorAll(f'[data-lnum="{sourceline}"]'):
|
||||||
color = parseCSSColor(val) # Use the csscolor library to get an rgba 4-tuple
|
tags.push(elem.tagName.toLowerCase())
|
||||||
catch error
|
if elem is node:
|
||||||
color = null
|
break
|
||||||
|
return v'[sourceline, tags]'
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(property, val):
|
||||||
|
color = None
|
||||||
|
if property.indexOf('color') > -1:
|
||||||
|
try:
|
||||||
|
color = window.parseCSSColor(val) # Use the csscolor library to get an rgba 4-tuple
|
||||||
|
except:
|
||||||
|
color = None
|
||||||
return color
|
return color
|
||||||
|
|
||||||
get_style_properties = (style, all_properties, node_style, is_ancestor) ->
|
|
||||||
|
def get_style_properties(style, all_properties, node_style, is_ancestor):
|
||||||
i = 0
|
i = 0
|
||||||
properties = []
|
properties = v'[]'
|
||||||
while i < style.length
|
while i < style.length:
|
||||||
property = style.item(i)?.toLowerCase()
|
property = style.item(i)?.toLowerCase()
|
||||||
val = style.getPropertyValue(property)
|
val = style.getPropertyValue(property)
|
||||||
if property and val and (not is_ancestor or INHERITED_PROPS.hasOwnProperty(property))
|
if property and val and (not is_ancestor or INHERITED_PROPS[property]):
|
||||||
properties.push([property, val, style.getPropertyPriority(property), get_color(property, val)])
|
properties.push(v'[property, val, style.getPropertyPriority(property), get_color(property, val)]')
|
||||||
if not all_properties.hasOwnProperty(property)
|
if not all_properties.hasOwnProperty(property):
|
||||||
cval = node_style.getPropertyValue(property)
|
all_properties[property] = v'[node_style.getPropertyValue(property), get_color(property, cval)]'
|
||||||
all_properties[property] = [cval, get_color(property, cval)]
|
|
||||||
i += 1
|
i += 1
|
||||||
return properties
|
return properties
|
||||||
|
|
||||||
process_rules = (node, cssRules, address, sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans) ->
|
|
||||||
for rule, rule_index in cssRules
|
def process_rules(node, cssRules, address, sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans):
|
||||||
|
for rule_index in range(cssRules.length):
|
||||||
|
rule = cssRules[rule_index]
|
||||||
rule_address = address.concat([rule_index])
|
rule_address = address.concat([rule_index])
|
||||||
if rule.type == CSSRule.MEDIA_RULE
|
if rule.type is CSSRule.MEDIA_RULE:
|
||||||
process_rules(node, rule.cssRules, rule_address, sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans)
|
process_rules(node, rule.cssRules, rule_address, sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans)
|
||||||
continue
|
continue
|
||||||
if rule.type != CSSRule.STYLE_RULE
|
if rule.type is not CSSRule.STYLE_RULE:
|
||||||
continue
|
continue
|
||||||
# As a performance improvement, instead of running the match on every
|
# As a performance improvement, instead of running the match on every
|
||||||
# rule, we simply check if its selector is one of the matching
|
# rule, we simply check if its selector is one of the matching
|
||||||
# selectors returned by getMatchedCSSRules. However,
|
# selectors returned by getMatchedCSSRules. However,
|
||||||
# getMatchedCSSRules ignores rules in media queries that dont apply, so we check them manually
|
# getMatchedCSSRules ignores rules in media queries that dont apply, so we check them manually
|
||||||
st = rule.selectorText
|
st = rule.selectorText
|
||||||
if st and (matching_selectors.hasOwnProperty(st) or (rule_address.length > 1 and node.webkitMatchesSelector(st)))
|
if st and (matching_selectors.hasOwnProperty(st) or (rule_address.length > 1 and node.matches(st))):
|
||||||
type = 'sheet'
|
type = 'sheet'
|
||||||
href = sheet.href
|
href = sheet.href
|
||||||
if href == null
|
if href is None:
|
||||||
href = get_sourceline_address(sheet.ownerNode)
|
href = get_sourceline_address(sheet.ownerNode)
|
||||||
type = 'elem'
|
type = 'elem'
|
||||||
parts = st.split(',') # We only want the first matching selector
|
parts = st.split(',') # We only want the first matching selector
|
||||||
if parts.length > 1
|
if parts.length > 1:
|
||||||
for q in parts
|
for q in parts:
|
||||||
if node.webkitMatchesSelector(q)
|
if node.matches(q):
|
||||||
st = q
|
st = q
|
||||||
break
|
break
|
||||||
properties = get_style_properties(rule.style, all_properties, node_style, is_ancestor)
|
properties = get_style_properties(rule.style, all_properties, node_style, is_ancestor)
|
||||||
if properties.length > 0
|
if properties.length > 0:
|
||||||
data = {'selector':st, 'type':type, 'href':href, 'properties':properties, 'rule_address':rule_address, 'sheet_index':sheet_index}
|
data = {'selector':st, 'type':type, 'href':href, 'properties':properties, 'rule_address':rule_address, 'sheet_index':sheet_index}
|
||||||
ans.push(data)
|
ans.push(data)
|
||||||
|
|
||||||
get_matched_css = (node, is_ancestor, all_properties) ->
|
def get_matched_css(node, is_ancestor, all_properties):
|
||||||
# WebKit sets parentStyleSheet == null for rules returned by getMatchedCSSRules so we cannot use them directly
|
# WebKit sets parentStyleSheet == null for rules returned by getMatchedCSSRules so we cannot use them directly
|
||||||
rules = node.ownerDocument.defaultView.getMatchedCSSRules(node, '')
|
rules = node.ownerDocument.defaultView.getMatchedCSSRules(node, '')
|
||||||
if not rules
|
if not rules:
|
||||||
rules = []
|
rules = v'[]'
|
||||||
matching_selectors = {}
|
matching_selectors = {}
|
||||||
for rule in rules
|
for rule in rules:
|
||||||
matching_selectors[rule.selectorText] = true
|
matching_selectors[rule.selectorText] = True
|
||||||
ans = []
|
ans = v'[]'
|
||||||
node_style = window.getComputedStyle(node)
|
node_style = window.getComputedStyle(node)
|
||||||
|
|
||||||
for sheet, sheet_index in document.styleSheets
|
sheets = document.styleSheets
|
||||||
if sheet.disabled or not sheet.cssRules
|
for sheet_index in range(sheets.length):
|
||||||
|
sheet = sheets[sheet_index]
|
||||||
|
if sheet.disabled or not sheet.cssRules:
|
||||||
continue
|
continue
|
||||||
process_rules(node, sheet.cssRules, [], sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans)
|
process_rules(node, sheet.cssRules, [], sheet, sheet_index, matching_selectors, all_properties, node_style, is_ancestor, ans)
|
||||||
|
|
||||||
if node.getAttribute('style')
|
if node.getAttribute('style'):
|
||||||
properties = get_style_properties(node.style, all_properties, node_style, is_ancestor)
|
properties = get_style_properties(node.style, all_properties, node_style, is_ancestor)
|
||||||
if properties.length > 0
|
if properties.length > 0:
|
||||||
data = {'selector':null, 'type':'inline', 'href':get_sourceline_address(node), 'properties':properties, 'rule_address':null, 'sheet_index':null}
|
data = {'selector':None, 'type':'inline', 'href':get_sourceline_address(node), 'properties':properties, 'rule_address':None, 'sheet_index':None}
|
||||||
ans.push(data)
|
ans.push(data)
|
||||||
|
|
||||||
return ans.reverse()
|
return ans.reverse()
|
||||||
|
|
||||||
scroll_to_node = (node) ->
|
def scroll_to_node(node):
|
||||||
if node is document.body
|
if node is document.body:
|
||||||
window.scrollTo(0, 0)
|
window.scrollTo(0, 0)
|
||||||
else
|
else:
|
||||||
node.scrollIntoView()
|
node.scrollIntoView()
|
||||||
|
|
||||||
class PreviewIntegration
|
|
||||||
|
class PreviewIntegration:
|
||||||
|
|
||||||
###
|
###
|
||||||
# Namespace to expose all the functions used for integration with the Tweak
|
# Namespace to expose all the functions used for integration with the Tweak
|
||||||
# Book Preview Panel.
|
# Book Preview Panel.
|
||||||
###
|
###
|
||||||
|
|
||||||
constructor: () ->
|
def __init__(self):
|
||||||
if not this instanceof arguments.callee
|
self.blocks_found = False
|
||||||
throw new Error('PreviewIntegration constructor called as function')
|
self.in_split_mode = False
|
||||||
this.blocks_found = false
|
|
||||||
this.in_split_mode = false
|
|
||||||
|
|
||||||
go_to_line: (lnum) =>
|
def go_to_line(self, lnum):
|
||||||
for node in document.querySelectorAll('[data-lnum="' + lnum + '"]')
|
for node in document.querySelectorAll(f'[data-lnum="{lnum}"]'):
|
||||||
if is_hidden(node)
|
if is_hidden(node):
|
||||||
continue
|
continue
|
||||||
scroll_to_node(node)
|
scroll_to_node(node)
|
||||||
|
|
||||||
go_to_sourceline_address: (sourceline, tags) =>
|
|
||||||
for node, index in document.querySelectorAll('[data-lnum="' + sourceline + '"]')
|
|
||||||
if index >= tags.length or node.tagName.toLowerCase() != tags[index]
|
|
||||||
break
|
break
|
||||||
if index == tags.length - 1 and not is_hidden(node)
|
|
||||||
return scroll_to_node(node)
|
|
||||||
this.go_to_line(sourceline)
|
|
||||||
|
|
||||||
line_numbers: () =>
|
def go_to_sourceline_address(self, sourceline, tags):
|
||||||
found_body = false
|
nodes = document.querySelectorAll(f'[data-lnum="{sourceline}"]')
|
||||||
ans = []
|
for index in range(nodes.length):
|
||||||
for node in document.getElementsByTagName('*')
|
node = nodes[index]
|
||||||
if not found_body and node.tagName.toLowerCase() == "body"
|
if index >= tags.length or node.tagName.toLowerCase() is not tags[index]:
|
||||||
found_body = true
|
break
|
||||||
if found_body
|
if index == tags.length - 1 and not is_hidden(node):
|
||||||
ans.push(node.getAttribute("data-lnum"))
|
return scroll_to_node(node)
|
||||||
|
self.go_to_line(sourceline)
|
||||||
|
|
||||||
|
def line_numbers(self):
|
||||||
|
found_body = False
|
||||||
|
ans = v'[]'
|
||||||
|
for node in document.getElementsByTagName('*'):
|
||||||
|
if not found_body and node.tagName.toLowerCase() is "body":
|
||||||
|
found_body = True
|
||||||
|
if found_body:
|
||||||
|
ans.push(node.dataset.lnum)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
find_blocks: () =>
|
def find_blocks(self):
|
||||||
if this.blocks_found
|
if self.blocks_found:
|
||||||
return
|
return
|
||||||
for elem in document.body.getElementsByTagName('*')
|
for elem in document.body.getElementsByTagName('*'):
|
||||||
if is_block(elem) and not in_table(elem)
|
if is_block(elem) and not in_table(elem):
|
||||||
elem.setAttribute('data-is-block', '1')
|
elem.setAttribute('data-is-block', '1')
|
||||||
this.blocks_found = true
|
self.blocks_found = True
|
||||||
|
|
||||||
split_mode: (enabled) =>
|
def split_mode(self, enabled):
|
||||||
this.in_split_mode = enabled
|
self.in_split_mode = enabled
|
||||||
document.body.setAttribute('data-in-split-mode', if enabled then '1' else '0')
|
document.body.setAttribute('data-in-split-mode', '1' if enabled else '0')
|
||||||
if enabled
|
if enabled:
|
||||||
this.find_blocks()
|
self.find_blocks()
|
||||||
|
|
||||||
report_split: (node) =>
|
def report_split(self, node):
|
||||||
loc = []
|
loc = v'[]'
|
||||||
totals = []
|
totals = v'[]'
|
||||||
parent = find_containing_block(node)
|
parent = find_containing_block(node)
|
||||||
while parent and parent.tagName.toLowerCase() != 'body'
|
while parent and parent.tagName.toLowerCase() is not 'body':
|
||||||
totals.push(parent.parentNode.children.length)
|
totals.push(parent.parentNode.children.length)
|
||||||
num = 0
|
num = 0
|
||||||
sibling = parent.previousElementSibling
|
sibling = parent.previousElementSibling
|
||||||
while sibling
|
while sibling:
|
||||||
num += 1
|
num += 1
|
||||||
sibling = sibling.previousElementSibling
|
sibling = sibling.previousElementSibling
|
||||||
loc.push(num)
|
loc.push(num)
|
||||||
@ -300,57 +305,60 @@ class PreviewIntegration
|
|||||||
totals.reverse()
|
totals.reverse()
|
||||||
window.py_bridge.request_split(JSON.stringify(loc), JSON.stringify(totals))
|
window.py_bridge.request_split(JSON.stringify(loc), JSON.stringify(totals))
|
||||||
|
|
||||||
onload: () =>
|
def onload(self):
|
||||||
window.document.body.addEventListener('click', this.onclick, true)
|
window.document.body.addEventListener('click', this.onclick, True)
|
||||||
|
|
||||||
onclick: (event) =>
|
def onclick(self, event):
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if this.in_split_mode
|
if self.in_split_mode:
|
||||||
this.report_split(event.target)
|
self.report_split(event.target)
|
||||||
else
|
else:
|
||||||
e = event.target
|
e = event.target
|
||||||
address = get_sourceline_address(e)
|
address = get_sourceline_address(e)
|
||||||
# Find the closest containing link, if any
|
# Find the closest containing link, if any
|
||||||
href = tn = ''
|
href = tn = ''
|
||||||
while e and e != document.body and e != document and (tn != 'a' or not href)
|
while e and e != document.body and e != document and (tn is not 'a' or not href):
|
||||||
tn = e.tagName?.toLowerCase()
|
tn = e.tagName?.toLowerCase()
|
||||||
href = e.getAttribute('href')
|
href = e.getAttribute('href')
|
||||||
e = e.parentNode
|
e = e.parentNode
|
||||||
window.py_bridge.request_sync(tn, href, JSON.stringify(address))
|
window.py_bridge.request_sync(tn, href, JSON.stringify(address))
|
||||||
return false
|
return False
|
||||||
|
|
||||||
go_to_anchor: (anchor, lnum) =>
|
def go_to_anchor(self, anchor, lnum):
|
||||||
elem = document.getElementById(anchor)
|
elem = document.getElementById(anchor)
|
||||||
if not elem
|
if not elem:
|
||||||
elem = document.querySelector('[name="' + anchor + '"]')
|
elem = document.querySelector(f'[name="{anchor}"]')
|
||||||
if elem
|
if elem:
|
||||||
elem.scrollIntoView()
|
elem.scrollIntoView()
|
||||||
address = get_sourceline_address(elem)
|
address = get_sourceline_address(elem)
|
||||||
window.py_bridge.request_sync('', '', address)
|
window.py_bridge.request_sync('', '', address)
|
||||||
|
|
||||||
live_css: (sourceline, tags) =>
|
def live_css(self, sourceline, tags):
|
||||||
target = null
|
target = None
|
||||||
i = 0
|
i = 0
|
||||||
for node in document.querySelectorAll('[data-lnum="' + sourceline + '"]')
|
for node in document.querySelectorAll(f'[data-lnum="{sourceline}"]'):
|
||||||
if node.tagName?.toLowerCase() != tags[i]
|
if node.tagName?.toLowerCase() is not tags[i]:
|
||||||
return JSON.stringify(null)
|
return JSON.stringify(None)
|
||||||
i += 1
|
i += 1
|
||||||
target = node
|
target = node
|
||||||
if i >= tags.length
|
if i >= tags.length:
|
||||||
break
|
break
|
||||||
all_properties = {}
|
all_properties = {}
|
||||||
original_target = target
|
ans = {'nodes':v'[]', 'computed_css':all_properties}
|
||||||
ans = {'nodes':[], 'computed_css':all_properties}
|
is_ancestor = False
|
||||||
is_ancestor = false
|
while target and target.ownerDocument:
|
||||||
while target and target.ownerDocument
|
|
||||||
css = get_matched_css(target, is_ancestor, all_properties)
|
css = get_matched_css(target, is_ancestor, all_properties)
|
||||||
# We want to show the Matched CSS rules header even if no rules matched
|
# We want to show the Matched CSS rules header even if no rules matched
|
||||||
if css.length > 0 or not is_ancestor
|
if css.length > 0 or not is_ancestor:
|
||||||
ans['nodes'].push({'name':target.tagName?.toLowerCase(), 'css':css, 'is_ancestor':is_ancestor, 'sourceline':target.getAttribute('data-lnum')})
|
ans.nodes.push({
|
||||||
|
'name':target.tagName?.toLowerCase(),
|
||||||
|
'css':css, 'is_ancestor':is_ancestor,
|
||||||
|
'sourceline':target.getAttribute('data-lnum')
|
||||||
|
})
|
||||||
target = target.parentNode
|
target = target.parentNode
|
||||||
is_ancestor = true
|
is_ancestor = True
|
||||||
return JSON.stringify(ans)
|
return JSON.stringify(ans)
|
||||||
|
|
||||||
|
|
||||||
window.calibre_preview_integration = new PreviewIntegration()
|
window.calibre_preview_integration = PreviewIntegration()
|
||||||
window.onload = window.calibre_preview_integration.onload
|
window.onload = window.calibre_preview_integration.onload
|
Loading…
x
Reference in New Issue
Block a user