(function() { if (window.RDReaderBridge) { return; } window.addEventListener('error', function(event) { try { var message = event && (event.message || (event.error && event.error.message)) || 'Unknown JavaScript error'; window.webkit.messageHandlers.{{JAVASCRIPT_ERROR_MESSAGE}}.postMessage(message); } catch (error) { } }); function serializeRange(range) { if (window.rangy && typeof window.rangy.serializeRange === 'function') { return window.rangy.serializeRange(range, range && range.startContainer && range.startContainer.ownerDocument); } return null; } function rangeFromInfo(rangeInfo) { if (window.rangy && typeof window.rangy.deserializeRange === 'function') { return window.rangy.deserializeRange(rangeInfo, document); } return null; } var visiblePageIndex = 0; var configuredPageCount = 1; var configuredPageStride = 1; var relayoutTimer = null; var fixedLayoutDocument = false; function isFixedLayoutDocument() { if (fixedLayoutDocument) { return true; } fixedLayoutDocument = !!document.getElementById('ss-fixed-root') || !!document.querySelector('.ss-fixed-page'); return fixedLayoutDocument; } function ensurePaginationStructure() { if (isFixedLayoutDocument()) { return null; } var viewport = document.getElementById('ss-reader-viewport'); var content = document.getElementById('ss-reader-content'); if (viewport && content) { return content; } viewport = document.createElement('div'); viewport.id = 'ss-reader-viewport'; content = document.createElement('div'); content.id = 'ss-reader-content'; while (document.body.firstChild) { content.appendChild(document.body.firstChild); } viewport.appendChild(content); document.body.appendChild(viewport); return content; } function paginationContent() { if (isFixedLayoutDocument()) { return null; } return ensurePaginationStructure(); } function effectivePageStride() { return Math.max(1, configuredPageStride || 1); } function measuredPageCount() { if (isFixedLayoutDocument()) { return 1; } var content = paginationContent(); var scrollingElement = document.scrollingElement || document.documentElement || document.body; var scrollWidth = Math.max( content ? content.scrollWidth : 0, content ? content.offsetWidth : 0, scrollingElement ? scrollingElement.scrollWidth : 0, document.documentElement ? document.documentElement.scrollWidth : 0, document.body ? document.body.scrollWidth : 0, effectivePageStride() ); return Math.max(1, Math.ceil(scrollWidth / effectivePageStride())); } function totalPageCount() { return Math.max(configuredPageCount, measuredPageCount()); } function currentPageOffset() { return Math.round(visiblePageIndex * effectivePageStride()); } function reportProgression(fragment) { var pageCount = totalPageCount(); var progression = pageCount <= 1 ? 0 : visiblePageIndex / Math.max(pageCount - 1, 1); var lastProgression = progression; window.webkit.messageHandlers.{{PROGRESSION_CHANGED_MESSAGE}}.postMessage({ progression: progression, lastProgression: lastProgression, fragment: fragment || null }); } function setVisiblePageIndex(pageIndex) { if (isFixedLayoutDocument()) { visiblePageIndex = 0; return; } var pageCount = totalPageCount(); var maxPageIndex = Math.max(0, pageCount - 1); var safePageIndex = Math.max(0, Math.min(maxPageIndex, Math.round(pageIndex || 0))); visiblePageIndex = safePageIndex; var content = paginationContent(); if (content) { content.style.transform = 'translate3d(' + (-currentPageOffset()) + 'px, 0, 0)'; } } function scheduleRelayout() { if (isFixedLayoutDocument()) { return; } clearTimeout(relayoutTimer); relayoutTimer = setTimeout(function() { setVisiblePageIndex(visiblePageIndex); reportProgression(null); }, 60); } function pageIndexForElement(target) { if (isFixedLayoutDocument()) { return 0; } if (!target) { return 0; } var rect = target.getBoundingClientRect(); var absoluteLeft = Math.max(0, rect.left + currentPageOffset()); return Math.max(0, Math.floor(absoluteLeft / effectivePageStride())); } function fragmentTarget(fragment) { if (!fragment) { return null; } var contexts = documentContexts(document, 0, 0); for (var index = 0; index < contexts.length; index += 1) { var context = contexts[index]; var target = context.doc.getElementById(fragment) || context.doc.querySelector('[name="' + fragment.replace(/"/g, '\\"') + '"]'); if (target) { return { context: context, target: target }; } } return null; } var storedHighlights = []; var storedSearchPresentation = null; function normalizeHref(rawHref) { if (!rawHref) { return ''; } var value = String(rawHref); value = value.replace(/^.*?:\/\/[^/]+\//, ''); value = value.split('#')[0]; value = value.replace(/^\/+/, ''); try { value = decodeURIComponent(value); } catch (error) { } return value; } function clearHighlights() { storedHighlights = []; } function setHighlights(items) { storedHighlights = Array.isArray(items) ? items.slice() : []; } function setSearchPresentation(payload) { storedSearchPresentation = payload || null; } function textNodes(doc) { if (!doc || !doc.body) { return []; } var walker = doc.createTreeWalker(doc.body, NodeFilter.SHOW_TEXT, { acceptNode: function(node) { if (!node || !node.nodeValue || !node.nodeValue.trim()) { return NodeFilter.FILTER_REJECT; } var parent = node.parentNode; if (!parent || !parent.nodeName) { return NodeFilter.FILTER_REJECT; } var name = parent.nodeName.toLowerCase(); if (['script', 'style', 'noscript'].indexOf(name) >= 0) { return NodeFilter.FILTER_REJECT; } return NodeFilter.FILTER_ACCEPT; } }); var nodes = []; while (walker.nextNode()) { nodes.push(walker.currentNode); } return nodes; } function documentContexts(rootDoc, baseOffsetX, baseOffsetY) { var items = []; if (!rootDoc) { return items; } var offsetX = baseOffsetX || 0; var offsetY = baseOffsetY || 0; items.push({ doc: rootDoc, href: normalizeHref(rootDoc.location && rootDoc.location.href), offsetX: offsetX, offsetY: offsetY }); var frames = rootDoc.querySelectorAll ? rootDoc.querySelectorAll('iframe') : []; frames.forEach(function(frame) { try { if (!frame.contentDocument) { return; } var frameRect = frame.getBoundingClientRect(); documentContexts( frame.contentDocument, offsetX + frameRect.left, offsetY + frameRect.top ).forEach(function(item) { items.push(item); }); } catch (error) { } }); return items; } var hookedDocuments = new WeakSet(); var hookedFrames = new WeakSet(); function selectionForDocument(doc) { if (!doc) { return null; } try { var view = doc.defaultView || window; return view.getSelection ? view.getSelection() : null; } catch (error) { return null; } } function currentSelectionContext() { var contexts = documentContexts(document, 0, 0); for (var index = 0; index < contexts.length; index += 1) { var context = contexts[index]; var selection = selectionForDocument(context.doc); if (!selection || selection.rangeCount === 0 || selection.isCollapsed) { continue; } var text = selection.toString ? selection.toString() : ''; if (!text || !text.trim()) { continue; } return { context: context, selection: selection }; } return null; } function rectPayloadForRange(range, offsetX, offsetY) { if (!range || range.collapsed) { return []; } var rects = []; Array.prototype.forEach.call(range.getClientRects(), function(rect) { if (!rect || rect.width < 0.5 || rect.height < 0.5) { return; } rects.push({ x: rect.left + (offsetX || 0), y: rect.top + (offsetY || 0), width: rect.width, height: rect.height }); }); return rects; } function collectHighlightDecorations() { return storedHighlights.map(function(item, index) { if (!item || !item.rangeInfo) { return null; } var range = rangeFromInfo(item.rangeInfo); var rects = rectPayloadForRange(range, 0, 0); if (!rects.length) { return null; } return { key: item.id || ('highlight-' + index), kind: item.style === 'underline' ? 'underline' : 'highlight', color: item.color || '#F8E16C', rangeInfo: item.rangeInfo, rects: rects }; }).filter(Boolean); } function collectSearchDecorations() { if (!storedSearchPresentation || !storedSearchPresentation.keyword) { return []; } var resources = new Map(); if (Array.isArray(storedSearchPresentation.resources)) { storedSearchPresentation.resources.forEach(function(resource) { resources.set(normalizeHref(resource.href), resource); }); } var keyword = String(storedSearchPresentation.keyword); var lowerKeyword = keyword.toLowerCase(); var decorations = []; documentContexts(document, 0, 0).forEach(function(context) { var resourceState = resources.get(context.href); if (!resourceState || resourceState.matchCount <= 0) { return; } var activeIndex = typeof resourceState.activeLocalMatchIndex === 'number' ? resourceState.activeLocalMatchIndex : null; var currentLocalIndex = 0; var activeRect = null; var contextStartIndex = decorations.length; textNodes(context.doc).forEach(function(node) { var source = node.nodeValue || ''; var lowerSource = source.toLowerCase(); var cursor = 0; while (cursor < source.length) { var foundIndex = lowerSource.indexOf(lowerKeyword, cursor); if (foundIndex < 0) { break; } var range = context.doc.createRange(); range.setStart(node, foundIndex); range.setEnd(node, foundIndex + keyword.length); var rects = rectPayloadForRange(range, context.offsetX, context.offsetY); if (rects.length) { var isActive = currentLocalIndex === activeIndex; decorations.push({ key: context.href + ':' + currentLocalIndex, kind: isActive ? 'activeSearch' : 'search', rects: rects }); if (isActive) { activeRect = rects[0]; } } currentLocalIndex += 1; cursor = foundIndex + keyword.length; } }); if (activeRect && !isFixedLayoutDocument()) { var previousPageIndex = visiblePageIndex; var activePageIndex = Math.max(0, Math.floor(Math.max(0, activeRect.x + currentPageOffset()) / effectivePageStride())); setVisiblePageIndex(activePageIndex); var deltaX = (activePageIndex - previousPageIndex) * effectivePageStride(); if (deltaX !== 0) { for (var index = contextStartIndex; index < decorations.length; index += 1) { decorations[index].rects = decorations[index].rects.map(function(rect) { return { x: rect.x - deltaX, y: rect.y, width: rect.width, height: rect.height }; }); } } } }); return decorations; } function resolveDecorations() { return { highlights: collectHighlightDecorations(), search: collectSearchDecorations() }; } function selectionPayload() { var payloadContext = currentSelectionContext(); if (!payloadContext) { return null; } var range = payloadContext.selection.getRangeAt(0); var text = payloadContext.selection.toString(); if (!text || !text.trim()) { return null; } var pageCount = totalPageCount(); var progression = pageCount <= 1 ? 0 : visiblePageIndex / Math.max(pageCount - 1, 1); var lastProgression = progression; return { href: payloadContext.context.href || null, text: text, rangeInfo: serializeRange(range), progression: progression, lastProgression: lastProgression }; } var selectionTimer = null; function notifySelectionChange() { clearTimeout(selectionTimer); selectionTimer = setTimeout(function() { var payload = selectionPayload(); window.webkit.messageHandlers.{{SELECTION_CHANGED_MESSAGE}}.postMessage(payload); }, 120); } function handleDocumentClick(event) { var anchor = event.target.closest ? event.target.closest('a[href]') : null; if (!anchor) { return; } var href = anchor.getAttribute('href'); if (!href) { return; } if (/^(https?:|mailto:|tel:)/i.test(href)) { event.preventDefault(); window.webkit.messageHandlers.{{EXTERNAL_LINK_MESSAGE}}.postMessage({ url: href }); return; } event.preventDefault(); window.webkit.messageHandlers.{{INTERNAL_LINK_MESSAGE}}.postMessage({ href: href }); } function installDocumentHooks(doc) { if (!doc || hookedDocuments.has(doc)) { return; } hookedDocuments.add(doc); doc.addEventListener('selectionchange', notifySelectionChange); doc.addEventListener('click', handleDocumentClick, true); } function installFrameHooks(rootDoc) { if (!rootDoc || !rootDoc.querySelectorAll) { return; } Array.prototype.forEach.call(rootDoc.querySelectorAll('iframe'), function(frame) { if (hookedFrames.has(frame)) { return; } hookedFrames.add(frame); var hookFrameDocument = function() { try { if (!frame.contentDocument) { return; } installDocumentHooks(frame.contentDocument); installFrameHooks(frame.contentDocument); } catch (error) { } }; frame.addEventListener('load', function() { hookFrameDocument(); scheduleRelayout(); }, { passive: true }); hookFrameDocument(); }); } installDocumentHooks(document); installFrameHooks(document); window.addEventListener('resize', function() { if (isFixedLayoutDocument()) { return; } setVisiblePageIndex(visiblePageIndex); reportProgression(null); }, { passive: true }); window.addEventListener('load', scheduleRelayout, { passive: true }); if (!isFixedLayoutDocument() && document.fonts && document.fonts.ready) { document.fonts.ready.then(scheduleRelayout).catch(function() {}); } if (!isFixedLayoutDocument()) { Array.prototype.forEach.call(document.querySelectorAll('img, iframe, video'), function(node) { node.addEventListener('load', scheduleRelayout, { passive: true }); node.addEventListener('error', scheduleRelayout, { passive: true }); }); } window.RDReaderBridge = { applyPagination: function(styleText) { if (isFixedLayoutDocument()) { return; } if (window.RDInjectedCSS) { window.RDInjectedCSS.setStyle('ss-reader-pagination', styleText, { includeFrames: false }); } else { var style = document.getElementById('ss-reader-pagination'); if (!style) { style = document.createElement('style'); style.id = 'ss-reader-pagination'; document.head.appendChild(style); } style.textContent = styleText; } ensurePaginationStructure(); installFrameHooks(document); }, setPageMetrics: function(pageCount, pageStride) { if (isFixedLayoutDocument()) { return; } configuredPageCount = Math.max(1, Math.round(pageCount || 1)); configuredPageStride = Math.max(1, Number(pageStride) || configuredPageStride || 1); setVisiblePageIndex(visiblePageIndex); scheduleRelayout(); }, scrollToPage: function(pageIndex) { if (isFixedLayoutDocument()) { return; } setVisiblePageIndex(pageIndex); reportProgression(null); }, scrollToLocation: function(location, fallbackPageIndex, targetRangeInfo) { if (targetRangeInfo) { var highlightRange = rangeFromInfo(targetRangeInfo); var highlightRects = rectPayloadForRange(highlightRange, 0, 0); if (highlightRects.length) { var highlightRect = highlightRects[0]; var highlightAbsoluteLeft = Math.max(0, highlightRect.x + currentPageOffset()); setVisiblePageIndex(Math.max(0, Math.floor(highlightAbsoluteLeft / effectivePageStride()))); reportProgression(location && location.fragment ? location.fragment : null); return; } } if (location && location.fragment) { var resolvedTarget = fragmentTarget(location.fragment); if (resolvedTarget) { if (isFixedLayoutDocument()) { reportProgression(location.fragment); return; } var rect = resolvedTarget.target.getBoundingClientRect(); var absoluteLeft = Math.max(0, rect.left + resolvedTarget.context.offsetX + currentPageOffset()); setVisiblePageIndex(Math.max(0, Math.floor(absoluteLeft / effectivePageStride()))); reportProgression(location.fragment); return; } } var pageCount = totalPageCount(); var progression = location && typeof location.progression === 'number' ? location.progression : 0; var derivedPageIndex = progression > 0 && pageCount > 1 ? Math.round(progression * Math.max(pageCount - 1, 0)) : (fallbackPageIndex || 0); this.scrollToPage(derivedPageIndex); }, setHighlights: setHighlights, clearHighlights: clearHighlights, setSearchPresentation: setSearchPresentation, resolveDecorations: resolveDecorations, reportProgression: reportProgression, selectionPayload: selectionPayload }; })();