UNPKG

r2-navigator-js

Version:

Readium 2 'navigator' for NodeJS (TypeScript)

535 lines (534 loc) 23.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.setKeyUpEventHandler = exports.setKeyDownEventHandler = exports.installNavigatorDOM = exports.readiumCssUpdate = exports.readiumCssOnOff = exports.fixedLayoutZoomPercent = void 0; const tslib_1 = require("tslib"); const IS_DEV = (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "dev"); const debug_ = require("debug"); const electron_1 = require("electron"); const media_overlays_1 = require("./media-overlays"); const context_menu_1 = require("../common/context-menu"); const events_1 = require("../common/events"); const readium_css_settings_1 = require("../common/readium-css-settings"); const sessions_1 = require("../common/sessions"); const styles_1 = require("../common/styles"); const url_params_1 = require("./common/url-params"); const highlight_1 = require("./highlight"); const location_1 = require("./location"); const media_overlays_2 = require("./media-overlays"); const readaloud_1 = require("./readaloud"); const readium_css_1 = require("./readium-css"); const soundtrack_1 = require("./soundtrack"); const ELEMENT_ID_SLIDING_VIEWPORT = "r2_navigator_sliding_viewport"; const ELEMENT_ID_CAPTIONS = "r2_navigator_captions_overlay"; const ELEMENT_ID_READIUM_CSS_STYLE = "r2_navigator_readium_css"; const captionsOverlayCssStyles = ` overflow: hidden; overflow-y: auto; display: flex; justify-content: center; position: absolute; left: 0; top: 0; right: 0; bottom: 0; box-sizing: border-box; border: 0; margin: 0; padding: 2em; line-height: initial; user-select: none; `.replace(/\n/g, " ").replace(/\s\s+/g, " ").trim(); const captionsOverlayParaCssStyles = ` margin: 0; margin-top: auto; margin-bottom: auto; padding: 0; max-width: 900px; font-weight: bolder; text-align: center; `.replace(/\n/g, " ").replace(/\s\s+/g, " ").trim(); const readiumCssStyle = ` @font-face { font-family: AccessibleDfA; font-style: normal; font-weight: normal; src: local("AccessibleDfA"), url("{RCSS_BASE_URL}fonts/AccessibleDfA.otf") format("opentype"); } @font-face { font-family: "IA Writer Duospace"; font-style: normal; font-weight: normal; src: local("iAWriterDuospace-Regular"), url("{RCSS_BASE_URL}fonts/iAWriterDuospace-Regular.ttf") format("truetype"); } `; const debug = debug_("r2:navigator#electron/renderer/index"); const win = global.window; let _resizeSkip = 0; let _resizeWebviewsNeedReset = true; let _resizeTimeout; win.addEventListener("resize", () => { var _a, _b, _c; if (!win.READIUM2) { return; } if (((_c = (_b = (_a = win.READIUM2.publication) === null || _a === void 0 ? void 0 : _a.Metadata) === null || _b === void 0 ? void 0 : _b.Rendition) === null || _c === void 0 ? void 0 : _c.Layout) !== "fixed") { return; } if (_resizeSkip > 0) { debug("Window resize (TOP), SKIP ...", _resizeSkip); return; } if (_resizeWebviewsNeedReset) { _resizeWebviewsNeedReset = false; debug("Window resize (TOP), IMMEDIATE"); const activeWebViews = win.READIUM2.getActiveWebViews(); for (const activeWebView of activeWebViews) { const wvSlot = activeWebView.getAttribute("data-wv-slot"); if (wvSlot) { debug("Window resize (TOP), IMMEDIATE ... setWebViewStyle"); (0, location_1.setWebViewStyle)(activeWebView, wvSlot); } } } if (_resizeTimeout) { clearTimeout(_resizeTimeout); } _resizeTimeout = win.setTimeout(() => tslib_1.__awaiter(void 0, void 0, void 0, function* () { var _d; debug("Window resize (TOP), DEFERRED"); _resizeTimeout = undefined; _resizeWebviewsNeedReset = true; const activeWebViews = win.READIUM2.getActiveWebViews(); _resizeSkip = activeWebViews.length; for (const activeWebView of activeWebViews) { const wvSlot = activeWebView.getAttribute("data-wv-slot"); if (wvSlot) { try { if ((_d = activeWebView.READIUM2) === null || _d === void 0 ? void 0 : _d.DOMisReady) { yield activeWebView.send("R2_EVENT_WINDOW_RESIZE", win.READIUM2.fixedLayoutZoomPercent); } } catch (e) { debug(e); } } } }), 1000); }); electron_1.ipcRenderer.on("accessibility-support-changed", (_e, accessibilitySupportEnabled) => { if (!win.READIUM2) { return; } debug("accessibility-support-changed event received in WebView ", accessibilitySupportEnabled); win.READIUM2.isScreenReaderMounted = accessibilitySupportEnabled; }); function readiumCssApplyToWebview(loc, activeWebView, rcss) { var _a; const actualReadiumCss = (0, readium_css_1.obtainReadiumCss)(rcss); activeWebView.READIUM2.readiumCss = actualReadiumCss; const payloadRcss = (0, readium_css_1.adjustReadiumCssJsonMessageForFixedLayout)(activeWebView, actualReadiumCss); if (activeWebView.style.transform && activeWebView.style.transform !== "none" && !activeWebView.hasAttribute("data-wv-fxl")) { activeWebView.style.opacity = "0"; setTimeout(() => tslib_1.__awaiter(this, void 0, void 0, function* () { var _b; (0, location_1.shiftWebview)(activeWebView, 0, undefined); if ((_b = activeWebView.READIUM2) === null || _b === void 0 ? void 0 : _b.DOMisReady) { yield activeWebView.send(events_1.R2_EVENT_READIUMCSS, payloadRcss); } }), 10); } else { setTimeout(() => tslib_1.__awaiter(this, void 0, void 0, function* () { var _c; if ((_c = activeWebView.READIUM2) === null || _c === void 0 ? void 0 : _c.DOMisReady) { yield activeWebView.send(events_1.R2_EVENT_READIUMCSS, payloadRcss); } }), 0); } if (loc && loc.locator.href === ((_a = activeWebView.READIUM2.link) === null || _a === void 0 ? void 0 : _a.Href)) { setTimeout(() => { debug("readiumCssOnOff -> handleLinkLocator"); (0, location_1.handleLinkLocator)(loc.locator, actualReadiumCss); }, 60); } } const _fixedLayoutZoomPercentTimers = {}; function fixedLayoutZoomPercent(zoomPercent) { win.READIUM2.domSlidingViewport.style.overflow = zoomPercent === 0 ? "hidden" : "auto"; if (win.READIUM2) { win.READIUM2.fixedLayoutZoomPercent = zoomPercent; } const activeWebViews = win.READIUM2.getActiveWebViews(); for (const activeWebView of activeWebViews) { if (_fixedLayoutZoomPercentTimers[activeWebView.id]) { win.clearTimeout(_fixedLayoutZoomPercentTimers[activeWebView.id]); _fixedLayoutZoomPercentTimers[activeWebView.id] = undefined; } const wvSlot = activeWebView.getAttribute("data-wv-slot"); if (wvSlot) { debug("fixedLayoutZoomPercent ... setWebViewStyle"); (0, location_1.setWebViewStyle)(activeWebView, wvSlot); _fixedLayoutZoomPercentTimers[activeWebView.id] = win.setTimeout(() => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; try { _fixedLayoutZoomPercentTimers[activeWebView.id] = undefined; if ((_a = activeWebView.READIUM2) === null || _a === void 0 ? void 0 : _a.DOMisReady) { yield activeWebView.send("R2_EVENT_WINDOW_RESIZE", zoomPercent); } } catch (e) { debug(e); } }), 500); } } } exports.fixedLayoutZoomPercent = fixedLayoutZoomPercent; function readiumCssOnOff(rcss) { const loc = (0, location_1.getCurrentReadingLocation)(); const activeWebViews = win.READIUM2.getActiveWebViews(); for (const activeWebView of activeWebViews) { readiumCssApplyToWebview(loc, activeWebView, rcss); } } exports.readiumCssOnOff = readiumCssOnOff; function readiumCssUpdate(rcss) { return readiumCssOnOff(rcss); } exports.readiumCssUpdate = readiumCssUpdate; let _webview1; let _webview2; function createWebViewInternal(preloadScriptPath) { const wv = document.createElement("webview"); wv.setAttribute("webpreferences", `enableRemoteModule=0, allowRunningInsecureContent=0, backgroundThrottling=0, nodeIntegration=0, contextIsolation=0, nodeIntegrationInWorker=0, sandbox=0, webSecurity=1, webviewTag=0, partition=${sessions_1.R2_SESSION_WEBVIEW}`); wv.setAttribute("partition", sessions_1.R2_SESSION_WEBVIEW); const publicationURL_ = win.READIUM2.publicationURL; if (publicationURL_) { wv.setAttribute("httpreferrer", publicationURL_); } debug("createWebViewInternal ... setWebViewStyle"); (0, location_1.setWebViewStyle)(wv, styles_1.WebViewSlotEnum.center); wv.setAttribute("preload", preloadScriptPath); wv.addEventListener("did-start-loading", () => { debug("DOMisReady... did-start-loading => false"); wv.READIUM2.DOMisReady = false; }); wv.addEventListener("did-navigate-in-page", () => { debug("DOMisReady... did-navigate-in-page => true"); wv.READIUM2.DOMisReady = true; }); wv.addEventListener("dom-ready", () => { debug("DOMisReady... dom-ready => true"); wv.READIUM2.DOMisReady = true; wv.clearHistory(); if (IS_DEV) { electron_1.ipcRenderer.send(context_menu_1.CONTEXT_MENU_SETUP, wv.getWebContentsId()); } if (win.READIUM2) { (0, readaloud_1.ttsVoice)(win.READIUM2.ttsVoice); (0, readaloud_1.ttsPlaybackRate)(win.READIUM2.ttsPlaybackRate); (0, readaloud_1.ttsClickEnable)(win.READIUM2.ttsClickEnabled); (0, readaloud_1.ttsSentenceDetectionEnable)(win.READIUM2.ttsSentenceDetectionEnabled); (0, readaloud_1.ttsSkippabilityEnable)(win.READIUM2.ttsSkippabilityEnabled); (0, readaloud_1.ttsOverlayEnable)(win.READIUM2.ttsOverlayEnabled); } (0, readaloud_1.checkTtsState)(wv); }); wv.addEventListener("ipc-message", (event) => { var _a, _b; const webview = event.currentTarget; if (webview !== wv) { debug("Wrong navigator webview?!"); return; } if (event.channel === events_1.R2_EVENT_MEDIA_OVERLAY_INTERRUPT) { (0, media_overlays_1.mediaOverlaysInterrupt)(); } else if (event.channel === events_1.R2_EVENT_KEYBOARD_FOCUS_REQUEST) { debug("KEYBOARD FOCUS REQUEST (2) ", webview.id, (_a = win.document.activeElement) === null || _a === void 0 ? void 0 : _a.id); if (win.document.activeElement && win.document.activeElement.blur) { win.document.activeElement.blur(); } const iframe = (_b = webview.shadowRoot) === null || _b === void 0 ? void 0 : _b.querySelector("iframe"); if (iframe) { iframe.focus(); } else { webview.focus(); } } else if (event.channel === events_1.R2_EVENT_SHOW) { webview.style.opacity = "1"; } else if (event.channel === events_1.R2_EVENT_FXL_CONFIGURE) { const payload = event.args[0]; debug("R2_EVENT_FXL_CONFIGURE ... setWebViewStyle"); if (payload.fxl) { (0, location_1.setWebViewStyle)(webview, styles_1.WebViewSlotEnum.center, payload.fxl); } else { (0, location_1.setWebViewStyle)(webview, styles_1.WebViewSlotEnum.center, null); } _resizeSkip--; } else if (event.channel === events_1.R2_EVENT_WEBVIEW_KEYDOWN) { const payload = event.args[0]; if (_keyDownEventHandler) { _keyDownEventHandler(payload, payload.elementName, payload.elementAttributes); } } else if (event.channel === events_1.R2_EVENT_WEBVIEW_KEYUP) { const payload = event.args[0]; if (_keyUpEventHandler) { _keyUpEventHandler(payload, payload.elementName, payload.elementAttributes); } } else if (event.channel === events_1.R2_EVENT_CAPTIONS) { const payload = event.args[0]; let captionElement = win.document.getElementById(ELEMENT_ID_CAPTIONS); let rssStyleElement = win.document.getElementById(ELEMENT_ID_READIUM_CSS_STYLE); const rootElement = win.document.getElementById(ELEMENT_ID_SLIDING_VIEWPORT); if (payload.text && rootElement) { if (!rssStyleElement) { const urlStr = win.READIUM2.publicationURL.startsWith(sessions_1.READIUM2_ELECTRON_HTTP_PROTOCOL) ? (0, sessions_1.convertCustomSchemeToHttpUrl)(win.READIUM2.publicationURL) : win.READIUM2.publicationURL; const rcssUrl = new URL(urlStr); rcssUrl.pathname = `${readium_css_settings_1.READIUM_CSS_URL_PATH}/`; rssStyleElement = win.document.createElement("style"); rssStyleElement.setAttribute("id", ELEMENT_ID_READIUM_CSS_STYLE); const styleTxtNode = win.document.createTextNode(readiumCssStyle.replace(/{RCSS_BASE_URL}/g, rcssUrl.toString())); rssStyleElement.appendChild(styleTxtNode); win.document.head.appendChild(rssStyleElement); } if (!captionElement) { captionElement = win.document.createElement("div"); captionElement.setAttribute("id", ELEMENT_ID_CAPTIONS); const para = win.document.createElement("p"); captionElement.appendChild(para); rootElement.appendChild(captionElement); } captionElement.setAttribute("style", captionsOverlayCssStyles + (payload.containerStyle ? ` ${payload.containerStyle}` : " ")); const p = captionElement.firstElementChild; if (p) { p.setAttribute("style", captionsOverlayParaCssStyles + (payload.textStyle ? ` ${payload.textStyle}` : " ")); p.textContent = payload.text; } } else { if (captionElement && captionElement.parentNode) { captionElement.parentNode.removeChild(captionElement); } } } else if (event.channel === events_1.R2_EVENT_CLIPBOARD_COPY) { const clipboardInterceptor = win.READIUM2.clipboardInterceptor; if (clipboardInterceptor) { const payload = event.args[0]; clipboardInterceptor(payload); } } else if (event.channel === events_1.R2_EVENT_PAGE_TURN_RES && event.args[0].go === "" && event.args[0].direction === "") { (0, readaloud_1.checkTtsState)(wv); } else if (!(0, highlight_1.highlightsHandleIpcMessage)(event.channel, event.args, webview) && !(0, readaloud_1.ttsHandleIpcMessage)(event.channel, event.args, webview) && !(0, location_1.locationHandleIpcMessage)(event.channel, event.args, webview) && !(0, media_overlays_2.mediaOverlaysHandleIpcMessage)(event.channel, event.args, webview) && !(0, soundtrack_1.soundtrackHandleIpcMessage)(event.channel, event.args, webview)) { debug("webview ipc-message"); debug(event.channel); } }); return wv; } function createWebView(second) { const preloadScriptPath = win.READIUM2.preloadScriptPath; const domSlidingViewport = win.READIUM2.domSlidingViewport; if (second) { if (_webview2) { destroyWebView(true); } _webview2 = createWebViewInternal(preloadScriptPath); _webview2.READIUM2 = { id: 2, link: undefined, readiumCss: undefined, }; _webview2.setAttribute("id", "r2_webview2"); domSlidingViewport.appendChild(_webview2); } else { if (_webview1) { destroyWebView(false); } _webview1 = createWebViewInternal(preloadScriptPath); _webview1.READIUM2 = { id: 1, link: undefined, readiumCss: undefined, }; _webview1.setAttribute("id", "r2_webview1"); domSlidingViewport.appendChild(_webview1); } } function destroyWebView(second) { const domSlidingViewport = win.READIUM2.domSlidingViewport; if (second) { if (_webview2) { domSlidingViewport.removeChild(_webview2); _webview2.READIUM2 = undefined; _webview2 = undefined; } } else { if (_webview1) { domSlidingViewport.removeChild(_webview1); _webview1.READIUM2 = undefined; _webview1 = undefined; } } } function installNavigatorDOM(publication, publicationURL, rootHtmlElementID, preloadScriptPath, location, enableScreenReaderAccessibilityWebViewHardRefresh, clipboardInterceptor, sessionInfo, rcss) { const domRootElement = document.getElementById(rootHtmlElementID); if (!domRootElement) { debug("!rootHtmlElementID ???"); return; } const domSlidingViewport = document.createElement("div"); domSlidingViewport.setAttribute("id", ELEMENT_ID_SLIDING_VIEWPORT); domSlidingViewport.setAttribute("style", "display: block; position: relative; width: 100%; height: 100%; " + "margin: 0; padding: 0; box-sizing: border-box; background: white; overflow: hidden;"); win.READIUM2 = { DEBUG_VISUALS: false, clipboardInterceptor, createFirstWebView: createWebView, createSecondWebView: () => { createWebView(true); }, destroyFirstWebView: destroyWebView, destroySecondWebView: () => { destroyWebView(true); }, domRootElement, domSlidingViewport, enableScreenReaderAccessibilityWebViewHardRefresh: enableScreenReaderAccessibilityWebViewHardRefresh ? false : false, fixedLayoutZoomPercent: 0, getActiveWebViews: () => { const arr = []; if (_webview1) { arr.push(_webview1); } if (_webview2) { arr.push(_webview2); } return arr; }, getFirstOrSecondWebView: () => { return _webview1 ? _webview1 : _webview2; }, getFirstWebView: () => { return _webview1; }, getSecondWebView: (create) => { if (!_webview2 && create) { createWebView(true); } return _webview2; }, isScreenReaderMounted: false, preloadScriptPath, publication, publicationURL, sessionInfo, ttsClickEnabled: false, ttsOverlayEnabled: false, ttsPlaybackRate: 1, ttsSkippabilityEnabled: false, ttsSentenceDetectionEnabled: true, ttsVoice: null, }; electron_1.ipcRenderer.send("accessibility-support-changed"); if (IS_DEV) { debug("||||||++||||| installNavigatorDOM: ", JSON.stringify(location)); const debugVisualz = (win.localStorage && win.localStorage.getItem(url_params_1.URL_PARAM_DEBUG_VISUALS) === "true") ? true : false; debug("debugVisuals GET: ", debugVisualz); win.READIUM2.DEBUG_VISUALS = debugVisualz; win.READIUM2.debug = (debugVisuals) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _a; debug("debugVisuals SET: ", debugVisuals); win.READIUM2.DEBUG_VISUALS = debugVisuals; if (win.localStorage) { win.localStorage.setItem(url_params_1.URL_PARAM_DEBUG_VISUALS, debugVisuals ? "true" : "false"); } const loc = (0, location_1.getCurrentReadingLocation)(); const activeWebViews = win.READIUM2.getActiveWebViews(); for (const activeWebView of activeWebViews) { const payload = { debugVisuals }; setTimeout(() => tslib_1.__awaiter(this, void 0, void 0, function* () { var _b; if ((_b = activeWebView.READIUM2) === null || _b === void 0 ? void 0 : _b.DOMisReady) { yield activeWebView.send(events_1.R2_EVENT_DEBUG_VISUALS, payload); } }), 0); if (loc && loc.locator.href === ((_a = activeWebView.READIUM2.link) === null || _a === void 0 ? void 0 : _a.Href)) { yield new Promise((res, _rej) => { setTimeout(() => { debug("READIUM2.debug -> handleLinkLocator"); (0, location_1.handleLinkLocator)(loc.locator, activeWebView.READIUM2.readiumCss); res(); }, 100); }); } } }); win.READIUM2.debugItems = (href, cssSelector, cssClass, cssStyles) => { var _a; if (cssStyles) { debug("debugVisuals ITEMS: ", `${cssSelector} --- ${cssClass} --- ${cssStyles}`); } const activeWebViews = win.READIUM2.getActiveWebViews(); for (const activeWebView of activeWebViews) { if (((_a = activeWebView.READIUM2.link) === null || _a === void 0 ? void 0 : _a.Href) !== href) { continue; } const d = win.READIUM2.DEBUG_VISUALS; const payload = { debugVisuals: d, cssSelector, cssClass, cssStyles }; setTimeout(() => tslib_1.__awaiter(this, void 0, void 0, function* () { var _b; if ((_b = activeWebView.READIUM2) === null || _b === void 0 ? void 0 : _b.DOMisReady) { yield activeWebView.send(events_1.R2_EVENT_DEBUG_VISUALS, payload); } }), 0); } }; } domRootElement.appendChild(domSlidingViewport); createWebView(); setTimeout(() => { debug("installNavigatorDOM -> handleLinkLocator"); (0, location_1.handleLinkLocator)(location, rcss); }, 100); } exports.installNavigatorDOM = installNavigatorDOM; let _keyDownEventHandler; function setKeyDownEventHandler(func) { _keyDownEventHandler = func; } exports.setKeyDownEventHandler = setKeyDownEventHandler; let _keyUpEventHandler; function setKeyUpEventHandler(func) { _keyUpEventHandler = func; } exports.setKeyUpEventHandler = setKeyUpEventHandler; //# sourceMappingURL=dom.js.map