UNPKG

r2-navigator-js

Version:

Readium 2 'navigator' for NodeJS (TypeScript)

1,142 lines 175 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tslib_1 = require("tslib"); const debounce = require("debounce"); const debug_ = require("debug"); const electron_1 = require("electron"); const tabbable_1 = require("tabbable"); const styles_1 = require("../../common/styles"); const UrlUtils_1 = require("r2-utils-js/dist/es7-es2016/src/_utils/http/UrlUtils"); const sessions_1 = require("../../common/sessions"); const events_1 = require("../../common/events"); const highlight_1 = require("../../common/highlight"); const readium_css_inject_1 = require("../../common/readium-css-inject"); const selection_1 = require("../../common/selection"); const styles_2 = require("../../common/styles"); const animateProperty_1 = require("../common/animateProperty"); const cssselector3_1 = require("../common/cssselector3"); const dom_text_utils_1 = require("../common/dom-text-utils"); const easings_1 = require("../common/easings"); const popup_dialog_1 = require("../common/popup-dialog"); const querystring_1 = require("../common/querystring"); const rect_utils_1 = require("../common/rect-utils"); const url_params_1 = require("../common/url-params"); const audiobook_1 = require("./audiobook"); const epubReadingSystem_1 = require("./epubReadingSystem"); const highlight_2 = require("./highlight"); const popoutImages_1 = require("./popoutImages"); const popupFootNotes_1 = require("./popupFootNotes"); const readaloud_1 = require("./readaloud"); const readium_css_1 = require("./readium-css"); const selection_2 = require("./selection"); const IS_DEV = (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev"); const DEBUG_TRACE = true && IS_DEV; if (IS_DEV) { const cr = require("../common/console-redirect"); cr.consoleRedirect("r2:navigator#electron/renderer/webview/preload", process.stdout, process.stderr, true); } const debug = debug_("r2:navigator#electron/renderer/webview/preload"); let __locEventID = 0; const INJECTED_LINK_TXT = "__"; const win = global.window; win.READIUM2 = { lastClickedTextChar: undefined, DEBUG_VISUALS: false, fxlViewportHeight: 0, fxlViewportScale: 1, fxlViewportWidth: 0, fxlZoomPercent: 0, hashElement: null, isAudio: false, ignorekeyDownUpEvents: false, accessibilitySupportEnabled: false, isClipboardIntercept: false, isFixedLayout: false, locationHashOverride: undefined, locationHashOverrideInfo: { locEventID: undefined, audioPlaybackInfo: undefined, docInfo: undefined, epubPage: undefined, epubPageID: undefined, headings: undefined, href: "", locations: { cfi: undefined, cssSelector: undefined, position: undefined, progression: undefined, xpath: undefined, }, paginationInfo: undefined, secondWebViewHref: undefined, selectionInfo: undefined, selectionIsNew: undefined, title: undefined, userInteract: false, }, ttsHighlightStyle: highlight_1.HighlightDrawTypeBackground, ttsHighlightColor: undefined, ttsHighlightColor_WORD: undefined, ttsHighlightStyle_WORD: undefined, ttsClickEnabled: false, ttsOverlayEnabled: false, ttsPlaybackRate: 1, ttsAndMediaOverlaysManualPlayNext: false, ttsSkippabilityEnabled: false, ttsSentenceDetectionEnabled: true, ttsVoices: null, urlQueryParams: win.location.search ? (0, querystring_1.getURLQueryParams)(win.location.search) : undefined, webViewSlot: styles_2.WebViewSlotEnum.center, }; win.alert = (...args) => { console.log.apply(win, args); }; win.confirm = (...args) => { console.log.apply(win, args); return false; }; win.prompt = (...args) => { console.log.apply(win, args); return ""; }; const CSS_PIXEL_TOLERANCE = 5; (0, selection_2.setSelectionChangeAction)(win, () => { if (DEBUG_TRACE) debug("setSelectionChangeAction: notifyReadingLocationDebounced()..."); notifyReadingLocationDebounced(true, false, true); }); const TOUCH_SWIPE_DELTA_MIN = 80; const TOUCH_SWIPE_LONG_PRESS_MAX_TIME = 500; const TOUCH_SWIPE_MAX_TIME = 500; let touchstartEvent; let touchEventEnd; win.document.addEventListener("touchstart", (event) => { if ((0, popup_dialog_1.isPopupDialogOpen)(win.document)) { touchstartEvent = undefined; touchEventEnd = undefined; return; } if (event.changedTouches.length !== 1) { return; } touchstartEvent = event; }, true); win.document.addEventListener("touchend", (event) => { if ((0, popup_dialog_1.isPopupDialogOpen)(win.document)) { touchstartEvent = undefined; touchEventEnd = undefined; return; } if (event.changedTouches.length !== 1) { return; } if (!touchstartEvent) { return; } const startTouch = touchstartEvent.changedTouches[0]; const endTouch = event.changedTouches[0]; if (!startTouch || !endTouch) { return; } const deltaX = (startTouch.clientX - endTouch.clientX) / win.devicePixelRatio; const deltaY = (startTouch.clientY - endTouch.clientY) / win.devicePixelRatio; if (Math.abs(deltaX) < TOUCH_SWIPE_DELTA_MIN && Math.abs(deltaY) < TOUCH_SWIPE_DELTA_MIN) { if (touchEventEnd) { touchstartEvent = undefined; touchEventEnd = undefined; return; } if (event.timeStamp - touchstartEvent.timeStamp > TOUCH_SWIPE_LONG_PRESS_MAX_TIME) { touchstartEvent = undefined; touchEventEnd = undefined; return; } touchstartEvent = undefined; touchEventEnd = event; return; } touchEventEnd = undefined; if (event.timeStamp - touchstartEvent.timeStamp > TOUCH_SWIPE_MAX_TIME) { touchstartEvent = undefined; return; } const slope = (startTouch.clientY - endTouch.clientY) / (startTouch.clientX - endTouch.clientX); if (Math.abs(slope) > 0.5) { touchstartEvent = undefined; return; } const rtl = (0, readium_css_1.isRTL)(); if (deltaX > 0) { const payload = { go: rtl ? "PREVIOUS" : "NEXT", nav: true, }; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); } else { const payload = { go: rtl ? "NEXT" : "PREVIOUS", nav: true, }; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); } touchstartEvent = undefined; }, true); function keyDownUpEventHandler(ev, keyDown) { if (win.READIUM2.ignorekeyDownUpEvents) { return; } const elementName = (ev.target && ev.target.nodeName) ? ev.target.nodeName : ""; const elementAttributes = {}; if (ev.target && ev.target.attributes) { for (let i = 0; i < ev.target.attributes.length; i++) { const attr = ev.target.attributes[i]; elementAttributes[attr.name] = attr.value; } } const payload = { altKey: ev.altKey, code: ev.code, ctrlKey: ev.ctrlKey, elementAttributes, elementName, key: ev.key, metaKey: ev.metaKey, shiftKey: ev.shiftKey, }; electron_1.ipcRenderer.sendToHost(keyDown ? events_1.R2_EVENT_WEBVIEW_KEYDOWN : events_1.R2_EVENT_WEBVIEW_KEYUP, payload); } win.document.addEventListener("keydown", (ev) => { keyDownUpEventHandler(ev, true); }, { capture: true, once: false, passive: false, }); win.document.addEventListener("keyup", (ev) => { keyDownUpEventHandler(ev, false); }, { capture: true, once: false, passive: false, }); win.READIUM2.isAudio = win.location.protocol === "data:"; if (win.READIUM2.urlQueryParams) { let readiumEpubReadingSystemJson; const base64EpubReadingSystem = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_EPUBREADINGSYSTEM]; if (base64EpubReadingSystem) { try { const str = Buffer.from(base64EpubReadingSystem, "base64").toString("utf8"); readiumEpubReadingSystemJson = JSON.parse(str); } catch (err) { debug(err); } } if (readiumEpubReadingSystemJson) { (0, epubReadingSystem_1.setWindowNavigatorEpubReadingSystem)(win, readiumEpubReadingSystemJson); } win.READIUM2.DEBUG_VISUALS = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_DEBUG_VISUALS] === "true"; win.READIUM2.accessibilitySupportEnabled = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_A11Y_SUPPORT_ENABLED] === "true"; win.READIUM2.isClipboardIntercept = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_CLIPBOARD_INTERCEPT] === "true"; win.READIUM2.webViewSlot = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_WEBVIEW_SLOT] === "left" ? styles_2.WebViewSlotEnum.left : (win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_WEBVIEW_SLOT] === "right" ? styles_2.WebViewSlotEnum.right : styles_2.WebViewSlotEnum.center); } if (IS_DEV) { electron_1.ipcRenderer.on(events_1.R2_EVENT_DEBUG_VISUALS, (_event, payload) => { win.READIUM2.DEBUG_VISUALS = payload.debugVisuals; if (!payload.debugVisuals) { const existings = win.document.querySelectorAll(`*[${styles_2.readPosCssStylesAttr1}], *[${styles_2.readPosCssStylesAttr2}], *[${styles_2.readPosCssStylesAttr3}], *[${styles_2.readPosCssStylesAttr4}]`); existings.forEach((existing) => { existing.removeAttribute(`${styles_2.readPosCssStylesAttr1}`); existing.removeAttribute(`${styles_2.readPosCssStylesAttr2}`); existing.removeAttribute(`${styles_2.readPosCssStylesAttr3}`); existing.removeAttribute(`${styles_2.readPosCssStylesAttr4}`); }); } if (payload.cssClass) { if (_blacklistIdClassForCssSelectors.indexOf(payload.cssClass) < 0) { _blacklistIdClassForCssSelectors.push(payload.cssClass.toLowerCase()); } if (payload.debugVisuals && payload.cssStyles && payload.cssStyles.length) { const idSuffix = `debug_for_class_${payload.cssClass}`; (0, readium_css_inject_1.appendCSSInline)(win.document, idSuffix, payload.cssStyles); if (payload.cssSelector) { const toHighlights = win.document.querySelectorAll(payload.cssSelector); toHighlights.forEach((toHighlight) => { const clazz = `${payload.cssClass}`; if (!toHighlight.classList.contains(clazz)) { toHighlight.classList.add(clazz); } }); } } else { const existings = win.document.querySelectorAll(`.${payload.cssClass}`); existings.forEach((existing) => { existing.classList.remove(`${payload.cssClass}`); }); } } }); } function isVisible(allowPartial, element, domRect) { if (DEBUG_TRACE) debug("isVisible:", getCssSelector(element), allowPartial); if (DEBUG_TRACE && domRect) debug("isVisible domRect:", domRect.x, domRect.y, domRect.width, domRect.height); if (win.READIUM2.isFixedLayout) { return true; } else if (!win.document || !win.document.documentElement || !win.document.body) { return false; } if (element === win.document.body || element === win.document.documentElement) { if (DEBUG_TRACE) debug("isVisible: HTML BODY always true"); return true; } const blacklisted = checkBlacklisted(element); if (blacklisted) { return false; } const elStyle = win.getComputedStyle(element); if (elStyle) { const display = elStyle.getPropertyValue("display"); if (display === "none") { if (IS_DEV) { debug("isVisible element DISPLAY NONE"); } return false; } const opacity = elStyle.getPropertyValue("opacity"); if (opacity === "0") { if (IS_DEV) { debug("isVisible element OPACITY ZERO"); } return false; } } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const isVWM = (0, readium_css_1.isVerticalWritingMode)(); if (!(0, readium_css_inject_1.isPaginated)(win.document)) { const rect = domRect || element.getBoundingClientRect(); if (isVWM) { if (rect.left >= 0 && (rect.left + rect.width) <= win.document.documentElement.clientWidth) { return true; } if (allowPartial && (rect.left >= 0 || (rect.left + rect.width) <= win.document.documentElement.clientWidth)) { return true; } } else { if (rect.top >= 0 && (rect.top + rect.height) <= win.document.documentElement.clientHeight) { return true; } if (allowPartial && (rect.top >= 0 || (rect.top + rect.height) <= win.document.documentElement.clientWidth)) { return true; } } return false; } if (isVWM) { debug("isVisible FALSE VWM paginated"); return false; } const scrollLeftPotentiallyExcessive = getScrollOffsetIntoView(element, domRect); const extraShift = styles_2.ENABLE_EXTRA_COLUMN_SHIFT_METHOD ? scrollElement.scrollLeftExtra : 0; let currentOffset = scrollElement.scrollLeft; if (extraShift) { currentOffset += (((currentOffset < 0) ? -1 : 1) * extraShift); } if (scrollLeftPotentiallyExcessive[0] >= (currentOffset - 10) && scrollLeftPotentiallyExcessive[0] <= (currentOffset + 10)) { if (DEBUG_TRACE) debug("isVisible TRUE (!!!allowPartial)", scrollLeftPotentiallyExcessive[0], scrollLeftPotentiallyExcessive[1], currentOffset); return true; } if (allowPartial) { if (scrollLeftPotentiallyExcessive[1] >= (currentOffset - 10) && scrollLeftPotentiallyExcessive[1] <= (currentOffset + 10)) { if (DEBUG_TRACE) debug("isVisible TRUE (allowPartial)", scrollLeftPotentiallyExcessive[0], scrollLeftPotentiallyExcessive[1], currentOffset); return true; } } if (DEBUG_TRACE) debug("isVisible FALSE", scrollLeftPotentiallyExcessive[0], scrollLeftPotentiallyExcessive[1], currentOffset); return false; } function isVisible_(location) { let visible = false; if (win.READIUM2.isAudio) { visible = true; } else if (win.READIUM2.isFixedLayout) { visible = true; } else if (!win.document || !win.document.documentElement || !win.document.body) { visible = false; } else if (!location || !location.cssSelector) { visible = false; } else { let selected = null; try { selected = win.document.querySelector(location.cssSelector); } catch (err) { debug(err); } if (selected) { visible = isVisible(false, selected, undefined); } } return visible; } electron_1.ipcRenderer.on(events_1.R2_EVENT_LOCATOR_VISIBLE, (_event, payload, eventID) => { payload.visible = isVisible_(payload.location); electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_LOCATOR_VISIBLE, payload, eventID); }); electron_1.ipcRenderer.on(events_1.R2_EVENT_SCROLLTO, (_event, payload) => { if (DEBUG_TRACE) debug("R2_EVENT_SCROLLTO"); if (win.READIUM2.isAudio) { return; } showHideContentMask(false, win.READIUM2.isFixedLayout); (0, selection_2.clearCurrentSelection)(win); (0, popup_dialog_1.closePopupDialogs)(win.document); if (!win.READIUM2.urlQueryParams) { win.READIUM2.urlQueryParams = {}; } if (payload.isSecondWebView) { win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_SECOND_WEBVIEW] = "1"; } else { win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_SECOND_WEBVIEW] = "0"; } if (payload.previous) { win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_PREVIOUS] = "true"; } else { if (typeof win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_PREVIOUS] !== "undefined") { delete win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_PREVIOUS]; } } if (payload.goto) { win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO] = payload.goto; } else { if (typeof win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO] !== "undefined") { delete win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO]; } } if (payload.gotoDomRange) { win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO_DOM_RANGE] = payload.gotoDomRange; } else { if (typeof win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO_DOM_RANGE] !== "undefined") { delete win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO_DOM_RANGE]; } } if (win.READIUM2.isFixedLayout) { win.READIUM2.locationHashOverride = win.document.body; resetLocationHashOverrideInfo(); const isVWM = (0, readium_css_1.isVerticalWritingMode)(); const x = ((isVWM || (0, readium_css_1.isRTL)()) ? win.document.documentElement.clientWidth - 2 : 1); const y = 1; if (DEBUG_TRACE) debug("R2_EVENT_SCROLLTO: processXYRaw()..."); processXYRaw(x, y, false, false); if (DEBUG_TRACE) debug("R2_EVENT_SCROLLTO: notifyReadingLocationDebounced()..."); notifyReadingLocationDebounced(); return; } let delayScrollIntoView = false; if (payload.hash) { debug(".hashElement = 1"); win.READIUM2.hashElement = win.document.getElementById(payload.hash); if (win.READIUM2.DEBUG_VISUALS) { if (win.READIUM2.hashElement) { win.READIUM2.hashElement.setAttribute(styles_2.readPosCssStylesAttr1, "R2_EVENT_SCROLLTO hashElement"); } } win.location.href = "#" + payload.hash; delayScrollIntoView = true; } else { const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const scrollTop = scrollElement.scrollTop; const scrollLeft = scrollElement.scrollLeft; win.location.href = "#"; delayScrollIntoView = true; setTimeout(() => { debug("location HREF # hash, reset scroll left/top: ", scrollTop, scrollLeft); scrollElement.scrollTop = scrollTop; scrollElement.scrollLeft = scrollLeft; }, 0); win.READIUM2.hashElement = null; } win.READIUM2.locationHashOverride = undefined; resetLocationHashOverrideInfo(); if (delayScrollIntoView) { setTimeout(() => { if (DEBUG_TRACE) debug("R2_EVENT_SCROLLTO: delayScrollIntoView + scrollToHashRaw()..."); scrollToHashRaw(false, true); }, 100); } else { if (DEBUG_TRACE) debug("R2_EVENT_SCROLLTO: !delayScrollIntoView + scrollToHashRaw()..."); scrollToHashRaw(false, true); } }); function resetLocationHashOverrideInfo() { win.READIUM2.locationHashOverrideInfo = { locEventID: undefined, audioPlaybackInfo: undefined, docInfo: undefined, epubPage: undefined, epubPageID: undefined, headings: undefined, href: "", locations: { cfi: undefined, cssSelector: undefined, position: undefined, progression: undefined, xpath: undefined, }, paginationInfo: undefined, secondWebViewHref: undefined, selectionInfo: undefined, selectionIsNew: undefined, title: undefined, userInteract: false, }; } let _lastAnimState; function elementCapturesKeyboardArrowKeys(target) { let curElement = target; while (curElement && curElement.nodeType === Node.ELEMENT_NODE) { const editable = curElement.getAttribute("contenteditable"); if (editable) { return true; } const arrayOfKeyboardCaptureElements = ["input", "textarea", "video", "audio", "select"]; if (arrayOfKeyboardCaptureElements.indexOf(curElement.tagName.toLowerCase()) >= 0) { return true; } curElement = curElement.parentNode; } return false; } function ensureTwoPageSpreadWithOddColumnsIsOffsetTempDisable() { if (!styles_2.ENABLE_EXTRA_COLUMN_SHIFT_METHOD) { return 0; } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const val = scrollElement.scrollLeftExtra; if (val === 0) { return 0; } scrollElement.scrollLeftExtra = 0; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_SHIFT_VIEW_X, { offset: 0, backgroundColor: undefined }); return val; } function ensureTwoPageSpreadWithOddColumnsIsOffsetReEnable(scrollLeftExtra) { if (!styles_2.ENABLE_EXTRA_COLUMN_SHIFT_METHOD) { return; } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); scrollElement.scrollLeftExtra = scrollLeftExtra; const scrollLeftExtraBackgroundColor = scrollElement.scrollLeftExtraBackgroundColor; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_SHIFT_VIEW_X, { backgroundColor: scrollLeftExtraBackgroundColor ? scrollLeftExtraBackgroundColor : undefined, offset: ((0, readium_css_1.isRTL)() ? 1 : -1) * scrollLeftExtra, }); } function ensureTwoPageSpreadWithOddColumnsIsOffset(scrollOffset, maxScrollShift) { if (!styles_2.ENABLE_EXTRA_COLUMN_SHIFT_METHOD) { return; } if (!win || !win.document || !win.document.body || !win.document.documentElement) { return; } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); let dialogPopup = (0, popup_dialog_1.isPopupDialogOpen)(win.document); if (dialogPopup) { const diagEl = win.document.getElementById(styles_2.POPUP_DIALOG_CLASS); if (diagEl) { const isCollapsed = diagEl.classList.contains(styles_2.POPUP_DIALOG_CLASS_COLLAPSE); if (isCollapsed) { dialogPopup = false; } } } const noChange = dialogPopup || !(0, readium_css_inject_1.isPaginated)(win.document) || !(0, readium_css_1.isTwoPageSpread)() || (0, readium_css_1.isVerticalWritingMode)() || maxScrollShift <= 0 || Math.abs(scrollOffset) <= maxScrollShift; if (noChange) { scrollElement.scrollLeftExtra = 0; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_SHIFT_VIEW_X, { offset: 0, backgroundColor: undefined }); return; } const extraOffset = Math.abs(scrollOffset) - maxScrollShift; let backgroundColor; const docStyle = win.getComputedStyle(win.document.documentElement); if (docStyle) { backgroundColor = docStyle.getPropertyValue("background-color"); } if (!backgroundColor || backgroundColor === "transparent") { const bodyStyle = win.getComputedStyle(win.document.body); backgroundColor = bodyStyle.getPropertyValue("background-color"); if (backgroundColor === "transparent") { backgroundColor = undefined; } } scrollElement.scrollLeftExtra = extraOffset; scrollElement.scrollLeftExtraBackgroundColor = backgroundColor; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_SHIFT_VIEW_X, { backgroundColor: backgroundColor ? backgroundColor : undefined, offset: ((0, readium_css_1.isRTL)() ? 1 : -1) * extraOffset, }); } function onEventPageTurn(payload) { if (DEBUG_TRACE) debug("onEventPageTurn"); let leftRightKeyWasUsedInsideKeyboardCapture = false; if (win.document.activeElement && elementCapturesKeyboardArrowKeys(win.document.activeElement)) { if (win.document.hasFocus()) { leftRightKeyWasUsedInsideKeyboardCapture = true; } else { const oldDate = win.document.activeElement.r2_leftrightKeyboardTimeStamp; if (oldDate) { const newDate = new Date(); const msDiff = newDate.getTime() - oldDate.getTime(); if (msDiff <= 300) { leftRightKeyWasUsedInsideKeyboardCapture = true; } } } } if (leftRightKeyWasUsedInsideKeyboardCapture) { return; } (0, selection_2.clearCurrentSelection)(win); (0, popup_dialog_1.closePopupDialogs)(win.document); if (win.READIUM2.isAudio || win.READIUM2.isFixedLayout || !win.document.body) { electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); return; } if (!win.document || !win.document.documentElement) { return; } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const reduceMotion = win.document.documentElement.classList.contains(styles_2.ROOT_CLASS_REDUCE_MOTION); const isPaged = (0, readium_css_inject_1.isPaginated)(win.document); const goPREVIOUS = payload.go === "PREVIOUS"; const animationTime = 300; if (_lastAnimState && _lastAnimState.animating) { win.cancelAnimationFrame(_lastAnimState.id); _lastAnimState.object[_lastAnimState.property] = _lastAnimState.destVal; } const isVWM = (0, readium_css_1.isVerticalWritingMode)(); if (!goPREVIOUS) { const maxScrollShift = (0, readium_css_1.calculateMaxScrollShift)().maxScrollShift; const maxScrollShiftTolerated = maxScrollShift - CSS_PIXEL_TOLERANCE; if (isPaged) { const unit = isVWM ? scrollElement.offsetHeight : scrollElement.offsetWidth; let scrollElementOffset = Math.round(isVWM ? scrollElement.scrollTop : scrollElement.scrollLeft); const isNegative = scrollElementOffset < 0; const scrollElementOffsetAbs = Math.abs(scrollElementOffset); const fractional = scrollElementOffsetAbs / unit; const integral = Math.floor(fractional); const decimal = fractional - integral; const partial = decimal * unit; if (partial <= CSS_PIXEL_TOLERANCE) { scrollElementOffset = (isNegative ? -1 : 1) * integral * unit; } else if (partial >= (unit - CSS_PIXEL_TOLERANCE)) { scrollElementOffset = (isNegative ? -1 : 1) * (integral + 1) * unit; } if (isVWM && (scrollElementOffsetAbs < maxScrollShiftTolerated) || !isVWM && (scrollElementOffsetAbs < maxScrollShiftTolerated)) { const scrollOffsetPotentiallyExcessive_ = isVWM ? (scrollElementOffset + unit) : (scrollElementOffset + ((0, readium_css_1.isRTL)() ? -1 : 1) * unit); const nWholes = Math.floor(scrollOffsetPotentiallyExcessive_ / unit); const scrollOffsetPotentiallyExcessive = nWholes * unit; ensureTwoPageSpreadWithOddColumnsIsOffset(scrollOffsetPotentiallyExcessive, maxScrollShift); const scrollOffset = (scrollOffsetPotentiallyExcessive < 0 ? -1 : 1) * Math.min(Math.abs(scrollOffsetPotentiallyExcessive), maxScrollShift); const targetObj = scrollElement; const targetProp = isVWM ? "scrollTop" : "scrollLeft"; if (reduceMotion) { _lastAnimState = undefined; if (DEBUG_TRACE) debug("onEventPageTurn: !goPREVIOUS + isPaged + reduceMotion + scroll?..."); targetObj[targetProp] = scrollOffset; } else { _ignoreScrollEvent = true; _lastAnimState = (0, animateProperty_1.animateProperty)(win.cancelAnimationFrame, (_cancelled) => { _ignoreScrollEvent = false; if (DEBUG_TRACE) debug("onEventPageTurn: !goPREVIOUS + isPaged + !reduceMotion + onScrollDebounced()..."); onScrollDebounced(); }, targetProp, animationTime, targetObj, scrollOffset, win.requestAnimationFrame, easings_1.easings.easeInOutQuad); } payload.go = ""; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); return; } } else { if (isVWM && (Math.abs(scrollElement.scrollLeft) < (maxScrollShiftTolerated - CSS_PIXEL_TOLERANCE)) || !isVWM && (Math.abs(scrollElement.scrollTop) < (maxScrollShiftTolerated - CSS_PIXEL_TOLERANCE))) { const newVal = isVWM ? (scrollElement.scrollLeft + ((0, readium_css_1.isRTL)() ? -1 : 1) * win.document.documentElement.clientWidth) : (scrollElement.scrollTop + win.document.documentElement.clientHeight); const targetObj = scrollElement; const targetProp = isVWM ? "scrollLeft" : "scrollTop"; if (reduceMotion) { _lastAnimState = undefined; if (DEBUG_TRACE) debug("onEventPageTurn: !goPREVIOUS + !isPaged + reduceMotion + scroll?..."); targetObj[targetProp] = newVal; } else { _ignoreScrollEvent = true; _lastAnimState = (0, animateProperty_1.animateProperty)(win.cancelAnimationFrame, (_cancelled) => { _ignoreScrollEvent = false; if (DEBUG_TRACE) debug("onEventPageTurn: !goPREVIOUS + !isPaged + !reduceMotion + onScrollDebounced()..."); onScrollDebounced(); }, targetProp, animationTime, targetObj, newVal, win.requestAnimationFrame, easings_1.easings.easeInOutQuad); } payload.go = ""; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); return; } } } else if (goPREVIOUS) { if (isPaged) { const unit = isVWM ? scrollElement.offsetHeight : scrollElement.offsetWidth; let scrollElementOffset = Math.round(isVWM ? scrollElement.scrollTop : scrollElement.scrollLeft); const isNegative = scrollElementOffset < 0; const scrollElementOffsetAbs = Math.abs(scrollElementOffset); const fractional = scrollElementOffsetAbs / unit; const integral = Math.floor(fractional); const decimal = fractional - integral; const partial = decimal * unit; if (partial <= CSS_PIXEL_TOLERANCE) { scrollElementOffset = (isNegative ? -1 : 1) * integral * unit; } else if (partial >= (unit - CSS_PIXEL_TOLERANCE)) { scrollElementOffset = (isNegative ? -1 : 1) * (integral + 1) * unit; } if (isVWM && (scrollElementOffsetAbs > 0) || !isVWM && (scrollElementOffsetAbs > 0)) { const scrollOffset_ = isVWM ? (scrollElementOffset - unit) : (scrollElementOffset - ((0, readium_css_1.isRTL)() ? -1 : 1) * unit); const nWholes = (0, readium_css_1.isRTL)() ? Math.floor(scrollOffset_ / unit) : Math.ceil(scrollOffset_ / unit); const scrollOffset = nWholes * unit; ensureTwoPageSpreadWithOddColumnsIsOffset(scrollOffset, 0); const targetObj = scrollElement; const targetProp = isVWM ? "scrollTop" : "scrollLeft"; if (reduceMotion) { _lastAnimState = undefined; if (DEBUG_TRACE) debug("onEventPageTurn: goPREVIOUS + isPaged + reduceMotion + scroll?..."); targetObj[targetProp] = scrollOffset; } else { _ignoreScrollEvent = true; _lastAnimState = (0, animateProperty_1.animateProperty)(win.cancelAnimationFrame, (_cancelled) => { _ignoreScrollEvent = false; if (DEBUG_TRACE) debug("onEventPageTurn: goPREVIOUS + isPaged + !reduceMotion + onScrollDebounced()..."); onScrollDebounced(); }, targetProp, animationTime, targetObj, scrollOffset, win.requestAnimationFrame, easings_1.easings.easeInOutQuad); } payload.go = ""; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); return; } } else { if (isVWM && (Math.abs(scrollElement.scrollLeft) > CSS_PIXEL_TOLERANCE) || !isVWM && (Math.abs(scrollElement.scrollTop) > CSS_PIXEL_TOLERANCE)) { const newVal = isVWM ? (scrollElement.scrollLeft - ((0, readium_css_1.isRTL)() ? -1 : 1) * win.document.documentElement.clientWidth) : (scrollElement.scrollTop - win.document.documentElement.clientHeight); const targetObj = scrollElement; const targetProp = isVWM ? "scrollLeft" : "scrollTop"; if (reduceMotion) { _lastAnimState = undefined; if (DEBUG_TRACE) debug("onEventPageTurn: goPREVIOUS + !isPaged + reduceMotion + scroll?..."); targetObj[targetProp] = newVal; } else { _ignoreScrollEvent = true; _lastAnimState = (0, animateProperty_1.animateProperty)(win.cancelAnimationFrame, (_cancelled) => { _ignoreScrollEvent = false; if (DEBUG_TRACE) debug("onEventPageTurn: goPREVIOUS + !isPaged + !reduceMotion + onScrollDebounced()..."); onScrollDebounced(); }, targetProp, animationTime, targetObj, newVal, win.requestAnimationFrame, easings_1.easings.easeInOutQuad); } payload.go = ""; electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); return; } } } electron_1.ipcRenderer.sendToHost(events_1.R2_EVENT_PAGE_TURN_RES, payload); } electron_1.ipcRenderer.on(events_1.R2_EVENT_PAGE_TURN, (_event, payload) => { if (DEBUG_TRACE) debug("R2_EVENT_PAGE_TURN"); focusScrollDebounced.clear(); processXYDebouncedImmediate.clear(); notifyReadingLocationDebounced.clear(); notifyReadingLocationDebouncedImmediate.clear(); scrollToHashDebounced.clear(); onScrollDebounced.clear(); handleFocusInDebounced.clear(); setTimeout(() => { if (DEBUG_TRACE) debug("R2_EVENT_PAGE_TURN: onEventPageTurn()..."); onEventPageTurn(payload); }, 100); }); function focusElement(element, preventScroll) { if (DEBUG_TRACE) debug("focusElement", getCssSelector(element)); if (preventScroll && (element === win.document.activeElement)) { if (DEBUG_TRACE) debug("focusElement: preventScroll && document.activeElement"); return; } if (element === win.document.body || !(0, tabbable_1.isFocusable)(element)) { const attr = element.getAttribute("tabindex"); if (!attr) { element.setAttribute("tabindex", "-1"); element.classList.add(styles_2.CSS_CLASS_NO_FOCUS_OUTLINE); if (DEBUG_TRACE) debug("focusElement: tabindex -1"); } } if (element === win.document.body) { if (DEBUG_TRACE) debug("focusElement: body, preventScroll"); _ignoreFocusInEvent = true; element.focus({ preventScroll: true }); } else { if (DEBUG_TRACE) debug("focusElement: !body, preventScroll?", preventScroll); _ignoreFocusInEvent = true; element.focus({ preventScroll }); } } const tempLinkTargetOutline = (element, time, alt) => { if (DEBUG_TRACE) debug("tempLinkTargetOutline", getCssSelector(element)); if (win.document.documentElement.classList.contains(styles_1.DISABLE_TEMPORARY_NAV_TARGET_OUTLINE_CLASS) || win.document.documentElement.classList.contains(styles_2.TTS_CLASS_PLAYING) || win.document.documentElement.classList.contains(styles_2.TTS_CLASS_PAUSED)) { return; } let skip = false; const targets = win.document.querySelectorAll(`.${styles_2.LINK_TARGET_CLASS}`); targets.forEach((t) => { if (alt && !t.classList.contains(styles_2.LINK_TARGET_ALT_CLASS)) { skip = true; return; } t.classList.remove(styles_2.LINK_TARGET_CLASS); t.classList.remove(styles_2.LINK_TARGET_ALT_CLASS); }); if (skip) { return; } element.style.animation = "none"; void element.offsetWidth; element.style.animation = ""; element.classList.add(styles_2.LINK_TARGET_CLASS); if (alt) { element.classList.add(styles_2.LINK_TARGET_ALT_CLASS); } if (element._timeoutTargetClass) { clearTimeout(element._timeoutTargetClass); element._timeoutTargetClass = undefined; } element._timeoutTargetClass = setTimeout(() => { element.classList.remove(styles_2.LINK_TARGET_CLASS); element.classList.remove(styles_2.LINK_TARGET_ALT_CLASS); }, time); }; let _lastAnimState2; const animationTime2 = 400; function scrollElementIntoView(element, doFocus, animate, domRect, center) { if (DEBUG_TRACE) debug("scrollElementIntoView", getCssSelector(element)); if (win.READIUM2.DEBUG_VISUALS) { const existings = win.document.querySelectorAll(`*[${styles_2.readPosCssStylesAttr3}]`); existings.forEach((existing) => { existing.removeAttribute(`${styles_2.readPosCssStylesAttr3}`); }); element.setAttribute(styles_2.readPosCssStylesAttr3, "scrollElementIntoView"); } if (win.READIUM2.isFixedLayout) { if (DEBUG_TRACE) debug("scrollElementIntoView: isFixedLayout"); return; } if (doFocus) { tempLinkTargetOutline(element, 2000, false); if (DEBUG_TRACE) debug("scrollElementIntoView: doFocus + focusElement()"); focusElement(element, !!domRect); } setTimeout(() => { const isPaged = (0, readium_css_inject_1.isPaginated)(win.document); if (isPaged) { if (DEBUG_TRACE) debug("scrollElementIntoView: isPaged + scrollIntoView()"); scrollIntoView(element, domRect); } else { const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const rect = domRect || element.getBoundingClientRect(); if (!center && isVisible(false, element, domRect)) { if (DEBUG_TRACE) debug("scrollElementIntoView: !center && isVisible"); } else { const isVWM = (0, readium_css_1.isVerticalWritingMode)(); const scrollTopMax = isVWM ? ((0, readium_css_1.isRTL)() ? -1 : 1) * (scrollElement.scrollWidth - win.document.documentElement.clientWidth) : scrollElement.scrollHeight - win.document.documentElement.clientHeight; let offset = isVWM ? scrollElement.scrollLeft + (rect.left - (win.document.documentElement.clientWidth / 2) + (center ? (rect.width / 2) : 0)) : scrollElement.scrollTop + (rect.top - (win.document.documentElement.clientHeight / 2) + (center ? (rect.height / 2) : 0)); if (isVWM && (0, readium_css_1.isRTL)()) { if (offset < scrollTopMax) { offset = scrollTopMax; } else if (offset > 0) { offset = 0; } } else { if (offset > scrollTopMax) { offset = scrollTopMax; } else if (offset < 0) { offset = 0; } } const diff = Math.abs((isVWM ? scrollElement.scrollLeft : scrollElement.scrollTop) - offset); if (diff < 10) { return; } const targetProp = isVWM ? "scrollLeft" : "scrollTop"; if (animate) { const reduceMotion = win.document.documentElement.classList.contains(styles_2.ROOT_CLASS_REDUCE_MOTION); if (_lastAnimState2 && _lastAnimState2.animating) { win.cancelAnimationFrame(_lastAnimState2.id); _lastAnimState2.object[_lastAnimState2.property] = _lastAnimState2.destVal; } const targetObj = scrollElement; if (reduceMotion) { _lastAnimState2 = undefined; if (DEBUG_TRACE) debug("scrollElementIntoView: animate + reduceMotion + scroll?..."); targetObj[targetProp] = offset; } else { _ignoreScrollEvent = true; _lastAnimState2 = (0, animateProperty_1.animateProperty)(win.cancelAnimationFrame, (_cancelled) => { _ignoreScrollEvent = false; if (DEBUG_TRACE) debug("scrollElementIntoView: animate + !reduceMotion + onScrollDebounced()..."); onScrollDebounced(); }, targetProp, animationTime2, targetObj, offset, win.requestAnimationFrame, easings_1.easings.easeInOutQuad); } } else { if (DEBUG_TRACE) debug("scrollElementIntoView: !animate + scroll?..."); scrollElement[targetProp] = offset; } } } }, 0); } function getScrollOffsetIntoView(element, domRect) { if (!win.document || !win.document.documentElement || !win.document.body || !(0, readium_css_inject_1.isPaginated)(win.document) || (0, readium_css_1.isVerticalWritingMode)()) { return [0, 0]; } const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const rect = domRect || element.getBoundingClientRect(); const columnDimension = (0, readium_css_1.calculateColumnDimension)(); const isTwoPage = (0, readium_css_1.isTwoPageSpread)(); const fullOffset = ((0, readium_css_1.isRTL)() ? ((columnDimension * (isTwoPage ? 2 : 1)) - (rect.left + rect.width)) : rect.left) + (((0, readium_css_1.isRTL)() ? -1 : 1) * scrollElement.scrollLeft); const columnIndex = Math.floor(fullOffset / columnDimension); const spreadIndex = isTwoPage ? Math.floor(columnIndex / 2) : columnIndex; const off = ((0, readium_css_1.isRTL)() ? -1 : 1) * (spreadIndex * (columnDimension * (isTwoPage ? 2 : 1))); const fullOffsetEnd = fullOffset + (((0, readium_css_1.isRTL)() ? 1 : 1) * rect.width); const columnIndexEnd = Math.floor(fullOffsetEnd / columnDimension); const spreadIndexEnd = isTwoPage ? Math.floor(columnIndexEnd / 2) : columnIndexEnd; const offEnd = ((0, readium_css_1.isRTL)() ? -1 : 1) * (spreadIndexEnd * (columnDimension * (isTwoPage ? 2 : 1))); return [off, offEnd]; } function scrollIntoView(element, domRect) { if (!win.document || !win.document.documentElement || !win.document.body || !(0, readium_css_inject_1.isPaginated)(win.document)) { return; } const maxScrollShift = (0, readium_css_1.calculateMaxScrollShift)().maxScrollShift; const scrollLeftPotentiallyExcessive = getScrollOffsetIntoView(element, domRect); ensureTwoPageSpreadWithOddColumnsIsOffset(scrollLeftPotentiallyExcessive[0], maxScrollShift); const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); const scrollOffset = (scrollLeftPotentiallyExcessive[0] < 0 ? -1 : 1) * Math.min(Math.abs(scrollLeftPotentiallyExcessive[0]), maxScrollShift); scrollElement.scrollLeft = scrollOffset; scrollElement.scrollTop = 0; } const scrollToHashRaw = (animate, skipRedraw) => { var _a; if (DEBUG_TRACE) debug("scrollToHashRaw"); if (!win.document || !win.document.body || !win.document.documentElement) { return; } if (!skipRedraw) { if (DEBUG_TRACE) debug("scrollToHashRaw: recreateAllHighlightsRaw()..."); (0, highlight_2.recreateAllHighlightsRaw)(win); } const isPaged = (0, readium_css_inject_1.isPaginated)(win.document); const isVWM = (0, readium_css_1.isVerticalWritingMode)(); if (win.READIUM2.locationHashOverride) { if (DEBUG_TRACE) debug("scrollToHashRaw: locationHashOverride + scrollElementIntoView()..."); scrollElementIntoView(win.READIUM2.locationHashOverride, true, animate, undefined); if (DEBUG_TRACE) debug("scrollToHashRaw: locationHashOverride + notifyReadingLocationDebounced()..."); notifyReadingLocationDebounced(); return; } else if (win.READIUM2.hashElement) { win.READIUM2.locationHashOverride = win.READIUM2.hashElement; if (DEBUG_TRACE) debug("scrollToHashRaw: hashElement + scrollElementIntoView()..."); scrollElementIntoView(win.READIUM2.hashElement, true, animate, undefined); if (DEBUG_TRACE) debug("scrollToHashRaw: hashElement + notifyReadingLocationDebounced()..."); notifyReadingLocationDebounced(); return; } else { const scrollElement = (0, readium_css_1.getScrollingElement)(win.document); if (win.READIUM2.urlQueryParams) { const previous = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_PREVIOUS]; const isPreviousNavDirection = previous === "true"; if (isPreviousNavDirection) { const { maxScrollShift, maxScrollShiftAdjusted } = (0, readium_css_1.calculateMaxScrollShift)(); _ignoreScrollEvent = true; if (isPaged) { if (isVWM) { scrollElement.scrollLeft = 0; scrollElement.scrollTop = maxScrollShift; } else { const scrollLeftPotentiallyExcessive = ((0, readium_css_1.isRTL)() ? -1 : 1) * maxScrollShiftAdjusted; ensureTwoPageSpreadWithOddColumnsIsOffset(scrollLeftPotentiallyExcessive, maxScrollShift); const scrollLeft = ((0, readium_css_1.isRTL)() ? -1 : 1) * maxScrollShift; scrollElement.scrollLeft = scrollLeft; scrollElement.scrollTop = 0; } } else { if (isVWM) { scrollElement.scrollLeft = ((0, readium_css_1.isRTL)() ? -1 : 1) * maxScrollShift; scrollElement.scrollTop = 0; } else { scrollElement.scrollLeft = 0; scrollElement.scrollTop = maxScrollShift; } } win.READIUM2.locationHashOverride = undefined; resetLocationHashOverrideInfo(); setTimeout(() => { const x = ((isVWM || (0, readium_css_1.isRTL)()) ? win.document.documentElement.clientWidth - 2 : 1); const y = 1; if (DEBUG_TRACE) debug("scrollToHashRaw: urlQueryParams + isPreviousNavDirection + processXYRaw()..."); processXYRaw(x, y, false, false); showHideContentMask(false, win.READIUM2.isFixedLayout); if (!win.READIUM2.locationHashOverride) { if (DEBUG_TRACE) debug("scrollToHashRaw: urlQueryParams + isPreviousNavDirection + notifyReadingLocationDebounced()..."); notifyReadingLocationDebounced(); } setTimeout(() => { _ignoreScrollEvent = false; }, 10); }, 60); return; } const gto = win.READIUM2.urlQueryParams[url_params_1.URL_PARAM_GOTO]; let gotoCssSelector; let gotoProgression; if (gto) { const locStr = Buffer.from(gto, "base64").toString("utf8"); const locObj = JSON.parse(locStr); gotoCssSelector = locObj.cssSelector; gotoProgression = locObj.progression; } if (gotoCssSelector) { gotoCssSelector = gotoCssSelector.replace(/\+/g