public paste
gZRAXhL5P
—19 views162,468 chars162.4 KB
stdout
Paste content
// ==UserScript==
// @name 菠萝猫原生阅读自动接续(Via)
// @namespace local.boluomao.nativepager
// @version 1.8.0
// @description 自动阅读与接续、原位目录、历史进度、手机备份恢复、连读内存控制与广告防护。
// @downloadURL https://paste.brianrianrehan.com/raw/gZRAXhL5P
// @match https://www.boluomao1.com/book/*
// @match https://www.boluomao1.com/read/*
// @match https://www.rrssk.com/*
// @run-at document-start
// @grant none
// @noframes
// ==/UserScript==
/* 不依赖 GM_*;原生阅读未响应时显示兼容阅读层,不自动处理验证码。
* 与 Via 广告规则配合使用;请停用旧版“菠萝猫纯净连读”脚本。
*/
(function () {
'use strict';
if (window.top !== window) return;
if (location.hostname === 'www.rrssk.com') {
installSearchResultLinks();
return;
}
if (location.hostname !== 'www.boluomao1.com' ||
!/^\/(?:book|read)\//.test(location.pathname) || window.__bmNativePagerInstalled) return;
window.__bmNativePagerInstalled = true;
var origin = location.origin;
var searchEntry = new URL('https://www.rrssk.com/boluomao.html');
var searchNavigationUntil = 0;
var match = location.pathname.match(/^\/read\/(\d+)\/\d+\.html$/);
var bookId = match && match[1];
var state = { source: null, first: null, next: null, pages: [], seen: new Set(),
reader: null, content: null, status: null, paper: null, toolbar: null, toolbarHandle: null,
tocPanel: null, tocList: null, tocPages: null, tocStatus: null, tocOpen: false,
tocPage: 1, tocMax: 1, tocSeq: 0, tocCache: new Map(), tocController: null,
progressPanel: null, progressList: null, progressStatus: null, progressOpen: false, progressSeq: 0,
progressTimer: null, progressTouched: false, savedProgress: null, pendingResume: null,
progressHistory: { furthest: null, recent: [] }, chapterOrders: new Map(), chapterTitles: new Map(), forwardLinks: new Map(),
busy: false, blocked: false, readSeq: 0, autoEntryTimer: null, autoEntryDone: false,
compactedPages: 0, compacting: false, backupDownloadAnchor: null, backupDownloadUrl: null,
error: '', lastRequest: 0, timer: null };
function installSearchResultLinks() {
if (window.__bmSearchLinksInstalled) return;
window.__bmSearchLinksInstalled = true;
window.addEventListener('click', function (event) {
if (!event.target || !event.target.closest) return;
var card = event.target.closest('.book-card');
if (!card) return;
var raw = '';
var handler = card.getAttribute('onclick') || '';
var opened = handler.match(/^\s*window\.open\(\s*(['"])([^'"]*)\1\s*(?:,\s*(['"])_blank\3\s*)?\)\s*;?\s*$/);
if (handler && !opened) return;
if (opened) raw = opened[2];
else {
var link = event.target.closest('a[href]') || card.querySelector('a[href]');
if (link) raw = link.getAttribute('href');
}
var searchTarget = currentSearchBook(raw);
if (!searchTarget) return;
event.preventDefault(); event.stopImmediatePropagation();
location.assign(searchTarget.href);
}, true);
}
function currentSearchBook(value) {
try {
var wrapper = new URL(value, location.href);
var raw = value;
if (wrapper.origin === location.origin && wrapper.searchParams.get('action') === 'go') {
var encoded = wrapper.searchParams.get('t');
if (!encoded || encoded.length > 2048) return null;
raw = atob(encoded);
}
var logo = document.querySelector('.logo-text');
var boluomaoSource = !!logo && /^菠萝猫$/.test(logo.textContent.trim());
if (/^\/\d+\.html$/.test(raw)) {
if (!boluomaoSource) return null;
raw = 'https://www.boluomao1.com/book' + raw;
} else if (/^\/(?:book|read)\//.test(raw)) {
if (!boluomaoSource) return null;
raw = 'https://www.boluomao1.com' + raw;
}
var target = new URL(raw);
if (target.protocol !== 'https:' || target.port || target.username || target.password ||
!/^(?:www\.)?boluomao(?:1)?\.com$/.test(target.hostname) ||
!/^\/(?:book\/\d+|read\/\d+\/\d+)\.html$/.test(target.pathname)) return null;
var valid = true;
target.searchParams.forEach(function (item, key) {
if (!/^(?:p|cp)$/.test(key) || !/^[1-9]\d*$/.test(item)) valid = false;
});
if (!valid) return null;
target.hostname = 'www.boluomao1.com';
return target;
} catch (_) { return null; }
}
function intentionalSearchNavigation(value) {
try {
var target = new URL(value);
return Date.now() <= searchNavigationUntil && target.href === searchEntry.href;
} catch (_) { return false; }
}
// 图片在打包时压缩并直接内置。脚本导入手机后,切换背景无需网络或外部文件。
var paperImages = {
amber: 'data:image/jpeg;base64,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',
sage: 'data:image/jpeg;base64,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'
};
var themes = [
{ id: 'ivory', label: '米白', bg: '#faf6eb', text: '#333333', bar: 'rgba(250,246,235,.96)' },
{ id: 'amber', label: '仿 Alook 暖纸', bg: '#e0c5a2', text: '#333333', bar: 'rgba(224,197,162,.96)',
image: paperImages.amber, tint: 'rgba(221,196,165,.8)' },
{ id: 'sage', label: '浅绿纸', bg: '#d1efd7', text: '#2e3931', bar: 'rgba(209,239,215,.96)',
image: paperImages.sage, tint: 'rgba(209,239,215,.45)' },
{ id: 'night', label: '夜间', bg: '#141517', text: '#dededc', bar: 'rgba(20,21,23,.96)' }
];
var themeKey = 'bm-reader-theme-v1';
var currentTheme = 'amber';
try { currentTheme = localStorage.getItem(themeKey) || 'amber'; } catch (_) {}
if (!themes.some(function (theme) { return theme.id === currentTheme; })) currentTheme = 'amber';
var toolbarKey = 'bm-reader-toolbar-collapsed-v1';
var toolbarCollapsed = false;
try { toolbarCollapsed = localStorage.getItem(toolbarKey) === '1'; } catch (_) {}
var tocPageKey = 'bm-reader-catalog-page-' + bookId;
var progressKey = 'bm-reader-progress-' + bookId;
var historyKey = 'bm-reader-progress-history-' + bookId + '-v1';
var chapterOrderKey = 'bm-reader-chapter-order-' + bookId + '-v1';
var chapterTitleKey = 'bm-reader-chapter-title-' + bookId + '-v1';
var pendingKey = 'bm-reader-pending-resume-' + bookId;
try {
var savedCp = Number(localStorage.getItem(tocPageKey));
if (savedCp >= 1 && savedCp <= 100 && savedCp % 1 === 0) state.tocPage = savedCp;
} catch (_) {}
if (bookId) {
try {
var storedOrders = JSON.parse(localStorage.getItem(chapterOrderKey) || '[]');
if (Array.isArray(storedOrders)) storedOrders.forEach(function (item) {
if (Array.isArray(item) && /^\/read\/\d+\/\d+\.html$/.test(item[0]) &&
Number.isInteger(item[1]) && item[1] > 0 && item[1] <= 100000) {
state.chapterOrders.set(item[0], item[1]);
}
});
} catch (_) {}
try {
var storedTitles = JSON.parse(localStorage.getItem(chapterTitleKey) || '[]');
if (Array.isArray(storedTitles)) storedTitles.forEach(function (item) {
if (Array.isArray(item) && typeof item[0] === 'string' && typeof item[1] === 'string') {
state.chapterTitles.set(item[0], cleanChapterTitle(item[1]));
}
});
} catch (_) {}
}
try { state.savedProgress = JSON.parse(localStorage.getItem(progressKey) || 'null'); } catch (_) {}
if (bookId) loadProgressHistory();
try { state.pendingResume = JSON.parse(sessionStorage.getItem(pendingKey) || 'null'); } catch (_) {}
var fontKey = 'bm-reader-font-size-v1';
var fontSize = 22;
var fontTouch = { button: null, at: 0 };
try {
var savedFont = Number(localStorage.getItem(fontKey));
if (savedFont >= 16 && savedFont <= 34 && savedFont % 1 === 0) fontSize = savedFont;
} catch (_) {}
function applyFontSize(size) {
var anchor = captureFontAnchor();
fontSize = Math.max(16, Math.min(34, size));
try { localStorage.setItem(fontKey, String(fontSize)); } catch (_) {}
if (state.content) state.content.style.setProperty('font-size', fontSize + 'px', 'important');
restoreFontAnchor(anchor);
if (!state.toolbar) return;
var value = state.toolbar.querySelector('[data-bm-font-value]');
if (value) {
value.textContent = String(fontSize);
value.setAttribute('aria-label', '当前字号 ' + fontSize + ' 像素');
}
var buttons = state.toolbar.querySelectorAll('[data-bm-font-step]');
for (var i = 0; i < buttons.length; i++) {
var step = Number(buttons[i].getAttribute('data-bm-font-step'));
buttons[i].disabled = step < 0 ? fontSize <= 16 : fontSize >= 34;
buttons[i].style.setProperty('opacity', buttons[i].disabled ? '.4' : '1', 'important');
}
}
function captureFontAnchor() {
if (!activeReader() || !state.reader.scrollTop) return null;
var anchorY = state.reader.getBoundingClientRect().top + Math.min(120, state.reader.clientHeight * .22);
var paragraphs = state.content.querySelectorAll('p[data-bm-progress-index]');
for (var i = 0; i < paragraphs.length; i++) {
var rect = paragraphs[i].getBoundingClientRect();
if (!(rect.height > 0) || rect.bottom <= anchorY) continue;
return { node: paragraphs[i], y: anchorY,
ratio: Math.max(0, Math.min(1, (anchorY - rect.top) / rect.height)) };
}
return null;
}
function restoreFontAnchor(anchor) {
if (!anchor || !anchor.node.isConnected || !activeReader()) return;
var rect = anchor.node.getBoundingClientRect();
if (!(rect.height > 0)) return;
var delta = rect.top + rect.height * anchor.ratio - anchor.y;
if (Number.isFinite(delta)) state.reader.scrollTop += delta;
}
function setToolbarCollapsed(collapsed) {
var previous = toolbarCollapsed;
toolbarCollapsed = !!collapsed;
try { localStorage.setItem(toolbarKey, toolbarCollapsed ? '1' : '0'); } catch (_) {}
if (!state.toolbar || !state.toolbarHandle) return;
var top = state.reader ? state.reader.scrollTop : 0;
var height = state.toolbar.getBoundingClientRect().height || state.toolbar.offsetHeight || 48;
state.toolbar.hidden = toolbarCollapsed;
state.toolbar.style.setProperty('display', toolbarCollapsed ? 'none' : 'flex', 'important');
state.toolbarHandle.hidden = !toolbarCollapsed;
state.toolbarHandle.style.setProperty('display', toolbarCollapsed ? 'block' : 'none', 'important');
if (state.reader && previous !== toolbarCollapsed) {
if (toolbarCollapsed) state.reader.scrollTop = Math.max(0, top - height);
else if (top > 0) state.reader.scrollTop = top +
(state.toolbar.getBoundingClientRect().height || state.toolbar.offsetHeight || height);
}
}
function themeFor(id) {
for (var i = 0; i < themes.length; i++) if (themes[i].id === id) return themes[i];
return themes[1];
}
function themeBackground(theme) {
if (!theme.image) return 'none';
var image = 'url("' + theme.image + '")';
return theme.tint ? 'linear-gradient(' + theme.tint + ',' + theme.tint + '),' + image : image;
}
function applyTheme(id) {
var theme = themeFor(id);
currentTheme = theme.id;
try { localStorage.setItem(themeKey, currentTheme); } catch (_) {}
if (!state.reader || !state.paper) return;
state.reader.setAttribute('data-bm-reader-theme', theme.id);
state.reader.style.setProperty('background-color', theme.bg, 'important');
state.reader.style.setProperty('color', theme.text, 'important');
state.paper.style.setProperty('background-color', theme.bg, 'important');
state.paper.style.setProperty('background-image', themeBackground(theme), 'important');
state.toolbar.style.setProperty('background-color', theme.bar, 'important');
var buttons = state.toolbar.querySelectorAll('[data-bm-theme-choice]');
for (var i = 0; i < buttons.length; i++) {
var selected = buttons[i].getAttribute('data-bm-theme-choice') === theme.id;
buttons[i].setAttribute('aria-pressed', selected ? 'true' : 'false');
buttons[i].style.setProperty('border-color', selected ? (theme.id === 'night' ? '#eee' : '#48423a') : '#aaa', 'important');
buttons[i].style.setProperty('box-shadow', selected ? '0 0 0 2px ' + theme.bar : 'none', 'important');
}
var fontButtons = state.toolbar.querySelectorAll('[data-bm-font-step], [data-bm-toc-open], [data-bm-resume]');
for (var j = 0; j < fontButtons.length; j++) {
fontButtons[j].style.setProperty('color', theme.text, 'important');
fontButtons[j].style.setProperty('border-color', theme.text, 'important');
fontButtons[j].style.setProperty('background-color',
theme.id === 'night' ? 'rgba(255,255,255,.1)' : 'rgba(255,255,255,.22)', 'important');
}
var fontValue = state.toolbar.querySelector('[data-bm-font-value]');
if (fontValue) fontValue.style.setProperty('color', theme.text, 'important');
var collapse = state.toolbar.querySelector('[data-bm-toolbar-collapse]');
if (collapse) collapse.style.setProperty('color', theme.text, 'important');
if (state.toolbarHandle) {
state.toolbarHandle.style.setProperty('background-color', theme.bar, 'important');
state.toolbarHandle.style.setProperty('color', theme.text, 'important');
}
[state.tocPanel, state.progressPanel].forEach(function (panel) {
if (panel) {
panel.style.setProperty('background-color', theme.bg, 'important');
panel.style.setProperty('background-image', themeBackground(theme), 'important');
panel.style.setProperty('color', theme.text, 'important');
var panelHeader = panel.querySelector('[data-bm-toc-header], [data-bm-progress-header]');
if (panelHeader) panelHeader.style.setProperty('background-color', theme.bar, 'important');
var panelButtons = panel.querySelectorAll('button');
for (var k = 0; k < panelButtons.length; k++) {
panelButtons[k].style.setProperty('color', theme.text, 'important');
}
}
});
}
function installReaderStyle() {
if (document.getElementById('bm-reader-theme-style')) return;
var style = document.createElement('style');
style.id = 'bm-reader-theme-style';
style.textContent = '.via-reader-body[data-bm-auto-reader]{position:fixed;top:0;right:0;bottom:0;left:0;' +
'overflow:auto;z-index:2147483647!important;overscroll-behavior:contain}' +
'.via-reader-body[data-bm-auto-reader] .via-reader-header{margin:0 16px 24px;text-align:left}' +
'.via-reader-body[data-bm-auto-reader] .via-reader-header h1{padding:24px 0 0;margin:0;font-size:1.4em;line-height:1.6}' +
'.via-reader-body[data-bm-auto-reader] .via-reader-content{margin:10px 16px 0;overflow-wrap:break-word}' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content{' +
'text-align:left!important;color:inherit!important}' +
'.via-reader-body[data-bm-reader-theme] .via-reader-header,' +
'.via-reader-body[data-bm-reader-theme] .via-reader-header h1,' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content h1,' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content h2,' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content p{' +
'color:inherit!important}' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content p:not([style*="text-align"]){' +
'font-size:inherit!important;line-height:1.7!important;padding:0!important;' +
'margin:0 0 .45em!important;text-align:left!important;text-indent:2em!important;' +
'overflow-wrap:break-word}' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content h1,' +
'.via-reader-body[data-bm-reader-theme] .via-reader-content h2{' +
'line-height:1.5!important;text-indent:0!important;margin:1em 0 .6em!important}' +
'.via-reader-body[data-bm-reader-theme="night"] .via-reader-content a,' +
'.via-reader-body[data-bm-reader-theme="night"] .via-reader-header a{' +
'color:#9ec8ff!important}' +
'.via-reader-body [data-bm-toolbar-expand]:active,' +
'.via-reader-body [data-bm-toolbar-expand]:focus-visible{' +
'opacity:.72!important}';
(document.head || document.documentElement).appendChild(style);
}
function installThemeControls(reader) {
var frame = reader.firstElementChild;
if (!frame || !frame.querySelector('.via-reader-header')) return;
// 始终覆盖整个 WebView,避免旧自定义 CSS 给阅读浮层留下侧边空白。
reader.style.setProperty('position', 'fixed', 'important');
['top', 'right', 'bottom', 'left'].forEach(function (side) {
reader.style.setProperty(side, '0', 'important');
});
reader.style.setProperty('width', '100%', 'important');
// 释放前文和调整字号时由脚本保持可见段落,避免浏览器再次补偿滚动。
reader.style.setProperty('overflow-anchor', 'none', 'important');
var paper = document.createElement('div');
paper.setAttribute('data-bm-theme-paper', '');
paper.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:0;pointer-events:none;' +
'background-position:center center;background-size:cover;background-repeat:no-repeat;';
reader.insertBefore(paper, frame);
frame.style.setProperty('position', 'relative');
frame.style.setProperty('z-index', '1');
frame.style.setProperty('width', '100%', 'important');
var bar = document.createElement('div');
bar.setAttribute('data-bm-theme-bar', '');
bar.setAttribute('role', 'group');
bar.setAttribute('aria-label', '阅读背景与字号');
bar.style.cssText = 'position:sticky;top:0;z-index:2;display:flex;flex-wrap:nowrap;gap:3px;' +
'align-items:center;box-sizing:border-box;width:100%;' +
'padding:4px;min-height:40px;overflow:hidden;' +
'border-bottom:1px solid rgba(90,80,70,.18);';
var palette = document.createElement('div');
palette.setAttribute('data-bm-theme-palette', '');
palette.style.cssText = 'display:flex;flex:1 1 auto;min-width:113px;gap:3px;' +
'align-items:center;overflow-x:auto;white-space:nowrap;';
themes.forEach(function (theme) {
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('data-bm-theme-choice', theme.id);
button.setAttribute('aria-label', theme.label);
button.title = theme.label;
button.style.cssText = 'flex:0 0 26px;width:26px;height:26px;box-sizing:border-box;' +
'padding:0;border:2px solid #aaa;border-radius:5px;cursor:pointer;' +
'background-position:center;background-size:cover;background-repeat:no-repeat;';
button.style.setProperty('background-color', theme.bg, 'important');
button.style.setProperty('background-image', themeBackground(theme), 'important');
palette.appendChild(button);
});
bar.appendChild(palette);
var tocButton = document.createElement('button');
tocButton.type = 'button';
tocButton.setAttribute('data-bm-toc-open', '');
tocButton.setAttribute('aria-label', '打开章节目录');
tocButton.textContent = '目录';
tocButton.style.cssText = 'flex:0 0 34px;width:34px;height:32px;box-sizing:border-box;' +
'padding:0;border:1px solid currentColor;border-radius:5px;' +
'font-size:13px;font-weight:600;cursor:pointer;';
bar.appendChild(tocButton);
var resumeButton = document.createElement('button');
resumeButton.type = 'button';
resumeButton.setAttribute('data-bm-resume', '');
resumeButton.setAttribute('aria-label', '查看阅读进度');
resumeButton.title = '查看阅读进度';
resumeButton.textContent = '进度';
resumeButton.style.cssText = 'flex:0 0 34px;width:34px;height:32px;box-sizing:border-box;' +
'padding:0;border:1px solid currentColor;border-radius:5px;' +
'font-size:13px;font-weight:600;cursor:pointer;';
bar.appendChild(resumeButton);
var fontControls = document.createElement('div');
fontControls.setAttribute('data-bm-font-controls', '');
fontControls.style.cssText = 'display:flex;flex:0 0 auto;gap:3px;align-items:center;margin-left:auto;';
[
{ step: -1, text: 'A−', label: '缩小字号' },
{ step: 1, text: 'A+', label: '放大字号' }
].forEach(function (item, index) {
if (index) {
var value = document.createElement('span');
value.setAttribute('data-bm-font-value', '');
value.setAttribute('role', 'status');
value.setAttribute('aria-live', 'polite');
value.style.cssText = 'flex:0 0 18px;width:18px;text-align:center;' +
'font-size:11px;font-weight:600;line-height:1;';
fontControls.appendChild(value);
}
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('data-bm-font-step', String(item.step));
button.setAttribute('aria-label', item.label);
button.title = item.label;
button.textContent = item.text;
button.style.cssText = 'flex:0 0 30px;width:30px;height:32px;box-sizing:border-box;' +
'padding:0;border:1px solid currentColor;border-radius:5px;' +
'font-size:17px;font-weight:700;line-height:1;cursor:pointer;';
fontControls.appendChild(button);
});
var collapse = document.createElement('button');
collapse.type = 'button';
collapse.setAttribute('data-bm-toolbar-collapse', '');
collapse.setAttribute('aria-label', '收起阅读工具栏');
collapse.textContent = '⌃';
collapse.style.cssText = 'flex:0 0 24px;width:24px;height:32px;box-sizing:border-box;' +
'padding:0;border:0;background:none;font-size:24px;line-height:1;cursor:pointer;';
bar.appendChild(fontControls);
bar.appendChild(collapse);
frame.insertBefore(bar, frame.firstChild);
var handle = document.createElement('button');
handle.type = 'button';
handle.setAttribute('data-bm-toolbar-expand', '');
handle.setAttribute('aria-label', '展开阅读工具栏');
handle.textContent = '☰';
handle.style.cssText = 'position:fixed;top:6px;right:6px;z-index:5;opacity:.28;' +
'width:34px;height:34px;padding:0;border:1px solid currentColor;' +
'border-radius:7px;font-size:18px;line-height:1;cursor:pointer;';
reader.appendChild(handle);
state.paper = paper; state.toolbar = bar; state.toolbarHandle = handle;
installCataloguePanel(reader);
installProgressPanel(reader);
applyTheme(currentTheme);
applyFontSize(fontSize);
updateResumeButton();
setToolbarCollapsed(toolbarCollapsed);
}
function installCataloguePanel(reader) {
var panel = document.createElement('div');
panel.setAttribute('data-bm-toc-panel', '');
panel.setAttribute('role', 'dialog');
panel.setAttribute('aria-label', '章节目录');
panel.hidden = true;
panel.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:6;' +
'box-sizing:border-box;width:100%;overflow-y:auto;overscroll-behavior:contain;' +
'background-position:center;background-size:cover;background-repeat:no-repeat;';
panel.style.setProperty('display', 'none', 'important');
var header = document.createElement('div');
header.setAttribute('data-bm-toc-header', '');
header.style.cssText = 'position:sticky;top:0;z-index:1;display:flex;' +
'align-items:center;justify-content:space-between;box-sizing:border-box;' +
'min-height:52px;padding:8px 14px;border-bottom:1px solid rgba(80,70,60,.2);';
var title = document.createElement('strong');
title.textContent = '章节目录';
title.style.cssText = 'font-size:18px;line-height:1.4;';
var close = document.createElement('button');
close.type = 'button';
close.setAttribute('data-bm-toc-close', '');
close.setAttribute('aria-label', '返回阅读');
close.textContent = '返回阅读 ×';
close.style.cssText = 'padding:6px 8px;border:1px solid currentColor;' +
'border-radius:5px;background:none;font-size:14px;';
header.appendChild(title); header.appendChild(close); panel.appendChild(header);
var status = document.createElement('div');
status.setAttribute('data-bm-toc-status', '');
status.setAttribute('role', 'status');
status.style.cssText = 'padding:12px 16px;font-size:14px;line-height:1.5;';
status.hidden = true; panel.appendChild(status);
var pages = document.createElement('div');
pages.setAttribute('data-bm-toc-pages', '');
pages.setAttribute('aria-label', '目录分页');
pages.style.cssText = 'display:flex;gap:5px;overflow-x:auto;padding:10px 12px;' +
'box-sizing:border-box;white-space:nowrap;';
panel.appendChild(pages);
var list = document.createElement('div');
list.setAttribute('data-bm-toc-list', '');
list.style.cssText = 'padding:0 8px 24px;box-sizing:border-box;';
panel.appendChild(list); reader.appendChild(panel);
state.tocPanel = panel; state.tocStatus = status; state.tocPages = pages; state.tocList = list;
}
function installProgressPanel(reader) {
var panel = document.createElement('div');
panel.setAttribute('data-bm-progress-panel', '');
panel.setAttribute('role', 'dialog');
panel.setAttribute('aria-label', '阅读进度');
panel.hidden = true;
panel.style.cssText = 'position:fixed;top:0;right:0;bottom:0;left:0;z-index:6;' +
'box-sizing:border-box;width:100%;overflow-y:auto;overscroll-behavior:contain;' +
'background-position:center;background-size:cover;background-repeat:no-repeat;';
panel.style.setProperty('display', 'none', 'important');
var header = document.createElement('div');
header.setAttribute('data-bm-progress-header', '');
header.style.cssText = 'position:sticky;top:0;z-index:1;display:flex;' +
'align-items:center;justify-content:space-between;box-sizing:border-box;' +
'min-height:52px;padding:8px 14px;border-bottom:1px solid rgba(80,70,60,.2);';
var title = document.createElement('strong');
title.textContent = '阅读进度';
title.style.cssText = 'font-size:18px;line-height:1.4;';
var close = document.createElement('button');
close.type = 'button';
close.setAttribute('data-bm-progress-close', '');
close.setAttribute('aria-label', '返回阅读');
close.textContent = '返回阅读 ×';
close.style.cssText = 'padding:6px 8px;border:1px solid currentColor;' +
'border-radius:5px;background:none;font-size:14px;';
header.appendChild(title);
if (reader.hasAttribute('data-bm-auto-reader')) {
var exit = document.createElement('button');
exit.type = 'button'; exit.textContent = '退出阅读';
exit.setAttribute('data-bm-reader-exit', '');
exit.style.cssText = 'padding:6px 8px;border:1px solid currentColor;border-radius:5px;background:none;font-size:14px;';
header.appendChild(exit);
}
header.appendChild(close); panel.appendChild(header);
var status = document.createElement('div');
status.setAttribute('data-bm-progress-status', '');
status.setAttribute('role', 'status');
status.style.cssText = 'padding:12px 16px;font-size:14px;line-height:1.5;';
status.hidden = true; panel.appendChild(status);
installBackupControls(panel);
var memoryNote = document.createElement('div');
memoryNote.setAttribute('data-bm-memory-note', '');
memoryNote.textContent = '较早的正文已释放。要回看,可从进度或目录重新打开。';
memoryNote.style.cssText = 'padding:6px 16px;font-size:12px;line-height:1.5;opacity:.75;';
memoryNote.hidden = !state.compactedPages; panel.appendChild(memoryNote);
var list = document.createElement('div');
list.setAttribute('data-bm-progress-list', '');
list.style.cssText = 'padding:8px 12px 28px;box-sizing:border-box;';
panel.appendChild(list); reader.appendChild(panel);
state.progressPanel = panel; state.progressList = list; state.progressStatus = status;
}
// Backups contain owned reader settings and positions only, never chapter text or site credentials.
var backupMaxBytes = 5 * 1024 * 1024;
var backupFormat = 'boluomao-via-reader-backup';
function backupObject(value) {
return !!value && typeof value === 'object' && !Array.isArray(value);
}
function backupInteger(value, minimum, maximum) {
return typeof value === 'number' && Number.isInteger(value) && value >= minimum && value <= maximum;
}
function backupError(message) { throw new Error(message || '备份内容格式不正确,未修改现有进度。'); }
function backupReadUrl(value, id) {
if (typeof value !== 'string' || value.length > 500) return null;
try {
var url = new URL(value);
if (url.protocol !== 'https:' || url.origin !== origin || url.username || url.password || url.hash ||
!new RegExp('^/read/' + id + '/\\d+\\.html$').test(url.pathname)) return null;
var valid = true; var pages = 0;
url.searchParams.forEach(function (item, key) {
if (key !== 'p' || !/^[1-9]\d*$/.test(item) || Number(item) > 10000 || ++pages > 1) valid = false;
});
if (!valid) return null;
if (url.searchParams.get('p') === '1') url.searchParams.delete('p');
return url;
} catch (_) { return null; }
}
function backupPosition(value, id, orders, titles) {
if (value === null || typeof value === 'undefined') return null;
if (!backupObject(value)) backupError();
var url = backupReadUrl(value.url, id);
if (!url || !backupInteger(value.index, 0, 100000) || typeof value.ratio !== 'number' ||
!Number.isFinite(value.ratio) || value.ratio < 0 || value.ratio > 1 ||
typeof value.time !== 'number' || !Number.isFinite(value.time) || value.time < 0 ||
value.time > 8640000000000000 ||
(typeof value.order !== 'undefined' && !backupInteger(value.order, 0, 100000)) ||
(typeof value.title !== 'undefined' && (typeof value.title !== 'string' || value.title.length > 120))) backupError();
return { url: url.href, index: value.index, ratio: value.ratio, time: value.time,
order: orders.get(url.pathname) || value.order || 0,
title: titles.get(url.pathname) || cleanChapterTitle(value.title || '').slice(0, 120) };
}
function backupMap(value, id, kind) {
if (!Array.isArray(value) || value.length > 100000) backupError();
var map = new Map();
value.forEach(function (entry) {
if (!Array.isArray(entry) || entry.length !== 2 || typeof entry[0] !== 'string' ||
!new RegExp('^/read/' + id + '/\\d+\\.html$').test(entry[0]) || map.has(entry[0])) backupError();
if (kind === 'orders') {
if (!backupInteger(entry[1], 1, 100000)) backupError();
map.set(entry[0], entry[1]);
} else {
if (typeof entry[1] !== 'string' || entry[1].length > 120) backupError();
map.set(entry[0], cleanChapterTitle(entry[1]));
}
});
return map;
}
function validateBackupBook(value) {
if (!backupObject(value) || typeof value.id !== 'string' || !/^\d{1,18}$/.test(value.id) ||
!backupObject(value.history) || !Array.isArray(value.history.recent) || value.history.recent.length > 1000 ||
!backupInteger(value.catalogPage, 1, 100)) backupError();
var orders = backupMap(value.chapterOrders, value.id, 'orders');
var titles = backupMap(value.chapterTitles, value.id, 'titles');
return { id: value.id, latest: backupPosition(value.latest, value.id, orders, titles),
history: { furthest: backupPosition(value.history.furthest, value.id, orders, titles),
recent: value.history.recent.map(function (entry) {
var position = backupPosition(entry, value.id, orders, titles);
if (!position) backupError();
return position;
}) },
chapterOrders: Array.from(orders), chapterTitles: Array.from(titles), catalogPage: value.catalogPage };
}
function validateBackup(value) {
if (!backupObject(value) || value.format !== backupFormat || value.schemaVersion !== 1 ||
!backupObject(value.settings) || !Array.isArray(value.books) || value.books.length > 500 ||
!themes.some(function (theme) { return theme.id === value.settings.theme; }) ||
!backupInteger(value.settings.fontSize, 16, 34) || typeof value.settings.toolbarCollapsed !== 'boolean') {
backupError('这不是支持的阅读备份,或备份版本不兼容;现有进度未修改。');
}
var ids = new Set();
var books = value.books.map(function (book) {
var checked = validateBackupBook(book);
if (ids.has(checked.id)) backupError();
ids.add(checked.id); return checked;
});
return { format: backupFormat, schemaVersion: 1, scriptVersion: '1.8.0',
exportedAt: typeof value.exportedAt === 'string' && value.exportedAt.length <= 40 ? value.exportedAt : '',
settings: { theme: value.settings.theme, fontSize: value.settings.fontSize,
toolbarCollapsed: value.settings.toolbarCollapsed }, books: books };
}
function backupStoredJson(key, fallback) {
var text = localStorage.getItem(key);
if (text === null) return fallback;
try { return JSON.parse(text); }
catch (_) { backupError('已有阅读记录损坏,备份操作已停止;现有记录未修改。'); }
}
function collectBackupBook(id) {
var prefix = 'bm-reader-';
var latest = backupStoredJson(prefix + 'progress-' + id, null);
var history = backupStoredJson(prefix + 'progress-history-' + id + '-v1', { furthest: null, recent: [] });
var orders = backupStoredJson(prefix + 'chapter-order-' + id + '-v1', []);
var titles = backupStoredJson(prefix + 'chapter-title-' + id + '-v1', []);
var pageText = localStorage.getItem(prefix + 'catalog-page-' + id);
var page = pageText === null ? 1 : Number(pageText);
return validateBackupBook({ id: id, latest: latest, history: history,
chapterOrders: orders, chapterTitles: titles, catalogPage: page });
}
function collectBackup() {
var ids = new Set();
for (var i = 0; i < localStorage.length; i++) {
var key = localStorage.key(i); var found;
if ((found = /^bm-reader-progress-(\d{1,18})$/.exec(key)) ||
(found = /^bm-reader-progress-history-(\d{1,18})-v1$/.exec(key)) ||
(found = /^bm-reader-chapter-(?:order|title)-(\d{1,18})-v1$/.exec(key)) ||
(found = /^bm-reader-catalog-page-(\d{1,18})$/.exec(key))) ids.add(found[1]);
}
if (ids.size > 500) backupError('书籍记录超过备份上限,未生成文件。');
return validateBackup({ format: backupFormat, schemaVersion: 1, scriptVersion: '1.8.0',
exportedAt: new Date().toISOString(), settings: { theme: currentTheme, fontSize: fontSize,
toolbarCollapsed: toolbarCollapsed }, books: Array.from(ids).sort().map(collectBackupBook) });
}
function backupWithin(first, second, id) {
var a = backupReadUrl(first.url, id); var b = backupReadUrl(second.url, id);
if (a.pathname !== b.pathname) return null;
return progressPage(a) - progressPage(b) || first.index - second.index || first.ratio - second.ratio;
}
function mergeBackupBook(local, incoming) {
var id = incoming.id; var orders = new Map(incoming.chapterOrders); var titles = new Map(incoming.chapterTitles);
local.chapterOrders.forEach(function (entry) { orders.set(entry[0], entry[1]); });
local.chapterTitles.forEach(function (entry) { if (entry[1]) titles.set(entry[0], entry[1]); });
function normalize(entry) { return backupPosition(entry, id, orders, titles); }
var latest = !local.latest ? incoming.latest : !incoming.latest || local.latest.time >= incoming.latest.time ? local.latest : incoming.latest;
var recent = new Map();
[local.history.furthest, incoming.history.furthest, local.latest, incoming.latest]
.concat(local.history.recent, incoming.history.recent).forEach(function (entry) {
if (!entry) return;
var position = normalize(entry); var path = backupReadUrl(position.url, id).pathname; var old = recent.get(path);
if (!old) { recent.set(path, position); return; }
var winner = backupWithin(position, old, id) > 0 ? position : old;
winner.time = Math.max(position.time, old.time); recent.set(path, winner);
});
var furthest = normalize(local.history.furthest || local.latest || incoming.history.furthest || incoming.latest);
recent.forEach(function (entry) {
if (!furthest) { furthest = entry; return; }
var within = backupWithin(entry, furthest, id);
if ((within !== null && within > 0) || (within === null && entry.order && furthest.order && entry.order > furthest.order)) {
furthest = entry;
}
});
return { id: id, latest: normalize(latest), history: { furthest: furthest,
recent: Array.from(recent.values()).sort(function (a, b) { return b.time - a.time; }).slice(0, 1000) },
chapterOrders: Array.from(orders), chapterTitles: Array.from(titles), catalogPage: local.catalogPage || incoming.catalogPage };
}
function backupWrites(book, writes) {
var id = book.id;
writes.set('bm-reader-progress-' + id, JSON.stringify(book.latest));
writes.set('bm-reader-progress-history-' + id + '-v1', JSON.stringify(book.history));
writes.set('bm-reader-chapter-order-' + id + '-v1', JSON.stringify(book.chapterOrders));
writes.set('bm-reader-chapter-title-' + id + '-v1', JSON.stringify(book.chapterTitles));
writes.set('bm-reader-catalog-page-' + id, String(book.catalogPage));
}
function applyBackupData(data) {
var checked = validateBackup(data); var writes = new Map(); var merged = [];
checked.books.forEach(function (incoming) {
var local = collectBackupBook(incoming.id);
var result = mergeBackupBook(local, incoming);
if (localStorage.getItem('bm-reader-catalog-page-' + incoming.id) === null) result.catalogPage = incoming.catalogPage;
backupWrites(result, writes); merged.push(result);
});
writes.set(themeKey, checked.settings.theme); writes.set(fontKey, String(checked.settings.fontSize));
writes.set(toolbarKey, checked.settings.toolbarCollapsed ? '1' : '0');
var before = new Map();
writes.forEach(function (_, key) { before.set(key, localStorage.getItem(key)); });
var applied = [];
try {
writes.forEach(function (value, key) { localStorage.setItem(key, value); applied.push(key); });
} catch (_) {
var rollbackFailed = false;
applied.reverse().forEach(function (key) {
try {
var old = before.get(key);
if (old === null) localStorage.removeItem(key); else localStorage.setItem(key, old);
} catch (ignored) { rollbackFailed = true; }
});
backupError(rollbackFailed ? '存储空间不足,部分记录无法回滚;请保留备份文件并释放浏览器存储后重试。' :
'存储空间不足,恢复已撤销;现有记录未修改。');
}
for (var i = 0; i < merged.length; i++) {
if (merged[i].id !== bookId) continue;
state.chapterOrders = new Map(merged[i].chapterOrders); state.chapterTitles = new Map(merged[i].chapterTitles);
state.savedProgress = merged[i].latest; state.progressHistory = merged[i].history; state.tocPage = merged[i].catalogPage;
state.progressTouched = false;
if (state.progressTimer) { clearTimeout(state.progressTimer); state.progressTimer = null; }
break;
}
applyTheme(checked.settings.theme); applyFontSize(checked.settings.fontSize);
setToolbarCollapsed(checked.settings.toolbarCollapsed); updateResumeButton(); renderProgressHistory();
setBackupStatus('已合并 ' + checked.books.length + ' 本书的进度,背景、字号和工具栏设置已恢复。');
return checked;
}
function setBackupStatus(message) {
if (state.backupStatus) state.backupStatus.textContent = message || '';
}
function toggleBackupText(force) {
if (!state.backupTextPanel) return;
var show = typeof force === 'boolean' ? force : state.backupTextPanel.hidden;
state.backupTextPanel.hidden = !show;
state.backupTextPanel.style.setProperty('display', show ? 'block' : 'none', 'important');
}
function exportBackup() {
try {
if (state.progressTouched) saveProgressNow(true);
var data = collectBackup(); var text = JSON.stringify(data, null, 2);
if (text.length > backupMaxBytes || new Blob([text]).size > backupMaxBytes) backupError('备份超过 5 MB 上限,未生成文件。');
if (state.backupText) state.backupText.value = text;
if (!window.URL || !URL.createObjectURL) {
toggleBackupText(true); setBackupStatus('此浏览器不支持文件下载,请复制下面的备份文本保存。'); return;
}
if (state.backupDownloadUrl) URL.revokeObjectURL(state.backupDownloadUrl);
if (state.backupDownloadAnchor) state.backupDownloadAnchor.remove();
var url = URL.createObjectURL(new Blob([text], { type: 'application/json;charset=utf-8' }));
var anchor = document.createElement('a'); anchor.href = url;
anchor.download = '菠萝猫阅读备份-' + new Date().toISOString().slice(0, 10) + '.json';
anchor.style.display = 'none';
state.backupDownloadAnchor = anchor; state.backupDownloadUrl = url;
document.body.appendChild(anchor); anchor.click();
setBackupStatus('已生成 ' + data.books.length + ' 本书的备份。若手机未下载文件,点“备份文本”复制保存。');
setTimeout(function () {
if (state.backupDownloadAnchor === anchor) { state.backupDownloadAnchor = null; state.backupDownloadUrl = null; }
anchor.remove(); URL.revokeObjectURL(url);
}, 60000);
} catch (error) { setBackupStatus(error.message || '备份生成失败。'); }
}
function restoreBackupText() {
try {
var text = state.backupText ? state.backupText.value.trim() : '';
if (!text || text.length > backupMaxBytes || new Blob([text]).size > backupMaxBytes) backupError('请选择或粘贴不超过 5 MB 的阅读备份。');
var data;
try { data = JSON.parse(text); } catch (_) { backupError('备份不是有效 JSON;现有进度未修改。'); }
applyBackupData(data);
} catch (error) { setBackupStatus(error.message || '备份恢复失败,现有记录未修改。'); }
}
function importBackupFile(file) {
if (!file) return;
if (!backupInteger(file.size, 1, backupMaxBytes)) { setBackupStatus('请选择不超过 5 MB 的阅读备份文件。'); return; }
var reader = new FileReader(); setBackupStatus('正在读取备份文件…');
reader.onload = function () {
if (state.backupText) state.backupText.value = String(reader.result || '');
restoreBackupText();
};
reader.onerror = function () { setBackupStatus('文件读取失败,请用“备份文本”粘贴恢复。'); };
try { reader.readAsText(file, 'UTF-8'); }
catch (_) { setBackupStatus('无法打开此文件,请用“备份文本”粘贴恢复。'); }
}
function copyBackupText() {
var text = state.backupText && state.backupText.value;
if (!text) { setBackupStatus('先点“导出备份”生成文本,或粘贴已有备份。'); return; }
function manualCopy() {
toggleBackupText(true); state.backupText.focus(); state.backupText.select();
var copied = false;
try { copied = document.execCommand('copy'); } catch (_) {}
setBackupStatus(copied ? '备份文本已复制,请保存到手机文件或笔记中。' : '请长按下面的文本,全选并复制保存。');
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function () {
setBackupStatus('备份文本已复制,请保存到手机文件或笔记中。');
}, manualCopy);
} else manualCopy();
}
function installBackupControls(panel) {
var area = document.createElement('div'); area.setAttribute('data-bm-backup-area', '');
area.style.cssText = 'padding:10px 12px 4px;box-sizing:border-box;font-size:14px;line-height:1.5;';
var row = document.createElement('div'); row.style.cssText = 'display:flex;flex-wrap:wrap;gap:8px;';
[['export', '导出备份'], ['import', '导入恢复'], ['toggle-text', '备份文本']].forEach(function (item) {
var button = document.createElement('button'); button.type = 'button';
button.setAttribute('data-bm-backup-' + item[0], ''); button.textContent = item[1];
button.style.cssText = 'padding:6px 10px;border:1px solid currentColor;border-radius:5px;background:none;font-size:14px;';
row.appendChild(button);
});
area.appendChild(row);
var status = document.createElement('div'); status.setAttribute('data-bm-backup-status', '');
status.setAttribute('role', 'status'); status.style.cssText = 'font-size:12px;line-height:1.5;padding-top:6px;';
status.textContent = '备份全部书的阅读进度和外观设置。'; area.appendChild(status); state.backupStatus = status;
var file = document.createElement('input'); file.type = 'file'; file.accept = '.json,application/json,text/plain';
file.setAttribute('data-bm-backup-file', ''); file.style.display = 'none';
file.addEventListener('change', function () { var selected = file.files && file.files[0]; file.value = ''; importBackupFile(selected); });
area.appendChild(file); state.backupFile = file;
var fallback = document.createElement('div'); fallback.setAttribute('data-bm-backup-text-panel', '');
fallback.hidden = true; fallback.style.setProperty('display', 'none', 'important');
var help = document.createElement('div'); help.textContent = '文件下载或选择失败时,可复制备份文本保存;以后粘贴到这里恢复。';
help.style.cssText = 'padding:8px 0;font-size:12px;'; fallback.appendChild(help);
var text = document.createElement('textarea'); text.setAttribute('data-bm-backup-text', '');
text.setAttribute('aria-label', '阅读备份 JSON 文本'); text.placeholder = '粘贴阅读备份 JSON'; text.spellcheck = false;
text.style.cssText = 'display:block;width:100%;height:120px;box-sizing:border-box;border:1px solid currentColor;border-radius:5px;' +
'padding:8px;background:none;color:inherit;font-size:12px;font-family:monospace;line-height:1.5;';
fallback.appendChild(text); state.backupText = text;
var actions = document.createElement('div'); actions.style.cssText = 'display:flex;gap:8px;padding-top:8px;';
[['copy', '复制文本'], ['restore-text', '从文本恢复']].forEach(function (item) {
var button = document.createElement('button'); button.type = 'button';
button.setAttribute('data-bm-backup-' + item[0], ''); button.textContent = item[1];
button.style.cssText = 'padding:6px 10px;border:1px solid currentColor;border-radius:5px;background:none;font-size:14px;';
actions.appendChild(button);
});
fallback.appendChild(actions); area.appendChild(fallback); state.backupTextPanel = fallback;
panel.insertBefore(area, panel.children[1] || null);
}
function setProgressStatus(message) {
if (!state.progressStatus) return;
state.progressStatus.textContent = message || '';
state.progressStatus.hidden = !message;
}
function closeProgressPanel() {
if (!state.progressPanel) return;
state.progressOpen = false;
state.progressSeq++;
stopCatalogueRequest();
state.progressPanel.hidden = true;
state.progressPanel.style.setProperty('display', 'none', 'important');
setProgressStatus('');
schedule();
}
function openProgressPanel() {
if (!state.progressPanel || state.progressOpen) return;
if (state.tocOpen) closeCatalogue();
if (state.progressTimer) { clearTimeout(state.progressTimer); state.progressTimer = null; }
if (state.progressTouched || !normalizeProgress(state.savedProgress)) saveProgressNow();
state.progressOpen = true;
if (state.timer) { clearTimeout(state.timer); state.timer = null; }
state.progressPanel.hidden = false;
state.progressPanel.style.setProperty('display', 'block', 'important');
setProgressStatus('');
renderProgressHistory();
}
function progressLabel(progress) {
return progress.title || '章节名称待补全';
}
function addProgressChoice(label, progress) {
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('data-bm-progress-url', progress.url);
button.setAttribute('data-bm-progress-index', String(progress.index));
button.setAttribute('data-bm-progress-ratio', String(progress.ratio));
button.style.cssText = 'display:block;width:100%;box-sizing:border-box;padding:12px 10px;' +
'border:0;border-bottom:1px solid rgba(90,80,70,.18);' +
'background:none;text-align:left;font-size:16px;line-height:1.5;';
var caption = document.createElement('strong');
caption.textContent = label;
caption.style.cssText = 'display:block;font-size:12px;opacity:.75;font-weight:600;';
var location = document.createElement('span');
location.setAttribute('data-bm-progress-title', '');
location.textContent = progressLabel(progress);
location.style.cssText = 'display:block;font-weight:600;';
var detail = document.createElement('span');
detail.setAttribute('data-bm-progress-detail', '');
var progressUrl = readUrl(progress.url);
detail.textContent = '第' + progressPage(progressUrl) + '页 · 第' + (progress.index + 1) + '段';
detail.style.cssText = 'display:block;font-size:13px;opacity:.75;';
button.appendChild(caption); button.appendChild(location);
button.appendChild(detail);
state.progressList.appendChild(button);
}
function renderProgressHistory() {
if (!state.progressList) return;
state.progressList.textContent = '';
var furthest = normalizeProgress(state.progressHistory.furthest);
if (!furthest && !state.progressHistory.recent.length) {
var empty = document.createElement('div');
empty.textContent = '还没有保存的阅读进度。';
empty.style.cssText = 'padding:16px 10px;font-size:14px;';
state.progressList.appendChild(empty);
return;
}
if (furthest) addProgressChoice('最远已读 · 固定在顶部', furthest);
var recent = state.progressHistory.recent.slice().sort(function (a, b) { return b.time - a.time; });
for (var i = 0; i < recent.length; i++) {
var entry = normalizeProgress(recent[i]);
if (!entry || (furthest && sameProgressPosition(entry, furthest))) continue;
addProgressChoice(!furthest ? '最近打开' : entry.order ? '其他位置' : '其他位置 · 顺序待确认', entry);
}
applyTheme(currentTheme);
}
function setCatalogueStatus(message) {
if (!state.tocStatus) return;
state.tocStatus.textContent = message || '';
state.tocStatus.hidden = !message;
}
function closeCatalogue() {
if (!state.tocPanel) return;
state.tocOpen = false;
state.tocSeq++;
stopCatalogueRequest();
state.tocPanel.hidden = true;
state.tocPanel.style.setProperty('display', 'none', 'important');
setCatalogueStatus('');
schedule();
}
function openCatalogue() {
if (!state.tocPanel || state.tocOpen) return;
if (state.progressTimer) { clearTimeout(state.progressTimer); state.progressTimer = null; }
if (state.progressTouched || !normalizeProgress(state.savedProgress)) saveProgressNow();
state.tocOpen = true;
if (state.timer) { clearTimeout(state.timer); state.timer = null; }
state.tocPanel.hidden = false;
state.tocPanel.style.setProperty('display', 'block', 'important');
loadCatalogue(state.tocPage);
}
function stopCatalogueRequest() {
if (state.tocController) {
state.tocController.abort();
state.tocController = null;
}
}
async function fetchInertPage(url, kind) {
var controller = typeof AbortController === 'function' ? new AbortController() : null;
state.tocController = controller;
var timeout;
try {
return await Promise.race([(async function () {
var response = await fetch(url.href, { credentials: 'same-origin', redirect: 'manual',
signal: controller ? controller.signal : undefined });
if (response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400)) {
throw new Error(kind + '需要登录或验证,当前阅读已保留');
}
if (!response.ok) throw new Error(kind + '加载失败(' + response.status + '),当前阅读已保留');
if (response.url) {
var finalUrl = siteUrl(response.url);
if (!finalUrl || finalUrl.pathname !== url.pathname || finalUrl.search !== url.search) {
throw new Error(kind + '响应地址异常,当前阅读已保留');
}
}
var template = document.createElement('template');
template.innerHTML = await response.text();
return template.content;
})(), new Promise(function (_, reject) {
timeout = setTimeout(function () {
if (controller) controller.abort();
reject(new Error(kind + '加载超时,当前阅读已保留'));
}, 20000);
})]);
} finally {
clearTimeout(timeout);
if (state.tocController === controller) state.tocController = null;
}
}
function parseCatalogue(doc, pageUrl, cp) {
var title = doc.querySelector('title');
if (doc.querySelector('input[name="GOEDGE_WAF_CAPTCHA_CODE"], #loginForm') ||
(title && /^(?:身份验证|安全验证|访问验证|人机验证|登录\s*[-—]|Just a moment)/i.test(title.textContent.trim()))) {
throw new Error('目录需要登录或验证,当前阅读已保留');
}
var chapterBox = doc.querySelector('#chapters .direList');
if (!chapterBox) throw new Error('未找到原站章节目录,当前阅读已保留');
var anchors = chapterBox.querySelectorAll('li .name > a[href]');
var chapters = [];
var seen = new Set();
for (var i = 0; i < anchors.length; i++) {
var url = readUrl(anchors[i].getAttribute('href'), pageUrl.href);
if (!url || url.search || seen.has(url.href)) continue;
var label = anchors[i].textContent.trim();
if (!label) continue;
var order = (cp - 1) * 100 + i + 1;
chapters.push({ url: url, label: label, order: order }); seen.add(url.href);
state.chapterOrders.set(url.pathname, order);
state.chapterTitles.set(url.pathname, cleanChapterTitle(label));
}
try { localStorage.setItem(chapterOrderKey, JSON.stringify(Array.from(state.chapterOrders))); } catch (_) {}
try { localStorage.setItem(chapterTitleKey, JSON.stringify(Array.from(state.chapterTitles))); } catch (_) {}
reconcileProgressOrders();
if (!chapters.length) throw new Error('这一页没有可用章节,当前阅读已保留');
var ranges = {};
ranges[cp] = String((cp - 1) * 100 + 1) + '-' + String((cp - 1) * 100 + chapters.length);
var max = cp;
var rangeLinks = doc.querySelectorAll('#chapters .page-range-list a[href]');
for (var j = 0; j < rangeLinks.length; j++) {
var rangeUrl = siteUrl(rangeLinks[j].getAttribute('href'), pageUrl.href);
if (!rangeUrl || rangeUrl.pathname !== '/book/' + bookId + '.html') continue;
var page = Number(rangeUrl.searchParams.get('cp'));
if (!(page >= 1 && page <= 100 && page % 1 === 0)) continue;
ranges[page] = rangeLinks[j].textContent.trim() || String(page);
max = Math.max(max, page);
}
var currentRange = doc.querySelector('#chapters .page-range-list .cur');
if (currentRange) ranges[cp] = currentRange.textContent.trim() || ranges[cp];
return { chapters: chapters, ranges: ranges, max: max };
}
function renderCatalogue(data, cp) {
if (!state.tocOpen || !state.tocPages || !state.tocList) return;
state.tocMax = data.max;
state.tocPages.textContent = '';
for (var page = 1; page <= data.max; page++) {
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('data-bm-toc-page', String(page));
button.setAttribute('aria-label', '目录 ' + (data.ranges[page] || page));
button.textContent = data.ranges[page] || String(page);
button.style.cssText = 'flex:0 0 auto;padding:6px 8px;border:1px solid currentColor;' +
'border-radius:5px;background:none;font-size:12px;';
if (page === cp) {
button.setAttribute('aria-current', 'page');
button.style.setProperty('font-weight', '700');
}
state.tocPages.appendChild(button);
}
state.tocList.textContent = '';
data.chapters.forEach(function (chapter) {
var button = document.createElement('button');
button.type = 'button';
button.setAttribute('data-bm-toc-url', chapter.url.href);
button.textContent = chapter.label;
button.style.cssText = 'display:block;width:100%;box-sizing:border-box;padding:11px 10px;' +
'border:0;border-bottom:1px solid rgba(90,80,70,.18);' +
'background:none;text-align:left;font-size:16px;line-height:1.5;';
if (state.first && chapter.url.pathname === state.first.url.pathname) {
button.setAttribute('aria-current', 'page');
button.style.setProperty('font-weight', '700');
}
state.tocList.appendChild(button);
});
setCatalogueStatus('');
applyTheme(currentTheme);
}
async function loadCatalogue(cp) {
if (!state.tocOpen || !(cp >= 1 && cp <= 100 && cp % 1 === 0)) return;
stopCatalogueRequest();
state.tocPage = cp;
try { localStorage.setItem(tocPageKey, String(cp)); } catch (_) {}
var token = ++state.tocSeq;
setCatalogueStatus('正在加载原站目录…');
if (state.tocCache.has(cp)) {
renderCatalogue(state.tocCache.get(cp), cp);
return;
}
if (state.tocList) state.tocList.textContent = '';
var url = new URL('/book/' + bookId + '.html?cp=' + cp, origin);
try {
var doc = await fetchInertPage(url, '目录');
var data = parseCatalogue(doc, url, cp);
if (!state.tocOpen || token !== state.tocSeq) return;
state.tocCache.set(cp, data);
renderCatalogue(data, cp);
} catch (error) {
if (state.tocOpen && token === state.tocSeq) setCatalogueStatus(error.message || '目录加载失败,当前阅读已保留');
}
}
async function chooseCatalogueChapter(value) {
var url = readUrl(value);
if (!state.tocOpen || !url || url.search) return;
stopCatalogueRequest();
if (state.first && pageKey(state.first.url) === pageKey(url)) {
state.reader.scrollTop = 0;
closeCatalogue();
saveChapterStart(url);
return;
}
var token = ++state.tocSeq;
setCatalogueStatus('正在读取章节…');
try {
var doc = await fetchInertPage(url, '本章');
var page = extract(doc, url);
if (!state.tocOpen || token !== state.tocSeq) return;
showCatalogueChapter(page);
} catch (error) {
if (state.tocOpen && token === state.tocSeq) setCatalogueStatus(error.message || '本章暂不能打开,当前阅读已保留');
}
}
function saveChapterStart(url) {
state.progressTouched = false;
recordProgress({ url: url.href, index: 0, ratio: 0, time: Date.now(),
title: titleForProgress(url) });
}
function showCatalogueChapter(page, checkpoint) {
if (!activeReader() || !state.source) throw new Error('阅读模式已关闭,当前章节未切换');
var target = readUrl(page.url.href);
if (!target) throw new Error('章节地址不正确,当前阅读已保留');
try { history.replaceState(history.state, '', target.href); }
catch (_) { throw new Error('浏览器无法原位切换地址,当前阅读已保留'); }
rememberChapterTitle(page);
// 只复制解码后的纯文本;下载页面中的脚本、图片和广告节点都不进入阅读层。
state.readSeq++;
if (state.timer) { clearTimeout(state.timer); state.timer = null; }
if (state.progressTimer) { clearTimeout(state.progressTimer); state.progressTimer = null; }
state.pendingResume = null;
try { sessionStorage.removeItem(pendingKey); } catch (_) {}
replaceReadingPages(page, []);
delete page.content;
state.first = page; state.next = page.next; state.pages = [];
state.seen = new Set([pageKey(target)]);
state.busy = false; state.blocked = false; state.error = '';
markProgressParagraphs();
state.reader.scrollTop = 0;
if (state.tocOpen) closeCatalogue();
if (state.progressOpen) closeProgressPanel();
if (checkpoint) {
var selected = normalizeProgress(checkpoint);
selected.time = Date.now();
selected.title = page.title;
recordProgress(selected);
state.pendingResume = selected;
restorePendingProgress();
} else saveChapterStart(target);
updateStatus();
}
function replaceReadingPages(page, remaining) {
state.source.textContent = '';
state.content.textContent = '';
for (var i = 0; i < page.paragraphs.length; i++) {
var paragraph = document.createElement('p');
paragraph.textContent = page.paragraphs[i];
state.source.appendChild(paragraph);
state.content.appendChild(paragraph.cloneNode(true));
}
state.source.setAttribute('data-bm-page', pageKey(page.url));
state.content.setAttribute('data-bm-page', pageKey(page.url));
remaining.forEach(function (item) { appendPage(state.source, item); appendPage(state.content, item); });
var heading = document.querySelector('.conC h1.title');
if (heading) heading.textContent = page.title;
var readerHeading = state.reader.querySelector('.via-reader-header h1');
if (readerHeading) readerHeading.textContent = page.title;
document.title = page.docTitle || page.title;
var breadcrumb = document.querySelector('.conC .position > span:last-child');
if (breadcrumb) breadcrumb.textContent = page.title;
var chapterInfo = document.querySelector('.conC .toolBox > .font14');
if (chapterInfo) chapterInfo.textContent = page.chapterInfo || '';
var staleChapterActions = document.querySelector('.conC .btnErrorW');
if (staleChapterActions) staleChapterActions.remove();
var navigation = document.querySelector('.readPage');
if (navigation) {
navigation.textContent = '';
if (page.next) {
var next = document.createElement('a');
next.className = 'btnGreen';
next.href = page.next.href;
next.textContent = page.next.pathname === page.url.pathname ? '下一页 >' : '下一章 >';
navigation.appendChild(next);
}
var catalogue = document.createElement('a');
catalogue.className = 'btnBlue';
catalogue.href = '/book/' + bookId + '.html';
catalogue.textContent = '目录';
navigation.appendChild(catalogue);
}
}
function siteUrl(value, base) {
try {
var url = new URL(value, base || location.href);
if (url.protocol === 'https:' && url.origin === origin && !url.username && !url.password) return url;
} catch (_) {}
return null;
}
function readUrl(value, base) {
var url = siteUrl(value, base);
if (!url || !bookId || !new RegExp('^/read/' + bookId + '/\\d+\\.html$').test(url.pathname)) return null;
url.hash = '';
return url;
}
function pageKey(url) {
var copy = new URL(url.href);
copy.hash = '';
if (copy.searchParams.get('p') === '1') copy.searchParams.delete('p');
if (copy.searchParams.sort) copy.searchParams.sort();
return copy.href;
}
function normalizeProgress(value) {
if (!value || typeof value !== 'object') return null;
var url = readUrl(value.url);
var index = Number(value.index);
var ratio = Number(value.ratio);
if (!url || !(index >= 0 && index % 1 === 0) || !(ratio >= 0 && ratio <= 1)) return null;
var order = state.chapterOrders.get(url.pathname) || Number(value.order) || 0;
return { url: url.href, index: index, ratio: ratio, time: Number(value.time) || 0,
title: state.chapterTitles.get(url.pathname) ||
(typeof value.title === 'string' ? cleanChapterTitle(value.title).slice(0, 120) : '') || titleForProgress(url),
order: Number.isInteger(order) && order > 0 && order <= 100000 ? order : 0 };
}
function progressPage(url) {
var page = Number(url.searchParams.get('p')) || 1;
return Number.isInteger(page) && page >= 1 ? page : 1;
}
function compareWithinChapter(a, b) {
var first = readUrl(a.url);
var second = readUrl(b.url);
if (!first || !second || first.pathname !== second.pathname) return null;
var pageDifference = progressPage(first) - progressPage(second);
return pageDifference || a.index - b.index || a.ratio - b.ratio;
}
function sameProgressPosition(a, b) {
var first = readUrl(a.url);
var second = readUrl(b.url);
return !!first && !!second && pageKey(first) === pageKey(second) &&
a.index === b.index && Math.abs(a.ratio - b.ratio) < .001;
}
function followsKnownNext(previous, current) {
var from = readUrl(previous.url);
var to = readUrl(current.url);
if (!from || !to) return false;
var key = pageKey(from);
var wanted = pageKey(to);
var visited = new Set();
while (!visited.has(key)) {
visited.add(key);
key = state.forwardLinks.get(key);
if (!key) return false;
if (key === wanted) return true;
}
return false;
}
function isFurtherProgress(candidate, previous) {
if (!previous) return true;
var within = compareWithinChapter(candidate, previous);
if (within !== null) return within > 0;
if (candidate.order && previous.order) return candidate.order > previous.order;
return followsKnownNext(previous, candidate);
}
function persistProgressHistory() {
try { localStorage.setItem(historyKey, JSON.stringify(state.progressHistory)); } catch (_) {}
}
function recordProgress(value, writeLegacy, promote) {
var progress = normalizeProgress(value);
if (!progress) return;
if (writeLegacy !== false) {
state.savedProgress = progress;
try { localStorage.setItem(progressKey, JSON.stringify(progress)); } catch (_) {}
}
var recent = state.progressHistory.recent;
var path = readUrl(progress.url).pathname;
for (var i = 0; i < recent.length; i++) {
var old = normalizeProgress(recent[i]);
if (!old || readUrl(old.url).pathname !== path) continue;
if (compareWithinChapter(old, progress) > 0) {
old.time = progress.time;
if (!old.order) old.order = progress.order;
progress = old;
}
recent.splice(i, 1);
break;
}
recent.unshift(progress);
if (recent.length > 1000) recent.length = 1000;
var furthest = normalizeProgress(state.progressHistory.furthest);
if (promote !== false && isFurtherProgress(progress, furthest)) state.progressHistory.furthest = progress;
persistProgressHistory();
updateResumeButton();
if (state.progressOpen) renderProgressHistory();
}
function loadProgressHistory() {
try {
var stored = JSON.parse(localStorage.getItem(historyKey) || 'null');
if (stored && typeof stored === 'object') {
state.progressHistory.furthest = normalizeProgress(stored.furthest);
if (Array.isArray(stored.recent)) {
state.progressHistory.recent = stored.recent.map(normalizeProgress).filter(Boolean).slice(0, 1000);
}
}
} catch (_) {}
var legacy = normalizeProgress(state.savedProgress);
if (legacy && !state.progressHistory.recent.some(function (entry) {
return sameProgressPosition(entry, legacy) && entry.time >= legacy.time;
})) recordProgress(legacy, false);
}
function reconcileProgressOrders() {
var history = state.progressHistory;
history.furthest = normalizeProgress(history.furthest);
history.recent = history.recent.map(normalizeProgress).filter(Boolean);
for (var i = 0; i < history.recent.length; i++) {
if ((history.furthest || history.recent[i].order) &&
isFurtherProgress(history.recent[i], history.furthest)) history.furthest = history.recent[i];
}
persistProgressHistory();
if (state.progressOpen) renderProgressHistory();
}
function cleanChapterTitle(value) {
return String(value || '').trim().replace(/\s*[((](?:第\s*\d+\s*页|\d+\s*\/\s*\d+(?:页)?)[))]\s*$/, '');
}
function rememberChapterTitle(page) {
if (!page || !page.url || !page.title) return;
var title = cleanChapterTitle(page.title);
if (title && !state.chapterTitles.get(page.url.pathname)) state.chapterTitles.set(page.url.pathname, title);
try { localStorage.setItem(chapterTitleKey, JSON.stringify(Array.from(state.chapterTitles))); } catch (_) {}
reconcileProgressOrders();
}
function titleForProgress(url) {
if (state.chapterTitles.has(url.pathname)) return state.chapterTitles.get(url.pathname);
if (state.first && state.first.url.pathname === url.pathname) return cleanChapterTitle(state.first.title);
for (var i = 0; i < state.pages.length; i++) {
if (state.pages[i].url.pathname === url.pathname) return cleanChapterTitle(state.pages[i].title);
}
return '';
}
function updateResumeButton() {
if (!state.toolbar) return;
var button = state.toolbar.querySelector('[data-bm-resume]');
if (!button) return;
button.disabled = !normalizeProgress(state.progressHistory.furthest) &&
!state.progressHistory.recent.length;
button.style.setProperty('opacity', button.disabled ? '.45' : '1', 'important');
}
function markProgressParagraphs() {
if (!state.content || !state.first) return;
var counts = Object.create(null);
var paragraphs = state.content.querySelectorAll('p');
for (var i = 0; i < paragraphs.length; i++) {
var paragraph = paragraphs[i];
if (paragraph.hasAttribute('data-bm-native-nav') ||
paragraph.closest('.readPage, .page2-chapter')) continue;
var page = paragraph.closest('[data-bm-page]');
var url = page ? readUrl(page.getAttribute('data-bm-page')) : state.first.url;
if (!url) continue;
var key = pageKey(url);
var index = counts[key] || 0;
counts[key] = index + 1;
paragraph.setAttribute('data-bm-progress-url', url.href);
paragraph.setAttribute('data-bm-progress-index', String(index));
}
}
function saveProgressNow(allowProgressPanel) {
if (!activeReader() || state.tocOpen || (state.progressOpen && allowProgressPanel !== true) || !state.first) return;
var paragraphs = state.content.querySelectorAll('p[data-bm-progress-index]');
var anchor = state.reader.getBoundingClientRect().top + Math.min(120, state.reader.clientHeight * .22);
var selected = null;
var rect = null;
for (var i = 0; i < paragraphs.length; i++) {
var candidate = paragraphs[i];
if (candidate.hidden || candidate.closest('[hidden], .readPage, .page2-chapter')) continue;
var bounds = candidate.getBoundingClientRect();
if (!(bounds.height > 0)) continue;
selected = candidate; rect = bounds;
if (bounds.bottom > anchor) break;
}
if (!selected || !rect) return;
var url = readUrl(selected.getAttribute('data-bm-progress-url'));
var index = Number(selected.getAttribute('data-bm-progress-index'));
if (!url || !(index >= 0 && index % 1 === 0)) return;
var ratio = Math.max(0, Math.min(1, (anchor - rect.top) / rect.height));
recordProgress({ url: url.href, index: index, ratio: Math.round(ratio * 1000) / 1000,
time: Date.now(), title: titleForProgress(url) });
}
function scheduleProgress() {
if (state.tocOpen || state.progressOpen) return;
state.progressTouched = true;
if (state.progressTimer) return;
state.progressTimer = setTimeout(function () {
state.progressTimer = null;
saveProgressNow();
compactReadPages();
}, 800);
}
function restoreProgressHere(progress) {
if (!activeReader()) return false;
var targetUrl = readUrl(progress.url);
if (!targetUrl) return false;
var paragraphs = state.content.querySelectorAll('p[data-bm-progress-index]');
for (var i = 0; i < paragraphs.length; i++) {
var paragraph = paragraphs[i];
var url = readUrl(paragraph.getAttribute('data-bm-progress-url'));
if (!url || pageKey(url) !== pageKey(targetUrl) ||
Number(paragraph.getAttribute('data-bm-progress-index')) !== progress.index) continue;
var rect = paragraph.getBoundingClientRect();
if (!(rect.height > 0)) return false;
var anchor = state.reader.getBoundingClientRect().top + Math.min(120, state.reader.clientHeight * .22);
state.reader.scrollTop += rect.top + progress.ratio * rect.height - anchor;
return true;
}
return false;
}
function restorePendingProgress() {
var pending = normalizeProgress(state.pendingResume);
if (!pending || !state.first || pageKey(readUrl(pending.url)) !== pageKey(state.first.url)) return;
var attempts = 0;
function attempt() {
if (!activeReader()) return;
markProgressParagraphs();
if (restoreProgressHere(pending)) {
state.pendingResume = null;
try { sessionStorage.removeItem(pendingKey); } catch (_) {}
saveProgressNow();
} else if (++attempts < 4) setTimeout(attempt, 250);
else {
state.pendingResume = null;
try { sessionStorage.removeItem(pendingKey); } catch (_) {}
}
}
attempt();
}
async function chooseHistoryPosition(button) {
if (!state.progressOpen) return;
var url = readUrl(button.getAttribute('data-bm-progress-url'));
var index = Number(button.getAttribute('data-bm-progress-index'));
var ratio = Number(button.getAttribute('data-bm-progress-ratio'));
if (!url || !(index >= 0 && index % 1 === 0) || !(ratio >= 0 && ratio <= 1)) return;
var choices = [state.progressHistory.furthest].concat(state.progressHistory.recent);
var progress = null;
for (var i = 0; i < choices.length; i++) {
var entry = normalizeProgress(choices[i]);
if (entry && pageKey(readUrl(entry.url)) === pageKey(url) && entry.index === index &&
Math.abs(entry.ratio - ratio) < .001) { progress = entry; break; }
}
if (!progress) return;
if (progress.index === 0 && progress.ratio === 0 && state.first &&
pageKey(state.first.url) === pageKey(url)) {
state.reader.scrollTop = 0;
closeProgressPanel();
progress.time = Date.now(); recordProgress(progress);
return;
}
if (restoreProgressHere(progress)) {
closeProgressPanel();
progress.time = Date.now(); recordProgress(progress);
return;
}
stopCatalogueRequest();
var token = ++state.progressSeq;
setProgressStatus('正在打开保存的位置…');
try {
var doc = await fetchInertPage(url, '进度');
var page = extract(doc, url);
if (!state.progressOpen || token !== state.progressSeq) return;
showCatalogueChapter(page, progress);
} catch (error) {
if (state.progressOpen && token === state.progressSeq) {
setProgressStatus(error.message || '暂时无法打开,当前阅读已保留');
}
}
}
// 这三个函数是当前章节“书签后、页码后、页脚”的广告插入入口。
// 与单独的站外保护脚本共存:已有不可改写的保护不再覆盖。
['C061V4rne', 'ZJo6i4ZXG', 'r2lTyoQWN'].forEach(function (name) {
try { Object.defineProperty(window, name, { value: function () {}, writable: false, configurable: false }); }
catch (_) {}
});
try {
Object.defineProperty(window, 'open', { value: function () { return null; }, writable: false, configurable: false });
} catch (_) {
try { window.open = function () { return null; }; } catch (ignored) {}
}
function guard(event) {
var target = event.target;
if (!target || !target.closest) return;
var download = target.closest('a[href]');
if (download && download === state.backupDownloadAnchor && state.backupDownloadUrl &&
download.getAttribute('href') === state.backupDownloadUrl) {
event.stopImmediatePropagation();
return;
}
var backupField = target.closest('[data-bm-backup-file], [data-bm-backup-text]');
if (backupField) { event.stopImmediatePropagation(); return; }
var backupControl = target.closest('[data-bm-backup-export], [data-bm-backup-import], ' +
'[data-bm-backup-toggle-text], [data-bm-backup-copy], [data-bm-backup-restore-text]');
if (backupControl) {
if (event.type === 'click') {
event.preventDefault();
if (backupControl.hasAttribute('data-bm-backup-export')) exportBackup();
else if (backupControl.hasAttribute('data-bm-backup-import')) state.backupFile.click();
else if (backupControl.hasAttribute('data-bm-backup-toggle-text')) toggleBackupText();
else if (backupControl.hasAttribute('data-bm-backup-copy')) copyBackupText();
else if (backupControl.hasAttribute('data-bm-backup-restore-text')) restoreBackupText();
}
event.stopImmediatePropagation(); return;
}
var searchControl = target.closest('.btnSearchM, .searchBox form, .searchBoxM input');
if (searchControl) {
if (event.type === 'click') {
event.preventDefault();
searchNavigationUntil = Date.now() + 5000;
location.assign(searchEntry.href);
}
event.stopImmediatePropagation();
return;
}
var readerControl = target.closest('[data-bm-toc-open], [data-bm-toc-close], ' +
'[data-bm-toc-page], [data-bm-toc-url], [data-bm-toolbar-collapse], ' +
'[data-bm-toolbar-expand], [data-bm-resume], [data-bm-progress-close], ' +
'[data-bm-progress-url], [data-bm-reader-exit]');
if (readerControl) {
if (event.type === 'click') {
event.preventDefault();
if (readerControl.hasAttribute('data-bm-toc-open')) openCatalogue();
else if (readerControl.hasAttribute('data-bm-toc-close')) closeCatalogue();
else if (readerControl.hasAttribute('data-bm-toc-page')) {
loadCatalogue(Number(readerControl.getAttribute('data-bm-toc-page')));
} else if (readerControl.hasAttribute('data-bm-toc-url')) {
chooseCatalogueChapter(readerControl.getAttribute('data-bm-toc-url'));
} else if (readerControl.hasAttribute('data-bm-progress-close')) closeProgressPanel();
else if (readerControl.hasAttribute('data-bm-reader-exit')) exitAssistedReader();
else if (readerControl.hasAttribute('data-bm-progress-url')) chooseHistoryPosition(readerControl);
else if (readerControl.hasAttribute('data-bm-toolbar-collapse')) setToolbarCollapsed(true);
else if (readerControl.hasAttribute('data-bm-toolbar-expand')) setToolbarCollapsed(false);
else if (readerControl.hasAttribute('data-bm-resume')) openProgressPanel();
}
event.stopImmediatePropagation();
return;
}
var fontButton = target.closest('[data-bm-font-step]');
if (fontButton) {
if (event.type === 'touchstart') fontTouch.button = fontButton;
if (event.type === 'touchend' && fontTouch.button === fontButton) {
if (!fontButton.disabled) applyFontSize(fontSize + Number(fontButton.getAttribute('data-bm-font-step')));
fontTouch.at = Date.now();
}
if (event.type === 'click') {
event.preventDefault();
if (!fontButton.disabled && !(fontTouch.button === fontButton && Date.now() - fontTouch.at < 700)) {
applyFontSize(fontSize + Number(fontButton.getAttribute('data-bm-font-step')));
}
fontTouch.button = null;
}
event.stopImmediatePropagation();
return;
}
var themeButton = target.closest('[data-bm-theme-choice]');
if (themeButton) {
if (event.type === 'click' || event.type === 'touchend') {
if (event.type === 'click') event.preventDefault();
applyTheme(themeButton.getAttribute('data-bm-theme-choice'));
}
event.stopImmediatePropagation();
return;
}
var anchor = target.closest('a[href], a[_href]');
if (anchor && state.reader && state.reader.contains(anchor) && /^\s*目录\s*$/.test(anchor.textContent)) {
var indexUrl = siteUrl(anchor.getAttribute('href'));
if (indexUrl && indexUrl.pathname === '/book/' + bookId + '.html') {
if (event.type === 'click') { event.preventDefault(); openCatalogue(); }
event.stopImmediatePropagation();
return;
}
}
// Via 原生阅读的上一页/下一页使用 href="#" 和 _href="实际地址"。
var href = anchor && (anchor.getAttribute('href') || '#');
var placeholder = href && /^javascript:\s*(?:void\s*\(\s*0\s*\)\s*;?|;)$/i.test(href.trim());
if (anchor && ((!placeholder && !siteUrl(href)) ||
(anchor.hasAttribute('_href') && !siteUrl(anchor.getAttribute('_href'))))) {
event.preventDefault();
event.stopImmediatePropagation();
return;
}
if (target.closest('[data-bm-pager-status]')) event.stopImmediatePropagation();
}
['click', 'auxclick', 'pointerdown', 'mousedown', 'touchstart', 'touchend'].forEach(function (type) {
window.addEventListener(type, guard, { capture: true, passive: false });
});
window.addEventListener('submit', function (event) {
if (event.target && event.target.matches && event.target.matches('.searchBox form')) {
event.preventDefault(); event.stopImmediatePropagation();
searchNavigationUntil = Date.now() + 5000;
location.assign(searchEntry.href);
return;
}
if (event.target && event.target.action && !siteUrl(event.target.action)) {
event.preventDefault(); event.stopImmediatePropagation();
}
}, true);
if (window.navigation && window.navigation.addEventListener) {
window.navigation.addEventListener('navigate', function (event) {
if (event.cancelable && !siteUrl(event.destination.url) &&
!(state.backupDownloadUrl && event.destination.url === state.backupDownloadUrl) &&
!intentionalSearchNavigation(event.destination.url)) event.preventDefault();
});
}
if (!bookId) {
try { localStorage.__via_reader_enabled__ = null; } catch (_) {}
var catalogMatch = location.pathname.match(/^\/book\/(\d+)\.html$/);
var visitedCp = Number(new URLSearchParams(location.search).get('cp'));
if (catalogMatch && visitedCp >= 1 && visitedCp <= 100 && visitedCp % 1 === 0) {
try { localStorage.setItem('bm-reader-catalog-page-' + catalogMatch[1], String(visitedCp)); } catch (_) {}
}
if (catalogMatch) {
var rememberBookOrders = function () {
var cp = visitedCp >= 1 && visitedCp <= 100 && visitedCp % 1 === 0 ? visitedCp : 1;
var key = 'bm-reader-chapter-order-' + catalogMatch[1] + '-v1';
var titleKey = 'bm-reader-chapter-title-' + catalogMatch[1] + '-v1';
var orders = new Map();
var titles = new Map();
try {
var stored = JSON.parse(localStorage.getItem(key) || '[]');
if (Array.isArray(stored)) stored.forEach(function (item) {
if (Array.isArray(item) && typeof item[0] === 'string' && Number.isInteger(item[1])) {
orders.set(item[0], item[1]);
}
});
} catch (_) {}
try {
var knownTitles = JSON.parse(localStorage.getItem(titleKey) || '[]');
if (Array.isArray(knownTitles)) knownTitles.forEach(function (item) {
if (Array.isArray(item) && typeof item[0] === 'string' && typeof item[1] === 'string') {
titles.set(item[0], cleanChapterTitle(item[1]));
}
});
} catch (_) {}
var links = document.querySelectorAll('#chapters .direList li .name > a[href]');
for (var i = 0; i < links.length; i++) {
var chapter = siteUrl(links[i].getAttribute('href'));
if (chapter && new RegExp('^/read/' + catalogMatch[1] + '/\\d+\\.html$').test(chapter.pathname) &&
!chapter.search) {
orders.set(chapter.pathname, (cp - 1) * 100 + i + 1);
titles.set(chapter.pathname, cleanChapterTitle(links[i].textContent));
}
}
if (links.length) try { localStorage.setItem(key, JSON.stringify(Array.from(orders))); } catch (_) {}
if (links.length) try { localStorage.setItem(titleKey, JSON.stringify(Array.from(titles))); } catch (_) {}
};
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', rememberBookOrders, { once: true });
else rememberBookOrders();
}
return;
}
function decode(value) {
var raw = atob(value);
var bytes = new Uint8Array(raw.length);
for (var i = 0; i < raw.length; i++) bytes[i] = raw.charCodeAt(i) ^ ((i % 127) + 1);
if (typeof TextDecoder === 'function') return new TextDecoder('utf-8', { fatal: true }).decode(bytes);
var percent = '';
for (var j = 0; j < bytes.length; j++) percent += '%' + ('0' + bytes[j].toString(16)).slice(-2);
return decodeURIComponent(percent);
}
function hidden(node, boundary) {
for (var el = node; el && el !== boundary; el = el.parentElement) {
var style = (el.getAttribute('style') || '').replace(/\s/g, '').toLowerCase();
if (el.hidden || el.getAttribute('aria-hidden') === 'true' || el.classList.contains('honeypot') ||
/(?:display:none|visibility:hidden|opacity:0(?:;|$)|font-size:0(?:px)?(?:;|$))/.test(style)) return true;
}
return false;
}
function isChapterArtifact(text) {
// 只过滤整段残留的空调用;正常正文中的英文 chapter 或引号不会受影响。
return /^\s*chapter_\s*\(\s*\)\s*;?\s*$/i.test(text);
}
function extract(doc, url) {
var title = doc.querySelector('title');
var docTitle = doc.title || (title ? title.textContent : '');
if (doc.querySelector('input[name="GOEDGE_WAF_CAPTCHA_CODE"], #loginForm') ||
/^(?:身份验证|安全验证|访问验证|人机验证|登录\s*[-—]|Just a moment)/i.test(docTitle.trim())) {
throw new Error('下一页需要验证码或登录,自动接续已停止');
}
var content = doc.querySelector('.conC .content');
if (!content) throw new Error('未找到下一页正文,自动接续已停止');
var nodes = content.querySelectorAll('p');
var paragraphs = [];
for (var i = 0; i < nodes.length; i++) {
if (hidden(nodes[i], content)) continue;
var text = nodes[i].textContent;
if (nodes[i].hasAttribute('data-obf')) {
try { text = decode(nodes[i].getAttribute('data-obf')); }
catch (_) { throw new Error('正文解码失败,自动接续已停止'); }
}
if (text && text.trim() && !isChapterArtifact(text)) paragraphs.push(text.trim());
}
if (!paragraphs.length) throw new Error('下一页没有可读取的正文,自动接续已停止');
var heading = doc.querySelector('.conC h1.title');
var links = doc.querySelectorAll('.readPage a[href]');
var next = null;
// 有章内分页时优先下一页,避免网站同时提供下一章而跳过本章后半页。
for (var kind = 0; kind < 2 && !next; kind++) {
var pattern = kind === 0 ? /^下一页/ : /^下一章/;
for (var j = 0; j < links.length; j++) {
if (!pattern.test(links[j].textContent.trim())) continue;
next = readUrl(links[j].getAttribute('href'), url.href);
if (!next) throw new Error('下一页地址不属于本站这本书,自动接续已停止');
break;
}
}
var chapterInfo = doc.querySelector('.conC .toolBox > .font14');
return { url: url, title: heading ? heading.textContent.trim() : docTitle,
docTitle: docTitle, chapterInfo: chapterInfo ? chapterInfo.textContent.trim() : '',
paragraphs: paragraphs, next: next, content: content };
}
function hasPage(target, url) {
var wanted = pageKey(url);
if (target.getAttribute('data-bm-page') === wanted) return true;
var nodes = target.querySelectorAll('[data-bm-page]');
for (var i = 0; i < nodes.length; i++) if (nodes[i].getAttribute('data-bm-page') === wanted) return true;
return false;
}
function appendPage(target, page) {
if (!target || hasPage(target, page.url)) return;
var section = document.createElement('section');
section.setAttribute('data-bm-page', pageKey(page.url));
if (page.chapterStart) {
var title = document.createElement('h2'); title.textContent = page.title; section.appendChild(title);
}
page.paragraphs.forEach(function (text) {
var paragraph = document.createElement('p'); paragraph.textContent = text; section.appendChild(paragraph);
});
target.appendChild(section);
}
// 只保留当前阅读附近的正文窗口;进度/目录仍能重新打开已经释放的章节。
// 布局尚不可测量或读者正在回看前文时,宁可延后,也不删掉可见文字。
function compactReadPages() {
if (state.compacting || state.busy || state.tocOpen || state.progressOpen || document.hidden ||
!activeReader() || !state.first || state.pages.length + 1 <= 12) return;
var pages = [state.first].concat(state.pages);
var paragraphs = state.content.querySelectorAll('p[data-bm-progress-index]');
var viewport = state.reader.getBoundingClientRect();
var height = state.reader.clientHeight;
if (!(height > 0) || !Number.isFinite(viewport.top)) return;
var anchorY = viewport.top + Math.min(120, height * .22);
var visible = null;
var bounds = null;
var starts = new Map();
for (var i = 0; i < paragraphs.length; i++) {
var paragraph = paragraphs[i];
var rect = paragraph.getBoundingClientRect();
var url = readUrl(paragraph.getAttribute('data-bm-progress-url'));
if (!url || !(rect.height > 0) || !Number.isFinite(rect.top) || !Number.isFinite(rect.bottom)) continue;
var key = pageKey(url);
if (!starts.has(key)) starts.set(key, rect.top);
if (!visible && rect.bottom > anchorY && rect.top < viewport.top + height) {
visible = paragraph; bounds = rect;
}
}
if (!visible) return;
var visibleKey = pageKey(readUrl(visible.getAttribute('data-bm-progress-url')));
var activeIndex = pages.findIndex(function (page) { return pageKey(page.url) === visibleKey; });
var cut = Math.min(pages.length - 8, activeIndex - 2);
while (cut > 0 && !(starts.has(pageKey(pages[cut].url)) &&
starts.get(pageKey(pages[cut].url)) <= viewport.top - 64)) cut--;
if (!(cut > 0)) return;
var index = visible.getAttribute('data-bm-progress-index');
var retained = pages.slice(cut);
// 关闭再开启原生阅读会重新抽取源正文,因此源页面也必须同步收窄。
try { history.replaceState(history.state, '', retained[0].url.href); } catch (_) { return; }
state.compacting = true;
try {
saveProgressNow();
replaceReadingPages(retained[0], retained.slice(1));
delete retained[0].content;
state.first = retained[0]; state.pages = retained.slice(1);
state.compactedPages += cut;
markProgressParagraphs();
var updated = state.content.querySelectorAll('p[data-bm-progress-index]');
for (var j = 0; j < updated.length; j++) {
if (updated[j].getAttribute('data-bm-progress-url') !== visible.getAttribute('data-bm-progress-url') ||
updated[j].getAttribute('data-bm-progress-index') !== index) continue;
var after = updated[j].getBoundingClientRect();
if (after.height > 0 && Number.isFinite(after.top)) state.reader.scrollTop += after.top - bounds.top;
break;
}
if (state.progressPanel) {
var note = state.progressPanel.querySelector('[data-bm-memory-note]');
if (note) note.hidden = false;
}
} finally { state.compacting = false; }
}
function hideReaderNavigation(reader, content) {
var metadata = reader.querySelector('.via-reader-header > div');
if (metadata) metadata.remove();
var siteNav = content.querySelectorAll('.readPage, .page2-chapter');
for (var i = 0; i < siteNav.length; i++) siteNav[i].remove();
// Via 自己在正文末尾附上一组目录/上一页/下一页链接;自动接续和顶部目录
// 已接管这些功能,因此直接从正文移除,避免朗读或正文识别再次读到它们。
var paragraphs = content.querySelectorAll('p');
for (var j = 0; j < paragraphs.length; j++) {
var paragraph = paragraphs[j];
if (!paragraph.querySelector('a[_href], a[href*="/book/"]')) continue;
var text = paragraph.textContent.replace(/[\s||·•]/g, '');
if (!/^(?:(?:上一|下一)(?:页|章)|目录)+$/.test(text)) continue;
if (paragraph.nextElementSibling && paragraph.nextElementSibling.tagName === 'BR') {
paragraph.nextElementSibling.remove();
}
paragraph.remove();
}
}
function updateStatus() {
if (!state.status) return;
var message = state.blocked ? state.error : state.busy ? '正在接续下一页…' : '';
if (state.status.getAttribute('data-message') === message) return;
state.status.setAttribute('data-message', message);
state.status.textContent = message;
state.status.hidden = !message;
if (state.blocked && state.next) {
var manual = document.createElement('a');
manual.href = state.next.href; manual.textContent = '打开该页手动处理';
state.status.appendChild(document.createTextNode(' ')); state.status.appendChild(manual);
}
}
function halt(message) {
state.blocked = true; state.error = message;
if (state.timer) { clearTimeout(state.timer); state.timer = null; }
updateStatus();
}
function activeReader() {
if (!state.reader || !state.reader.isConnected || !state.content || !state.content.isConnected || state.reader.hidden) return false;
var style = window.getComputedStyle(state.reader);
return style.display !== 'none' && style.visibility !== 'hidden' && style.visibility !== 'collapse';
}
function nearEnd() {
if (!activeReader() || document.hidden || state.tocOpen || state.progressOpen) return false;
var height = state.reader.clientHeight;
if (!(height > 0 && state.reader.scrollHeight >= height)) return false;
return state.reader.scrollHeight - state.reader.scrollTop - height <= Math.min(350, height * 0.6);
}
function schedule() {
if (state.timer || state.busy || state.blocked || !state.next || !nearEnd()) return;
state.timer = setTimeout(function () {
state.timer = null;
if (nearEnd()) loadNext();
}, Math.max(250, 2000 - (Date.now() - state.lastRequest)));
}
async function loadNext() {
if (state.busy || state.blocked || !state.next || !nearEnd()) return;
var readSeq = state.readSeq;
var url = readUrl(state.next.href);
if (!url) { halt('下一页地址不属于本站这本书,自动接续已停止'); return; }
if (state.seen.has(pageKey(url))) { halt('下一页链接重复,自动接续已停止'); return; }
state.busy = true; state.lastRequest = Date.now(); updateStatus();
var controller = typeof AbortController === 'function' ? new AbortController() : null;
var timeout;
try {
var operation = (async function () {
var response = await fetch(url.href, { credentials: 'same-origin', redirect: 'manual',
signal: controller ? controller.signal : undefined });
if (response.type === 'opaqueredirect' || (response.status >= 300 && response.status < 400)) {
throw new Error('下一页要求跳转,可能需要验证码或登录,自动接续已停止');
}
if (!response.ok) throw new Error('下一页请求失败(' + response.status + '),自动接续已停止');
if (response.url) {
var finalUrl = readUrl(response.url);
if (!finalUrl || pageKey(finalUrl) !== pageKey(url)) throw new Error('下一页响应地址异常,自动接续已停止');
}
var html = await response.text();
// template 是惰性文档:后页的脚本、图片与 iframe 不执行、不请求。
var template = document.createElement('template'); template.innerHTML = html;
return extract(template.content, url);
})();
var page = await Promise.race([operation, new Promise(function (_, reject) {
timeout = setTimeout(function () {
if (controller) controller.abort();
reject(new Error('下一页网络超时,自动接续已停止'));
}, 20000);
})]);
if (readSeq !== state.readSeq) return;
var previous = state.pages.length ? state.pages[state.pages.length - 1].url : state.first.url;
state.forwardLinks.set(pageKey(previous), pageKey(page.url));
if (previous.pathname !== page.url.pathname && state.chapterOrders.has(previous.pathname) &&
!state.chapterOrders.has(page.url.pathname)) {
state.chapterOrders.set(page.url.pathname, state.chapterOrders.get(previous.pathname) + 1);
try { localStorage.setItem(chapterOrderKey, JSON.stringify(Array.from(state.chapterOrders))); } catch (_) {}
}
page.chapterStart = previous.pathname !== page.url.pathname;
rememberChapterTitle(page);
delete page.content;
state.pages.push(page); state.seen.add(pageKey(page.url)); state.next = page.next;
appendPage(state.source, page);
if (activeReader()) { appendPage(state.content, page); markProgressParagraphs(); }
} catch (error) {
if (readSeq === state.readSeq) {
halt(error.name === 'AbortError' ? '下一页网络超时,自动接续已停止' : error.message || '自动接续失败');
}
} finally {
clearTimeout(timeout);
if (readSeq === state.readSeq) { state.busy = false; updateStatus(); compactReadPages(); }
// 等下一次读近底部的滚动,不在一次操作后递归预抓整本书。
}
}
function bindReader() {
var reader = document.querySelector('.via-reader-body');
var content = reader && reader.querySelector('.via-reader-content');
if (reader && content) {
state.autoEntryDone = true;
if (state.autoEntryTimer) { clearTimeout(state.autoEntryTimer); state.autoEntryTimer = null; }
}
if (!state.source || !reader || !content) {
if (state.reader) {
state.reader.removeEventListener('scroll', schedule);
state.reader.removeEventListener('scroll', scheduleProgress);
}
state.reader = state.content = state.status = state.paper = state.toolbar = state.toolbarHandle = null;
state.tocPanel = state.tocList = state.tocPages = state.tocStatus = null;
state.tocOpen = false; state.tocSeq++;
state.progressPanel = state.progressList = state.progressStatus = null;
state.progressOpen = false; state.progressSeq++;
stopCatalogueRequest();
if (state.timer) { clearTimeout(state.timer); state.timer = null; }
if (state.progressTimer) { clearTimeout(state.progressTimer); state.progressTimer = null; }
return;
}
if (reader === state.reader && content === state.content) return;
if (state.reader) {
state.reader.removeEventListener('scroll', schedule);
state.reader.removeEventListener('scroll', scheduleProgress);
}
state.reader = reader; state.content = content;
state.progressTouched = false;
installThemeControls(reader);
hideReaderNavigation(reader, content);
state.pages.forEach(function (page) { appendPage(state.source, page); appendPage(content, page); });
markProgressParagraphs();
recordProgress({ url: state.first.url.href, index: 0, ratio: 0,
time: Date.now(), title: state.first.title }, false, false);
// 状态行在正文外,不改变阅读界面,也不混进下一次 Readability 正文抽取。
var status = document.createElement('div'); status.setAttribute('data-bm-pager-status', '');
status.setAttribute('role', 'status');
status.style.cssText = 'font-size:.85em;line-height:1.8;margin:12px 16px 24px;';
status.hidden = true; content.parentElement.appendChild(status); state.status = status;
reader.addEventListener('scroll', schedule, { passive: true });
reader.addEventListener('scroll', scheduleProgress, { passive: true });
restorePendingProgress();
updateStatus(); schedule();
}
function start() {
var initial = readUrl(location.href);
var first;
try { first = extract(document, initial); }
catch (_) {
try { localStorage.__via_reader_enabled__ = null; } catch (ignored) {}
return;
}
state.source = first.content; state.first = first; state.next = first.next;
rememberChapterTitle(first);
installReaderStyle();
state.source.setAttribute('data-bm-page', pageKey(initial)); state.seen.add(pageKey(initial));
// 原节点保留,只把网站编码的文字还原,供 Via 自身 Readability 抽取。
var nodes = state.source.querySelectorAll('p[data-obf]');
for (var i = 0; i < nodes.length; i++) {
if (!hidden(nodes[i], state.source)) nodes[i].textContent = decode(nodes[i].getAttribute('data-obf'));
}
var sourceParagraphs = state.source.querySelectorAll('p');
for (var j = 0; j < sourceParagraphs.length; j++) {
if (isChapterArtifact(sourceParagraphs[j].textContent)) sourceParagraphs[j].remove();
}
// 只有完整章节正文请求 Via 自动开启;原生阅读脚本打开后会清除标记。
if (!document.querySelector('.via-reader-body') &&
document.querySelector('.conC h1.title') && document.querySelector('.readPage')) {
try { localStorage.__via_reader_enabled__ = '1'; } catch (_) {}
requestAutoReader();
}
new MutationObserver(bindReader).observe(document.body, { childList: true, subtree: true });
window.addEventListener('resize', schedule, { passive: true });
document.addEventListener('visibilitychange', function () {
if (document.hidden && (state.progressTouched || !normalizeProgress(state.savedProgress))) saveProgressNow();
else schedule();
});
window.addEventListener('pagehide', function () {
if (state.progressTouched || !normalizeProgress(state.savedProgress)) saveProgressNow();
});
bindReader();
}
function requestAutoReader() {
if (state.autoEntryDone || state.autoEntryTimer) return;
state.autoEntryTimer = setTimeout(function () {
state.autoEntryTimer = null;
if (state.autoEntryDone || !state.first || !document.body) return;
state.autoEntryDone = true;
if (document.querySelector('.via-reader-body')) { bindReader(); return; }
var reader = document.createElement('div');
reader.className = 'via-reader-body';
reader.setAttribute('data-bm-auto-reader', '');
var frame = document.createElement('div');
frame.style.cssText = 'width:100%;box-sizing:border-box;font-size:' + fontSize + 'px;';
var header = document.createElement('div'); header.className = 'via-reader-header';
var title = document.createElement('h1'); title.textContent = state.first.title;
header.appendChild(title); frame.appendChild(header);
var content = document.createElement('div'); content.className = 'via-reader-content';
state.first.paragraphs.forEach(function (text) {
var paragraph = document.createElement('p'); paragraph.textContent = text; content.appendChild(paragraph);
});
frame.appendChild(content); reader.appendChild(frame);
var key = 'via-reader-data';
if (!document.body.hasAttribute(key)) document.body.setAttribute(key, document.body.style.overflow);
document.body.style.overflow = 'hidden';
var viewport = document.querySelector('meta[name="viewport"]');
if (viewport && !viewport.hasAttribute(key)) {
viewport.setAttribute(key, viewport.getAttribute('content') || '');
viewport.setAttribute('content', 'width=device-width');
}
try { localStorage.__via_reader_enabled__ = null; } catch (_) {}
document.body.appendChild(reader);
bindReader();
}, 500);
}
function exitAssistedReader() {
var reader = state.reader;
if (!reader || !reader.hasAttribute('data-bm-auto-reader')) return;
if (state.progressOpen) closeProgressPanel();
saveProgressNow();
state.autoEntryDone = true;
if (state.autoEntryTimer) { clearTimeout(state.autoEntryTimer); state.autoEntryTimer = null; }
var key = 'via-reader-data';
if (document.body.hasAttribute(key)) {
document.body.style.overflow = document.body.getAttribute(key);
document.body.removeAttribute(key);
}
var viewport = document.querySelector('meta[name="viewport"]');
if (viewport && viewport.hasAttribute(key)) {
viewport.setAttribute('content', viewport.getAttribute(key)); viewport.removeAttribute(key);
}
reader.remove(); bindReader();
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', start, { once: true });
else start();
})();
$ tail -f comments.log
Comments [0]
// no comments yet