ReadViewSDK/Sources/RDReaderView/EPUBCore/Resources/epub-bridge.js
shen 54798ba578 refactor: 添加中文注释 + 优化模块结构
- 给全部 78 个 Swift 源文件添加详细的中文注释(文件级、类级、方法级)
- 删除 LegacyRDReaderController/ 死代码目录(16 文件 4592 行)
- 根目录翻页容器文件移入 ReaderView/ 目录
- Resources/ 移入 EPUBCore/Resources/(与使用者归属一致)
- RDEPUBTextIndexTable.swift 移入 EPUBTextRendering/(消除反向依赖)
- RDURLReaderController.swift 移入 EPUBUI/(入口控制器归入 UI 层)
- 更新 podspec 资源路径
2026-05-25 10:19:14 +08:00

298 lines
11 KiB
JavaScript

(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 nodePath(node) {
var path = [];
var current = node;
while (current && current !== document) {
var parent = current.parentNode;
if (!parent) { break; }
var index = Array.prototype.indexOf.call(parent.childNodes, current);
path.unshift(index);
current = parent;
}
return path;
}
function nodeFromPath(path) {
var current = document;
for (var i = 0; i < path.length; i += 1) {
if (!current || !current.childNodes || current.childNodes.length <= path[i]) { return null; }
current = current.childNodes[path[i]];
}
return current;
}
function serializeRange(range) {
return JSON.stringify({
kind: 'dom-range',
startPath: nodePath(range.startContainer),
startOffset: range.startOffset,
endPath: nodePath(range.endContainer),
endOffset: range.endOffset
});
}
function rangeFromInfo(rangeInfo) {
try {
var payload = typeof rangeInfo === 'string' ? JSON.parse(rangeInfo) : rangeInfo;
var startNode = nodeFromPath(payload.startPath || []);
var endNode = nodeFromPath(payload.endPath || []);
if (!startNode || !endNode) { return null; }
var range = document.createRange();
range.setStart(startNode, payload.startOffset || 0);
range.setEnd(endNode, payload.endOffset || 0);
return range;
} catch (error) {
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 unwrapHighlights() {
document.querySelectorAll('mark.ss-reader-highlight').forEach(function(mark) {
var parent = mark.parentNode;
if (!parent) { return; }
while (mark.firstChild) {
parent.insertBefore(mark.firstChild, mark);
}
parent.removeChild(mark);
});
}
function setHighlights(items) {
unwrapHighlights();
if (!Array.isArray(items)) { return; }
items.forEach(function(item) {
if (!item || !item.rangeInfo) { return; }
var range = rangeFromInfo(item.rangeInfo);
if (!range || range.collapsed) { return; }
try {
var mark = document.createElement('mark');
var style = item.style || 'highlight';
mark.className = 'ss-reader-highlight ss-reader-highlight-' + style;
mark.style.color = 'inherit';
if (style === 'underline') {
mark.style.background = 'transparent';
mark.style.textDecorationLine = 'underline';
mark.style.textDecorationStyle = 'solid';
mark.style.textDecorationThickness = '2px';
mark.style.textDecorationColor = item.color || '#F8E16C';
} else {
mark.style.background = item.color || '#F8E16C';
}
mark.dataset.highlightId = item.id || '';
var fragment = range.extractContents();
mark.appendChild(fragment);
range.insertNode(mark);
if (mark.parentNode && mark.parentNode.normalize) {
mark.parentNode.normalize();
}
} catch (error) {
}
});
}
function selectionPayload() {
var selection = window.getSelection();
if (!selection || selection.rangeCount === 0 || selection.isCollapsed) {
return null;
}
var range = selection.getRangeAt(0);
var text = 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 {
text: text,
rangeInfo: serializeRange(range),
progression: progression,
lastProgression: lastProgression
};
}
var selectionTimer = null;
document.addEventListener('selectionchange', function() {
clearTimeout(selectionTimer);
selectionTimer = setTimeout(function() {
var payload = selectionPayload();
window.webkit.messageHandlers.{{SELECTION_CHANGED_MESSAGE}}.postMessage(payload);
}, 120);
});
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 });
});
}
document.addEventListener('click', function(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 });
}, true);
window.RDReaderBridge = {
applyPagination: function(styleText) {
if (isFixedLayoutDocument()) { return; }
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();
},
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) {
if (location && location.fragment) {
var target = document.getElementById(location.fragment) || document.querySelector('[name="' + location.fragment.replace(/"/g, '\\"') + '"]');
if (target) {
setVisiblePageIndex(pageIndexForElement(target));
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: unwrapHighlights,
reportProgression: reportProgression,
selectionPayload: selectionPayload
};
})();