linefinder v1.0.12
Table of Contents generated with DocToc
µ.LINE
: Distribute Lines across HTML iFrames
How it Works and What it Does
µ.LINE.Finder
Configuration
linemarker_tagname
('mu-linemarker'
): Which HTML tag name to use when drawing a rectangle for each line of text. These are normally transparent (hence invisible) but can be made visible for debugging.linecover_tagname
('mu-linecover'
): Which HTML tag name to use for the rectangles that are used to cover the first line of text in aniframe
that is only partially visible. This will normally be without outline and opaque white to 'erase' the line in question, but can be made visible for debugging.debug_class_name
('debug'
): Which CSS class name to use on thebody
element for debugging.line_step_factor
(1 / 2
): The relative minimum height to recognize a line step. When iterating over single characters in the text, the distanced = cy - ay
from the bottom of the charactercy
and the average bottom of the line rectangleay
is compared with the average character heightch
; if distanced
is found to exceed the productch * line_step_factor
, then it is assumed the current character is the first one of the next line.inject_stylesheet_after
(null
),inject_stylesheet_before
(null
): A prebuilt stylesheet can be injected into the current document. Since relative order of stylesheets is essential, two convenient methods are provided that accept either a CSS selector or a DOM element to determine the insertion point. For example, if you have a linked CSS-Reset stylesheet, you typically want to have that to be the first stylesheet with all the defaults; it would then be appropriate to callfinder.inject_stylesheet_after 'link[href$="reset.css"]'
with the default styles for themu-linemarker
andmu-linecover
elements. Note that the selector can match one or more elements; only the first match will be considered.
The CSS rules defined in the injected stylesheet for iFrames, linemarkers and linecovers are:
/* for normal look: */
${linemarker_tagname} { ... }
${linecover_tagname} { ... }
/* for debugging: */
.${debug_class_name} iframe { ... }
.${debug_class_name} ${linemarker_tagname} { ... }
.${debug_class_name} ${linecover_tagname} { ... }
The debug button has these style selectors:
button#${debug_button_id} { ... }
@media print { button#${debug_button_id} { ... } }
Structure of a Document
- two HTML files:
- the 'galley' document which contains a
<mu-galley>
element (user-defined tag with CSSdisplay: block;
); the nodes directly under this (and their child nodes) will be traversed by aµ.LINE.Finder
instance in (hopefully) the intended reading order. - the 'main' document which contains any number of
<iframe>
elements thesrc
attribute of which should point to the same galley document.
- the 'galley' document which contains a
Sample code:
µ.DOM.ready ->
### (1) ###
cfg =
paragraph_selector: 'mu-galley > p'
iframe_selector: 'iframe'
insert_stylesheet_after: 'link[href$="reset.css"]'
insert_debug_button: true
#.........................................................................................................
### (2) ###
if ( not µ.DOM.page_is_inside_iframe() ) and ( µ.DOM.select_first 'mu-galley', null )?
distributor = new µ.LINE.Distributor cfg
await distributor.mark_lines()
return null
#.........................................................................................................
### (3) ###
return null unless µ.LINE.Distributor.is_main_document()
#.........................................................................................................
### (4) ###
distributor = new µ.LINE.Distributor cfg
await distributor.distribute_lines()
return null
(1) Configuration values, valid for both the galley and the main document.
(2) Here we test whether the current document is the standalone galley document (i.e. not displayed inside an iframe). If so, instantiate a
LINE.Distributor
instance and mark lines; the linemarkers in the galley document are only for debugging and demonstration and so the call todistributor.mark_lines()
may be skipped.(3) Do not continue unless we are in main document.
(4) If in main document, instantiate a distributor and await the finishing of
distributor.distribute_lines()
. This call is asynchronous so can be watched live. The default style has transparent linemarkers and opaque white linecovers; when debugging is activated, their are displayed with translucent yellow and red backgrounds.
DB Backend for Persistence
- Scroll offsets and coordinates of combteeth preserved in SQLite DB
µ.LINE.Distributor
Distributing Lines with iFrames: Pros and Cons
Pros
- works
Cons
- each iFrame establishes a new context, each iFrame loads entire galley document over again, so browser might need high amount of RAM
- cannot highlight across iFrame boundary (in fact, can highlight text in each iFrame simultaneously)
- even the very capable SingleFile extension does not preserve iFrame scroll offsets
To Do
- – in
Distributor::constructor
, we create aFinder
instance only to insert the Linefinder stylesheet; doing it this way means the document in the iframe gets multiple copies - – find better name for 'main' document (which contains the iframes with views into the 'galley' document)
- – find better a name for the iframe elements (like 'window (to galley)'? 'view (of galley)'?)
- – to simplify talking about document construction and to abstract from the implementation detail
that we are using
<iframe>
s, consider to implement a custom element class. This would also give a chance to gloss over the somehat intricateiframe.contentDocument.documentElement
&c shenanigans- as described in
- https://stackoverflow.com/questions/53480991/creating-custom-node-to-extend-htmliframeelement
- https://learn.liferay.com/w/dxp/building-applications/client-extensions/front-end-client-extensions/understanding-custom-element-and-iframe-client-extensions
- https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements
- as described in