initial commit

This commit is contained in:
2026-09-21 13:41:40 +09:00
commit 855c7328df
411 changed files with 85352 additions and 0 deletions
+516
View File
@@ -0,0 +1,516 @@
(function () {
"use strict";
// This fixed program runs only in Qt's isolated ApplicationWorld. It adds
// no DOM nodes, IDs, bridges, document scripts, or host capabilities.
if (globalThis.__docviewReader) return;
const MAX_INDEX = 2000;
let headings = [];
let options = { fixed: false, rtl: false, publisherStyle: true };
let originalTypography = null;
let focusedLink = null;
let linkOutline = null;
let restoredRange = null;
let layoutAnchor = null, resourceRestoreFrame = 0;
let userScrollPending = false, userScrollFrame = 0;
let navigationTargets = [], navigationRevision = "";
let resourceIssues = Object.create(null);
// Only aggregate directive classes. Never copy blockedURI, sourceFile,
// originalPolicy or sample into the application or its saved state.
document.addEventListener("securitypolicyviolation", event => {
if (!event.isTrusted || event.disposition !== "enforce") return;
const directive = String(event.effectiveDirective || "").split("-")[0];
const category = ({img:"image", style:"style", font:"font", script:"script", frame:"frame",
child:"frame", connect:"connect", form:"form", base:"base", object:"object", media:"media"})[directive] || "other";
const key = "csp." + category;
resourceIssues[key] = Math.min(999999, (resourceIssues[key] || 0) + 1);
});
function text(node, limit = 512) {
return String(node ? node.textContent || "" : "").replace(/\s+/g, " ").trim().slice(0, limit);
}
function visible(element) {
if (!element || !element.isConnected || element.hidden || element.closest("[hidden],[aria-hidden='true']")) return false;
const style = getComputedStyle(element);
return style.display !== "none" && style.visibility !== "hidden" && style.visibility !== "collapse" && element.getClientRects().length > 0;
}
function axis() {
const style = getComputedStyle(document.body || document.documentElement);
const vertical = /^(vertical|sideways)/.test(style.writingMode);
return { vertical, reverse: vertical && /-rl$/.test(style.writingMode), writingMode: style.writingMode };
}
function progression() {
const root = document.scrollingElement || document.documentElement;
const a = axis();
const total = a.vertical ? root.scrollWidth - innerWidth : root.scrollHeight - innerHeight;
const position = a.vertical ? (a.reverse ? -scrollX : scrollX) : scrollY;
return total > 0 ? Math.max(0, Math.min(1, position / total)) : 0;
}
function setProgression(value) {
const root = document.scrollingElement || document.documentElement;
const a = axis();
const p = Math.max(0, Math.min(1, Number(value) || 0));
const total = a.vertical ? root.scrollWidth - innerWidth : root.scrollHeight - innerHeight;
if (a.vertical) scrollTo({ left: Math.max(0, total) * p * (a.reverse ? -1 : 1), top: 0, behavior: "instant" });
else scrollTo({ left: 0, top: Math.max(0, total) * p, behavior: "instant" });
}
function hrefFor(reference, base = document.baseURI) {
try {
const url = new URL(reference, base);
if (url.protocol !== "doc:" || url.host !== location.host || url.username || url.password) return "";
return url.pathname.replace(/^\//, "") + url.search + url.hash;
} catch (_) { return ""; }
}
function headingIndex() {
headings = [];
const nodes = document.querySelectorAll("h1,h2,h3,h4,h5,h6,[role='heading']");
let truncated = false;
for (const node of nodes) {
if (!visible(node)) continue;
const name = node.localName.toLowerCase();
const nativeLevel = /^h[1-6]$/.test(name) ? Number(name[1]) : 0;
const ariaLevel = Number(node.getAttribute("aria-level"));
const level = nativeLevel || (Number.isInteger(ariaLevel) && ariaLevel > 0 && ariaLevel <= 6 ? ariaLevel : 0);
if (!level) continue;
if (headings.length === MAX_INDEX) { truncated = true; break; }
headings.push({ element: node, level, title: text(node) || "見出し(レベル" + level + ")" });
}
return { truncated, rows: headings.map((h, index) => ({ index, title: h.title, level: h.level, depth: h.level - 1,
href: h.element.id ? hrefFor("#" + encodeURIComponent(h.element.id), location.href) : "", source: "html-heading", precision: "exact" })) };
}
function index() {
const result = headingIndex();
const outline = [];
const navs = document.querySelectorAll("[role='doc-toc'],nav");
for (const nav of navs) {
// XHTML keeps epub:type in its XML namespace; HTML retains the
// prefixed attribute name. CSS attribute selectors do not match both.
const epubType = nav.getAttributeNS("http://www.idpf.org/2007/ops", "type") || nav.getAttribute("epub:type") || "";
if (nav.getAttribute("role") !== "doc-toc" && !epubType.split(/\s+/).includes("toc")) continue;
for (const link of nav.querySelectorAll("a[href]")) {
if (!visible(link) || outline.length >= MAX_INDEX) continue;
let depth = 0;
for (let parent = link.parentElement; parent && parent !== nav; parent = parent.parentElement)
if (parent.localName.toLowerCase() === "ol" || parent.localName.toLowerCase() === "ul") ++depth;
const href = hrefFor(link.getAttribute("href"));
outline.push({ title: text(link) || "目次項目", href, depth: Math.max(0, depth - 1), missing: !href, source: "html-toc" });
}
}
return { title: String(document.title || "").slice(0, 4096), headings: result.rows, outline,
truncated: result.truncated, scriptCount: document.scripts.length,
fixedViewport: viewport(), writingMode: axis().writingMode };
}
function viewport() {
const element = document.querySelector("meta[name='viewport']");
const value = element ? element.getAttribute("content") || "" : "";
const w = /(?:^|[,;\s])width\s*=\s*([0-9.]+)/i.exec(value);
const h = /(?:^|[,;\s])height\s*=\s*([0-9.]+)/i.exec(value);
let width = w ? Number(w[1]) : 0, height = h ? Number(h[1]) : 0;
if ((!width || !height) && document.documentElement.localName === "svg") {
const box = document.documentElement.viewBox && document.documentElement.viewBox.baseVal;
if (box) { width = box.width; height = box.height; }
}
return width > 0 && height > 0 && width <= 100000 && height <= 100000 ? { width, height } : {};
}
function cfiFor(node, offset = 0) {
const steps = [];
let target = node;
if (!target) return "";
if (target.nodeType === Node.TEXT_NODE) {
let elements = 0, previousText = 0;
for (const sibling of target.parentNode.childNodes) {
if (sibling === target) break;
if (sibling.nodeType === Node.ELEMENT_NODE) { ++elements; previousText = 0; }
if (sibling.nodeType === Node.TEXT_NODE) previousText += sibling.length;
}
steps.unshift(elements * 2 + 1);
offset += previousText;
target = target.parentElement;
}
while (target && target !== document.documentElement && steps.length < 128) {
let order = 1;
for (let sibling = target.previousElementSibling; sibling; sibling = sibling.previousElementSibling) ++order;
steps.unshift(order * 2);
target = target.parentElement;
}
return target === document.documentElement ? "epubcfi(/" + steps.join("/") + ":" + Math.max(0, offset) + ")" : "";
}
function resolveCfi(cfi) {
if (typeof cfi !== "string" || cfi.length > 4096 || !/^epubcfi\(.*\)$/.test(cfi)) return null;
let value = cfi.slice(8, -1);
if (value.includes("!")) value = value.slice(value.lastIndexOf("!") + 1);
value = value.replace(/\[(?:\^.|[^\]])*\]/g, "");
if (!/^\/(?:[1-9][0-9]*\/)*[1-9][0-9]*(?::[0-9]+)?$/.test(value)) return null;
const parts = value.split(":");
const steps = parts[0].slice(1).split("/").map(Number);
if (steps.length > 128) return null;
let node = document.documentElement;
let offset = Number(parts[1] || 0);
for (let i = 0; i < steps.length; ++i) {
const step = steps[i];
if (step % 2 === 0) node = node && node.children && node.children[step / 2 - 1];
else {
if (!node || i !== steps.length - 1) return null;
const slot = (step - 1) / 2;
let elements = 0, found = null;
for (const child of node.childNodes) {
if (child.nodeType === Node.ELEMENT_NODE) ++elements;
else if (child.nodeType === Node.TEXT_NODE && elements === slot) {
if (offset <= child.length) { found = child; break; }
offset -= child.length;
}
}
node = found;
}
if (!node) return null;
}
const range = document.createRange();
if (node.nodeType === Node.TEXT_NODE) range.setStart(node, Math.min(offset, node.length));
else range.setStart(node, Math.min(offset, node.childNodes.length));
range.collapse(true);
return range;
}
function visibleRange() {
const a = axis();
const x = Math.max(1, Math.min(innerWidth - 1, a.vertical && a.reverse ? innerWidth - 20 : 20));
const y = Math.max(1, Math.min(innerHeight - 1, 20));
let range = document.caretRangeFromPoint ? document.caretRangeFromPoint(x, y) : null;
if (range && range.startContainer.nodeType === Node.TEXT_NODE) return range;
const element = document.elementFromPoint(x, y) || document.body || document.documentElement;
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT);
let node, inspected = 0;
while ((node = walker.nextNode()) && ++inspected < 1000) {
if (!node.textContent.trim() || !visible(node.parentElement)) continue;
range = document.createRange(); range.setStart(node, 0); range.collapse(true); return range;
}
range = document.createRange(); range.selectNodeContents(element); range.collapse(true); return range;
}
function locate() {
// A font can change layout before its loadingdone event, and a location
// poll can run before the image's animation-frame restoration. Retain
// the pre-layout anchor until that restoration has used it.
if (layoutAnchor && layoutMoved(layoutAnchor)) resourcesSettled();
// Reflow can put a different character at the viewport sampling point.
// Keep the restored logical character until the reader actually scrolls.
const keep = restoredRange && document.documentElement.contains(restoredRange.range.startContainer) &&
Math.abs(scrollX - restoredRange.x) < 1 && Math.abs(scrollY - restoredRange.y) < 1;
if (!keep) restoredRange = null;
const range = keep ? restoredRange.range : visibleRange();
const node = range.startContainer;
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
const rect = range.getBoundingClientRect();
const result = { url: location.href, cfi: cfiFor(node, range.startOffset), fragment: element && element.id || "",
progression: progression(), textQuote: node.nodeType === Node.TEXT_NODE ? node.textContent.slice(range.startOffset, range.startOffset + 160) : text(element, 160),
offsetX: rect.left / Math.max(1, innerWidth), offsetY: rect.top / Math.max(1, innerHeight), writingMode: axis().writingMode,
currentOutline: currentNavigation(range), navigationRevision };
if (!resourceRestoreFrame) layoutAnchor = { range: range.cloneRange(), saved: result,
x:scrollX, y:scrollY, documentX:rect.left + scrollX, documentY:rect.top + scrollY,
width:innerWidth, height:innerHeight };
return result;
}
function layoutMoved(anchor) {
if (!anchor.range.startContainer.isConnected || anchor.width !== innerWidth || anchor.height !== innerHeight) return false;
const rect = anchor.range.getBoundingClientRect();
return Math.abs(rect.left + scrollX - anchor.documentX) > 1 || Math.abs(rect.top + scrollY - anchor.documentY) > 1;
}
function cancelResourceRestore() {
if (resourceRestoreFrame) cancelAnimationFrame(resourceRestoreFrame);
resourceRestoreFrame = 0;
layoutAnchor = null;
}
function resourcesSettled() {
if (resourceRestoreFrame || !layoutAnchor || options.fixed) return;
const anchor = layoutAnchor;
resourceRestoreFrame = requestAnimationFrame(() => {
resourceRestoreFrame = 0;
// Retain the character and its viewport fraction across a decoded
// image or font swap. Never insert helper nodes into the source DOM.
// Clear the old snapshot before locate(), otherwise it would see
// the same completed layout change and schedule another frame.
const current = layoutAnchor === anchor;
if (current) layoutAnchor = null;
if (current && anchor.range.startContainer.isConnected)
positionRange(anchor.range, anchor.saved);
userScrollPending = false;
locate();
});
}
document.addEventListener("load", event => {
if (event.isTrusted && event.target instanceof HTMLImageElement) resourcesSettled();
}, true);
document.addEventListener("error", event => {
if (event.isTrusted && event.target instanceof HTMLImageElement) resourcesSettled();
}, true);
if (document.fonts) {
document.fonts.addEventListener("loadingdone", resourcesSettled);
document.fonts.addEventListener("loadingerror", resourcesSettled);
}
for (const type of ["wheel", "touchstart", "touchmove", "pointerdown", "pointermove", "keydown"])
document.addEventListener(type, event => {
if (!event.isTrusted || (type === "pointermove" && !event.buttons)) return;
cancelResourceRestore();
// Clicking without scrolling must not leave a 400 ms gap without
// any anchor. A following input scroll still takes precedence.
if (document.body) locate();
userScrollPending = true;
// Input intent applies to this frame, not to an unrelated native
// anchor adjustment long after a click that did not scroll.
if (userScrollFrame) cancelAnimationFrame(userScrollFrame);
userScrollFrame = requestAnimationFrame(() => { userScrollFrame = 0; userScrollPending = false; });
}, {capture:true, passive:true});
document.addEventListener("scroll", () => {
if (layoutAnchor && Math.abs(scrollX - layoutAnchor.x) < 1 && Math.abs(scrollY - layoutAnchor.y) < 1) return;
if (!userScrollPending && layoutAnchor && layoutMoved(layoutAnchor)) {
// Native overflow-anchor can move the scroll offset as resources
// reflow. This is not a new reader navigation.
resourcesSettled();
return;
}
userScrollPending = false;
cancelResourceRestore();
restoredRange = null;
locate();
}, {passive:true});
window.addEventListener("hashchange", () => { cancelResourceRestore(); locate(); });
function configureNavigation(args) {
navigationTargets = []; navigationRevision = "";
if (!args || typeof args.revision !== "string" || !/^[0-9]{1,19}$/.test(args.revision) ||
!Array.isArray(args.targets) || args.targets.length > 20000) return false;
const targets = [];
for (const item of args.targets) {
if (!item || !Number.isInteger(item.row) || item.row < 0 || item.row >= 20000) return false;
let element;
if (Number.isInteger(item.heading) && item.heading >= 0 && item.heading < headings.length)
element = headings[item.heading].element;
else if (typeof item.fragment === "string" && item.fragment.length <= 2048)
element = item.fragment ? document.getElementById(item.fragment) : document.body || document.documentElement;
if (!visible(element)) continue;
const range = document.createRange(); range.selectNodeContents(element); range.collapse(true);
targets.push({ row:item.row, element, range });
}
targets.sort((a, b) => a.range.compareBoundaryPoints(Range.START_TO_START, b.range) || a.row - b.row);
navigationTargets = targets; navigationRevision = args.revision;
return true;
}
function currentNavigation(position) {
// DOM order is stable across zoom/reflow. Avoid measuring every target's
// layout at each 400 ms location poll, even for a 20,000-item EPUB nav.
let low = 0, high = navigationTargets.length;
while (low < high) {
const mid = (low + high) >>> 1;
if (navigationTargets[mid].range.compareBoundaryPoints(Range.START_TO_START, position) <= 0) low = mid + 1;
else high = mid;
}
for (let i = low - 1; i >= 0; --i)
if (visible(navigationTargets[i].element)) return navigationTargets[i].row;
return -1;
}
function positionRange(range, saved) {
const node = range.startContainer;
const element = node.nodeType === Node.ELEMENT_NODE ? node : node.parentElement;
if (!element || !visible(element)) return false;
element.scrollIntoView({ block: "start", inline: "start", behavior: "instant" });
const rect = range.getBoundingClientRect();
const a = axis();
const offset = a.vertical ? Number(saved.offsetX) : Number(saved.offsetY);
const fraction = Number.isFinite(offset) && Math.abs(offset) <= 2 ? offset : 0;
if (a.vertical) scrollBy({ left: rect.left - fraction * innerWidth, behavior: "instant" });
else scrollBy({ top: rect.top - fraction * innerHeight, behavior: "instant" });
restoredRange = { range: range.cloneRange(), x: scrollX, y: scrollY };
return true;
}
function restore(saved) {
if (!saved || typeof saved !== "object") return { restored: false, approximate: true };
let range = resolveCfi(saved.cfi);
if (range && positionRange(range, saved)) return { restored: true, approximate: false };
const fragment = typeof saved.fragment === "string" ? saved.fragment.slice(0, 1024) : "";
const element = fragment ? document.getElementById(fragment) : null;
if (element && visible(element)) {
restoredRange = null;
element.scrollIntoView({ block: "start", inline: "start", behavior: "instant" });
// A failed character-level CFI resolved only to its containing ID.
return { restored: true, approximate: Boolean(saved.cfi) };
}
const quote = typeof saved.textQuote === "string" ? saved.textQuote.slice(0, 160) : "";
if (quote) {
const walker = document.createTreeWalker(document.body || document.documentElement, NodeFilter.SHOW_TEXT);
let node, inspected = 0;
while ((node = walker.nextNode()) && ++inspected < 100000) {
const offset = node.textContent.indexOf(quote);
if (offset >= 0 && visible(node.parentElement)) {
range = document.createRange(); range.setStart(node, offset); range.collapse(true);
if (positionRange(range, saved)) return { restored: true, approximate: true };
}
}
}
setProgression(saved.progression);
return { restored: true, approximate: true };
}
function heading(direction) {
if (!headings.length) headingIndex();
const a = axis();
const candidates = direction > 0 ? headings : headings.slice().reverse();
const seen = new Set();
for (const h of candidates) {
const rect = h.element.getBoundingClientRect();
const distance = a.vertical ? (a.reverse ? innerWidth - rect.right : rect.left) : rect.top;
const position = Math.round(distance);
if (seen.has(position)) continue;
seen.add(position);
if ((direction > 0 && distance > 3) || (direction < 0 && distance < -3)) {
const x = scrollX, y = scrollY;
h.element.scrollIntoView({ block: "start", inline: "start", behavior: "instant" });
// Several final headings can clamp to the same destination.
// Check the current layout's actual result, so a repeated
// destination cannot masquerade as movement or block a boundary.
const after = h.element.getBoundingClientRect();
if (Math.abs(scrollX - x) < 1 && Math.abs(scrollY - y) < 1 &&
Math.abs(after.left - rect.left) < 1 && Math.abs(after.top - rect.top) < 1) continue;
return { moved: true, title: h.title };
}
}
return { moved: false, boundary: direction, empty: !headings.length };
}
function applyOptions(next) {
options = Object.assign({}, options, next || {});
if (!options.fixed && options.publisherStyle === false) {
const size = Number(options.fontSize), height = Number(options.lineHeight);
if (!originalTypography) {
const body = document.body || document.documentElement;
const base = parseFloat(getComputedStyle(body).fontSize) || 16;
const nodes = [document.documentElement, body];
// Inline priorities override authored fixed-pixel declarations.
// Relative heading/ruby sizes survive; DOM nodes and CFI paths do not change.
for (const node of body.querySelectorAll("*")) {
if (node.namespaceURI !== "http://www.w3.org/1999/xhtml") continue;
if (Array.from(node.childNodes).some(child => child.nodeType === Node.TEXT_NODE && child.textContent.trim())) nodes.push(node);
}
originalTypography = Array.from(new Set(nodes)).map(node => ({ node,
hadStyle: node.hasAttribute("style"),
font: node.style.getPropertyValue("font-size"), fontPriority: node.style.getPropertyPriority("font-size"),
line: node.style.getPropertyValue("line-height"), linePriority: node.style.getPropertyPriority("line-height"),
ratio: node === document.documentElement || node === body ? 1 : (parseFloat(getComputedStyle(node).fontSize) || base) / base }));
}
for (const entry of originalTypography) {
if (size >= 12 && size <= 40) entry.node.style.setProperty("font-size", size * entry.ratio + "px", "important");
if (height >= 1 && height <= 2.5) entry.node.style.setProperty("line-height", String(height), "important");
}
} else if (originalTypography) {
for (const entry of originalTypography) {
if (entry.font) entry.node.style.setProperty("font-size", entry.font, entry.fontPriority);
else entry.node.style.removeProperty("font-size");
if (entry.line) entry.node.style.setProperty("line-height", entry.line, entry.linePriority);
else entry.node.style.removeProperty("line-height");
if (!entry.hadStyle && !entry.node.getAttribute("style")) entry.node.removeAttribute("style");
}
originalTypography = null;
}
return { fixed: !!options.fixed, fixedViewport: viewport() };
}
function restoreLinkOutline() {
if (!focusedLink || !linkOutline) return;
for (const entry of linkOutline.properties) {
if (entry.value) focusedLink.style.setProperty(entry.name, entry.value, entry.priority);
else focusedLink.style.removeProperty(entry.name);
}
if (!linkOutline.hadStyle && !focusedLink.getAttribute("style")) focusedLink.removeAttribute("style");
linkOutline = null;
}
function clearLinkSelection() {
restoreLinkOutline();
focusedLink = null;
// selectedLink also supports a natively focused anchor. Clear both
// routes so Enter cannot follow a link after selection is cancelled.
const active = document.activeElement;
if (active && active.localName === "a") active.blur();
return { cleared: true };
}
function focusLink(direction) {
const links = Array.from(document.querySelectorAll("a[href]")).filter(visible).slice(0, 20000);
if (!links.length) return { empty: true };
const current = links.indexOf(focusedLink || document.activeElement);
const index = current < 0 ? (direction > 0 ? 0 : links.length - 1) : (current + direction + links.length) % links.length;
restoreLinkOutline();
focusedLink = links[index];
linkOutline = { hadStyle: focusedLink.hasAttribute("style"),
properties: ["outline-color", "outline-style", "outline-width", "outline-offset"].map(name => ({
name, value: focusedLink.style.getPropertyValue(name), priority: focusedLink.style.getPropertyPriority(name)
})) };
focusedLink.style.outline = "2px solid #008577";
focusedLink.style.outlineOffset = "3px";
focusedLink.focus({ preventScroll: false });
return { focused: true, title: text(focusedLink), index, count: links.length };
}
function selectedLink() {
const element = focusedLink && focusedLink.isConnected ? focusedLink : document.activeElement;
if (!element || element.localName !== "a" || !element.hasAttribute("href")) return { empty: true };
try {
const url = new URL(element.getAttribute("href"), document.baseURI);
if (!["doc:", "http:", "https:"].includes(url.protocol) || url.username || url.password) return { blocked: true };
if (url.protocol === "doc:" && url.host !== location.host) return { blocked: true };
return { url: url.href.slice(0, 8192), title: text(element) };
} catch (_) { return { blocked: true }; }
}
function executeCommand(id, args) {
args = args && typeof args === "object" ? args : {};
const a = axis();
const step = Math.max(1, Math.min(100, Number(args.count) || 1));
switch (id) {
case "resource.issues": {
const result = resourceIssues;
resourceIssues = Object.create(null);
return result;
}
case "index": return index();
case "location": return locate();
case "navigation.configure": return configureNavigation(args);
case "location.restore": return restore(args);
case "layout": return applyOptions(args);
case "link.next": return focusLink(1);
case "link.previous": return focusLink(-1);
case "link.clear": return clearLinkSelection();
case "link.activate": return selectedLink();
case "scroll.down": scrollBy({ top: 48 * step, behavior: "instant" }); break;
case "scroll.up": scrollBy({ top: -48 * step, behavior: "instant" }); break;
case "scroll.left": scrollBy({ left: -48 * step, behavior: "instant" }); break;
case "scroll.right": scrollBy({ left: 48 * step, behavior: "instant" }); break;
case "scroll.half_down": case "scroll.half_up": case "scroll.page_down": case "scroll.page_up": {
const forward = id.endsWith("down") ? 1 : -1;
const size = id.includes("half") ? 0.5 : 0.9;
scrollBy(a.vertical ? { left: innerWidth * size * forward * (a.reverse ? -1 : 1), behavior: "instant" }
: { top: innerHeight * size * forward, behavior: "instant" }); break;
}
case "nav.document_start": setProgression(0); break;
case "nav.document_end": setProgression(1); break;
case "nav.heading_next": return heading(1);
case "nav.heading_previous": return heading(-1);
case "heading.first": if (!headings.length) headingIndex(); if (headings.length) headings[0].element.scrollIntoView({block:"start",inline:"start"}); return {moved:!!headings.length};
case "heading.last": if (!headings.length) headingIndex(); if (headings.length) headings[headings.length-1].element.scrollIntoView({block:"start",inline:"start"}); return {moved:!!headings.length};
case "heading.index": {
const index = Number(args.index);
if (Number.isInteger(index) && index >= 0 && index < headings.length) headings[index].element.scrollIntoView({ block: "start", inline: "start", behavior: "instant" });
break;
}
default: return { handled: false };
}
return { handled: true };
}
function command(id, args) {
const passive = ["location", "index", "navigation.configure", "resource.issues"].includes(id);
if (!passive) { cancelResourceRestore(); userScrollPending = false; }
try { return executeCommand(id, args); }
finally { if (!passive && document.body) locate(); }
}
function block(event) { event.preventDefault(); event.stopImmediatePropagation(); }
document.addEventListener("submit", block, true);
document.addEventListener("reset", block, true);
document.addEventListener("beforeinput", block, true);
document.addEventListener("drop", block, true);
document.addEventListener("click", event => {
if (event.target.closest && event.target.closest("button,input,select,textarea,[contenteditable]")) block(event);
}, true);
document.addEventListener("keydown", event => {
if (event.target.closest && event.target.closest("button,input,select,textarea,[contenteditable]") && !(event.ctrlKey && /^(a|c)$/i.test(event.key))) block(event);
}, true);
document.addEventListener("play", event => { if (event.target.pause) event.target.pause(); }, true);
globalThis.__docviewReader = Object.freeze({ command, index, location: locate, restore });
})();