r2-navigator-js
Version:
Readium 2 'navigator' for NodeJS (TypeScript)
1,142 lines • 175 kB
JavaScript
"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