UNPKG

r2-navigator-js

Version:

Readium 2 'navigator' for NodeJS (TypeScript)

1,070 lines 105 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.recreateAllHighlightsDebounced = exports.setDrawMargin = exports.HIGHLIGHT_GROUP_PAGEBREAK = exports.HIGHLIGHT_GROUP_TTS = exports.ENABLE_PAGEBREAK_MARGIN_TEXT_EXPERIMENT = exports.ENABLE_CSS_HIGHLIGHTS = exports.ENABLE_FLOATING_UI = void 0; exports.getBoundingClientRectOfDocumentBody = getBoundingClientRectOfDocumentBody; exports.hideAllhighlights = hideAllhighlights; exports.destroyAllhighlights = destroyAllhighlights; exports.destroyHighlight = destroyHighlight; exports.destroyHighlightsGroup = destroyHighlightsGroup; exports.recreateAllHighlightsRaw = recreateAllHighlightsRaw; exports.recreateAllHighlights = recreateAllHighlights; exports.createHighlights = createHighlights; exports.createHighlight = createHighlight; const crypto = require("crypto"); const debounce = require("debounce"); const electron_1 = require("electron"); const events_1 = require("../../common/events"); const highlight_1 = require("../../common/highlight"); const readium_css_inject_1 = require("../../common/readium-css-inject"); const rect_utils_1 = require("../common/rect-utils"); const readium_css_1 = require("./readium-css"); const selection_1 = require("./selection"); const styles_1 = require("../../common/styles"); const readium_css_2 = require("./readium-css"); const core_1 = require("@flatten-js/core"); const { unify, subtract } = core_1.BooleanOperations; const dom_1 = require("@floating-ui/dom"); const IS_DEV = (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev"); window.DEBUG_RECTS = IS_DEV && rect_utils_1.VERBOSE; exports.ENABLE_FLOATING_UI = true; exports.ENABLE_CSS_HIGHLIGHTS = true && !!CSS.highlights; exports.ENABLE_PAGEBREAK_MARGIN_TEXT_EXPERIMENT = false; let lastMouseDownX = -1; let lastMouseDownY = -1; let bodyEventListenersSet = false; let _highlightsContainer; let _timeoutMouseMove; const TIMEOUT_MOUSE_MS = 200; const cleanupPolygon = (polygonAccumulator, off) => { const DEBUG_RECTS = window.DEBUG_RECTS; const minLength = Math.abs(off) + 1; let nSegments = 0; let nArcs = 0; let total = 0; if (DEBUG_RECTS) { console.log("--====}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); } for (const e of polygonAccumulator.edges) { const edge = e; if (edge.isSegment) { nSegments++; const segment = edge.shape; const l = segment.length; if (core_1.Utils.LE(l, minLength)) { total++; if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT small LENGTH: " + l + "(" + off + ")"); } } else { if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT ok LENGTH: " + l + "(" + off + ")"); } } } else if (edge.isArc) { nArcs++; if (DEBUG_RECTS) { console.log("--POLYGON ARC"); } } } if (DEBUG_RECTS) { console.log("--===="); console.log("--==== POLYGON SEGMENT small TOTAL 1: " + total); console.log("--==== POLYGON SEGMENT small SEGMENTS 1: " + nSegments); console.log("--==== POLYGON SEGMENT small ARCS 1: " + nArcs); } total = 0; nSegments = 0; nArcs = 0; if (DEBUG_RECTS) { console.log("--====}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); } for (const f of polygonAccumulator.faces) { const face = f; for (const e of face.edges) { const edge = e; if (edge.isSegment) { nSegments++; const segment = edge.shape; const l = segment.length; if (core_1.Utils.LE(l, minLength)) { total++; if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT small LENGTH: " + l + "(" + off + ")"); } } else { if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT ok LENGTH: " + l + "(" + off + ")"); } } } else if (edge.isArc) { nArcs++; if (DEBUG_RECTS) { console.log("--POLYGON ARC"); } } } } if (DEBUG_RECTS) { console.log("--===="); console.log("--==== POLYGON SEGMENT small TOTAL 2: " + total); console.log("--==== POLYGON SEGMENT small SEGMENTS 2: " + nSegments); console.log("--==== POLYGON SEGMENT small ARCS 2: " + nArcs); } total = 0; nSegments = 0; nArcs = 0; if (DEBUG_RECTS) { console.log("--====}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); } for (const f of polygonAccumulator.faces) { const face = f; let edge = face.first; while (edge) { if (edge.isSegment) { nSegments++; const segment = edge.shape; const l = segment.length; if (core_1.Utils.LE(l, minLength)) { total++; if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT small LENGTH: " + l + "(" + off + ")"); } } else { if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT ok LENGTH: " + l + "(" + off + ")"); } } } else if (edge.isArc) { nArcs++; if (DEBUG_RECTS) { console.log("--POLYGON ARC"); } } if (edge == face.last) { break; } edge = edge.next; } } if (DEBUG_RECTS) { console.log("--===="); console.log("--==== POLYGON SEGMENT small TOTAL 3: " + total); console.log("--==== POLYGON SEGMENT small SEGMENTS 3: " + nSegments); console.log("--==== POLYGON SEGMENT small ARCS 3: " + nArcs); console.log("--====}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}"); } const faces = Array.from(polygonAccumulator.faces); for (const f of faces) { const face = f; if (DEBUG_RECTS) { console.log("~~~~ POLY FACE"); } const edges = Array.from(face.edges); const edgeShapes = edges.map((edge) => edge.shape); let chainedEdgeShapes = []; while (edgeShapes.length) { if (DEBUG_RECTS) { console.log("~~~~ POLY EDGE SHAPE"); } if (!chainedEdgeShapes.length) { const last = edgeShapes.pop(); chainedEdgeShapes.push(last); continue; } const lastInChain = chainedEdgeShapes[chainedEdgeShapes.length - 1]; const lastInChainStartPoint = lastInChain.breakToFunctional ? lastInChain.start : lastInChain.start; const lastInChainEndPoint = lastInChain.breakToFunctional ? lastInChain.end : lastInChain.end; const shapesBefore = []; const shapesAfter = []; for (const edgeShape of edgeShapes) { const edgeShapeStartPoint = edgeShape.breakToFunctional ? edgeShape.start : edgeShape.start; const edgeShapeEndPoint = edgeShape.breakToFunctional ? edgeShape.end : edgeShape.end; if (core_1.Utils.EQ(lastInChainStartPoint.x, edgeShapeEndPoint.x) && core_1.Utils.EQ(lastInChainStartPoint.y, edgeShapeEndPoint.y)) { shapesBefore.push(edgeShape); } if (core_1.Utils.EQ(lastInChainEndPoint.x, edgeShapeStartPoint.x) && core_1.Utils.EQ(lastInChainEndPoint.y, edgeShapeStartPoint.y)) { shapesAfter.push(edgeShape); } } if (shapesBefore.length > 1 || shapesAfter.length > 1 || shapesAfter.length === 0) { if (DEBUG_RECTS) { console.log("~~~~ POLY SHAPES BEFORE/AFTER ABORT: " + shapesBefore.length + " ... " + shapesAfter.length); } chainedEdgeShapes = []; break; } const startPoint = shapesAfter[0].breakToFunctional ? shapesAfter[0].start : shapesAfter[0].start; const endPoint = shapesAfter[0].breakToFunctional ? shapesAfter[0].end : shapesAfter[0].end; if (DEBUG_RECTS) { console.log("*** SEGMENT/ARC --- START: (" + startPoint.x + ", " + startPoint.y + ") END: (" + endPoint.x + ", " + endPoint.y + ")"); } edgeShapes.splice(edgeShapes.indexOf(shapesAfter[0]), 1); chainedEdgeShapes.push(shapesAfter[0]); if (chainedEdgeShapes.length === edges.length) { const edgeShapeEndPoint = shapesAfter[0].breakToFunctional ? shapesAfter[0].end : shapesAfter[0].end; const firstInChainStartPoint = chainedEdgeShapes[0].breakToFunctional ? chainedEdgeShapes[0].start : chainedEdgeShapes[0].start; if (!core_1.Utils.EQ(firstInChainStartPoint.x, edgeShapeEndPoint.x) || !core_1.Utils.EQ(firstInChainStartPoint.y, edgeShapeEndPoint.y)) { if (DEBUG_RECTS) { console.log("~~~~ POLY SHAPES TAIL/HEAD ABORT"); } chainedEdgeShapes = []; break; } } } let previousSegment; let previousSmallSegment; const newEdgeShapes = []; let hasChanged = false; for (const edgeShape of chainedEdgeShapes) { if (!edgeShape.breakToFunctional) { const segment = edgeShape; const l = segment.length; if (DEBUG_RECTS) { console.log("--POLYGON SLOPES: " + (previousSegment === null || previousSegment === void 0 ? void 0 : previousSegment.slope) + " vs. " + segment.slope); } if (previousSegment && core_1.Utils.EQ(previousSegment.slope, segment.slope)) { if (DEBUG_RECTS) { console.log("--POLYGON SLOPE EQUAL ... merge :)"); } hasChanged = true; newEdgeShapes.pop(); const seg = new core_1.Segment(new core_1.Point(previousSegment.start.x, previousSegment.start.y), new core_1.Point(segment.end.x, segment.end.y)); newEdgeShapes.push(seg); previousSmallSegment = undefined; previousSegment = seg; if (chainedEdgeShapes.indexOf(edgeShape) === chainedEdgeShapes.length - 1 && !newEdgeShapes[0].breakToFunctional && core_1.Utils.EQ(newEdgeShapes[0].slope, seg.slope)) { if (DEBUG_RECTS) { console.log("--POLYGON SLOPE EQUAL (tail/head link) 1... merge :)"); } hasChanged = true; newEdgeShapes.splice(0, 1); const seg2 = new core_1.Segment(new core_1.Point(newEdgeShapes[0].start.x, newEdgeShapes[0].start.y), new core_1.Point(seg.end.x, seg.end.y)); newEdgeShapes.push(seg2); previousSmallSegment = undefined; previousSegment = seg2; } } else if (newEdgeShapes.length && chainedEdgeShapes.indexOf(edgeShape) === chainedEdgeShapes.length - 1 && !newEdgeShapes[0].breakToFunctional && core_1.Utils.EQ(newEdgeShapes[0].slope, segment.slope)) { if (DEBUG_RECTS) { console.log("--POLYGON SLOPE EQUAL (tail/head link) 2... merge :)"); } hasChanged = true; newEdgeShapes.splice(0, 1); const seg = new core_1.Segment(new core_1.Point(newEdgeShapes[0].start.x, newEdgeShapes[0].start.y), new core_1.Point(segment.end.x, segment.end.y)); newEdgeShapes.push(seg); previousSmallSegment = undefined; previousSegment = seg; } else if (core_1.Utils.LE(l, minLength)) { if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT small LENGTH: " + l + "(" + off + ")"); } if (previousSmallSegment) { if (DEBUG_RECTS) { console.log("-->>>> POLYGON SEGMENT small will merge :) ..."); } hasChanged = true; newEdgeShapes.pop(); const seg = new core_1.Segment(new core_1.Point(previousSmallSegment.start.x, previousSmallSegment.start.y), new core_1.Point(segment.end.x, segment.end.y)); newEdgeShapes.push(seg); previousSmallSegment = undefined; previousSegment = seg; } else if (newEdgeShapes.length && chainedEdgeShapes.indexOf(edgeShape) === chainedEdgeShapes.length - 1 && !newEdgeShapes[0].breakToFunctional && core_1.Utils.LE(newEdgeShapes[0].length, minLength)) { if (DEBUG_RECTS) { console.log("-->>>> POLYGON SEGMENT small (tail/head link) will merge :) ..."); } hasChanged = true; newEdgeShapes.splice(0, 1); const seg = new core_1.Segment(new core_1.Point(newEdgeShapes[0].start.x, newEdgeShapes[0].start.y), new core_1.Point(segment.end.x, segment.end.y)); ; newEdgeShapes.push(seg); previousSmallSegment = undefined; previousSegment = seg; } else { newEdgeShapes.push(segment); previousSmallSegment = segment; previousSegment = segment; } } else { if (DEBUG_RECTS) { console.log("--POLYGON SEGMENT ok LENGTH: " + l + "(" + off + ")"); } previousSmallSegment = undefined; newEdgeShapes.push(segment); previousSegment = segment; } } else { if (DEBUG_RECTS) { console.log("--POLYGON ARC"); } previousSmallSegment = undefined; previousSegment = undefined; newEdgeShapes.push(edgeShape); } } if (hasChanged) { if (DEBUG_RECTS) { console.log("-->>>> POLYGON face changed :)"); } polygonAccumulator.deleteFace(face); polygonAccumulator.addFace(newEdgeShapes); } } }; const addEdgePoints = (polygon, offset) => { const boxes = []; for (const f of polygon.faces) { const face = f; for (const edge of face.edges) { if (edge.isSegment) { const segment = edge.shape; const bStart = new core_1.Box(segment.start.x - offset, segment.start.y - offset, segment.start.x + offset * 2, segment.start.y + offset * 2); boxes.push(bStart); const bEnd = new core_1.Box(segment.end.x - offset, segment.end.y - offset, segment.end.x + offset * 2, segment.end.y + offset * 2); boxes.push(bEnd); } else { const arc = edge.shape; const bStart = new core_1.Box(arc.start.x - offset, arc.start.y - offset, arc.start.x + offset * 2, arc.start.y + offset * 2); boxes.push(bStart); const bEnd = new core_1.Box(arc.end.x - offset, arc.end.y - offset, arc.end.x + offset * 2, arc.end.y + offset * 2); boxes.push(bEnd); } } } for (const box of boxes) { polygon.addFace(box); } }; const BASE_ORIENTATION = core_1.ORIENTATION.CCW; const USE_SEGMENT_JOINS_NOT_ARCS = false; function arcSE(center, start, end, counterClockwise) { const DEBUG_RECTS = window.DEBUG_RECTS; const startAngle = Number((new core_1.Vector(center, start).slope).toPrecision(12)); let endAngle = Number((new core_1.Vector(center, end).slope).toPrecision(12)); if (core_1.Utils.EQ(startAngle, endAngle)) { if (DEBUG_RECTS) { console.log("--POLYGON ARC ORIENTATION CCW/CW inverse"); } endAngle += 2 * Math.PI; counterClockwise = !counterClockwise; } const r = Number((new core_1.Vector(center, start).length).toPrecision(12)); ; return new core_1.Arc(center, r, startAngle, endAngle, counterClockwise); } function offset_(polygon, off, useSegmentJoinsNotArcs) { const DEBUG_RECTS = window.DEBUG_RECTS; const postponeFinalUnify = off > 0; let polygonAccumulator = postponeFinalUnify ? undefined : polygon.clone(); for (const f of polygon.faces) { const face = f; for (const edge of face.edges) { if (edge.isSegment) { const polygonEdge = new core_1.Polygon(); const segment = edge.shape; const v_seg = new core_1.Vector(segment.end.x - segment.start.x, segment.end.y - segment.start.y); const v_seg_unit = v_seg.normalize(); const absOffset = Math.abs(off); const v_left = v_seg_unit.rotate90CCW().multiply(absOffset); const v_right = v_seg_unit.rotate90CW().multiply(absOffset); const seg_left = segment.translate(v_left).reverse(); const seg_right = segment.translate(v_right); const seg_left_ = new core_1.Segment(new core_1.Point(Number((seg_left.start.x).toPrecision(12)), Number((seg_left.start.y).toPrecision(12))), new core_1.Point(Number((seg_left.end.x).toPrecision(12)), Number((seg_left.end.y).toPrecision(12)))); const seg_right_ = new core_1.Segment(new core_1.Point(Number((seg_right.start.x).toPrecision(12)), Number((seg_right.start.y).toPrecision(12))), new core_1.Point(Number((seg_right.end.x).toPrecision(12)), Number((seg_right.end.y).toPrecision(12)))); const orientation = BASE_ORIENTATION === core_1.ORIENTATION.CCW ? core_1.CCW : core_1.CW; const cap1 = arcSE(segment.start, seg_left_.end, seg_right_.start, orientation); const cap2 = arcSE(segment.end, seg_right_.end, seg_left_.start, orientation); const cap1_ = useSegmentJoinsNotArcs ? new core_1.Segment(seg_left_.end, seg_right_.start) : cap1; const cap2_ = useSegmentJoinsNotArcs ? new core_1.Segment(seg_right_.end, seg_left_.start) : cap2; const face = polygonEdge.addFace([ seg_left_, cap1_, seg_right_, cap2_, ]); if (face.orientation() !== BASE_ORIENTATION) { if (DEBUG_RECTS) { console.log("--POLYGON FACE ORIENTATION CCW/CW reverse() 1"); } face.reverse(); } if (!(polygonAccumulator || polygonEdge).faces.size) { if (DEBUG_RECTS) { console.log("--################# POLYGON BEFORE unify/substract HAS NO FACES!! " + (polygonAccumulator || polygonEdge).faces.size); } } if (off > 0) { polygonAccumulator = polygonAccumulator ? unify(polygonAccumulator, polygonEdge) : polygonEdge; } else { polygonAccumulator = polygonAccumulator ? subtract(polygonAccumulator, polygonEdge) : polygonEdge; } if (!(polygonAccumulator || polygonEdge).faces.size) { if (DEBUG_RECTS) { console.log("--################# POLYGON AFTER unify/substract HAS NO FACES!! " + (polygonAccumulator || polygonEdge).faces.size); } if (!useSegmentJoinsNotArcs) { if (DEBUG_RECTS) { console.log("--##### POLYGON AFTER unify/substract try again without arc, only segment joiners ..."); } return offset_(polygon, off, true); } } else { if (DEBUG_RECTS) { console.log("--################# POLYGON AFTER unify/substract FACES: " + (polygonAccumulator || polygonEdge).edges.size + " /// " + (polygonAccumulator || polygonEdge).faces.size); } } for (const f of polygonAccumulator.faces) { const face = f; if (face.edges.length < 4) { if (DEBUG_RECTS) { console.log("-------- POLYGON FACE EDGES not at least 4??!"); } if (!useSegmentJoinsNotArcs) { if (DEBUG_RECTS) { console.log("--##### POLYGON AFTER unify/substract try again without arc, only segment joiners ..."); } return offset_(polygon, off, true); } } if (face.orientation() !== BASE_ORIENTATION) { if (DEBUG_RECTS) { console.log("-------- POLYGON FACE ORIENTATION"); } } } } else { console.log("!!!!!!!! POLYGON ARC??!"); return polygon; } } } Array.from((polygonAccumulator ? polygonAccumulator : polygon).faces).forEach((face) => { if (face.orientation() !== BASE_ORIENTATION) { if (DEBUG_RECTS) { console.log("--HIGH WEBVIEW-- removing polygon orientation face / inner hole (offset poly 1))"); } if (polygonAccumulator) { polygonAccumulator.deleteFace(face); } } }); if (polygonAccumulator && postponeFinalUnify) { polygonAccumulator = unify(polygonAccumulator, polygon); } Array.from((polygonAccumulator ? polygonAccumulator : polygon).faces).forEach((face) => { if (face.orientation() !== BASE_ORIENTATION) { if (DEBUG_RECTS) { console.log("--HIGH WEBVIEW-- removing polygon orientation face / inner hole (offset poly 2))"); } if (polygonAccumulator) { polygonAccumulator.deleteFace(face); } } }); if (polygonAccumulator) { if (!polygonAccumulator.faces.size) { if (DEBUG_RECTS) { console.log("--################# POLYGON INTERMEDIARY HAS NO FACES!! " + polygonAccumulator.faces.size); } } cleanupPolygon(polygonAccumulator, off); } let resPoly = polygonAccumulator ? polygonAccumulator : polygon; if (!resPoly.faces.size) { if (DEBUG_RECTS) { console.log("--################# POLYGON INTERMEDIARY HAS NO FACES!! " + resPoly.faces.size); } if (polygonAccumulator) { if (DEBUG_RECTS) { console.log("--################# FALLBACK TO SINGLE FACE POLY (BEFORE SUBSTRACT/UNIFY): " + polygon.faces.size); } resPoly = polygon; } } return resPoly; } function offset(originaPolygon, off, useSegmentJoinsNotArcs = USE_SEGMENT_JOINS_NOT_ARCS) { const DEBUG_RECTS = window.DEBUG_RECTS; off = Number((off).toPrecision(12)); if (core_1.Utils.EQ_0(off)) { return originaPolygon; } const singleFacePolygons = []; for (const f of originaPolygon.faces) { const face = f; const poly = new core_1.Polygon(); poly.addFace(face.edges.map((edge) => edge.shape)); singleFacePolygons.push(poly); } const singlePolygon = new core_1.Polygon(); for (const polygon of singleFacePolygons) { const resPoly = offset_(polygon, off, useSegmentJoinsNotArcs); for (const f of resPoly.faces) { const face = f; singlePolygon.addFace(face.edges.map(((edge) => edge.shape))); } } if (!singlePolygon.faces.size) { if (DEBUG_RECTS) { console.log("--##### POLYGON OFFSET HAS NO FACES!! " + singlePolygon.faces.size); } if (!useSegmentJoinsNotArcs) { if (DEBUG_RECTS) { console.log("--##### POLYGON OFFSET try again without arc, only segment joiners ..."); } return offset(originaPolygon, off, true); } } return singlePolygon; } const DEFAULT_BACKGROUND_COLOR = { blue: 0, green: 0, red: 255, }; const _highlights = []; exports.HIGHLIGHT_GROUP_TTS = "tts"; exports.HIGHLIGHT_GROUP_PAGEBREAK = "pagebreak"; let _drawMargin = false; const drawMargin = (h) => { if (h.group === exports.HIGHLIGHT_GROUP_TTS) { return false; } if (h.drawType === highlight_1.HighlightDrawTypeOpacityMask || h.drawType === highlight_1.HighlightDrawTypeOpacityMaskRuler || h.drawType === highlight_1.HighlightDrawTypeMarginBookmark) { return true; } if (h.group === exports.HIGHLIGHT_GROUP_PAGEBREAK) { return true; } if (Array.isArray(_drawMargin)) { if (h.group) { return _drawMargin.includes(h.group); } return false; } return _drawMargin; }; const setDrawMargin = (win, drawMargin) => { _drawMargin = drawMargin; if (IS_DEV) { console.log("--HIGH WEBVIEW-- _drawMargin: " + JSON.stringify(_drawMargin, null, 4)); } recreateAllHighlightsRaw(win); }; exports.setDrawMargin = setDrawMargin; const SVG_XML_NAMESPACE = "http://www.w3.org/2000/svg"; function getBoundingClientRectOfDocumentBody(win) { return win.document.body.getBoundingClientRect(); } function processMouseEvent(win, ev) { var _a; if (_timeoutMouseMove) { clearTimeout(_timeoutMouseMove); _timeoutMouseMove = undefined; } if (!_highlightsContainer) { return; } const isMouseMove = ev.type === "mousemove"; if (isMouseMove) { if (ev.buttons > 0) { return; } if (!_highlights.length) { return; } } const documant = win.document; const scrollElement = (0, readium_css_1.getScrollingElement)(documant); const x = ev.clientX; const y = ev.clientY; const paginated = (0, readium_css_inject_1.isPaginated)(documant); const bodyRect = getBoundingClientRectOfDocumentBody(win); const xOffset = paginated ? (-scrollElement.scrollLeft) : bodyRect.left; const yOffset = paginated ? (-scrollElement.scrollTop) : bodyRect.top; const scale = 1 / ((win.READIUM2 && win.READIUM2.isFixedLayout) ? win.READIUM2.fxlViewportScale : 1); let hit = false; let foundHighlight; let foundElement; for (let i = _highlights.length - 1; i >= 0; i--) { const highlight = _highlights[i]; const doDrawMargin = drawMargin(highlight); let highlightParent = documant.getElementById(`${highlight.id}`); if (!highlightParent) { highlightParent = _highlightsContainer.querySelector(`#${highlight.id}`); } if (!highlightParent) { continue; } let highlightFragment = highlightParent.firstElementChild; while (highlightFragment) { if (highlightFragment.namespaceURI === SVG_XML_NAMESPACE) { const svg = highlightFragment; hit = (!doDrawMargin || svg.classList.contains(styles_1.CLASS_HIGHLIGHT_CONTOUR_MARGIN)) && svg.polygon.contains(new core_1.Point((x - xOffset) * scale, (y - yOffset) * scale)); if (hit) { break; } } highlightFragment = highlightFragment.nextElementSibling; } if (hit) { foundHighlight = highlight; foundElement = highlightParent; break; } } let highlightContainer = _highlightsContainer.firstElementChild; while (highlightContainer) { if (!foundElement || foundElement !== highlightContainer) { highlightContainer.classList.remove(styles_1.CLASS_HIGHLIGHT_HOVER); } highlightContainer = highlightContainer.nextElementSibling; } if (!hit) { const _highlightsFloatingUI = win.document.getElementById(styles_1.ID_HIGHLIGHTS_FLOATING); if (_highlightsFloatingUI && _highlightsFloatingUI.style.display !== "none") { _highlightsFloatingUI.style.display = "none"; } documant.documentElement.classList.remove(styles_1.CLASS_HIGHLIGHT_CURSOR2); return; } if (foundElement && foundHighlight && foundHighlight.pointerInteraction) { if (isMouseMove) { foundElement.classList.add(styles_1.CLASS_HIGHLIGHT_HOVER); if (foundHighlight.group !== exports.HIGHLIGHT_GROUP_PAGEBREAK) { documant.documentElement.classList.add(styles_1.CLASS_HIGHLIGHT_CURSOR2); } const text = ((_a = foundHighlight.textPopup) === null || _a === void 0 ? void 0 : _a.text) ? foundHighlight.textPopup.text : undefined; if (text && _highlightsContainer) { _timeoutMouseMove = win.setTimeout(() => { var _a, _b; _timeoutMouseMove = undefined; if (!_highlightsContainer) { return; } const _highlightsFloatingUI = win.document.getElementById(styles_1.ID_HIGHLIGHTS_FLOATING); if (!_highlightsFloatingUI) { return; } const _highlightsFloatingUI_ARROW = _highlightsFloatingUI.firstElementChild; if (!_highlightsFloatingUI_ARROW) { return; } const _highlightsFloatingUI_TEXT = _highlightsFloatingUI_ARROW.nextElementSibling; if (!_highlightsFloatingUI_TEXT) { return; } const doDrawArrow = foundHighlight.drawType !== highlight_1.HighlightDrawTypeMarginBookmark; _highlightsFloatingUI_ARROW.style.display = doDrawArrow ? "block" : "none"; const dir = ((_a = foundHighlight.textPopup) === null || _a === void 0 ? void 0 : _a.dir) ? foundHighlight.textPopup.dir : "ltr"; const lang = ((_b = foundHighlight.textPopup) === null || _b === void 0 ? void 0 : _b.lang) ? foundHighlight.textPopup.lang : "en"; const zoom = foundElement.__inverseZoom || 1; if (dir) { _highlightsFloatingUI_TEXT.setAttribute("dir", dir); } else { _highlightsFloatingUI_TEXT.removeAttribute("dir"); } if (lang) { _highlightsFloatingUI_TEXT.setAttribute("lang", lang); _highlightsFloatingUI_TEXT.setAttributeNS("http://www.w3.org/XML/1998/", "lang", lang); } else { _highlightsFloatingUI_TEXT.removeAttribute("lang"); _highlightsFloatingUI_TEXT.removeAttributeNS("http://www.w3.org/XML/1998/", "lang"); } _highlightsFloatingUI_TEXT.style.writingMode = "horizontal-tb"; _highlightsFloatingUI_TEXT.textContent = text; if (!exports.ENABLE_FLOATING_UI) { const xx = (x - xOffset) * scale; const yy = (y - yOffset) * scale; Object.assign(_highlightsFloatingUI.style, { display: "block", left: `${xx * zoom}px`, top: `${yy * zoom}px`, }); } else { Object.assign(_highlightsFloatingUI.style, { display: "block", left: "0px", top: "-999999px", opacity: "0", }); const doDrawMargin = drawMargin(foundHighlight); let anchor = null; const all = foundElement.querySelectorAll("svg.R2_CLASS_HIGHLIGHT_CONTOUR > path"); if ((all === null || all === void 0 ? void 0 : all.length) > 0) { anchor = all[(all === null || all === void 0 ? void 0 : all.length) - 1]; } if (!anchor && doDrawMargin) { anchor = foundElement.querySelector("svg.R2_CLASS_HIGHLIGHT_CONTOUR_MARGIN > path"); } if (anchor) { const paginated = (0, readium_css_inject_1.isPaginated)(documant); const virtualElement = { getBoundingClientRect() { return { width: 0, height: 0, x: x, y: y, top: y, left: x, right: x, bottom: y, }; }, }; let _highlightsFloatingUI_; if (paginated) { const css = win.getComputedStyle(_highlightsFloatingUI); let width = parseFloat(css.width) || 0; let height = parseFloat(css.height) || 0; const offsetWidth = _highlightsFloatingUI.offsetWidth; const offsetHeight = _highlightsFloatingUI.offsetHeight; const shouldFallback = Math.round(width) !== offsetWidth || Math.round(height) !== offsetHeight; if (shouldFallback) { width = offsetWidth; height = offsetHeight; } _highlightsFloatingUI_ = documant.createElementNS(SVG_XML_NAMESPACE, "svg"); _highlightsFloatingUI_.setAttribute("id", styles_1.ID_HIGHLIGHTS_FLOATING + "_"); Object.assign(_highlightsFloatingUI_.style, { width: (width / zoom) + "px", height: (height / zoom) + "px", }); _highlightsContainer.append(_highlightsFloatingUI_); } const arrowLen = doDrawArrow ? _highlightsFloatingUI_ARROW.offsetWidth : 0; const floatingOffset = doDrawArrow ? (Math.sqrt(2 * arrowLen ** 2) / 2) : 0; (0, dom_1.computePosition)(anchor || virtualElement, paginated ? _highlightsFloatingUI_ : _highlightsFloatingUI, { strategy: paginated ? "fixed" : "absolute", placement: "bottom", middleware: [ (0, dom_1.offset)(floatingOffset), (0, dom_1.flip)(), (0, dom_1.shift)({ padding: 4 }), doDrawArrow ? (0, dom_1.arrow)({ padding: 8, element: _highlightsFloatingUI_ARROW }) : undefined, ].filter((v) => !!v), }) .then(({ x: fuix, y: fuiy, middlewareData, placement }) => { if (doDrawArrow && middlewareData.arrow && _highlightsFloatingUI_ARROW) { const side = placement.split("-")[0]; const staticSide = { top: "bottom", right: "left", bottom: "top", left: "right", }[side]; console.log("middlewareData.arrow", middlewareData.arrow.x, middlewareData.arrow.y); const { x: xarrow, y: yarrow } = middlewareData.arrow; if (xarrow != null || yarrow != null) { Object.assign(_highlightsFloatingUI_ARROW.style, { left: xarrow != null ? `${xarrow}px` : "", top: yarrow != null ? `${yarrow}px` : "", right: "", bottom: "", [staticSide]: `${-arrowLen / 2}px`, transform: staticSide === "top" ? "rotate(45deg)" : "rotate(225deg)", }); } } const xx = paginated ? (fuix - xOffset) * zoom : fuix; const yy = paginated ? (fuiy - yOffset) * zoom : fuiy; if (_highlightsFloatingUI) { Object.assign(_highlightsFloatingUI.style, { display: "block", left: `${xx}px`, top: `${yy}px`, opacity: "1", }); } if (_highlightsFloatingUI_) { _highlightsFloatingUI_.remove(); } }); } else { const xx = (x - xOffset) * scale; const yy = (y - yOffset) * scale; Object.assign(_highlightsFloatingUI.style, { display: "block", left: `${xx * zoom}px`, top: `${yy * zoom}px`, opacity: "1", }); } } }, TIMEOUT_MOUSE_MS); } } else if ((ev.type === "mouseup" || ev.type === "click") && foundHighlight.group !== exports.HIGHLIGHT_GROUP_PAGEBREAK) { documant.documentElement.classList.remove(styles_1.CLASS_HIGHLIGHT_CURSOR2); const _highlightsFloatingUI = win.document.getElementById(styles_1.ID_HIGHLIGHTS_FLOATING); if (_highlightsFloatingUI && _highlightsFloatingUI.style.display !== "none") { _highlightsFloatingUI.style.display = "none"; } ev.preventDefault(); ev.stopPropagation(); const payload = { highlight: foundHighlight, event: { type: ev.type, button: ev.button, alt: ev.altKey, shift: ev.shiftKey, ctrl: ev.ctrlKey, meta: ev.metaKey, x: ev.clientX, y: ev.clientY, }, }; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_HIGHLIGHT_CLICK, payload); } } else { const _highlightsFloatingUI = win.document.getElementById(styles_1.ID_HIGHLIGHTS_FLOATING); if (_highlightsFloatingUI && _highlightsFloatingUI.style.display !== "none") { _highlightsFloatingUI.style.display = "none"; } } } const computeInverseZoom = (bodyComputedStyle, rootComputedStyle) => { let zoomStr = rootComputedStyle.zoom; if (!zoomStr || zoomStr === "1") { zoomStr = bodyComputedStyle.zoom; } if (zoomStr) { const zoomFactor = parseFloat(zoomStr); if (zoomFactor !== 0) { const inverseZoom = 1 / zoomFactor; return inverseZoom; } } return 1; }; function ensureHighlightsContainer(win, _bodyComputedStyle, _rootComputedStyle) { const documant = win.document; if (!_highlightsContainer) { if (!bodyEventListenersSet) { bodyEventListenersSet = true; documant.body.addEventListener("mousedown", (ev) => { lastMouseDownX = ev.clientX; lastMouseDownY = ev.clientY; }, false); documant.body.addEventListener("mouseup", (ev) => { if ((Math.abs(lastMouseDownX - ev.clientX) < 3) && (Math.abs(lastMouseDownY - ev.clientY) < 3)) { processMouseEvent(win, ev); } }, false); documant.body.addEventListener("mousemove", (ev) => { processMouseEvent(win, ev); }, false); } const _highlightsContainer_ = documant.createElement("div"); _highlightsContainer_.setAttribute("aria-hidden", "true"); _highlightsContainer_.setAttribute("id", styles_1.ID_HIGHLIGHTS_CONTAINER); _highlightsContainer_.setAttribute("class", styles_1.CLASS_HIGHLIGHT_COMMON); _highlightsContainer_.setAttribute("style", `width: ${win.READIUM2.isFixedLayout ? "-webkit-fill-available" : "auto"} !important; ` + `height: ${win.READIUM2.isFixedLayout ? "-webkit-fill-available" : "auto"} !important; `); const _highlightsFloatingUI = documant.createElement("div"); _highlightsFloatingUI.setAttribute("id", styles_1.ID_HIGHLIGHTS_FLOATING); const _highlightsFloatingUI_ARROW = documant.createElement("div"); _highlightsFloatingUI.append(_highlightsFloatingUI_ARROW); const _highlightsFloatingUI_TEXT = documant.createElement("div"); _highlightsFloatingUI.append(_highlightsFloatingUI_TEXT); _highlightsContainer_.append(_highlightsFloatingUI); documant.body.append(_highlightsContainer_); _highlightsContainer = _highlightsContainer_; } return _highlightsContainer; } function hideAllhighlights(_documant) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- hideAllhighlights: " + _highlights.length); } if (exports.ENABLE_CSS_HIGHLIGHTS) { CSS.highlights.clear(); } if (_highlightsContainer) { _highlightsContainer.remove(); _highlightsContainer = undefined; } } function destroyAllhighlights(documant) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- destroyAllhighlights: " + _highlights.length); } hideAllhighlights(documant); _highlights.splice(0, _highlights.length); } function destroyHighlight(documant, id) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- destroyHighlight: " + id + " ... " + _highlights.length); } let i = -1; const highlight = _highlights.find((h, j) => { i = j; return h.id === id; }); if (highlight && i >= 0 && i < _highlights.length) { _highlights.splice(i, 1); } const highlightContainer = documant.getElementById(id); if (highlightContainer) { highlightContainer.remove(); } if (exports.ENABLE_CSS_HIGHLIGHTS && highlight && highlight.rangeCssHighlight) { const [_strRGB, cssHighlightID] = computeCssHighlightRGBID(highlight); const cssHighlight = CSS.highlights.get(cssHighlightID); if (cssHighlight && cssHighlight.has(highlight.rangeCssHighlight)) { cssHighlight.delete(highlight.rangeCssHighlight); } } } function destroyHighlightsGroup(documant, group) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- destroyHighlightsGroup: " + group + " ... " + _highlights.length); } while (true) { let i = -1; const highlight = _highlights.find((h, j) => { i = j; return h.group === group; }); if (highlight) { if (i >= 0 && i < _highlights.length) { _highlights.splice(i, 1); } const highlightContainer = documant.getElementById(highlight.id); if (highlightContainer) { highlightContainer.remove(); } if (exports.ENABLE_CSS_HIGHLIGHTS && highlight.rangeCssHighlight) { const [_strRGB, cssHighlightID] = computeCssHighlightRGBID(highlight); const cssHighlight = CSS.highlights.get(cssHighlightID); if (cssHighlight && cssHighlight.has(highlight.rangeCssHighlight)) { cssHighlight.delete(highlight.rangeCssHighlight); } } } else { break; } } } function recreateAllHighlightsRaw(win, highlights) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- recreateAllHighlightsRaw: " + _highlights.length + " ==> " + (highlights === null || highlights === void 0 ? void 0 : highlights.length)); } const documant = win.document; if (highlights === null || highlights === void 0 ? void 0 : highlights.length) { if (_highlights.length) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- recreateAllHighlightsRaw DESTROY OLD BEFORE RESTORE BACKUP: " + _highlights.length + " ==> " + highlights.length); } destroyAllhighlights(documant); } if (IS_DEV) { console.log("--HIGH WEBVIEW-- recreateAllHighlightsRaw RESTORE BACKUP: " + _highlights.length + " ==> " + highlights.length); } _highlights.push(...highlights); } if (!_highlights.length) { return; } if (!documant.body) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- NO BODY?! (retrying...): " + _highlights.length); } (0, exports.recreateAllHighlightsDebounced)(win); return; } hideAllhighlights(documant); const bodyRect = getBoundingClientRectOfDocumentBody(win); const rootComputedStyle = win.getComputedStyle(documant.documentElement); const bodyComputedStyle = win.getComputedStyle(documant.body); const docFrag = documant.createDocumentFragment(); for (const highlight of _highlights) { const r = adjustRangeInfo(win, highlight.range, highlight.selectionInfo); if (r) { highlight.range = r; } else if (r === null) { } else if (typeof r === "undefined") { continue; } let div; try { div = createHighlightDom(win, highlight, bodyRect, bodyComputedStyle, rootComputedStyle); } catch (err) { console.log("createHighlightDom ERROR:"); console.log(err); } if (div) { docFrag.append(div); } } if (IS_DEV) { console.log("--HIGH WEBVIEW-- createHighlightDom DONE: " + _highlights.length); } const highlightsContainer = ensureHighlightsContainer(win, bodyComputedStyle, rootComputedStyle); highlightsContainer.append(docFrag); } exports.recreateAllHighlightsDebounced = debounce((win) => { if (IS_DEV) { console.log("--HIGH WEBVIEW-- recreateAllHighlightsDebounced: " + _highlights.length); } recreateAllHighlightsRaw(win); }, 500); function recreateAllHighlights(win) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- recreateAllHighlights: " + _highlights.length); } hideAllhighlights(win.document); (0, exports.recreateAllHighlightsDebounced)(win); } function createHighlights(win, highDefs, pointerInteraction) { if (IS_DEV) { console.log("--HIGH WEBVIEW-- createHighlights: " + highDefs.length + " ... " + _highlights.length); } const documant = win.document; const highlights = []; const bodyRect = getBoundingClientRectOfDocumentBody(win); const rootComputedStyle = win.getComputedStyle(documant.documentElement); const bodyComputedStyle = win.getComputedStyle(documant.body); const docFrag = documant.createDocumentFragment(); for (const highDef of highDefs) { if (!highDef.selectionInfo && !highDef.range) { highlights.push(null); continue; } const hh = createHighlight(win, highDef.select