diff --git a/src/assets/lib/kookit.min.js b/src/assets/lib/kookit.min.js index 1181fb3e..94feef3f 100644 --- a/src/assets/lib/kookit.min.js +++ b/src/assets/lib/kookit.min.js @@ -1,18839 +1 @@ -import _ from 'underscore'; -import rangy from 'rangy/lib/rangy-core.js'; -import 'rangy/lib/rangy-textrange'; -import JSZip from 'jszip'; -import { unzlibSync } from 'fflate'; -import chardet from 'chardet'; -import untar from 'js-untar'; -import mammoth from 'mammoth'; -import { marked } from 'marked'; -import mhtml2html from 'mhtml2html'; - -/*! ***************************************************************************** -Copyright (c) Microsoft Corporation. - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. -***************************************************************************** */ - -function __awaiter(thisArg, _arguments, P, generator) { - function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } - return new (P || (P = Promise))(function (resolve, reject) { - function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } - function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } - function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } - step((generator = generator.apply(thisArg, _arguments || [])).next()); - }); -} - -const convertStyleNum = (value) => { - if (!value) - return 0; - return parseFloat(value + ""); -}; -const convertComputedNum = (value) => { - return parseFloat(value.substring(0, value.length - 2)); -}; -const handleIframeHeight = (element, readerMode, format, iframe, doc) => __awaiter(void 0, void 0, void 0, function* () { - yield Promise.race([ - Promise.all(Array.from([...doc.images, ...doc.querySelectorAll("image")]).map((img) => { - if (img.complete) - return Promise.resolve(img.naturalHeight !== 0); - return new Promise((resolve) => { - img.addEventListener("load", () => resolve(true)); - img.addEventListener("error", () => resolve(false)); - }); - })), - new Promise((resolve, reject) => { - setTimeout(() => { - // reject(new Error("Timeout")); - resolve("image load timeout"); - }, 10); - }), - ]); - yield handleImageSize(element, readerMode, format, doc); - handleTextStyle(doc); - if (readerMode !== "scroll") { - iframe.height = element.clientHeight + "px"; - if (readerMode === "double") { - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let pageWidth = (element.clientWidth + gap) / 2; - if (((doc.body.scrollWidth - doc.body.clientWidth) / pageWidth) % 2 === - 1) { - let tailElem = document.createElement("div"); - tailElem.setAttribute("style", "height: " + - doc.body.clientHeight + - "px; display: inline-block; width: " + - (pageWidth - gap) + - "px"); - doc.body.appendChild(tailElem); - } - } - } - else { - //fix text blocked issue under scroll readerMode, don't ask me why - iframe.height = doc.body.scrollHeight + "px"; - iframe.height = doc.body.scrollHeight + 300 + "px"; - } - // await new Promise((r) => setTimeout(r, 1)); -}); -const handleOneChapterDoc = (item, isSearch) => __awaiter(void 0, void 0, void 0, function* () { - let chapterText = ""; - if (item && item.load) { - let blob = yield fetch(yield item.load()).then((r) => r.blob()); - chapterText = yield blob.text(); - } - if (isSearch) { - return chapterText; - } - if (item && item.loadAsset) { - chapterText = yield handlePrecacheAssets(chapterText, item.loadAsset); - } - chapterText = handleImageMarker(chapterText); - return chapterText; -}); -const getImageElement = (Element) => { - return Array.from(Element.querySelectorAll("img, image")); -}; -const handlePrecacheAssets = (bookStr, loadAsset) => __awaiter(void 0, void 0, void 0, function* () { - let chapterDoc = new DOMParser().parseFromString(bookStr, "text/html"); - let imgDomList = getImageElement(chapterDoc); - for (let subindex = 0; subindex < imgDomList.length; subindex++) { - if (imgDomList[subindex].getAttribute("src")) { - imgDomList[subindex].src = yield loadAsset(imgDomList[subindex].getAttribute("src")); - } - else if (imgDomList[subindex].getAttribute("xlink:href")) { - imgDomList[subindex].setAttribute("xlink:href", yield loadAsset(imgDomList[subindex].getAttribute("xlink:href"))); - } - } - let linkList = Array.from(chapterDoc.getElementsByTagName("link")); - for (let index = 0; index < linkList.length; index++) { - const link = linkList[index]; - if (link.getAttribute("href")) { - link.href = yield loadAsset(link.getAttribute("href")); - } - } - return chapterDoc.documentElement.innerHTML; -}); -const handleImageMarker = (bookStr) => { - let chapterDoc = new DOMParser().parseFromString(bookStr, "text/html"); - let imgDomList = getImageElement(chapterDoc); - if (imgDomList.length === 0) { - return bookStr; - } - else { - for (let i = 0; i < imgDomList.length; i++) { - if (imgDomList[i].tagName === "image") { - continue; - } - var newItem = document.createElement("kookitmarker"); - var textnode = document.createTextNode("img"); - newItem.appendChild(textnode); - newItem.setAttribute("style", "visibility: hidden; position: absolute;display: inline-block; width: 0; height: 0;"); - // 找到图片元素在body中的位置,确保marker插入到body下 - let imgElement = imgDomList[i]; - // 找到包含当前图片的顶级body子元素 - let topLevelParent = imgElement; - while (topLevelParent.parentElement && - topLevelParent.parentElement !== chapterDoc.body) { - topLevelParent = topLevelParent.parentElement; - } - // 在该顶级元素后插入marker - if (topLevelParent.parentElement === chapterDoc.body) { - topLevelParent.insertAdjacentElement("afterend", newItem); - } - else { - // 如果找不到合适位置,插入到body末尾 - chapterDoc.body.appendChild(newItem); - } - } - return chapterDoc.documentElement.innerHTML; - } -}; -const createIframe = (element, scale) => { - var iframe = document.createElement("iframe"); - iframe.style.width = scale ? (scale - 0.4) * 100 + "%" : "100%"; - iframe.style.margin = "0"; - iframe.style.border = "0"; - iframe.style.padding = "0"; - iframe.style.minHeight = "calc(100% - 2px)"; - iframe.style.fontSize = "100%"; - iframe.style.font = "inherit"; - iframe.scrolling = "no"; - iframe.tabIndex = 0; - iframe.id = "kookit-iframe"; - iframe.style.verticalAlign = "baseline"; - element.innerHTML = ""; - element.appendChild(iframe); - // 控制iframe滚动到页面水平正中的位置 - if (scale) { - element.scrollLeft = element.scrollWidth / 2 - element.clientWidth / 2; - } -}; -const progressInfo = (readerMode, doc, element) => { - //TODO 是否有必要保留延时 - // if (parseInt(doc.body.scrollWidth / doc.body.clientWidth + "") === 1) { - // await new Promise((r) => setTimeout(r, 1000)); - // } - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - return { - totalPage: readerMode === "scroll" - ? Math.floor(element.scrollHeight / (element.clientHeight - 50)) - : readerMode === "single" - ? Math.round(parseFloat(doc.body.scrollWidth / (doc.body.clientWidth + gap) + "")) - : Math.round(parseFloat(doc.body.scrollWidth / (doc.body.clientWidth + gap) + "")) * 2, - currentPage: readerMode === "scroll" - ? Math.floor(element.scrollTop / (element.clientHeight - 50)) + 1 - : Math.round(parseFloat(convertStyleNum(doc.body.scrollLeft) / - (doc.body.clientWidth + gap) + - "")) + 1, - }; -}; -const handleTextStyle = (doc) => { - var _a; - let textNodes = doc.querySelectorAll("a, article, cite, div, li, p, span, pre, dt, dd, table, bold, font"); - for (let index = 0; index < textNodes.length; index++) { - const element = textNodes[index]; - if (element.className.indexOf("kookit-text") === -1 && - ((_a = element.parentElement) === null || _a === void 0 ? void 0 : _a.tagName) !== "RT") { - element.className = element.className + " kookit-text"; - } - } - let titleNodes = doc.querySelectorAll("h1, h2, h3, h4, h5, h6, title"); - for (let index = 0; index < titleNodes.length; index++) { - const element = titleNodes[index]; - if (element.className.indexOf("kookit-title") === -1) { - element.className = "kookit-title " + element.className; - } - } -}; -const getImageMeta = (url) => __awaiter(void 0, void 0, void 0, function* () { - const img = new Image(); - img.src = url; - try { - yield img.decode(); - } - catch (error) { - console.error(error); - } - return img; -}); -const handleImageSize = (element, readerMode, format, doc) => __awaiter(void 0, void 0, void 0, function* () { - var _a, _b; - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let scale = readerMode === "double" ? 2 : 1; - (element.clientWidth - gap) / scale; - let imgs = doc.querySelectorAll("img, image"); - for (let item of imgs) { - let parentItem = item.parentElement; - let maxHeight = 0; - let maxWidth = 0; - let width = item.naturalWidth; - let height = item.naturalHeight; - if (item.tagName === "image") { - let img = yield getImageMeta(item.getAttribute("xlink:href")); - width = img.naturalWidth; - height = img.naturalHeight; - } - if (format.startsWith("CB") && readerMode === "scroll") { - maxWidth = parentItem.offsetWidth; - } - else if (format.startsWith("CB") && readerMode === "single") { - maxHeight = element.clientHeight; - maxWidth = element.clientWidth; - } - else if (parentItem && - width && - height && - parentItem.clientHeight && - parentItem.clientWidth) { - let isImageScaleLargerThanElement = height / width > parentItem.clientHeight / parentItem.clientWidth; - if (isImageScaleLargerThanElement) { - maxHeight = parentItem.clientHeight; - maxWidth = parseInt((maxHeight * width) / height + ""); - } - else { - maxWidth = parentItem.clientWidth; - maxHeight = parseInt((maxWidth * height) / width + ""); - } - if (maxHeight > doc.body.clientHeight && readerMode !== "scroll") { - maxWidth = parseInt(maxWidth * (doc.body.clientHeight / maxHeight) + ""); - maxHeight = doc.body.clientHeight; - } - parentItem.style.textIndent = "0px"; - } - else if (parentItem && - parentItem.clientWidth && - parentItem.clientWidth > 0) { - maxWidth = parentItem.clientWidth; - maxHeight = parentItem.clientHeight; - parentItem.style.textIndent = "0px"; - } - else { - maxWidth = element.clientWidth; - maxHeight = element.clientHeight; - } - if (maxWidth) { - maxWidth = Math.min(readerMode === "scroll" || readerMode === "single" - ? element.clientWidth - : (element.clientWidth - gap) / 2, maxWidth); - } - else { - maxWidth = - readerMode === "scroll" || readerMode === "single" - ? element.clientWidth - : (element.clientWidth - gap) / 2; - } - if (width && height) { - if (width > height) { - maxHeight = maxWidth * (height / width); - } - else { - if (maxHeight / maxWidth > height / width) { - maxHeight = maxWidth * (height / width); - } - else { - maxWidth = maxHeight * (width / height); - } - } - } - if (maxWidth || maxHeight) { - //轻易不要改这里,很容易出问题 - item.setAttribute("style", (item.getAttribute("style") ? item.getAttribute("style") : "") + - ";" + - `max-width: ${maxWidth > 0 ? maxWidth + "px" : ""};max-height:${maxHeight > 0 ? maxHeight + "px" : ""}; margin: 0 auto; min-width: 0px; min-height: 0px; ${format.startsWith("CB") - ? `margin-left: calc(100% - ${item.clientWidth}px);` - : ""}`); - } - if (item.tagName === "image") { - (_a = item.parentElement) === null || _a === void 0 ? void 0 : _a.setAttribute("width", maxWidth); - (_b = item.parentElement) === null || _b === void 0 ? void 0 : _b.setAttribute("height", maxHeight); - } - if (format.startsWith("CB") && readerMode === "scroll") { - item.setAttribute("style", (item.getAttribute("style") ? item.getAttribute("style") : "") + - ";margin-left: 0px; width: 100%;"); - } - if (format.startsWith("CB") && readerMode !== "scroll") { - item.setAttribute("style", (item.getAttribute("style") ? item.getAttribute("style") : "") + - `;margin-left: calc(50% - ${item.getBoundingClientRect().width / 2}px);`); - } - } -}); -const handleLayout = (element, readerMode, doc) => { - let style = doc.createElement("style"); - style.id = "default-style"; - style.textContent = - "p,empty-line{display: inherit;margin-block-start: inherit;margin-block-end: inherit;margin-inline-start: inherit;margin-inline-end: inherit;}body{margin: 0px}"; - doc.head.appendChild(style); - if (readerMode === "scroll") { - return; - } - let scale = readerMode === "double" ? 2 : 1; - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - doc.body.setAttribute("style", `width: ${element.clientWidth + "px"};height: 100%;overflow-y: hidden;overflow-X: hidden;padding-left: 0px;padding-right: 0px;margin: 0px;box-sizing: border-box;touch-action:none; overscroll-behavior: none;max-width: inherit;column-fill: auto;column-gap: ${gap}px; column-width: ${(element.clientWidth - gap) / scale}px;`); -}; -function getSelectedElement(doc) { - const selection = doc.getSelection(); - if (!selection) - return null; - if (selection.rangeCount > 0) { - const range = selection.getRangeAt(0); - const selectedElement = range.startContainer.parentElement; - return selectedElement; - } - return null; -} - -const isString = (value) => { - return typeof value === "string" || value instanceof String; -}; -class GeneralParser { - constructor(book) { - this.book = book; - this.chapterList = []; - this.flattenChapters = []; - this.chapterDocList = []; - } - unescapeHtml(htmlStr) { - if (!htmlStr) - return ""; - const doc = new DOMParser().parseFromString(htmlStr, "text/html"); - return doc.documentElement.textContent || ""; - } - getChapter(toc) { - return __awaiter(this, void 0, void 0, function* () { - if (toc) { - this.chapterList = yield Promise.all(toc.map((item, index) => __awaiter(this, void 0, void 0, function* () { - let chapterIndex = index; - try { - chapterIndex = - item.href && (yield this.book.resolveHref(item.href)) - ? (yield this.book.resolveHref(item.href)).index - : chapterIndex; - } - catch (error) { - console.error(error); - } - return { - label: this.unescapeHtml(item.label) - ? this.unescapeHtml(item.label) - : chapterIndex + "", - href: item.href ? item.href : "title" + chapterIndex, - index: chapterIndex, - subitems: item.subitems ? yield this.getChapter(item.subitems) : [], - }; - }))); - } - else { - this.chapterList = yield Promise.all(this.book.sections.map((item, index) => __awaiter(this, void 0, void 0, function* () { - return { - label: this.unescapeHtml(item.label) - ? this.unescapeHtml(item.label) - : index + "", - href: item.href ? item.href : "title" + index, - index: index, - subitems: item.subitems ? yield this.getChapter(item.subitems) : [], - }; - }))); - } - this.flattenChapters = this.flatChapter(this.chapterList); - return this.chapterList; - }); - } - getChapterDoc() { - return __awaiter(this, void 0, void 0, function* () { - const chapterIndexList = this.flattenChapters.map((item) => item.index); - return this.book.sections.map((item, index) => { - if (chapterIndexList.indexOf(index) > -1) { - return { - label: this.unescapeHtml(this.flattenChapters[chapterIndexList.indexOf(index)].label), - href: this.flattenChapters[chapterIndexList.indexOf(index)].href, - text: item, - }; - } - else { - return { - label: "", - href: "", - text: item, - }; - } - }); - }); - } - flatChapter(chapters) { - let newChapter = []; - for (let i = 0; i < chapters.length; i++) { - if (chapters[i].subitems && chapters[i].subitems.length > 0) { - newChapter.push(chapters[i]); - newChapter = newChapter.concat(this.flatChapter(chapters[i].subitems)); - } - else { - newChapter.push(chapters[i]); - } - } - return newChapter; - } - getMetadata() { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - const metadata = this.book.metadata; - let author = metadata.author && - metadata.author[0] && - metadata.author[0].name && - isString(metadata.author[0].name) - ? metadata.author[0].name - : metadata.author && - metadata.author[0] && - isString(metadata.author[0]) - ? metadata.author[0] - : metadata.author && isString(metadata.author) - ? metadata.author - : ""; - try { - const blob = yield this.book.getCover(); - var reader = new FileReader(); - reader.readAsDataURL(blob); - reader.onloadend = () => { - resolve(Object.assign(Object.assign({}, metadata), { name: metadata.title, author: author, description: metadata.description, publisher: metadata.publisher, cover: reader.result })); - }; - } - catch (error) { - console.error(error); - try { - resolve(Object.assign(Object.assign({}, metadata), { name: metadata.title, author: author, description: metadata.description, publisher: metadata.publisher, cover: "" })); - } - catch (error) { - console.error(error); - reject(error); - } - } - })); - } -} - -const findIndices = (arr, f) => arr - .map((x, i, a) => f(x, i, a) ? i : null).filter(x => x != null); -const splitAt = (arr, is) => [-1, ...is, arr.length].reduce(({ xs, a }, b) => - ({ xs: xs?.concat([arr.slice(a + 1, b)]) ?? [], a: b }), {}).xs; -const concatArrays = (a, b) => - a.slice(0, -1).concat([a[a.length - 1].concat(b[0])]).concat(b.slice(1)); - -const isNumber = /\d/; -const isCFI = /^epubcfi\((.*)\)$/; -const escapeCFI = str => str.replace(/[\^[\](),;=]/g, '^$&'); - -const wrap = x => isCFI.test(x) ? x : `epubcfi(${x})`; -const unwrap = x => x.match(isCFI)?.[1] ?? x; - -const tokenizer = str => { - const tokens = []; - let state, escape, value = ''; - const push = x => (tokens.push(x), state = null, value = ''); - const cat = x => (value += x, escape = false); - for (const char of Array.from(str.trim()).concat('')) { - if (char === '^' && !escape) { - escape = true; - continue - } - if (state === '!') push(['!']); - else if (state === ',') push([',']); - else if (state === '/' || state === ':') { - if (isNumber.test(char)) { - cat(char); - continue - } else push([state, parseInt(value)]); - } else if (state === '~') { - if (isNumber.test(char) || char === '.') { - cat(char); - continue - } else push(['~', parseFloat(value)]); - } else if (state === '@') { - if (char === ':') { - push(['@', parseFloat(value)]); - state = '@'; - continue - } - if (isNumber.test(char) || char === '.') { - cat(char); - continue - } else push(['@', parseFloat(value)]); - } else if (state === '[') { - if (char === ';' && !escape) { - push(['[', value]); - state = ';'; - } else if (char === ',' && !escape) { - push(['[', value]); - state = '['; - } else if (char === ']' && !escape) push(['[', value]); - else cat(char); - continue - } else if (state?.startsWith(';')) { - if (char === '=' && !escape) { - state = `;${value}`; - value = ''; - } else if (char === ';' && !escape) { - push([state, value]); - state = ';'; - } else if (char === ']' && !escape) push([state, value]); - else cat(char); - continue - } - if (char === '/' || char === ':' || char === '~' || char === '@' - || char === '[' || char === '!' || char === ',') state = char; - } - return tokens -}; - -const findTokens = (tokens, x) => findIndices(tokens, ([t]) => t === x); - -const parser = tokens => { - const parts = []; - let state; - for (const [type, val] of tokens) { - if (type === '/') parts.push({ index: val }); - else { - const last = parts[parts.length - 1]; - if (type === ':') last.offset = val; - else if (type === '~') last.temporal = val; - else if (type === '@') last.spatial = (last.spatial ?? []).concat(val); - else if (type === ';s') last.side = val; - else if (type === '[') { - if (state === '/' && val) last.id = val; - else { - last.text = (last.text ?? []).concat(val); - continue - } - } - } - state = type; - } - return parts -}; - -// split at step indirections, then parse each part -const parserIndir = tokens => - splitAt(tokens, findTokens(tokens, '!')).map(parser); - -const parse = cfi => { - const tokens = tokenizer(unwrap(cfi)); - const commas = findTokens(tokens, ','); - if (!commas.length) return parserIndir(tokens) - const [parent, start, end] = splitAt(tokens, commas).map(parserIndir); - return { parent, start, end } -}; - -const partToString = ({ index, id, offset, temporal, spatial, text, side }) => { - const param = side ? `;s=${side}` : ''; - return `/${index}` - + (id ? `[${escapeCFI(id)}${param}]` : '') - // "CFI expressions [..] SHOULD include an explicit character offset" - + (offset != null && index % 2 ? `:${offset}` : '') - + (temporal ? `~${temporal}` : '') - + (spatial ? `@${spatial.join(':')}` : '') - + (text || (!id && side) ? '[' - + (text?.map(escapeCFI)?.join(',') ?? '') - + param + ']' : '') -}; - -const toInnerString = parsed => parsed.parent - ? [parsed.parent, parsed.start, parsed.end].map(toInnerString).join(',') - : parsed.map(parts => parts.map(partToString).join('')).join('!'); - -const toString = parsed => wrap(toInnerString(parsed)); - -const collapse = (x, toEnd) => typeof x === 'string' - ? toString(collapse(parse(x), toEnd)) - : x.parent ? concatArrays(x.parent, x[toEnd ? 'end' : 'start']) : x; - -const isTextNode = ({ nodeType }) => nodeType === 3 || nodeType === 4; -const isElementNode = ({ nodeType }) => nodeType === 1; - -// child nodes are organized such that the result is always -// [element, text, element, text, ..., element], -// regardless of the actual structure in the document; -// so multiple text nodes need to be combined, and nonexistent ones counted; -// see "Step Reference to Child Element or Character Data (/)" in EPUB CFI spec -const indexChildNodes = node => { - const nodes = Array.from(node.childNodes) - // "content other than element and character data is ignored" - .filter(node => isTextNode(node) || isElementNode(node)) - .reduce((arr, node) => { - let last = arr[arr.length - 1]; - if (!last) arr.push(node); - // "there is one chunk between each pair of child elements" - else if (isTextNode(node)) { - if (Array.isArray(last)) last.push(node); - else if (isTextNode(last)) arr[arr.length - 1] = [last, node]; - else arr.push(node); - } else { - if (isElementNode(last)) arr.push(null, node); - else arr.push(node); - } - return arr - }, []); - // "the first chunk is located before the first child element" - if (isElementNode(nodes[0])) nodes.unshift('first'); - // "the last chunk is located after the last child element" - if (isElementNode(nodes[nodes.length - 1])) nodes.push('last'); - // "'virtual' elements" - nodes.unshift('before'); // "0 is a valid index" - nodes.push('after'); // "n+2 is a valid index" - return nodes -}; - -const getNodeByIndex = (node, index) => node ? indexChildNodes(node)[index] : null; - -const partsToNode = (node, parts) => { - const { id } = parts[parts.length - 1]; - if (id) { - const el = node.ownerDocument.getElementById(id); - if (el) return { node: el, offset: 0 } - } - for (const { index } of parts) { - const newNode = getNodeByIndex(node, index); - // handle non-existent nodes - if (newNode === 'first') return { node: node.firstChild ?? node } - if (newNode === 'last') return { node: node.lastChild ?? node } - if (newNode === 'before') return { node, before: true } - if (newNode === 'after') return { node, after: true } - node = newNode; - } - const { offset } = parts[parts.length - 1]; - if (!Array.isArray(node)) return { node, offset } - // get underlying text node and offset from the chunk - let sum = 0; - for (const n of node) { - const { length } = n.nodeValue; - if (sum + length >= offset) return { node: n, offset: offset - sum } - sum += length; - } -}; - -const nodeToParts = (node, offset) => { - const { parentNode, id } = node; - const indexed = indexChildNodes(parentNode); - const index = indexed.findIndex(x => - Array.isArray(x) ? x.some(x => x === node) : x === node); - // adjust offset as if merging the text nodes in the chunk - const chunk = indexed[index]; - if (Array.isArray(chunk)) { - let sum = 0; - for (const x of chunk) { - if (x === node) { - sum += offset; - break - } else sum += x.nodeValue.length; - } - offset = sum; - } - const part = { id, index, offset }; - return parentNode !== node.ownerDocument.documentElement - ? nodeToParts(parentNode).concat(part) : [part] -}; - -const toRange = (doc, parts) => { - const startParts = collapse(parts); - const endParts = collapse(parts, true); - - const root = doc.documentElement; - const start = partsToNode(root, startParts[0]); - const end = partsToNode(root, endParts[0]); - - const range = doc.createRange(); - - if (start.before) range.setStartBefore(start.node); - else if (start.after) range.setStartAfter(start.node); - else range.setStart(start.node, start.offset); - - if (end.before) range.setEndBefore(end.node); - else if (end.after) range.setEndAfter(end.node); - else range.setEnd(end.node, end.offset); - return range -}; - -// faster way of getting CFIs for sorted elements in a single parent -const fromElements = elements => { - const results = []; - const { parentNode } = elements[0]; - const parts = nodeToParts(parentNode); - for (const [index, node] of indexChildNodes(parentNode).entries()) { - const el = elements[results.length]; - if (node === el) - results.push(toString([parts.concat({ id: el.id, index })])); - } - return results -}; - -const toElement = (doc, parts) => - partsToNode(doc.documentElement, collapse(parts)).node; - -const NS$1 = { - CONTAINER: "urn:oasis:names:tc:opendocument:xmlns:container", - XHTML: "http://www.w3.org/1999/xhtml", - OPF: "http://www.idpf.org/2007/opf", - EPUB: "http://www.idpf.org/2007/ops", - DC: "http://purl.org/dc/elements/1.1/", - DCTERMS: "http://purl.org/dc/terms/", - ENC: "http://www.w3.org/2001/04/xmlenc#", - NCX: "http://www.daisy.org/z3986/2005/ncx/", - XLINK: "http://www.w3.org/1999/xlink", - SMIL: "http://www.w3.org/ns/SMIL", -}; - -const MIME$2 = { - XML: "application/xml", - NCX: "application/x-dtbncx+xml", - XHTML: "application/xhtml+xml", - HTML: "text/html", - CSS: "text/css", - SVG: "image/svg+xml", - JS: /\/(x-)?(javascript|ecmascript)/, -}; - -// convert to camel case -const camel = (x) => - x.toLowerCase().replace(/[-:](.)/g, (_, g) => g.toUpperCase()); - -// remove leading, trailing, and excess internal whitespace -const whitespacePreLine = (str) => - str ? str.trim().replace(/\s{2,}/g, " ") : ""; - -const filterAttribute = (attr, value, isList) => - isList - ? (el) => el.getAttribute(attr)?.split(/\s/)?.includes(value) - : typeof value === "function" - ? (el) => value(el.getAttribute(attr)) - : (el) => el.getAttribute(attr) === value; - -const getAttributes = - (...xs) => - (el) => - el - ? Object.fromEntries(xs.map((x) => [camel(x), el.getAttribute(x)])) - : null; - -const getElementText$1 = (el) => whitespacePreLine(el?.textContent); - -const childGetter = (doc, ns) => { - // ignore the namespace if it doesn't appear in document at all - const useNS = doc.lookupNamespaceURI(null) === ns || doc.lookupPrefix(ns); - const f = useNS - ? (el, name) => (el) => el.namespaceURI === ns && el.localName === name - : (el, name) => (el) => el.localName === name; - return { - $: (el, name) => [...el.children].find(f(el, name)), - $$: (el, name) => [...el.children].filter(f(el, name)), - $$$: useNS - ? (el, name) => [...el.getElementsByTagNameNS(ns, name)] - : (el, name) => [...el.getElementsByTagName(ns, name)], - }; -}; - -const resolveURL = (url, relativeTo) => { - try { - // if (relativeTo.includes(":")) return new URL(url, relativeTo); - // the base needs to be a valid URL, so set a base URL and then remove it - const root = "whatever://whatever/"; - return decodeURI(new URL(url, root + relativeTo).href.replace(root, "")); - } catch (e) { - console.warn(e); - return url; - } -}; - -const isExternal = (uri) => /^(?!blob)\w+:/i.test(uri); - -// like `path.relative()` in Node.js -const pathRelative = (from, to) => { - if (!from) return to; - const as = from.replace(/\/$/, "").split("/"); - const bs = to.replace(/\/$/, "").split("/"); - const i = (as.length > bs.length ? as : bs).findIndex( - (_, i) => as[i] !== bs[i] - ); - return i < 0 - ? "" - : Array(as.length - i) - .fill("..") - .concat(bs.slice(i)) - .join("/"); -}; - -const pathDirname = (str) => str.slice(0, str.lastIndexOf("/") + 1); - -// replace asynchronously and sequentially -// same techinque as https://stackoverflow.com/a/48032528 -const replaceSeries$1 = async (str, regex, f) => { - const matches = []; - str.replace(regex, (...args) => (matches.push(args), null)); - const results = []; - for (const args of matches) results.push(await f(...args)); - return str.replace(regex, () => results.shift()); -}; - -const regexEscape = (str) => str.replace(/[-/\\^$*+?.()|[\]{}]/g, "\\$&"); - -const LANGS = { attrs: ["dir", "xml:lang"] }; -const ALTS = { - name: "alternate-script", - many: true, - ...LANGS, - props: ["file-as"], -}; -const CONTRIB = { - many: true, - ...LANGS, - props: [{ name: "role", many: true, attrs: ["scheme"] }, "file-as", ALTS], -}; -const METADATA = [ - { - name: "title", - many: true, - ...LANGS, - props: ["title-type", "display-seq", "file-as", ALTS], - }, - { - name: "identifier", - many: true, - props: [{ name: "identifier-type", attrs: ["scheme"] }], - }, - { name: "language", many: true }, - { name: "creator", ...CONTRIB }, - { name: "contributor", ...CONTRIB }, - { name: "publisher", ...LANGS, props: ["file-as", ALTS] }, - { name: "description", ...LANGS, props: [ALTS] }, - { name: "rights", ...LANGS, props: [ALTS] }, - { name: "date" }, - { name: "dcterms:modified", type: "meta" }, - { name: "subject", many: true, ...LANGS, props: ["term", "authority", ALTS] }, - { - name: "belongs-to-collection", - type: "meta", - many: true, - ...LANGS, - props: [ - "collection-type", - "group-position", - "dcterms:identifier", - "file-as", - ALTS, - { name: "belongs-to-collection", recursive: true }, - ], - }, -]; - -// NOTE: this only gets properties defined with the `refines` attribute, -// which is used in EPUB 3.0, deprecated in 3.1, then restored in 3.2; -// no support for `opf:` attributes of 2.0 and 3.1 -const getMetadata = (opf) => { - const { $, $$ } = childGetter(opf, NS$1.OPF); - - const $metadata = $(opf.documentElement, "metadata"); - const els = Array.from($metadata.children); - const getValue = (obj, el) => { - if (!el) return null; - const { props = [], attrs = [] } = obj; - const value = getElementText$1(el); - if (!props.length && !attrs.length) return value; - const id = el.getAttribute("id"); - const refines = id ? els.filter(filterAttribute("refines", "#" + id)) : []; - return Object.fromEntries( - [["value", value]] - .concat( - props.map((prop) => { - const { many, recursive } = prop; - const name = typeof prop === "string" ? prop : prop.name; - const filter = filterAttribute("property", name); - const subobj = recursive ? obj : prop; - return [ - camel(name), - many - ? refines.filter(filter).map((el) => getValue(subobj, el)) - : getValue(subobj, refines.find(filter)), - ]; - }) - ) - .concat(attrs.map((attr) => [camel(attr), el.getAttribute(attr)])) - ); - }; - const arr = els.filter(filterAttribute("refines", null)); - const metadata = Object.fromEntries( - METADATA.map((obj) => { - const { type, name, many } = obj; - const filter = - type === "meta" - ? (el) => - el.namespaceURI === NS$1.OPF && el.getAttribute("property") === name - : (el) => el.namespaceURI === NS$1.DC && el.localName === name; - return [ - camel(name), - many - ? arr.filter(filter).map((el) => getValue(obj, el)) - : getValue(obj, arr.find(filter)), - ]; - }) - ); - - const getProperties = (prefix) => - Object.fromEntries( - $$($metadata, "meta") - .filter(filterAttribute("property", (x) => x?.startsWith(prefix))) - .map((el) => [ - el.getAttribute("property").replace(prefix, ""), - getElementText$1(el), - ]) - ); - const rendition = getProperties("rendition:"); - const media = getProperties("media:"); - return { metadata, rendition, media }; -}; - -const parseNav = (doc, resolve = (f) => f) => { - const { $, $$, $$$ } = childGetter(doc, NS$1.XHTML); - const resolveHref = (href) => (href ? decodeURI(resolve(href)) : null); - const parseLI = (getType) => ($li) => { - const $a = $($li, "a") ?? $($li, "span"); - const $ol = $($li, "ol"); - const href = resolveHref($a?.getAttribute("href")); - const label = getElementText$1($a) || $a?.getAttribute("title"); - // TODO: get and concat alt/title texts in content - const result = { label, href, subitems: parseOL($ol) }; - if (getType) result.type = $a?.getAttributeNS(NS$1.EPUB, "type")?.split(/\s/); - return result; - }; - const parseOL = ($ol, getType) => - $ol ? $$($ol, "li").map(parseLI(getType)) : null; - const parseNav = ($nav, getType) => parseOL($($nav, "ol"), getType); - - const $$nav = $$$(doc, "nav"); - let toc = null, - pageList = null, - landmarks = null, - others = []; - for (const $nav of $$nav) { - const type = $nav.getAttributeNS(NS$1.EPUB, "type")?.split(/\s/) ?? []; - if (type.includes("toc")) toc ??= parseNav($nav); - else if (type.includes("page-list")) pageList ??= parseNav($nav); - else if (type.includes("landmarks")) landmarks ??= parseNav($nav, true); - else - others.push({ - label: getElementText$1($nav.firstElementChild), - type, - list: parseNav($nav), - }); - } - return { toc, pageList, landmarks, others }; -}; - -const parseNCX = (doc, resolve = (f) => f) => { - const { $, $$ } = childGetter(doc, NS$1.NCX); - const resolveHref = (href) => (href ? decodeURI(resolve(href)) : null); - const parseItem = (el) => { - const $label = $(el, "navLabel"); - const $content = $(el, "content"); - const label = getElementText$1($label); - const href = resolveHref($content.getAttribute("src")); - if (el.localName === "navPoint") { - const els = $$(el, "navPoint"); - return { label, href, subitems: els.length ? els.map(parseItem) : null }; - } - return { label, href }; - }; - const parseList = (el, itemName) => $$(el, itemName).map(parseItem); - const getSingle = (container, itemName) => { - const $container = $(doc.documentElement, container); - return $container ? parseList($container, itemName) : null; - }; - return { - toc: getSingle("navMap", "navPoint"), - pageList: getSingle("pageList", "pageTarget"), - others: $$(doc.documentElement, "navList").map((el) => ({ - label: getElementText$1($(el, "navLabel")), - list: parseList(el, "navTarget"), - })), - }; -}; - -const parseClock = (str) => { - if (!str) return; - const parts = str.split(":").map((x) => parseFloat(x)); - if (parts.length === 3) { - const [h, m, s] = parts; - return h * 60 * 60 + m * 60 + s; - } - if (parts.length === 2) { - const [m, s] = parts; - return m * 60 + s; - } - const [x, unit] = str.split(/(?=[^\d.])/); - const n = parseFloat(x); - const f = - unit === "h" ? 60 * 60 : unit === "min" ? 60 : unit === "ms" ? 0.001 : 1; - return n * f; -}; - -const parseSMIL = (doc, resolve = (f) => f) => { - const { $, $$$ } = childGetter(doc, NS$1.SMIL); - const resolveHref = (href) => (href ? decodeURI(resolve(href)) : null); - return $$$(doc, "par").map(($par) => { - const id = $($par, "text")?.getAttribute("src")?.split("#")?.[1]; - const $audio = $($par, "audio"); - return $audio - ? { - id, - audio: { - src: resolveHref($audio.getAttribute("src")), - clipBegin: parseClock($audio.getAttribute("clipBegin")), - clipEnd: parseClock($audio.getAttribute("clipEnd")), - }, - } - : { id }; - }); -}; - -const isUUID = - /([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/; - -const getUUID = (opf) => { - for (const el of opf.getElementsByTagNameNS(NS$1.DC, "identifier")) { - const [id] = getElementText$1(el).split(":").slice(-1); - if (isUUID.test(id)) return id; - } - return ""; -}; - -const getIdentifier = (opf) => - getElementText$1( - opf.getElementById(opf.documentElement.getAttribute("unique-identifier")) ?? - opf.getElementsByTagNameNS(NS$1.DC, "identifier")[0] - ); - -// https://www.w3.org/publishing/epub32/epub-ocf.html#sec-resource-obfuscation -const deobfuscate = async (key, length, blob) => { - const array = new Uint8Array(await blob.slice(0, length).arrayBuffer()); - length = Math.min(length, array.length); - for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length]; - return new Blob([array, blob.slice(length)], { type: blob.type }); -}; - -const WebCryptoSHA1 = async (str) => { - const data = new TextEncoder().encode(str); - const buffer = await globalThis.crypto.subtle.digest("SHA-1", data); - return new Uint8Array(buffer); -}; - -const deobfuscators = (sha1 = WebCryptoSHA1) => ({ - "http://www.idpf.org/2008/embedding": { - key: (opf) => - sha1( - getIdentifier(opf) - // eslint-disable-next-line no-control-regex - .replaceAll(/[\u0020\u0009\u000d\u000a]/g, "") - ), - decode: (key, blob) => deobfuscate(key, 1040, blob), - }, - "http://ns.adobe.com/pdf/enc#RC": { - key: (opf) => { - const uuid = getUUID(opf).replaceAll("-", ""); - return Uint8Array.from({ length: 16 }, (_, i) => - parseInt(uuid.slice(i * 2, i * 2 + 2), 16) - ); - }, - decode: (key, blob) => deobfuscate(key, 1024, blob), - }, -}); - -class Encryption { - #uris = new Map(); - #decoders = new Map(); - #algorithms; - constructor(algorithms) { - this.#algorithms = algorithms; - } - async init(encryption, opf) { - if (!encryption) return; - const data = Array.from( - encryption.getElementsByTagNameNS(NS$1.ENC, "EncryptedData"), - (el) => ({ - algorithm: el - .getElementsByTagNameNS(NS$1.ENC, "EncryptionMethod")[0] - ?.getAttribute("Algorithm"), - uri: el - .getElementsByTagNameNS(NS$1.ENC, "CipherReference")[0] - ?.getAttribute("URI"), - }) - ); - for (const { algorithm, uri } of data) { - if (!this.#decoders.has(algorithm)) { - const algo = this.#algorithms[algorithm]; - if (!algo) { - console.warn("Unknown encryption algorithm"); - continue; - } - const key = await algo.key(opf); - this.#decoders.set(algorithm, (blob) => algo.decode(key, blob)); - } - this.#uris.set(uri, algorithm); - } - } - getDecoder(uri) { - return this.#decoders.get(this.#uris.get(uri)) ?? ((x) => x); - } -} - -class Resources { - constructor({ opf, resolveHref }) { - this.opf = opf; - const { $, $$, $$$ } = childGetter(opf, NS$1.OPF); - const $manifest = $(opf.documentElement, "manifest"); - const $spine = $(opf.documentElement, "spine"); - const $$itemref = $$($spine, "itemref"); - - this.manifest = $$($manifest, "item") - .map( - getAttributes("href", "id", "media-type", "properties", "media-overlay") - ) - .map((item) => { - item.href = resolveHref(item.href); - item.properties = item.properties?.split(/\s/); - return item; - }); - //适配部分不标准的epub - if (this.manifest.length === 0) { - this.manifest = Array.from($manifest.children).map(item => { - const attrs = getAttributes("href", "id", "media-type", "properties", "media-overlay")(item); - attrs.href = resolveHref(attrs.href); - attrs.properties = attrs.properties?.split(/\s/); - return attrs; - }); - } - this.spine = $$itemref - .map(getAttributes("idref", "id", "linear", "properties")) - .map((item) => ((item.properties = item.properties?.split(/\s/)), item)); - this.pageProgressionDirection = $spine.getAttribute( - "page-progression-direction" - ); - - this.navPath = this.getItemByProperty("nav")?.href; - this.ncxPath = ( - this.getItemByID($spine.getAttribute("toc")) ?? - this.manifest.find((item) => item.mediaType === MIME$2.NCX) - )?.href; - - const $guide = $(opf.documentElement, "guide"); - if ($guide) - this.guide = $$($guide, "reference") - .map(getAttributes("type", "title", "href")) - .map(({ type, title, href }) => ({ - label: title, - type: type.split(/\s/), - href: resolveHref(href), - })); - this.cover = - this.getItemByProperty("cover-image") ?? - this.getItemByID("cover-image") ?? - // EPUB 2 compat - this.getItemByID( - $$$(opf, "meta") - .find(filterAttribute("name", "cover")) - ?.getAttribute("content") - ) ?? - this.getItemByID("cover") ?? - this.getItemByID("cover.jpg") ?? - this.getItemByID("cover.png") ?? - this.getItemByID("cover.jpeg") ?? - this.getItemByHref( - this.guide?.find((ref) => ref.type.includes("cover") && !ref.href.includes("html") && !ref.href.includes("xml"))?.href - ); - - this.cfis = fromElements($$itemref); - } - getItemByID(id) { - return this.manifest.find((item) => item.id === id); - } - getItemByHref(href) { - return this.manifest.find((item) => item.href === href); - } - getItemByProperty(prop) { - return this.manifest.find((item) => item.properties?.includes(prop)); - } - resolveCFI(cfi) { - const parts = parse(cfi); - const top = (parts.parent ?? parts).shift(); - let $itemref = toElement(this.opf, top); - // make sure it's an idref; if not, try again without the ID assertion - // mainly because Epub.js used to generate wrong ID assertions - // https://github.com/futurepress/epub.js/issues/1236 - if ($itemref && $itemref.nodeName !== "idref") { - top.at(-1).id = null; - $itemref = toElement(this.opf, top); - } - const idref = $itemref?.getAttribute("idref"); - const index = this.spine.findIndex((item) => item.idref === idref); - const anchor = (doc) => toRange(doc, parts); - return { index, anchor }; - } -} - -class Loader { - #cache = new Map(); - #children = new Map(); - #refCount = new Map(); - allowScript = false; - constructor({ loadText, loadBlob, resources }) { - this.loadText = loadText; - this.loadBlob = loadBlob; - this.manifest = resources.manifest; - this.assets = resources.manifest; - // needed only when replacing in (X)HTML w/o parsing (see below) - //.filter(({ mediaType }) => ![MIME.XHTML, MIME.HTML].includes(mediaType)) - } - createURL(href, data, type, parent) { - if (!data) return ""; - const url = URL.createObjectURL(new Blob([data], { type })); - this.#cache.set(href, url); - this.#refCount.set(href, 1); - if (parent) { - const childList = this.#children.get(parent); - if (childList) childList.push(href); - else this.#children.set(parent, [href]); - } - return url; - } - ref(href, parent) { - const childList = this.#children.get(parent); - if (!childList?.includes(href)) { - this.#refCount.set(href, this.#refCount.get(href) + 1); - //console.log(`referencing ${href}, now ${this.#refCount.get(href)}`) - if (childList) childList.push(href); - else this.#children.set(parent, [href]); - } - return this.#cache.get(href); - } - unref(href) { - if (!this.#refCount.has(href)) return; - const count = this.#refCount.get(href) - 1; - //console.log(`unreferencing ${href}, now ${count}`) - if (count < 1) { - //console.log(`unloading ${href}`) - URL.revokeObjectURL(this.#cache.get(href)); - this.#cache.delete(href); - this.#refCount.delete(href); - // unref children - const childList = this.#children.get(href); - if (childList) while (childList.length) this.unref(childList.pop()); - this.#children.delete(href); - } else this.#refCount.set(href, count); - } - // load manifest item, recursively loading all resources as needed - async loadItem(item, parents = []) { - if (!item) return null; - const { href, mediaType } = item; - - const isScript = MIME$2.JS.test(item.mediaType); - if (isScript && !this.allowScript) return null; - - const parent = parents.at(-1); - if (this.#cache.has(href)) return this.ref(href, parent); - - const shouldReplace = - (isScript || - [MIME$2.XHTML, MIME$2.HTML, MIME$2.CSS, MIME$2.SVG].includes(mediaType)) && - // prevent circular references - parents.every((p) => p !== href); - if (shouldReplace) return this.loadReplaced(item, parents); - return this.createURL(href, await this.loadBlob(href), mediaType, parent); - } - async loadHref(href, base, parents = []) { - if (isExternal(href)) return href; - const path = resolveURL(href, base); - let item = this.manifest.find((item) => item.href === path); - if (!item) { - item = { href: path, mediaType: "" }; - } - return this.loadItem(item, parents.concat(base)); - } - async loadReplaced(item, parents = []) { - const { href, mediaType } = item; - const parent = parents.at(-1); - const str = await this.loadText(href); - if (!str) return null; - - // note that one can also just use `replaceString` for everything: - // ``` - // const replaced = await this.replaceString(str, href, parents) - // return this.createURL(href, replaced, mediaType, parent) - // ``` - // which is basically what Epub.js does, which is simpler, but will - // break things like iframes (because you don't want to replace links) - // or text that just happen to be paths - - // parse and replace in HTML - if ([MIME$2.XHTML, MIME$2.HTML, MIME$2.SVG].includes(mediaType)) { - let doc = new DOMParser().parseFromString(str.trim(), mediaType); - // change to HTML if it's not valid XHTML - if (mediaType === MIME$2.XHTML && doc.querySelector("parsererror")) { - console.warn(doc.querySelector("parsererror").innerText); - item.mediaType = MIME$2.HTML; - doc = new DOMParser().parseFromString(str.trim(), item.mediaType); - } - // replace hrefs in XML processing instructions - // this is mainly for SVGs that use xml-stylesheet - if ([MIME$2.XHTML, MIME$2.SVG].includes(item.mediaType)) { - let child = doc.firstChild; - while (child instanceof ProcessingInstruction) { - if (child.data) { - const replacedData = await replaceSeries$1( - child.data, - /(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i, - (_, p1, p2, p3) => - this.loadHref(p2, href, parents).then((p2) => `${p1}${p2}${p3}`) - ); - child.replaceWith( - doc.createProcessingInstruction(child.target, replacedData) - ); - } - child = child.nextSibling; - } - } - // replace hrefs (excluding anchors) - // TODO: srcset? - const replace = async (el, attr) => - el.setAttribute( - attr, - await this.loadHref(el.getAttribute(attr), href, parents) - ); - for (const el of doc.querySelectorAll("link[href]")) - await replace(el, "href"); - for (const el of doc.querySelectorAll("[src]")) await replace(el, "src"); - for (const el of doc.querySelectorAll("[poster]")) - await replace(el, "poster"); - for (const el of doc.querySelectorAll("object[data]")) - await replace(el, "data"); - for (const el of doc.querySelectorAll("[*|href]:not([href]")) - el.setAttributeNS( - NS$1.XLINK, - "href", - await this.loadHref( - el.getAttributeNS(NS$1.XLINK, "href"), - href, - parents - ) - ); - // replace inline styles - for (const el of doc.querySelectorAll("style")) - if (el.textContent) - el.textContent = await this.replaceCSS(el.textContent, href, parents); - for (const el of doc.querySelectorAll("[style]")) - el.setAttribute( - "style", - await this.replaceCSS(el.getAttribute("style"), href, parents) - ); - // TODO: replace inline scripts? probably not worth the trouble - const result = new XMLSerializer().serializeToString(doc); - return this.createURL(href, result, item.mediaType, parent); - } - - const result = - mediaType === MIME$2.CSS - ? await this.replaceCSS(str, href, parents) - : await this.replaceString(str, href, parents); - return this.createURL(href, result, mediaType, parent); - } - async replaceCSS(str, href, parents = []) { - const replacedUrls = await replaceSeries$1( - str, - /url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi, - (_, url) => - this.loadHref(url, href, parents).then((url) => `url("${url}")`) - ); - // apart from `url()`, strings can be used for `@import` (but why?!) - const replacedImports = await replaceSeries$1( - replacedUrls, - /@import\s*["']([^"'\n]*?)["']/gi, - (_, url) => - this.loadHref(url, href, parents).then((url) => `@import "${url}"`) - ); - const w = window?.innerWidth ?? 800; - const h = window?.innerHeight ?? 600; - return ( - replacedImports - // unprefix as most of the props are (only) supported unprefixed - .replace(/-epub-/gi, "") - // replace vw and vh as they cause problems with layout - .replace(/(\d*\.?\d+)vw/gi, (_, d) => (parseFloat(d) * w) / 100 + "px") - .replace(/(\d*\.?\d+)vh/gi, (_, d) => (parseFloat(d) * h) / 100 + "px") - // `page-break-*` unsupported in columns; replace with `column-break-*` - .replace( - /page-break-(after|before|inside)/gi, - (_, x) => `-webkit-column-break-${x}` - ) - ); - } - // find & replace all possible relative paths for all assets without parsing - replaceString(str, href, parents = []) { - const assetMap = new Map(); - const urls = this.assets - .map((asset) => { - // do not replace references to the file itself - if (asset.href === href) return; - // href was decoded and resolved when parsing the manifest - const relative = pathRelative(pathDirname(href), asset.href); - const relativeEnc = encodeURI(relative); - const rootRelative = "/" + asset.href; - const rootRelativeEnc = encodeURI(rootRelative); - const set = new Set([ - relative, - relativeEnc, - rootRelative, - rootRelativeEnc, - ]); - for (const url of set) assetMap.set(url, asset); - return Array.from(set); - }) - .flat() - .filter((x) => x); - if (!urls.length) return str; - const regex = new RegExp(urls.map(regexEscape).join("|"), "g"); - return replaceSeries$1(str, regex, async (match) => - this.loadItem( - assetMap.get(match.replace(/^\//, "")), - parents.concat(href) - ) - ); - } - unloadItem(item) { - this.unref(item?.href); - } -} - -const getHTMLFragment = (doc, id) => - doc.getElementById(id) ?? doc.querySelector(`[name="${CSS.escape(id)}"]`); - -const getPageSpread$1 = (properties) => { - for (const p of properties) { - if (p === "page-spread-left" || p === "rendition:page-spread-left") - return "left"; - if (p === "page-spread-right" || p === "rendition:page-spread-right") - return "right"; - if (p === "rendition:page-spread-center") return "center"; - } -}; - -class EPUB { - parser = new DOMParser(); - #encryption; - constructor({ loadText, loadBlob, getSize, sha1 }) { - this.loadText = loadText; - this.loadBlob = loadBlob; - this.getSize = getSize; - this.#encryption = new Encryption(deobfuscators(sha1)); - } - #parseXML(str) { - if (str && str.includes("opf:scheme")) { - str = str.replaceAll("opf:scheme", "scheme"); - } - //error on line 41 at column 46: Comment must not contain '--' (double-hyphen) - if (str) { - // 修复常见的 XML 问题 - str = str - // 移除 BOM - .replace(/^\uFEFF/, '') - // 修复注释中的双连字符 - .replace(//g, (match, content) => { - const fixedContent = content.replace(/--/g, '- -'); - return ``; - }) - // 修复未正确转义的 & 符号(除了实体引用) - .replace(/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[\da-fA-F]+);)/g, '&') - // 移除控制字符(保留换行符、回车符、制表符) - .replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, ''); - } - - - return str ? this.parser.parseFromString(str.trim(), MIME$2.XML) : null; - } - async #loadXML(uri) { - return this.#parseXML(await this.loadText(uri)); - } - async init() { - const $container = await this.#loadXML("META-INF/container.xml"); - if (!$container) throw new Error("Failed to load container file"); - - const opfs = Array.from( - $container.getElementsByTagNameNS(NS$1.CONTAINER, "rootfile"), - getAttributes("full-path", "media-type") - ).filter((file) => file.mediaType === "application/oebps-package+xml"); - - if (!opfs.length) - throw new Error("No package document defined in container"); - const opfPath = opfs[0].fullPath; - const opf = await this.#loadXML(opfPath); - if (!opf) throw new Error("Failed to load package document"); - //当opf中包含parsererror时,说明不是标准的xml格式 - if (opf.querySelector("parsererror")) { - throw new Error("Package document is not a valid XML"); - } - - const $encryption = await this.#loadXML("META-INF/encryption.xml"); - await this.#encryption.init($encryption, opf); - - this.resources = new Resources({ - opf, - resolveHref: (url) => resolveURL(url, opfPath), - }); - const loader = new Loader({ - loadText: this.loadText, - loadBlob: (uri) => - Promise.resolve(this.loadBlob(uri)).then( - this.#encryption.getDecoder(uri) - ), - resources: this.resources, - }); - this.sections = this.resources.spine - .map((spineItem, index) => { - const { idref, linear, properties = [] } = spineItem; - const item = this.resources.getItemByID(idref); - if (!item) { - console.warn(`Could not find item with ID "${idref}" in manifest`); - return null; - } - return { - id: this.resources.getItemByID(idref)?.href, - load: () => loader.loadItem(item), - unload: () => loader.unloadItem(item), - createDocument: () => this.loadDocument(item), - size: this.getSize(item.href), - cfi: this.resources.cfis[index], - linear, - pageSpread: getPageSpread$1(properties), - resolveHref: (href) => resolveURL(href, item.href), - loadMediaOverlay: () => this.loadMediaOverlay(item), - }; - }); - - const { navPath, ncxPath } = this.resources; - if (navPath) - try { - const resolve = (url) => resolveURL(url, navPath); - const nav = parseNav(await this.#loadXML(navPath), resolve); - this.toc = nav.toc; - this.pageList = nav.pageList; - this.landmarks = nav.landmarks; - } catch (e) { - console.warn(e); - } - if ((!this.toc || this.toc.length === 0) && ncxPath) - try { - const resolve = (url) => resolveURL(url, ncxPath); - const ncx = parseNCX(await this.#loadXML(ncxPath), resolve); - this.toc = ncx.toc; - this.pageList = ncx.pageList; - } catch (e) { - console.warn(e); - } - this.landmarks ??= this.resources.guide; - - const { metadata, rendition, media } = getMetadata(opf); - this.rendition = rendition; - this.media = media; - media.duration = parseClock(media.duration); - this.dir = this.resources.pageProgressionDirection; - - this.rawMetadata = metadata; // useful for debugging, i guess - const title = metadata?.title?.[0]; - this.metadata = { - title: title?.value, - sortAs: title?.fileAs, - language: metadata?.language, - identifier: getIdentifier(opf), - description: metadata?.description?.value, - publisher: metadata?.publisher?.value, - published: metadata?.date, - modified: metadata?.dctermsModified, - subject: metadata?.subject - ?.filter(({ value, code }) => value || code) - ?.map(({ value, code, scheme }) => ({ name: value, code, scheme })), - rights: metadata?.rights?.value, - }; - const relators = { - art: "artist", - aut: "author", - bkp: "producer", - clr: "colorist", - edt: "editor", - ill: "illustrator", - trl: "translator", - pbl: "publisher", - }; - const mapContributor = (defaultKey) => (obj) => { - const keys = [ - ...new Set( - obj.role?.map( - ({ value, scheme }) => - (!scheme || scheme === "marc:relators" - ? relators[value] - : null) ?? defaultKey - ) - ), - ]; - const value = { name: obj.value, sortAs: obj.fileAs }; - return [keys?.length ? keys : [defaultKey], value]; - }; - metadata?.creator - ?.map(mapContributor("author")) - ?.concat(metadata?.contributor?.map?.(mapContributor("contributor"))) - ?.forEach(([keys, value]) => - keys.forEach((key) => { - if (this.metadata[key]) this.metadata[key].push(value); - else this.metadata[key] = [value]; - }) - ); - - return this; - } - async loadDocument(item) { - const str = await this.loadText(item.href); - return this.parser.parseFromString(str.trim(), item.mediaType); - } - async loadMediaOverlay(item) { - const id = item.mediaOverlay; - if (!id) return null; - const media = this.resources.getItemByID(id); - const doc = await this.#loadXML(media.href); - const parsed = parseSMIL(doc, (url) => resolveURL(url, media.href)); - return parsed; - } - resolveCFI(cfi) { - return this.resources.resolveCFI(cfi); - } - resolveHref(href) { - const [path, hash] = href.split("#"); - const item = this.resources.getItemByHref(decodeURI(path)); - if (!item) return null; - const index = this.resources.spine.findIndex( - ({ idref }) => idref === item.id - ); - const anchor = hash ? (doc) => getHTMLFragment(doc, hash) : () => 0; - return { index, anchor }; - } - splitTOCHref(href) { - return href?.split("#") ?? []; - } - getTOCFragment(doc, id) { - return ( - doc.getElementById(id) ?? doc.querySelector(`[name="${CSS.escape(id)}"]`) - ); - } - isExternal(uri) { - return isExternal(uri); - } - async getCover() { - const cover = this.resources?.cover; - return cover?.href - ? new Blob([await this.loadBlob(cover.href)], { type: cover.mediaType }) - : null; - } - async getCalibreBookmarks() { - const txt = await this.loadText("META-INF/calibre_bookmarks.txt"); - const magic = "encoding=json+base64:"; - if (txt?.startsWith(magic)) { - const json = atob(txt.slice(magic.length)); - return JSON.parse(json); - } - } -} - -const s2tData = { - 㐷: "傌", - 㐹: "㑶", - 㐽: "偑", - 㑇: "㑳", - 㑈: "倲", - 㑔: "㑯", - 㑩: "儸", - 㓆: "𠗣", - 㓥: "劏", - 㓰: "劃", - 㔉: "劚", - 㖊: "噚", - 㖞: "喎", - 㘎: "㘚", - 㚯: "㜄", - 㛀: "媰", - 㛟: "𡞵", - 㛠: "𡢃", - 㛣: "㜏", - 㛤: "孋", - 㛿: "𡠹", - 㟆: "㠏", - 㟜: "𡾱", - 㟥: "嵾", - 㡎: "幓", - 㤘: "㥮", - 㤽: "懤", - 㥪: "慺", - 㧏: "掆", - 㧐: "㩳", - 㧑: "撝", - 㧟: "擓", - 㧰: "擽", - 㨫: "㩜", - 㭎: "棡", - 㭏: "椲", - 㭣: "𣙎", - 㭤: "樢", - 㭴: "樫", - 㱩: "殰", - 㱮: "殨", - 㲿: "瀇", - 㳔: "濧", - 㳕: "灡", - 㳠: "澾", - 㳡: "濄", - 㳢: "𣾷", - 㳽: "瀰", - 㴋: "潚", - 㶉: "鸂", - 㶶: "燶", - 㶽: "煱", - 㺍: "獱", - 㻅: "璯", - 㻏: "𤫩", - 㻘: "𤪺", - 䀥: "䁻", - 䁖: "瞜", - 䂵: "碽", - 䃅: "磾", - 䅉: "稏", - 䅟: "穇", - 䅪: "𥢢", - 䇲: "筴", - 䉤: "籔", - 䌶: "䊷", - 䌷: "紬", - 䌸: "縳", - 䌹: "絅", - 䌺: "䋙", - 䌻: "䋚", - 䌼: "綐", - 䌽: "綵", - 䌾: "䋻", - 䌿: "䋹", - 䍀: "繿", - 䍁: "繸", - 䍠: "䍦", - 䎬: "䎱", - 䏝: "膞", - 䑽: "𦪙", - 䓓: "薵", - 䓕: "薳", - 䓖: "藭", - 䓨: "罃", - 䗖: "螮", - 䘛: "𧝞", - 䘞: "𧜗", - 䙊: "𧜵", - 䙌: "䙡", - 䙓: "襬", - 䜣: "訢", - 䜤: "鿁", - 䜥: "𧩙", - 䜧: "䜀", - 䜩: "讌", - 䝙: "貙", - 䞌: "𧵳", - 䞍: "䝼", - 䞎: "𧶧", - 䞐: "賰", - 䟢: "躎", - 䢀: "𨊰", - 䢁: "𨊸", - 䢂: "𨋢", - 䥺: "釾", - 䥽: "鏺", - 䥾: "䥱", - 䥿: "𨯅", - 䦀: "𨦫", - 䦁: "𨧜", - 䦂: "䥇", - 䦃: "鐯", - 䦅: "鐥", - 䦆: "钁", - 䦶: "䦛", - 䦷: "䦟", - 䩄: "靦", - 䭪: "𩞯", - 䯃: "𩣑", - 䯄: "騧", - 䯅: "䯀", - 䲝: "䱽", - 䲞: "𩶘", - 䲟: "鮣", - 䲠: "鰆", - 䲡: "鰌", - 䲢: "鰧", - 䲣: "䱷", - 䴓: "鳾", - 䴔: "鵁", - 䴕: "鴷", - 䴖: "鶄", - 䴗: "鶪", - 䴘: "鷉", - 䴙: "鸊", - 䶮: "龑", - 万: "萬", - 与: "與", - 丑: "醜", - 专: "專", - 业: "業", - 丛: "叢", - 东: "東", - 丝: "絲", - 丢: "丟", - 两: "兩", - 严: "嚴", - 丧: "喪", - 个: "個", - 丰: "豐", - 临: "臨", - 为: "爲", - 丽: "麗", - 举: "舉", - 么: "麼", - 义: "義", - 乌: "烏", - 乐: "樂", - 乔: "喬", - 习: "習", - 乡: "鄉", - 书: "書", - 买: "買", - 乱: "亂", - 了: "了", - 争: "爭", - 于: "於", - 亏: "虧", - 云: "雲", - 亘: "亙", - 亚: "亞", - 产: "產", - 亩: "畝", - 亲: "親", - 亵: "褻", - 亸: "嚲", - 亿: "億", - 仅: "僅", - 仆: "僕", - 仇: "仇", - 从: "從", - 仑: "侖", - 仓: "倉", - 仪: "儀", - 们: "們", - 价: "價", - 仿: "仿", - 众: "衆", - 优: "優", - 伙: "夥", - 会: "會", - 伛: "傴", - 伞: "傘", - 伟: "偉", - 传: "傳", - 伡: "俥", - 伣: "俔", - 伤: "傷", - 伥: "倀", - 伦: "倫", - 伧: "傖", - 伪: "僞", - 伫: "佇", - 体: "體", - 余: "餘", - 佛: "佛", - 佣: "傭", - 佥: "僉", - 侠: "俠", - 侣: "侶", - 侥: "僥", - 侦: "偵", - 侧: "側", - 侨: "僑", - 侩: "儈", - 侪: "儕", - 侬: "儂", - 侭: "儘", - 俊: "俊", - 俣: "俁", - 俦: "儔", - 俨: "儼", - 俩: "倆", - 俪: "儷", - 俫: "倈", - 俭: "儉", - 修: "修", - 借: "借", - 债: "債", - 倾: "傾", - 偬: "傯", - 偻: "僂", - 偾: "僨", - 偿: "償", - 傤: "儎", - 傥: "儻", - 傧: "儐", - 储: "儲", - 傩: "儺", - 僵: "僵", - 儿: "兒", - 克: "克", - 兑: "兌", - 兖: "兗", - 党: "黨", - 兰: "蘭", - 关: "關", - 兴: "興", - 兹: "茲", - 养: "養", - 兽: "獸", - 冁: "囅", - 内: "內", - 冈: "岡", - 册: "冊", - 写: "寫", - 军: "軍", - 农: "農", - 冬: "冬", - 冯: "馮", - 冲: "衝", - 决: "決", - 况: "況", - 冻: "凍", - 净: "淨", - 凄: "悽", - 准: "準", - 凉: "涼", - 凌: "凌", - 减: "減", - 凑: "湊", - 凛: "凜", - 几: "幾", - 凤: "鳳", - 凫: "鳧", - 凭: "憑", - 凯: "凱", - 凶: "兇", - 出: "出", - 击: "擊", - 凿: "鑿", - 刍: "芻", - 划: "劃", - 刘: "劉", - 则: "則", - 刚: "剛", - 创: "創", - 删: "刪", - 别: "別", - 刬: "剗", - 刭: "剄", - 刮: "刮", - 制: "制", - 刹: "剎", - 刽: "劊", - 刾: "㓨", - 刿: "劌", - 剀: "剴", - 剂: "劑", - 剐: "剮", - 剑: "劍", - 剥: "剝", - 剧: "劇", - 劝: "勸", - 办: "辦", - 务: "務", - 劢: "勱", - 动: "動", - 励: "勵", - 劲: "勁", - 劳: "勞", - 势: "勢", - 勋: "勳", - 勚: "勩", - 匀: "勻", - 匦: "匭", - 匮: "匱", - 区: "區", - 医: "醫", - 千: "千", - 升: "升", - 华: "華", - 协: "協", - 单: "單", - 卖: "賣", - 卜: "卜", - 占: "佔", - 卢: "盧", - 卤: "滷", - 卧: "臥", - 卫: "衛", - 却: "卻", - 卷: "卷", - 卺: "巹", - 厂: "廠", - 厅: "廳", - 历: "歷", - 厉: "厲", - 压: "壓", - 厌: "厭", - 厍: "厙", - 厐: "龎", - 厕: "廁", - 厘: "釐", - 厢: "廂", - 厣: "厴", - 厦: "廈", - 厨: "廚", - 厩: "廄", - 厮: "廝", - 县: "縣", - 叁: "叄", - 参: "參", - 叆: "靉", - 叇: "靆", - 双: "雙", - 发: "發", - 变: "變", - 叙: "敘", - 叠: "疊", - 只: "只", - 台: "臺", - 叶: "葉", - 号: "號", - 叹: "嘆", - 叽: "嘰", - 吁: "籲", - 吃: "喫", - 合: "合", - 吊: "吊", - 同: "同", - 后: "後", - 向: "向", - 吓: "嚇", - 吕: "呂", - 吗: "嗎", - 吨: "噸", - 听: "聽", - 启: "啓", - 吴: "吳", - 呐: "吶", - 呒: "嘸", - 呓: "囈", - 呕: "嘔", - 呖: "嚦", - 呗: "唄", - 员: "員", - 呙: "咼", - 呛: "嗆", - 呜: "嗚", - 周: "周", - 咏: "詠", - 咙: "嚨", - 咛: "嚀", - 咝: "噝", - 咤: "吒", - 咨: "諮", - 咸: "鹹", - 咽: "咽", - 哄: "哄", - 响: "響", - 哑: "啞", - 哒: "噠", - 哓: "嘵", - 哔: "嗶", - 哕: "噦", - 哗: "譁", - 哙: "噲", - 哜: "嚌", - 哝: "噥", - 哟: "喲", - 唇: "脣", - 唛: "嘜", - 唝: "嗊", - 唠: "嘮", - 唡: "啢", - 唢: "嗩", - 唤: "喚", - 啧: "嘖", - 啬: "嗇", - 啭: "囀", - 啮: "齧", - 啯: "嘓", - 啰: "囉", - 啴: "嘽", - 啸: "嘯", - 喂: "喂", - 喷: "噴", - 喽: "嘍", - 喾: "嚳", - 嗫: "囁", - 嗳: "噯", - 嘘: "噓", - 嘤: "嚶", - 嘱: "囑", - 噜: "嚕", - 噪: "噪", - 嚣: "囂", - 回: "回", - 团: "團", - 园: "園", - 困: "困", - 囱: "囪", - 围: "圍", - 囵: "圇", - 国: "國", - 图: "圖", - 圆: "圓", - 圣: "聖", - 圹: "壙", - 场: "場", - 坏: "壞", - 块: "塊", - 坚: "堅", - 坛: "壇", - 坜: "壢", - 坝: "壩", - 坞: "塢", - 坟: "墳", - 坠: "墜", - 垄: "壟", - 垅: "壠", - 垆: "壚", - 垒: "壘", - 垦: "墾", - 垩: "堊", - 垫: "墊", - 垭: "埡", - 垯: "墶", - 垱: "壋", - 垲: "塏", - 垴: "堖", - 埘: "塒", - 埙: "壎", - 埚: "堝", - 堑: "塹", - 堕: "墮", - 塆: "壪", - 墙: "牆", - 壮: "壯", - 声: "聲", - 壳: "殼", - 壶: "壺", - 壸: "壼", - 处: "處", - 备: "備", - 复: "復", - 够: "夠", - 夫: "夫", - 头: "頭", - 夸: "誇", - 夹: "夾", - 夺: "奪", - 奁: "奩", - 奂: "奐", - 奋: "奮", - 奖: "獎", - 奥: "奧", - 奸: "奸", - 妆: "妝", - 妇: "婦", - 妈: "媽", - 妩: "嫵", - 妪: "嫗", - 妫: "嬀", - 姗: "姍", - 姜: "姜", - 姹: "奼", - 娄: "婁", - 娅: "婭", - 娆: "嬈", - 娇: "嬌", - 娈: "孌", - 娘: "娘", - 娱: "娛", - 娲: "媧", - 娴: "嫺", - 婳: "嫿", - 婴: "嬰", - 婵: "嬋", - 婶: "嬸", - 媪: "媼", - 媭: "嬃", - 嫒: "嬡", - 嫔: "嬪", - 嫱: "嬙", - 嬷: "嬤", - 孙: "孫", - 学: "學", - 孪: "孿", - 宁: "寧", - 它: "它", - 宝: "寶", - 实: "實", - 宠: "寵", - 审: "審", - 宪: "憲", - 宫: "宮", - 家: "家", - 宽: "寬", - 宾: "賓", - 寝: "寢", - 对: "對", - 寻: "尋", - 导: "導", - 寿: "壽", - 将: "將", - 尔: "爾", - 尘: "塵", - 尝: "嘗", - 尧: "堯", - 尴: "尷", - 尸: "屍", - 尽: "盡", - 局: "局", - 层: "層", - 屃: "屓", - 屉: "屜", - 届: "屆", - 属: "屬", - 屡: "屢", - 屦: "屨", - 屿: "嶼", - 岁: "歲", - 岂: "豈", - 岖: "嶇", - 岗: "崗", - 岘: "峴", - 岚: "嵐", - 岛: "島", - 岩: "巖", - 岭: "嶺", - 岳: "嶽", - 岽: "崬", - 岿: "巋", - 峃: "嶨", - 峄: "嶧", - 峡: "峽", - 峣: "嶢", - 峤: "嶠", - 峥: "崢", - 峦: "巒", - 峰: "峯", - 崂: "嶗", - 崃: "崍", - 崄: "嶮", - 崭: "嶄", - 嵘: "嶸", - 嵚: "嶔", - 嵝: "嶁", - 巅: "巔", - 巨: "巨", - 巩: "鞏", - 巯: "巰", - 币: "幣", - 布: "布", - 帅: "帥", - 师: "師", - 帏: "幃", - 帐: "帳", - 帘: "簾", - 帜: "幟", - 带: "帶", - 帧: "幀", - 席: "席", - 帮: "幫", - 帱: "幬", - 帻: "幘", - 帼: "幗", - 幂: "冪", - 干: "幹", - 并: "並", - 幸: "幸", - 广: "廣", - 庄: "莊", - 庆: "慶", - 床: "牀", - 庐: "廬", - 庑: "廡", - 库: "庫", - 应: "應", - 庙: "廟", - 庞: "龐", - 废: "廢", - 庵: "庵", - 庼: "廎", - 廪: "廩", - 开: "開", - 异: "異", - 弃: "棄", - 弑: "弒", - 张: "張", - 弥: "彌", - 弦: "弦", - 弪: "弳", - 弯: "彎", - 弹: "彈", - 强: "強", - 归: "歸", - 当: "當", - 录: "錄", - 彟: "彠", - 彦: "彥", - 彨: "彲", - 彩: "彩", - 彻: "徹", - 征: "徵", - 径: "徑", - 徕: "徠", - 御: "御", - 忆: "憶", - 忏: "懺", - 志: "志", - 忧: "憂", - 念: "念", - 忾: "愾", - 怀: "懷", - 态: "態", - 怂: "慫", - 怃: "憮", - 怄: "慪", - 怅: "悵", - 怆: "愴", - 怜: "憐", - 总: "總", - 怼: "懟", - 怿: "懌", - 恋: "戀", - 恒: "恆", - 恤: "恤", - 恳: "懇", - 恶: "惡", - 恸: "慟", - 恹: "懨", - 恺: "愷", - 恻: "惻", - 恼: "惱", - 恽: "惲", - 悦: "悅", - 悫: "愨", - 悬: "懸", - 悭: "慳", - 悮: "悞", - 悯: "憫", - 惊: "驚", - 惧: "懼", - 惨: "慘", - 惩: "懲", - 惫: "憊", - 惬: "愜", - 惭: "慚", - 惮: "憚", - 惯: "慣", - 愈: "愈", - 愠: "慍", - 愤: "憤", - 愦: "憒", - 愿: "願", - 慑: "懾", - 慭: "憖", - 懑: "懣", - 懒: "懶", - 懔: "懍", - 戆: "戇", - 戋: "戔", - 戏: "戲", - 戗: "戧", - 战: "戰", - 戚: "戚", - 戬: "戩", - 戯: "戱", - 户: "戶", - 才: "才", - 扎: "扎", - 扑: "撲", - 托: "託", - 扣: "扣", - 执: "執", - 扩: "擴", - 扪: "捫", - 扫: "掃", - 扬: "揚", - 扰: "擾", - 折: "折", - 抚: "撫", - 抛: "拋", - 抟: "摶", - 抠: "摳", - 抡: "掄", - 抢: "搶", - 护: "護", - 报: "報", - 抵: "抵", - 担: "擔", - 拐: "拐", - 拟: "擬", - 拢: "攏", - 拣: "揀", - 拥: "擁", - 拦: "攔", - 拧: "擰", - 拨: "撥", - 择: "擇", - 挂: "掛", - 挚: "摯", - 挛: "攣", - 挜: "掗", - 挝: "撾", - 挞: "撻", - 挟: "挾", - 挠: "撓", - 挡: "擋", - 挢: "撟", - 挣: "掙", - 挤: "擠", - 挥: "揮", - 挦: "撏", - 挨: "挨", - 挽: "挽", - 捝: "挩", - 捞: "撈", - 损: "損", - 捡: "撿", - 换: "換", - 捣: "搗", - 据: "據", - 掳: "擄", - 掴: "摑", - 掷: "擲", - 掸: "撣", - 掺: "摻", - 掼: "摜", - 揽: "攬", - 揾: "搵", - 揿: "撳", - 搀: "攙", - 搁: "擱", - 搂: "摟", - 搄: "揯", - 搅: "攪", - 搜: "搜", - 携: "攜", - 摄: "攝", - 摅: "攄", - 摆: "擺", - 摇: "搖", - 摈: "擯", - 摊: "攤", - 撄: "攖", - 撑: "撐", - 撵: "攆", - 撷: "擷", - 撸: "擼", - 撺: "攛", - 擜: "㩵", - 擞: "擻", - 攒: "攢", - 敌: "敵", - 敚: "敓", - 敛: "斂", - 敩: "斆", - 数: "數", - 斋: "齋", - 斓: "斕", - 斗: "鬥", - 斩: "斬", - 断: "斷", - 旋: "旋", - 无: "無", - 旧: "舊", - 时: "時", - 旷: "曠", - 旸: "暘", - 昆: "昆", - 昙: "曇", - 昵: "暱", - 昼: "晝", - 昽: "曨", - 显: "顯", - 晋: "晉", - 晒: "曬", - 晓: "曉", - 晔: "曄", - 晕: "暈", - 晖: "暉", - 暂: "暫", - 暅: "𣈶", - 暗: "暗", - 暧: "曖", - 曲: "曲", - 术: "術", - 朱: "朱", - 朴: "樸", - 机: "機", - 杀: "殺", - 杂: "雜", - 权: "權", - 杆: "杆", - 杠: "槓", - 条: "條", - 来: "來", - 杨: "楊", - 杩: "榪", - 杯: "杯", - 杰: "傑", - 松: "松", - 板: "板", - 极: "極", - 构: "構", - 枞: "樅", - 枢: "樞", - 枣: "棗", - 枥: "櫪", - 枧: "梘", - 枨: "棖", - 枪: "槍", - 枫: "楓", - 枭: "梟", - 柜: "櫃", - 柠: "檸", - 柽: "檉", - 栀: "梔", - 栅: "柵", - 标: "標", - 栈: "棧", - 栉: "櫛", - 栊: "櫳", - 栋: "棟", - 栌: "櫨", - 栎: "櫟", - 栏: "欄", - 树: "樹", - 栖: "棲", - 栗: "栗", - 样: "樣", - 核: "核", - 栾: "欒", - 桠: "椏", - 桡: "橈", - 桢: "楨", - 档: "檔", - 桤: "榿", - 桥: "橋", - 桦: "樺", - 桧: "檜", - 桨: "槳", - 桩: "樁", - 桪: "樳", - 梁: "梁", - 梦: "夢", - 梼: "檮", - 梾: "棶", - 梿: "槤", - 检: "檢", - 棁: "梲", - 棂: "欞", - 椁: "槨", - 椝: "槼", - 椟: "櫝", - 椠: "槧", - 椢: "槶", - 椤: "欏", - 椫: "樿", - 椭: "橢", - 椮: "槮", - 楼: "樓", - 榄: "欖", - 榅: "榲", - 榇: "櫬", - 榈: "櫚", - 榉: "櫸", - 榝: "樧", - 槚: "檟", - 槛: "檻", - 槟: "檳", - 槠: "櫧", - 横: "橫", - 樯: "檣", - 樱: "櫻", - 橥: "櫫", - 橱: "櫥", - 橹: "櫓", - 橼: "櫞", - 檩: "檁", - 欢: "歡", - 欤: "歟", - 欧: "歐", - 欲: "欲", - 歼: "殲", - 殁: "歿", - 殇: "殤", - 残: "殘", - 殒: "殞", - 殓: "殮", - 殚: "殫", - 殡: "殯", - 殴: "毆", - 毁: "毀", - 毂: "轂", - 毕: "畢", - 毙: "斃", - 毡: "氈", - 毵: "毿", - 毶: "𣯶", - 氇: "氌", - 气: "氣", - 氢: "氫", - 氩: "氬", - 氲: "氳", - 汇: "匯", - 汉: "漢", - 汤: "湯", - 汹: "洶", - 沄: "澐", - 沈: "沈", - 沟: "溝", - 没: "沒", - 沣: "灃", - 沤: "漚", - 沥: "瀝", - 沦: "淪", - 沧: "滄", - 沨: "渢", - 沩: "潙", - 沪: "滬", - 沾: "沾", - 泛: "泛", - 泞: "濘", - 注: "注", - 泪: "淚", - 泶: "澩", - 泷: "瀧", - 泸: "瀘", - 泺: "濼", - 泻: "瀉", - 泼: "潑", - 泽: "澤", - 泾: "涇", - 洁: "潔", - 洒: "灑", - 洼: "窪", - 浃: "浹", - 浅: "淺", - 浆: "漿", - 浇: "澆", - 浈: "湞", - 浉: "溮", - 浊: "濁", - 测: "測", - 浍: "澮", - 济: "濟", - 浏: "瀏", - 浐: "滻", - 浑: "渾", - 浒: "滸", - 浓: "濃", - 浔: "潯", - 浕: "濜", - 涂: "塗", - 涌: "湧", - 涚: "涗", - 涛: "濤", - 涝: "澇", - 涞: "淶", - 涟: "漣", - 涠: "潿", - 涡: "渦", - 涢: "溳", - 涣: "渙", - 涤: "滌", - 润: "潤", - 涧: "澗", - 涨: "漲", - 涩: "澀", - 淀: "澱", - 渊: "淵", - 渌: "淥", - 渍: "漬", - 渎: "瀆", - 渐: "漸", - 渑: "澠", - 渔: "漁", - 渖: "瀋", - 渗: "滲", - 温: "溫", - 游: "遊", - 湾: "灣", - 湿: "溼", - 溁: "濚", - 溃: "潰", - 溅: "濺", - 溆: "漵", - 溇: "漊", - 滗: "潷", - 滚: "滾", - 滞: "滯", - 滟: "灩", - 滠: "灄", - 满: "滿", - 滢: "瀅", - 滤: "濾", - 滥: "濫", - 滦: "灤", - 滨: "濱", - 滩: "灘", - 滪: "澦", - 漓: "漓", - 潆: "瀠", - 潇: "瀟", - 潋: "瀲", - 潍: "濰", - 潜: "潛", - 潴: "瀦", - 澛: "瀂", - 澜: "瀾", - 濑: "瀨", - 濒: "瀕", - 灏: "灝", - 灭: "滅", - 灯: "燈", - 灵: "靈", - 灶: "竈", - 灾: "災", - 灿: "燦", - 炀: "煬", - 炉: "爐", - 炖: "燉", - 炜: "煒", - 炝: "熗", - 点: "點", - 炼: "煉", - 炽: "熾", - 烁: "爍", - 烂: "爛", - 烃: "烴", - 烛: "燭", - 烟: "煙", - 烦: "煩", - 烧: "燒", - 烨: "燁", - 烩: "燴", - 烫: "燙", - 烬: "燼", - 热: "熱", - 焕: "煥", - 焖: "燜", - 焘: "燾", - 煴: "熅", - 熏: "燻", - 爱: "愛", - 爷: "爺", - 牍: "牘", - 牦: "犛", - 牵: "牽", - 牺: "犧", - 犊: "犢", - 状: "狀", - 犷: "獷", - 犸: "獁", - 犹: "猶", - 狈: "狽", - 狝: "獮", - 狞: "獰", - 独: "獨", - 狭: "狹", - 狮: "獅", - 狯: "獪", - 狰: "猙", - 狱: "獄", - 狲: "猻", - 猃: "獫", - 猎: "獵", - 猕: "獼", - 猡: "玀", - 猪: "豬", - 猫: "貓", - 猬: "蝟", - 献: "獻", - 獭: "獺", - 玑: "璣", - 玙: "璵", - 玚: "瑒", - 玛: "瑪", - 玩: "玩", - 玮: "瑋", - 环: "環", - 现: "現", - 玱: "瑲", - 玺: "璽", - 珐: "琺", - 珑: "瓏", - 珰: "璫", - 珲: "琿", - 琎: "璡", - 琏: "璉", - 琐: "瑣", - 琼: "瓊", - 瑶: "瑤", - 瑷: "璦", - 瑸: "璸", - 璇: "璇", - 璎: "瓔", - 瓒: "瓚", - 瓮: "甕", - 瓯: "甌", - 电: "電", - 画: "畫", - 畅: "暢", - 畴: "疇", - 疖: "癤", - 疗: "療", - 疟: "瘧", - 疠: "癘", - 疡: "瘍", - 疬: "癧", - 疭: "瘲", - 疮: "瘡", - 疯: "瘋", - 疱: "皰", - 疴: "痾", - 症: "症", - 痈: "癰", - 痉: "痙", - 痒: "癢", - 痖: "瘂", - 痨: "癆", - 痪: "瘓", - 痫: "癇", - 痴: "癡", - 瘅: "癉", - 瘆: "瘮", - 瘗: "瘞", - 瘘: "瘻", - 瘪: "癟", - 瘫: "癱", - 瘾: "癮", - 瘿: "癭", - 癞: "癩", - 癣: "癬", - 癫: "癲", - 皂: "皁", - 皑: "皚", - 皱: "皺", - 皲: "皸", - 盏: "盞", - 盐: "鹽", - 监: "監", - 盖: "蓋", - 盗: "盜", - 盘: "盤", - 眍: "瞘", - 眦: "眥", - 眬: "矓", - 睁: "睜", - 睐: "睞", - 睑: "瞼", - 瞆: "瞶", - 瞒: "瞞", - 瞩: "矚", - 矩: "矩", - 矫: "矯", - 矶: "磯", - 矾: "礬", - 矿: "礦", - 砀: "碭", - 码: "碼", - 砖: "磚", - 砗: "硨", - 砚: "硯", - 砜: "碸", - 砺: "礪", - 砻: "礱", - 砾: "礫", - 础: "礎", - 硁: "硜", - 硕: "碩", - 硖: "硤", - 硗: "磽", - 硙: "磑", - 硚: "礄", - 确: "確", - 硵: "磠", - 硷: "礆", - 碍: "礙", - 碛: "磧", - 碜: "磣", - 碱: "鹼", - 礼: "禮", - 祃: "禡", - 祎: "禕", - 祢: "禰", - 祯: "禎", - 祷: "禱", - 祸: "禍", - 禀: "稟", - 禄: "祿", - 禅: "禪", - 离: "離", - 私: "私", - 秃: "禿", - 秆: "稈", - 秋: "秋", - 种: "種", - 秘: "祕", - 积: "積", - 称: "稱", - 秽: "穢", - 秾: "穠", - 稆: "穭", - 税: "稅", - 稣: "穌", - 稳: "穩", - 穑: "穡", - 穞: "穭", - 穷: "窮", - 窃: "竊", - 窍: "竅", - 窎: "窵", - 窑: "窯", - 窜: "竄", - 窝: "窩", - 窥: "窺", - 窦: "竇", - 窭: "窶", - 竖: "豎", - 竞: "競", - 笃: "篤", - 笋: "筍", - 笔: "筆", - 笕: "筧", - 笺: "箋", - 笼: "籠", - 笾: "籩", - 筑: "築", - 筚: "篳", - 筛: "篩", - 筜: "簹", - 筝: "箏", - 筹: "籌", - 筼: "篔", - 签: "籤", - 筿: "篠", - 简: "簡", - 箓: "籙", - 箦: "簀", - 箧: "篋", - 箨: "籜", - 箩: "籮", - 箪: "簞", - 箫: "簫", - 篑: "簣", - 篓: "簍", - 篮: "籃", - 篯: "籛", - 篱: "籬", - 簖: "籪", - 籁: "籟", - 籴: "糴", - 类: "類", - 籼: "秈", - 粜: "糶", - 粝: "糲", - 粤: "粵", - 粪: "糞", - 粮: "糧", - 粽: "糉", - 糁: "糝", - 糇: "餱", - 糍: "餈", - 系: "系", - 紧: "緊", - 絷: "縶", - 緼: "縕", - 縆: "緪", - 纟: "糹", - 纠: "糾", - 纡: "紆", - 红: "紅", - 纣: "紂", - 纤: "纖", - 纥: "紇", - 约: "約", - 级: "級", - 纨: "紈", - 纩: "纊", - 纪: "紀", - 纫: "紉", - 纬: "緯", - 纭: "紜", - 纮: "紘", - 纯: "純", - 纰: "紕", - 纱: "紗", - 纲: "綱", - 纳: "納", - 纴: "紝", - 纵: "縱", - 纶: "綸", - 纷: "紛", - 纸: "紙", - 纹: "紋", - 纺: "紡", - 纻: "紵", - 纼: "紖", - 纽: "紐", - 纾: "紓", - 线: "線", - 绀: "紺", - 绁: "紲", - 绂: "紱", - 练: "練", - 组: "組", - 绅: "紳", - 细: "細", - 织: "織", - 终: "終", - 绉: "縐", - 绊: "絆", - 绋: "紼", - 绌: "絀", - 绍: "紹", - 绎: "繹", - 经: "經", - 绐: "紿", - 绑: "綁", - 绒: "絨", - 结: "結", - 绔: "絝", - 绕: "繞", - 绖: "絰", - 绗: "絎", - 绘: "繪", - 给: "給", - 绚: "絢", - 绛: "絳", - 络: "絡", - 绝: "絕", - 绞: "絞", - 统: "統", - 绠: "綆", - 绡: "綃", - 绢: "絹", - 绣: "繡", - 绤: "綌", - 绥: "綏", - 绦: "絛", - 继: "繼", - 绨: "綈", - 绩: "績", - 绪: "緒", - 绫: "綾", - 绬: "緓", - 续: "續", - 绮: "綺", - 绯: "緋", - 绰: "綽", - 绱: "鞝", - 绲: "緄", - 绳: "繩", - 维: "維", - 绵: "綿", - 绶: "綬", - 绷: "繃", - 绸: "綢", - 绹: "綯", - 绺: "綹", - 绻: "綣", - 综: "綜", - 绽: "綻", - 绾: "綰", - 绿: "綠", - 缀: "綴", - 缁: "緇", - 缂: "緙", - 缃: "緗", - 缄: "緘", - 缅: "緬", - 缆: "纜", - 缇: "緹", - 缈: "緲", - 缉: "緝", - 缊: "縕", - 缋: "繢", - 缌: "緦", - 缍: "綞", - 缎: "緞", - 缏: "緶", - 缐: "線", - 缑: "緱", - 缒: "縋", - 缓: "緩", - 缔: "締", - 缕: "縷", - 编: "編", - 缗: "緡", - 缘: "緣", - 缙: "縉", - 缚: "縛", - 缛: "縟", - 缜: "縝", - 缝: "縫", - 缞: "縗", - 缟: "縞", - 缠: "纏", - 缡: "縭", - 缢: "縊", - 缣: "縑", - 缤: "繽", - 缥: "縹", - 缦: "縵", - 缧: "縲", - 缨: "纓", - 缩: "縮", - 缪: "繆", - 缫: "繅", - 缬: "纈", - 缭: "繚", - 缮: "繕", - 缯: "繒", - 缰: "繮", - 缱: "繾", - 缲: "繰", - 缳: "繯", - 缴: "繳", - 缵: "纘", - 罂: "罌", - 网: "網", - 罗: "羅", - 罚: "罰", - 罢: "罷", - 罴: "羆", - 羁: "羈", - 羟: "羥", - 羡: "羨", - 群: "羣", - 翘: "翹", - 翙: "翽", - 翚: "翬", - 耢: "耮", - 耧: "耬", - 耸: "聳", - 耻: "恥", - 聂: "聶", - 聋: "聾", - 职: "職", - 聍: "聹", - 联: "聯", - 聩: "聵", - 聪: "聰", - 肃: "肅", - 肠: "腸", - 肤: "膚", - 肮: "骯", - 肴: "餚", - 肾: "腎", - 肿: "腫", - 胀: "脹", - 胁: "脅", - 胄: "胄", - 胆: "膽", - 背: "背", - 胜: "勝", - 胡: "胡", - 胧: "朧", - 胨: "腖", - 胪: "臚", - 胫: "脛", - 胶: "膠", - 脉: "脈", - 脍: "膾", - 脏: "髒", - 脐: "臍", - 脑: "腦", - 脓: "膿", - 脔: "臠", - 脚: "腳", - 脱: "脫", - 脶: "腡", - 脸: "臉", - 腊: "臘", - 腌: "醃", - 腘: "膕", - 腭: "齶", - 腻: "膩", - 腼: "靦", - 腽: "膃", - 腾: "騰", - 膑: "臏", - 膻: "羶", - 臜: "臢", - 致: "致", - 舆: "輿", - 舍: "舍", - 舣: "艤", - 舰: "艦", - 舱: "艙", - 舻: "艫", - 艰: "艱", - 艳: "豔", - 艺: "藝", - 节: "節", - 芈: "羋", - 芗: "薌", - 芜: "蕪", - 芦: "蘆", - 芸: "芸", - 苁: "蓯", - 苇: "葦", - 苈: "藶", - 苋: "莧", - 苌: "萇", - 苍: "蒼", - 苎: "苧", - 苏: "蘇", - 苔: "苔", - 苧: "薴", - 苹: "蘋", - 范: "範", - 茎: "莖", - 茏: "蘢", - 茑: "蔦", - 茔: "塋", - 茕: "煢", - 茧: "繭", - 荆: "荊", - 荐: "薦", - 荙: "薘", - 荚: "莢", - 荛: "蕘", - 荜: "蓽", - 荝: "萴", - 荞: "蕎", - 荟: "薈", - 荠: "薺", - 荡: "蕩", - 荣: "榮", - 荤: "葷", - 荥: "滎", - 荦: "犖", - 荧: "熒", - 荨: "蕁", - 荩: "藎", - 荪: "蓀", - 荫: "蔭", - 荬: "蕒", - 荭: "葒", - 荮: "葤", - 药: "藥", - 莅: "蒞", - 莱: "萊", - 莲: "蓮", - 莳: "蒔", - 莴: "萵", - 莶: "薟", - 获: "獲", - 莸: "蕕", - 莹: "瑩", - 莺: "鶯", - 莼: "蓴", - 萚: "蘀", - 萝: "蘿", - 萤: "螢", - 营: "營", - 萦: "縈", - 萧: "蕭", - 萨: "薩", - 葱: "蔥", - 蒀: "蒕", - 蒇: "蕆", - 蒉: "蕢", - 蒋: "蔣", - 蒌: "蔞", - 蒏: "醟", - 蒙: "蒙", - 蓝: "藍", - 蓟: "薊", - 蓠: "蘺", - 蓣: "蕷", - 蓥: "鎣", - 蓦: "驀", - 蔂: "虆", - 蔑: "蔑", - 蔷: "薔", - 蔹: "蘞", - 蔺: "藺", - 蔼: "藹", - 蕰: "薀", - 蕲: "蘄", - 蕴: "蘊", - 薮: "藪", - 藓: "蘚", - 藴: "蘊", - 蘖: "櫱", - 虏: "虜", - 虑: "慮", - 虚: "虛", - 虫: "蟲", - 虬: "虯", - 虮: "蟣", - 虱: "蝨", - 虽: "雖", - 虾: "蝦", - 虿: "蠆", - 蚀: "蝕", - 蚁: "蟻", - 蚂: "螞", - 蚃: "蠁", - 蚕: "蠶", - 蚝: "蠔", - 蚬: "蜆", - 蛊: "蠱", - 蛎: "蠣", - 蛏: "蟶", - 蛮: "蠻", - 蛰: "蟄", - 蛱: "蛺", - 蛲: "蟯", - 蛳: "螄", - 蛴: "蠐", - 蜕: "蛻", - 蜗: "蝸", - 蜡: "蠟", - 蝇: "蠅", - 蝈: "蟈", - 蝉: "蟬", - 蝎: "蠍", - 蝼: "螻", - 蝾: "蠑", - 螀: "螿", - 螨: "蟎", - 蟏: "蠨", - 衅: "釁", - 衔: "銜", - 补: "補", - 表: "表", - 衬: "襯", - 衮: "袞", - 袄: "襖", - 袅: "嫋", - 袆: "褘", - 袜: "襪", - 袭: "襲", - 袯: "襏", - 装: "裝", - 裆: "襠", - 裈: "褌", - 裢: "褳", - 裣: "襝", - 裤: "褲", - 裥: "襉", - 褛: "褸", - 褴: "襤", - 襕: "襴", - 见: "見", - 观: "觀", - 觃: "覎", - 规: "規", - 觅: "覓", - 视: "視", - 觇: "覘", - 览: "覽", - 觉: "覺", - 觊: "覬", - 觋: "覡", - 觌: "觿", - 觍: "覥", - 觎: "覦", - 觏: "覯", - 觐: "覲", - 觑: "覷", - 觞: "觴", - 触: "觸", - 觯: "觶", - 訚: "誾", - 詟: "讋", - 誉: "譽", - 誊: "謄", - 讠: "訁", - 计: "計", - 订: "訂", - 讣: "訃", - 认: "認", - 讥: "譏", - 讦: "訐", - 讧: "訌", - 讨: "討", - 让: "讓", - 讪: "訕", - 讫: "訖", - 讬: "託", - 训: "訓", - 议: "議", - 讯: "訊", - 记: "記", - 讱: "訒", - 讲: "講", - 讳: "諱", - 讴: "謳", - 讵: "詎", - 讶: "訝", - 讷: "訥", - 许: "許", - 讹: "訛", - 论: "論", - 讻: "訩", - 讼: "訟", - 讽: "諷", - 设: "設", - 访: "訪", - 诀: "訣", - 证: "證", - 诂: "詁", - 诃: "訶", - 评: "評", - 诅: "詛", - 识: "識", - 诇: "詗", - 诈: "詐", - 诉: "訴", - 诊: "診", - 诋: "詆", - 诌: "謅", - 词: "詞", - 诎: "詘", - 诏: "詔", - 诐: "詖", - 译: "譯", - 诒: "詒", - 诓: "誆", - 诔: "誄", - 试: "試", - 诖: "詿", - 诗: "詩", - 诘: "詰", - 诙: "詼", - 诚: "誠", - 诛: "誅", - 诜: "詵", - 话: "話", - 诞: "誕", - 诟: "詬", - 诠: "詮", - 诡: "詭", - 询: "詢", - 诣: "詣", - 诤: "諍", - 该: "該", - 详: "詳", - 诧: "詫", - 诨: "諢", - 诩: "詡", - 诪: "譸", - 诫: "誡", - 诬: "誣", - 语: "語", - 诮: "誚", - 误: "誤", - 诰: "誥", - 诱: "誘", - 诲: "誨", - 诳: "誑", - 说: "說", - 诵: "誦", - 诶: "誒", - 请: "請", - 诸: "諸", - 诹: "諏", - 诺: "諾", - 读: "讀", - 诼: "諑", - 诽: "誹", - 课: "課", - 诿: "諉", - 谀: "諛", - 谁: "誰", - 谂: "諗", - 调: "調", - 谄: "諂", - 谅: "諒", - 谆: "諄", - 谇: "誶", - 谈: "談", - 谉: "讅", - 谊: "誼", - 谋: "謀", - 谌: "諶", - 谍: "諜", - 谎: "謊", - 谏: "諫", - 谐: "諧", - 谑: "謔", - 谒: "謁", - 谓: "謂", - 谔: "諤", - 谕: "諭", - 谖: "諼", - 谗: "讒", - 谘: "諮", - 谙: "諳", - 谚: "諺", - 谛: "諦", - 谜: "謎", - 谝: "諞", - 谞: "諝", - 谟: "謨", - 谠: "讜", - 谡: "謖", - 谢: "謝", - 谣: "謠", - 谤: "謗", - 谥: "諡", - 谦: "謙", - 谧: "謐", - 谨: "謹", - 谩: "謾", - 谪: "謫", - 谫: "譾", - 谬: "謬", - 谭: "譚", - 谮: "譖", - 谯: "譙", - 谰: "讕", - 谱: "譜", - 谲: "譎", - 谳: "讞", - 谴: "譴", - 谵: "譫", - 谶: "讖", - 谷: "谷", - 豮: "豶", - 贝: "貝", - 贞: "貞", - 负: "負", - 贠: "貟", - 贡: "貢", - 财: "財", - 责: "責", - 贤: "賢", - 败: "敗", - 账: "賬", - 货: "貨", - 质: "質", - 贩: "販", - 贪: "貪", - 贫: "貧", - 贬: "貶", - 购: "購", - 贮: "貯", - 贯: "貫", - 贰: "貳", - 贱: "賤", - 贲: "賁", - 贳: "貰", - 贴: "貼", - 贵: "貴", - 贶: "貺", - 贷: "貸", - 贸: "貿", - 费: "費", - 贺: "賀", - 贻: "貽", - 贼: "賊", - 贽: "贄", - 贾: "賈", - 贿: "賄", - 赀: "貲", - 赁: "賃", - 赂: "賂", - 赃: "贓", - 资: "資", - 赅: "賅", - 赆: "贐", - 赇: "賕", - 赈: "賑", - 赉: "賚", - 赊: "賒", - 赋: "賦", - 赌: "賭", - 赍: "齎", - 赎: "贖", - 赏: "賞", - 赐: "賜", - 赑: "贔", - 赒: "賙", - 赓: "賡", - 赔: "賠", - 赕: "賧", - 赖: "賴", - 赗: "賵", - 赘: "贅", - 赙: "賻", - 赚: "賺", - 赛: "賽", - 赜: "賾", - 赝: "贗", - 赞: "贊", - 赟: "贇", - 赠: "贈", - 赡: "贍", - 赢: "贏", - 赣: "贛", - 赪: "赬", - 赵: "趙", - 赶: "趕", - 趋: "趨", - 趱: "趲", - 趸: "躉", - 跃: "躍", - 跄: "蹌", - 跖: "蹠", - 跞: "躒", - 践: "踐", - 跶: "躂", - 跷: "蹺", - 跸: "蹕", - 跹: "躚", - 跻: "躋", - 踌: "躊", - 踪: "蹤", - 踬: "躓", - 踯: "躑", - 蹑: "躡", - 蹒: "蹣", - 蹰: "躕", - 蹿: "躥", - 躏: "躪", - 躜: "躦", - 躯: "軀", - 輼: "轀", - 车: "車", - 轧: "軋", - 轨: "軌", - 轩: "軒", - 轪: "軑", - 轫: "軔", - 转: "轉", - 轭: "軛", - 轮: "輪", - 软: "軟", - 轰: "轟", - 轱: "軲", - 轲: "軻", - 轳: "轤", - 轴: "軸", - 轵: "軹", - 轶: "軼", - 轷: "軤", - 轸: "軫", - 轹: "轢", - 轺: "軺", - 轻: "輕", - 轼: "軾", - 载: "載", - 轾: "輊", - 轿: "轎", - 辀: "輈", - 辁: "輇", - 辂: "輅", - 较: "較", - 辄: "輒", - 辅: "輔", - 辆: "輛", - 辇: "輦", - 辈: "輩", - 辉: "輝", - 辊: "輥", - 辋: "輞", - 辌: "輬", - 辍: "輟", - 辎: "輜", - 辏: "輳", - 辐: "輻", - 辑: "輯", - 辒: "轀", - 输: "輸", - 辔: "轡", - 辕: "轅", - 辖: "轄", - 辗: "輾", - 辘: "轆", - 辙: "轍", - 辚: "轔", - 辞: "辭", - 辟: "闢", - 辩: "辯", - 辫: "辮", - 边: "邊", - 辽: "遼", - 达: "達", - 迁: "遷", - 过: "過", - 迈: "邁", - 运: "運", - 还: "還", - 这: "這", - 进: "進", - 远: "遠", - 违: "違", - 连: "連", - 迟: "遲", - 迩: "邇", - 迳: "逕", - 迹: "跡", - 适: "適", - 选: "選", - 逊: "遜", - 递: "遞", - 逦: "邐", - 逻: "邏", - 遗: "遺", - 遥: "遙", - 邓: "鄧", - 邝: "鄺", - 邬: "鄔", - 邮: "郵", - 邹: "鄒", - 邺: "鄴", - 邻: "鄰", - 郁: "鬱", - 郏: "郟", - 郐: "鄶", - 郑: "鄭", - 郓: "鄆", - 郦: "酈", - 郧: "鄖", - 郸: "鄲", - 酂: "酇", - 酝: "醞", - 酦: "醱", - 酱: "醬", - 酸: "酸", - 酽: "釅", - 酾: "釃", - 酿: "釀", - 醖: "醞", - 采: "採", - 释: "釋", - 里: "裏", - 鉴: "鑑", - 銮: "鑾", - 錾: "鏨", - 钅: "釒", - 钆: "釓", - 钇: "釔", - 针: "針", - 钉: "釘", - 钊: "釗", - 钋: "釙", - 钌: "釕", - 钍: "釷", - 钎: "釺", - 钏: "釧", - 钐: "釤", - 钑: "鈒", - 钒: "釩", - 钓: "釣", - 钔: "鍆", - 钕: "釹", - 钖: "鍚", - 钗: "釵", - 钘: "鈃", - 钙: "鈣", - 钚: "鈈", - 钛: "鈦", - 钜: "鉅", - 钝: "鈍", - 钞: "鈔", - 钟: "鍾", - 钠: "鈉", - 钡: "鋇", - 钢: "鋼", - 钣: "鈑", - 钤: "鈐", - 钥: "鑰", - 钦: "欽", - 钧: "鈞", - 钨: "鎢", - 钩: "鉤", - 钪: "鈧", - 钫: "鈁", - 钬: "鈥", - 钭: "鈄", - 钮: "鈕", - 钯: "鈀", - 钰: "鈺", - 钱: "錢", - 钲: "鉦", - 钳: "鉗", - 钴: "鈷", - 钵: "鉢", - 钶: "鈳", - 钷: "鉕", - 钸: "鈽", - 钹: "鈸", - 钺: "鉞", - 钻: "鑽", - 钼: "鉬", - 钽: "鉭", - 钾: "鉀", - 钿: "鈿", - 铀: "鈾", - 铁: "鐵", - 铂: "鉑", - 铃: "鈴", - 铄: "鑠", - 铅: "鉛", - 铆: "鉚", - 铇: "鉋", - 铈: "鈰", - 铉: "鉉", - 铊: "鉈", - 铋: "鉍", - 铌: "鈮", - 铍: "鈹", - 铎: "鐸", - 铏: "鉶", - 铐: "銬", - 铑: "銠", - 铒: "鉺", - 铓: "鋩", - 铔: "錏", - 铕: "銪", - 铖: "鋮", - 铗: "鋏", - 铘: "鋣", - 铙: "鐃", - 铚: "銍", - 铛: "鐺", - 铜: "銅", - 铝: "鋁", - 铞: "銱", - 铟: "銦", - 铠: "鎧", - 铡: "鍘", - 铢: "銖", - 铣: "銑", - 铤: "鋌", - 铥: "銩", - 铦: "銛", - 铧: "鏵", - 铨: "銓", - 铩: "鎩", - 铪: "鉿", - 铫: "銚", - 铬: "鉻", - 铭: "銘", - 铮: "錚", - 铯: "銫", - 铰: "鉸", - 铱: "銥", - 铲: "鏟", - 铳: "銃", - 铴: "鐋", - 铵: "銨", - 银: "銀", - 铷: "銣", - 铸: "鑄", - 铹: "鐒", - 铺: "鋪", - 铻: "鋙", - 铼: "錸", - 铽: "鋱", - 链: "鏈", - 铿: "鏗", - 销: "銷", - 锁: "鎖", - 锂: "鋰", - 锃: "鋥", - 锄: "鋤", - 锅: "鍋", - 锆: "鋯", - 锇: "鋨", - 锈: "鏽", - 锉: "銼", - 锊: "鋝", - 锋: "鋒", - 锌: "鋅", - 锍: "鋶", - 锎: "鐦", - 锏: "鐧", - 锐: "銳", - 锑: "銻", - 锒: "鋃", - 锓: "鋟", - 锔: "鋦", - 锕: "錒", - 锖: "錆", - 锗: "鍺", - 锘: "鍩", - 错: "錯", - 锚: "錨", - 锛: "錛", - 锜: "錡", - 锝: "鍀", - 锞: "錁", - 锟: "錕", - 锠: "錩", - 锡: "錫", - 锢: "錮", - 锣: "鑼", - 锤: "錘", - 锥: "錐", - 锦: "錦", - 锧: "鑕", - 锨: "鍁", - 锩: "錈", - 锪: "鍃", - 锫: "錇", - 锬: "錟", - 锭: "錠", - 键: "鍵", - 锯: "鋸", - 锰: "錳", - 锱: "錙", - 锲: "鍥", - 锳: "鍈", - 锴: "鍇", - 锵: "鏘", - 锶: "鍶", - 锷: "鍔", - 锸: "鍤", - 锹: "鍬", - 锺: "鍾", - 锻: "鍛", - 锼: "鎪", - 锽: "鍠", - 锾: "鍰", - 锿: "鎄", - 镀: "鍍", - 镁: "鎂", - 镂: "鏤", - 镃: "鎡", - 镄: "鐨", - 镅: "鎇", - 镆: "鏌", - 镇: "鎮", - 镈: "鎛", - 镉: "鎘", - 镊: "鑷", - 镋: "钂", - 镌: "鐫", - 镍: "鎳", - 镎: "鎿", - 镏: "鎦", - 镐: "鎬", - 镑: "鎊", - 镒: "鎰", - 镓: "鎵", - 镔: "鑌", - 镕: "鎔", - 镖: "鏢", - 镗: "鏜", - 镘: "鏝", - 镙: "鏍", - 镚: "鏰", - 镛: "鏞", - 镜: "鏡", - 镝: "鏑", - 镞: "鏃", - 镟: "鏇", - 镠: "鏐", - 镡: "鐔", - 镢: "钁", - 镣: "鐐", - 镤: "鏷", - 镥: "鑥", - 镦: "鐓", - 镧: "鑭", - 镨: "鐠", - 镩: "鑹", - 镪: "鏹", - 镫: "鐙", - 镬: "鑊", - 镭: "鐳", - 镮: "鐶", - 镯: "鐲", - 镰: "鐮", - 镱: "鐿", - 镲: "鑔", - 镳: "鑣", - 镴: "鑞", - 镵: "鑱", - 镶: "鑲", - 长: "長", - 门: "門", - 闩: "閂", - 闪: "閃", - 闫: "閆", - 闬: "閈", - 闭: "閉", - 问: "問", - 闯: "闖", - 闰: "閏", - 闱: "闈", - 闲: "閒", - 闳: "閎", - 间: "間", - 闵: "閔", - 闶: "閌", - 闷: "悶", - 闸: "閘", - 闹: "鬧", - 闺: "閨", - 闻: "聞", - 闼: "闥", - 闽: "閩", - 闾: "閭", - 闿: "闓", - 阀: "閥", - 阁: "閣", - 阂: "閡", - 阃: "閫", - 阄: "鬮", - 阅: "閱", - 阆: "閬", - 阇: "闍", - 阈: "閾", - 阉: "閹", - 阊: "閶", - 阋: "鬩", - 阌: "閿", - 阍: "閽", - 阎: "閻", - 阏: "閼", - 阐: "闡", - 阑: "闌", - 阒: "闃", - 阓: "闠", - 阔: "闊", - 阕: "闋", - 阖: "闔", - 阗: "闐", - 阘: "闒", - 阙: "闕", - 阚: "闞", - 阛: "闤", - 队: "隊", - 阳: "陽", - 阴: "陰", - 阵: "陣", - 阶: "階", - 际: "際", - 陆: "陸", - 陇: "隴", - 陈: "陳", - 陉: "陘", - 陕: "陝", - 陦: "隯", - 陧: "隉", - 陨: "隕", - 险: "險", - 随: "隨", - 隐: "隱", - 隶: "隸", - 隽: "雋", - 难: "難", - 雇: "僱", - 雏: "雛", - 雕: "雕", - 雠: "讎", - 雳: "靂", - 雾: "霧", - 霁: "霽", - 霉: "黴", - 霡: "霢", - 霭: "靄", - 靓: "靚", - 靔: "靝", - 静: "靜", - 面: "面", - 靥: "靨", - 鞑: "韃", - 鞒: "鞽", - 鞯: "韉", - 鞲: "韝", - 韦: "韋", - 韧: "韌", - 韨: "韍", - 韩: "韓", - 韪: "韙", - 韫: "韞", - 韬: "韜", - 韵: "韻", - 页: "頁", - 顶: "頂", - 顷: "頃", - 顸: "頇", - 项: "項", - 顺: "順", - 须: "須", - 顼: "頊", - 顽: "頑", - 顾: "顧", - 顿: "頓", - 颀: "頎", - 颁: "頒", - 颂: "頌", - 颃: "頏", - 预: "預", - 颅: "顱", - 领: "領", - 颇: "頗", - 颈: "頸", - 颉: "頡", - 颊: "頰", - 颋: "頲", - 颌: "頜", - 颍: "潁", - 颎: "熲", - 颏: "頦", - 颐: "頤", - 频: "頻", - 颒: "頮", - 颓: "頹", - 颔: "頷", - 颕: "頴", - 颖: "穎", - 颗: "顆", - 题: "題", - 颙: "顒", - 颚: "顎", - 颛: "顓", - 颜: "顏", - 额: "額", - 颞: "顳", - 颟: "顢", - 颠: "顛", - 颡: "顙", - 颢: "顥", - 颣: "纇", - 颤: "顫", - 颥: "顬", - 颦: "顰", - 颧: "顴", - 风: "風", - 飏: "颺", - 飐: "颭", - 飑: "颮", - 飒: "颯", - 飓: "颶", - 飔: "颸", - 飕: "颼", - 飖: "颻", - 飗: "飀", - 飘: "飄", - 飙: "飆", - 飚: "飈", - 飞: "飛", - 飨: "饗", - 餍: "饜", - 饣: "飠", - 饤: "飣", - 饥: "飢", - 饦: "飥", - 饧: "餳", - 饨: "飩", - 饩: "餼", - 饪: "飪", - 饫: "飫", - 饬: "飭", - 饭: "飯", - 饮: "飲", - 饯: "餞", - 饰: "飾", - 饱: "飽", - 饲: "飼", - 饳: "飿", - 饴: "飴", - 饵: "餌", - 饶: "饒", - 饷: "餉", - 饸: "餄", - 饹: "餎", - 饺: "餃", - 饻: "餏", - 饼: "餅", - 饽: "餑", - 饾: "餖", - 饿: "餓", - 馀: "餘", - 馁: "餒", - 馂: "餕", - 馃: "餜", - 馄: "餛", - 馅: "餡", - 馆: "館", - 馇: "餷", - 馈: "饋", - 馉: "餶", - 馊: "餿", - 馋: "饞", - 馌: "饁", - 馍: "饃", - 馎: "餺", - 馏: "餾", - 馐: "饈", - 馑: "饉", - 馒: "饅", - 馓: "饊", - 馔: "饌", - 馕: "饢", - 马: "馬", - 驭: "馭", - 驮: "馱", - 驯: "馴", - 驰: "馳", - 驱: "驅", - 驲: "馹", - 驳: "駁", - 驴: "驢", - 驵: "駔", - 驶: "駛", - 驷: "駟", - 驸: "駙", - 驹: "駒", - 驺: "騶", - 驻: "駐", - 驼: "駝", - 驽: "駑", - 驾: "駕", - 驿: "驛", - 骀: "駘", - 骁: "驍", - 骂: "罵", - 骃: "駰", - 骄: "驕", - 骅: "驊", - 骆: "駱", - 骇: "駭", - 骈: "駢", - 骉: "驫", - 骊: "驪", - 骋: "騁", - 验: "驗", - 骍: "騂", - 骎: "駸", - 骏: "駿", - 骐: "騏", - 骑: "騎", - 骒: "騍", - 骓: "騅", - 骔: "騌", - 骕: "驌", - 骖: "驂", - 骗: "騙", - 骘: "騭", - 骙: "騤", - 骚: "騷", - 骛: "騖", - 骜: "驁", - 骝: "騮", - 骞: "騫", - 骟: "騸", - 骠: "驃", - 骡: "騾", - 骢: "驄", - 骣: "驏", - 骤: "驟", - 骥: "驥", - 骦: "驦", - 骧: "驤", - 髅: "髏", - 髋: "髖", - 髌: "髕", - 鬓: "鬢", - 鬶: "鬹", - 魇: "魘", - 魉: "魎", - 鱼: "魚", - 鱽: "魛", - 鱾: "魢", - 鱿: "魷", - 鲀: "魨", - 鲁: "魯", - 鲂: "魴", - 鲃: "䰾", - 鲄: "魺", - 鲅: "鮁", - 鲆: "鮃", - 鲇: "鮎", - 鲈: "鱸", - 鲉: "鮋", - 鲊: "鮓", - 鲋: "鮒", - 鲌: "鮊", - 鲍: "鮑", - 鲎: "鱟", - 鲏: "鮍", - 鲐: "鮐", - 鲑: "鮭", - 鲒: "鮚", - 鲓: "鮳", - 鲔: "鮪", - 鲕: "鮞", - 鲖: "鮦", - 鲗: "鰂", - 鲘: "鮜", - 鲙: "鱠", - 鲚: "鱭", - 鲛: "鮫", - 鲜: "鮮", - 鲝: "鮺", - 鲞: "鯗", - 鲟: "鱘", - 鲠: "鯁", - 鲡: "鱺", - 鲢: "鰱", - 鲣: "鰹", - 鲤: "鯉", - 鲥: "鰣", - 鲦: "鰷", - 鲧: "鯀", - 鲨: "鯊", - 鲩: "鯇", - 鲪: "鮶", - 鲫: "鯽", - 鲬: "鯒", - 鲭: "鯖", - 鲮: "鯪", - 鲯: "鯕", - 鲰: "鯫", - 鲱: "鯡", - 鲲: "鯤", - 鲳: "鯧", - 鲴: "鯝", - 鲵: "鯢", - 鲶: "鯰", - 鲷: "鯛", - 鲸: "鯨", - 鲹: "鰺", - 鲺: "鯴", - 鲻: "鯔", - 鲼: "鱝", - 鲽: "鰈", - 鲾: "鰏", - 鲿: "鱨", - 鳀: "鯷", - 鳁: "鰮", - 鳂: "鰃", - 鳃: "鰓", - 鳄: "鱷", - 鳅: "鰍", - 鳆: "鰒", - 鳇: "鰉", - 鳈: "鰁", - 鳉: "鱂", - 鳊: "鯿", - 鳋: "鰠", - 鳌: "鰲", - 鳍: "鰭", - 鳎: "鰨", - 鳏: "鰥", - 鳐: "鰩", - 鳑: "鰟", - 鳒: "鰜", - 鳓: "鰳", - 鳔: "鰾", - 鳕: "鱈", - 鳖: "鱉", - 鳗: "鰻", - 鳘: "鰵", - 鳙: "鱅", - 鳚: "䲁", - 鳛: "鰼", - 鳜: "鱖", - 鳝: "鱔", - 鳞: "鱗", - 鳟: "鱒", - 鳠: "鱯", - 鳡: "鱤", - 鳢: "鱧", - 鳣: "鱣", - 鳤: "䲘", - 鸟: "鳥", - 鸠: "鳩", - 鸡: "雞", - 鸢: "鳶", - 鸣: "鳴", - 鸤: "鳲", - 鸥: "鷗", - 鸦: "鴉", - 鸧: "鶬", - 鸨: "鴇", - 鸩: "鴆", - 鸪: "鴣", - 鸫: "鶇", - 鸬: "鸕", - 鸭: "鴨", - 鸮: "鴞", - 鸯: "鴦", - 鸰: "鴒", - 鸱: "鴟", - 鸲: "鴝", - 鸳: "鴛", - 鸴: "鷽", - 鸵: "鴕", - 鸶: "鷥", - 鸷: "鷙", - 鸸: "鴯", - 鸹: "鴰", - 鸺: "鵂", - 鸻: "鴴", - 鸼: "鵃", - 鸽: "鴿", - 鸾: "鸞", - 鸿: "鴻", - 鹀: "鵐", - 鹁: "鵓", - 鹂: "鸝", - 鹃: "鵑", - 鹄: "鵠", - 鹅: "鵝", - 鹆: "鵒", - 鹇: "鷳", - 鹈: "鵜", - 鹉: "鵡", - 鹊: "鵲", - 鹋: "鶓", - 鹌: "鵪", - 鹍: "鵾", - 鹎: "鵯", - 鹏: "鵬", - 鹐: "鵮", - 鹑: "鶉", - 鹒: "鶊", - 鹓: "鵷", - 鹔: "鷫", - 鹕: "鶘", - 鹖: "鶡", - 鹗: "鶚", - 鹘: "鶻", - 鹙: "鶖", - 鹚: "鷀", - 鹛: "鶥", - 鹜: "鶩", - 鹝: "鷊", - 鹞: "鷂", - 鹟: "鶲", - 鹠: "鶹", - 鹡: "鶺", - 鹢: "鷁", - 鹣: "鶼", - 鹤: "鶴", - 鹥: "鷖", - 鹦: "鸚", - 鹧: "鷓", - 鹨: "鷚", - 鹩: "鷯", - 鹪: "鷦", - 鹫: "鷲", - 鹬: "鷸", - 鹭: "鷺", - 鹮: "䴉", - 鹯: "鸇", - 鹰: "鷹", - 鹱: "鸌", - 鹲: "鸏", - 鹳: "鸛", - 鹴: "鸘", - 鹾: "鹺", - 麦: "麥", - 麸: "麩", - 麹: "麴", - 麺: "麪", - 麽: "麼", - 黄: "黃", - 黉: "黌", - 黡: "黶", - 黩: "黷", - 黪: "黲", - 黾: "黽", - 鼋: "黿", - 鼌: "鼂", - 鼍: "鼉", - 鼹: "鼴", - 齐: "齊", - 齑: "齏", - 齿: "齒", - 龀: "齔", - 龁: "齕", - 龂: "齗", - 龃: "齟", - 龄: "齡", - 龅: "齙", - 龆: "齠", - 龇: "齜", - 龈: "齦", - 龉: "齬", - 龊: "齪", - 龋: "齲", - 龌: "齷", - 龙: "龍", - 龚: "龔", - 龛: "龕", - 龟: "龜", - 鿎: "䃮", - 鿏: "䥑", - 鿒: "鿓", - 鿔: "鎶", - "𠀾": "𠁞", - "𠆲": "儣", - "𠆿": "𠌥", - "𠇹": "俓", - "𠉂": "㒓", - "𠉗": "𠏢", - "𠋆": "儭", - "𠚳": "𠠎", - "𠛅": "剾", - "𠛆": "𠞆", - "𠛾": "𪟖", - "𠡠": "勑", - "𠮶": "嗰", - "𠯟": "哯", - "𠯠": "噅", - "𠰱": "㘉", - "𠰷": "嚧", - "𠱞": "囃", - "𠲥": "𡅏", - "𠴛": "𡃕", - "𠴢": "𡄔", - "𠵸": "𡄣", - "𠵾": "㗲", - "𡋀": "𡓾", - "𡋗": "𡑭", - "𡋤": "壗", - "𡍣": "𡔖", - "𡒄": "壈", - "𡝠": "㜷", - "𡞋": "㜗", - "𡞱": "㜢", - "𡠟": "孎", - "𡥧": "孻", - "𡭜": "𡮉", - "𡭬": "𡮣", - "𡳃": "𡳳", - "𡳒": "𦘧", - "𡶴": "嵼", - "𡸃": "𡽗", - "𡺃": "嶈", - "𡺄": "嶘", - "𢋈": "㢝", - "𢗓": "㦛", - "𢘙": "𢤱", - "𢘝": "𢣚", - "𢘞": "𢣭", - "𢙏": "愻", - "𢙐": "憹", - "𢙑": "𢠼", - "𢙒": "憢", - "𢙓": "懀", - "𢛯": "㦎", - "𢠁": "懎", - "𢢐": "𤢻", - "𢧐": "戰", - "𢫊": "𢷮", - "𢫞": "𢶫", - "𢫬": "摋", - "𢬍": "擫", - "𢬦": "𢹿", - "𢭏": "擣", - "𢽾": "斅", - "𣃁": "斸", - "𣆐": "曥", - "𣈣": "𣋋", - "𣍨": "𦢈", - "𣍯": "腪", - "𣍰": "脥", - "𣎑": "臗", - "𣏢": "槫", - "𣐕": "桱", - "𣐤": "欍", - "𣑶": "𣠲", - "𣒌": "楇", - "𣓿": "橯", - "𣔌": "樤", - "𣗊": "樠", - "𣗋": "欓", - "𣗙": "㰙", - "𣘐": "㯤", - "𣘓": "𣞻", - "𣘴": "檭", - "𣘷": "𣝕", - "𣚚": "欘", - "𣞎": "𣠩", - "𣨼": "殢", - "𣭤": "𣯴", - "𣯣": "𣯩", - "𣱝": "氭", - "𣲗": "湋", - "𣲘": "潕", - "𣳆": "㵗", - "𣶩": "澅", - "𣶫": "𣿉", - "𣶭": "𪷓", - "𣷷": "𤅶", - "𣸣": "濆", - "𣺼": "灙", - "𣺽": "𤁣", - "𣽷": "瀃", - "𤆡": "熓", - "𤆢": "㷍", - "𤇃": "爄", - "𤇄": "熌", - "𤇭": "爖", - "𤇹": "熚", - "𤈶": "熉", - "𤈷": "㷿", - "𤊀": "𤒎", - "𤊰": "𤓩", - "𤋏": "熡", - "𤎺": "𤓎", - "𤎻": "𤑳", - "𤙯": "𤛮", - "𤝢": "𤢟", - "𤞃": "獩", - "𤞤": "玁", - "𤠋": "㺏", - "𤦀": "瓕", - "𤩽": "瓛", - "𤳄": "𤳸", - "𤶊": "癐", - "𤶧": "𤸫", - "𤻊": "㿗", - "𤽯": "㿧", - "𤾀": "皟", - "𤿲": "麬", - "𥁢": "䀉", - "𥅘": "𥌃", - "𥅴": "䀹", - "𥅿": "𥊝", - "𥆧": "瞤", - "𥇢": "䁪", - "𥎝": "䂎", - "𥐟": "礒", - "𥐯": "𥖅", - "𥐰": "𥕥", - "𥐻": "碙", - "𥞦": "𥞵", - "𥧂": "𥨐", - "𥩟": "竚", - "𥩺": "𥪂", - "𥫣": "籅", - "𥬀": "䉙", - "𥬞": "籋", - "𥬠": "篘", - "𥭉": "𥵊", - "𥮋": "𥸠", - "𥮜": "䉲", - "𥮾": "篸", - "𥱔": "𥵃", - "𥹥": "𥼽", - "𥺅": "䊭", - "𥺇": "𥽖", - "𦈈": "𥿊", - "𦈉": "緷", - "𦈋": "綇", - "𦈌": "綀", - "𦈎": "繟", - "𦈏": "緍", - "𦈐": "縺", - "𦈑": "緸", - "𦈒": "𦂅", - "𦈓": "䋿", - "𦈔": "縎", - "𦈕": "緰", - "𦈖": "䌈", - "𦈗": "𦃄", - "𦈘": "䌋", - "𦈙": "䌰", - "𦈚": "縬", - "𦈛": "繓", - "𦈜": "䌖", - "𦈝": "繏", - "𦈞": "䌟", - "𦈟": "䌝", - "𦈠": "䌥", - "𦈡": "繻", - "𦍠": "䍽", - "𦛨": "朥", - "𦝼": "膢", - "𦟗": "𦣎", - "𦨩": "𦪽", - "𦰏": "蓧", - "𦰴": "䕳", - "𦶟": "爇", - "𦶻": "𦾟", - "𦻕": "蘟", - "𧉐": "𧕟", - "𧉞": "䗿", - "𧌥": "𧎈", - "𧏖": "蠙", - "𧏗": "蠀", - "𧑏": "蠾", - "𧒭": "𧔥", - "𧜭": "䙱", - "𧝝": "襰", - "𧝧": "𧟀", - "𧮪": "詀", - "𧳕": "𧳟", - "𧹑": "䞈", - "𧹒": "買", - "𧹓": "𧶔", - "𧹔": "賬", - "𧹕": "𝻻", - "𧹖": "賟", - "𧹗": "贃", - "𧿈": "𨇁", - "𨀁": "躘", - "𨀱": "𨄣", - "𨁴": "𨅍", - "𨂺": "𨈊", - "𨄄": "𨈌", - "𨅛": "䠱", - "𨅫": "𨇞", - "𨅬": "躝", - "𨉗": "軉", - "𨐅": "軗", - "𨐆": "𨊻", - "𨐇": "𨏠", - "𨐈": "輄", - "𨐉": "𨎮", - "𨐊": "𨏥", - "𨑹": "䢨", - "𨟳": "𨣞", - "𨠨": "𨣧", - "𨡙": "𨢿", - "𨡺": "𨣈", - "𨤰": "𨤻", - "𨰾": "鎷", - "𨰿": "釳", - "𨱀": "𨥛", - "𨱁": "鈠", - "𨱂": "鈋", - "𨱃": "鈲", - "𨱄": "鈯", - "𨱅": "鉁", - "𨱆": "龯", - "𨱇": "銶", - "𨱈": "鋉", - "𨱉": "鍄", - "𨱊": "𨧱", - "𨱋": "錂", - "𨱌": "鏆", - "𨱍": "鎯", - "𨱎": "鍮", - "𨱏": "鎝", - "𨱐": "𨫒", - "𨱑": "鐄", - "𨱒": "鏉", - "𨱓": "鐎", - "𨱔": "鐏", - "𨱕": "𨮂", - "𨱖": "䥩", - "𨷿": "䦳", - "𨸀": "𨳕", - "𨸁": "𨳑", - "𨸂": "閍", - "𨸃": "閐", - "𨸄": "䦘", - "𨸅": "𨴗", - "𨸆": "𨵩", - "𨸇": "𨵸", - "𨸉": "𨶀", - "𨸊": "𨶏", - "𨸋": "𨶲", - "𨸌": "𨶮", - "𨸎": "𨷲", - "𨸘": "𨽏", - "𨸟": "䧢", - "𩏼": "䪏", - "𩏽": "𩏪", - "𩏾": "𩎢", - "𩏿": "䪘", - "𩐀": "䪗", - "𩓋": "顂", - "𩖕": "𩓣", - "𩖖": "顃", - "𩖗": "䫴", - "𩙥": "颰", - "𩙦": "𩗀", - "𩙧": "䬞", - "𩙨": "𩘹", - "𩙩": "𩘀", - "𩙪": "颷", - "𩙫": "颾", - "𩙬": "𩘺", - "𩙭": "𩘝", - "𩙮": "䬘", - "𩙯": "䬝", - "𩙰": "𩙈", - "𩟿": "𩚛", - "𩠀": "𩚥", - "𩠁": "𩚵", - "𩠂": "𩛆", - "𩠃": "𩛩", - "𩠅": "𩟐", - "𩠆": "𩜦", - "𩠇": "䭀", - "𩠈": "䭃", - "𩠉": "𩜇", - "𩠊": "𩜵", - "𩠋": "𩝔", - "𩠌": "餸", - "𩠎": "𩞄", - "𩠏": "𩞦", - "𩠠": "𩠴", - "𩡖": "𩡣", - "𩧦": "𩡺", - "𩧨": "駎", - "𩧩": "𩤊", - "𩧪": "䮾", - "𩧫": "駚", - "𩧬": "𩢡", - "𩧭": "䭿", - "𩧮": "𩢾", - "𩧯": "驋", - "𩧰": "䮝", - "𩧱": "𩥉", - "𩧲": "駧", - "𩧳": "𩢸", - "𩧴": "駩", - "𩧵": "𩢴", - "𩧶": "𩣏", - "𩧸": "𩣫", - "𩧺": "駶", - "𩧻": "𩣵", - "𩧼": "𩣺", - "𩧿": "䮠", - "𩨀": "騔", - "𩨁": "䮞", - "𩨂": "驄", - "𩨃": "騝", - "𩨄": "騪", - "𩨅": "𩤸", - "𩨆": "𩤙", - "𩨇": "䮫", - "𩨈": "騟", - "𩨉": "𩤲", - "𩨊": "騚", - "𩨋": "𩥄", - "𩨌": "𩥑", - "𩨍": "𩥇", - "𩨎": "龭", - "𩨏": "䮳", - "𩨐": "𩧆", - "𩩈": "䯤", - "𩬣": "𩭙", - "𩬤": "𩰀", - "𩭹": "鬖", - "𩯒": "𩯳", - "𩰰": "𩰹", - "𩲒": "𩳤", - "𩴌": "𩴵", - "𩽹": "魥", - "𩽺": "𩵩", - "𩽻": "𩵹", - "𩽼": "鯶", - "𩽽": "𩶱", - "𩽾": "鮟", - "𩽿": "𩶰", - "𩾁": "鯄", - "𩾂": "䲖", - "𩾃": "鮸", - "𩾄": "𩷰", - "𩾅": "𩸃", - "𩾆": "𩸦", - "𩾇": "鯱", - "𩾈": "䱙", - "𩾊": "䱬", - "𩾋": "䱰", - "𩾌": "鱇", - "𩾎": "𩽇", - "𪉂": "䲰", - "𪉃": "鳼", - "𪉄": "𩿪", - "𪉅": "𪀦", - "𪉆": "鴲", - "𪉈": "鴜", - "𪉉": "𪁈", - "𪉊": "鷨", - "𪉋": "𪀾", - "𪉌": "𪁖", - "𪉍": "鵚", - "𪉎": "𪂆", - "𪉏": "𪃏", - "𪉐": "𪃍", - "𪉑": "鷔", - "𪉒": "𪄕", - "𪉔": "𪄆", - "𪉕": "𪇳", - "𪎈": "䴬", - "𪎉": "麲", - "𪎊": "麨", - "𪎋": "䴴", - "𪎌": "麳", - "𪑅": "䵳", - "𪔭": "𪔵", - "𪚏": "𪘀", - "𪚐": "𪘯", - "𪜎": "𠿕", - "𪞝": "凙", - "𪟎": "㔋", - "𪟝": "勣", - "𪠀": "𧷎", - "𪠟": "㓄", - "𪠡": "𠬙", - "𪠳": "唓", - "𪠵": "㖮", - "𪠸": "嚛", - "𪠺": "𠽃", - "𪠽": "噹", - "𪡀": "嘺", - "𪡃": "嘪", - "𪡋": "噞", - "𪡏": "嗹", - "𪡛": "㗿", - "𪡞": "嘳", - "𪡺": "𡃄", - "𪢌": "㘓", - "𪢐": "𡃤", - "𪢒": "𡂡", - "𪢕": "嚽", - "𪢖": "𡅯", - "𪢠": "囒", - "𪢮": "圞", - "𪢸": "墲", - "𪣆": "埬", - "𪣒": "堚", - "𪣻": "塿", - "𪤄": "𡓁", - "𪤚": "壣", - "𪥠": "𧹈", - "𪥫": "孇", - "𪥰": "嬣", - "𪥿": "嬻", - "𪧀": "孾", - "𪧘": "寠", - "𪨊": "㞞", - "𪨗": "屩", - "𪨧": "崙", - "𪨩": "𡸗", - "𪨶": "輋", - "𪨷": "巗", - "𪨹": "𡹬", - "𪩇": "㟺", - "𪩎": "巊", - "𪩘": "巘", - "𪩛": "𡿖", - "𪩷": "幝", - "𪩸": "幩", - "𪪏": "廬", - "𪪑": "㢗", - "𪪞": "廧", - "𪪴": "𢍰", - "𪪼": "彃", - "𪫌": "徿", - "𪫡": "𢤩", - "𪫷": "㦞", - "𪫺": "憸", - "𪬚": "𢣐", - "𪬯": "𢤿", - "𪭝": "𢯷", - "𪭢": "摐", - "𪭧": "擟", - "𪭯": "𢶒", - "𪭵": "掚", - "𪭾": "撊", - "𪮃": "㨻", - "𪮋": "㩋", - "𪮖": "撧", - "𪮳": "𢺳", - "𪮶": "攋", - "𪯋": "㪎", - "𪰶": "曊", - "𪱥": "膹", - "𪱷": "梖", - "𪲎": "櫅", - "𪲔": "欐", - "𪲛": "檵", - "𪲮": "櫠", - "𪳍": "欇", - "𪳗": "𣜬", - "𪴙": "欑", - "𪵑": "毊", - "𪵣": "霼", - "𪵱": "濿", - "𪶄": "溡", - "𪶒": "𤄷", - "𪶮": "𣽏", - "𪷍": "㵾", - "𪷽": "灒", - "𪸕": "熂", - "𪸩": "煇", - "𪹀": "𤑹", - "𪹠": "𤓌", - "𪹳": "爥", - "𪹹": "𤒻", - "𪺣": "𤘀", - "𪺪": "𤜆", - "𪺭": "犞", - "𪺷": "獊", - "𪺸": "𤠮", - "𪺻": "㺜", - "𪺽": "猌", - "𪻐": "瑽", - "𪻨": "瓄", - "𪻲": "瑻", - "𪻺": "璝", - "𪼋": "㻶", - "𪼴": "𤬅", - "𪽈": "畼", - "𪽝": "𤳷", - "𪽪": "痮", - "𪽭": "𤷃", - "𪽮": "㿖", - "𪽴": "𤺔", - "𪽷": "瘱", - "𪾔": "盨", - "𪾢": "睍", - "𪾣": "眝", - "𪾦": "矑", - "𪾸": "矉", - "𪿊": "𥏝", - "𪿞": "𥖲", - "𪿫": "礮", - "𪿵": "𥗇", - "𫀌": "𥜰", - "𫀓": "𥜐", - "𫀨": "䅐", - "𫀬": "䅳", - "𫀮": "𥢷", - "𫁂": "䆉", - "𫁟": "竱", - "𫁡": "鴗", - "𫁱": "𥶽", - "𫁲": "䉑", - "𫁳": "𥯤", - "𫁷": "䉶", - "𫁺": "𥴼", - "𫂃": "簢", - "𫂆": "簂", - "𫂈": "䉬", - "𫂖": "𥴨", - "𫂿": "𥻦", - "𫃗": "𩏷", - "𫄙": "糺", - "𫄚": "䊺", - "𫄛": "紟", - "𫄜": "䋃", - "𫄝": "𥾯", - "𫄞": "䋔", - "𫄟": "絁", - "𫄠": "絙", - "𫄡": "絧", - "𫄢": "絥", - "𫄣": "繷", - "𫄤": "繨", - "𫄥": "纚", - "𫄦": "𦀖", - "𫄧": "綖", - "𫄨": "絺", - "𫄩": "䋦", - "𫄪": "𦅇", - "𫄫": "綟", - "𫄬": "緤", - "𫄭": "緮", - "𫄮": "䋼", - "𫄯": "𦃩", - "𫄰": "縍", - "𫄱": "繬", - "𫄲": "縸", - "𫄳": "縰", - "𫄴": "繂", - "𫄵": "𦅈", - "𫄶": "繈", - "𫄷": "繶", - "𫄸": "纁", - "𫄹": "纗", - "𫅅": "䍤", - "𫅗": "羵", - "𫅥": "𦒀", - "𫅭": "䎙", - "𫅼": "𦔖", - "𫆏": "聻", - "𫆝": "𦟼", - "𫆫": "𦡝", - "𫇘": "𦧺", - "𫇛": "艣", - "𫇪": "𦱌", - "𫇭": "蔿", - "𫇴": "蒭", - "𫇽": "蕽", - "𫈉": "蕳", - "𫈎": "葝", - "𫈟": "蔯", - "𫈵": "蕝", - "𫉁": "薆", - "𫉄": "藷", - "𫊪": "䗅", - "𫊮": "蠦", - "𫊸": "蟜", - "𫊹": "𧒯", - "𫊻": "蟳", - "𫋇": "蟂", - "𫋌": "蟘", - "𫋲": "䙔", - "𫋷": "襗", - "𫋹": "襓", - "𫋻": "襘", - "𫌀": "襀", - "𫌇": "襵", - "𫌋": "𧞫", - "𫌨": "覼", - "𫌪": "覛", - "𫌫": "𧡴", - "𫌬": "𧢄", - "𫌭": "覹", - "𫌯": "䚩", - "𫍐": "𧭹", - "𫍙": "訑", - "𫍚": "訞", - "𫍛": "訜", - "𫍜": "詓", - "𫍝": "諫", - "𫍞": "𧦝", - "𫍟": "𧦧", - "𫍠": "䛄", - "𫍡": "詑", - "𫍢": "譊", - "𫍣": "詷", - "𫍤": "譑", - "𫍥": "誂", - "𫍦": "譨", - "𫍧": "誺", - "𫍨": "誫", - "𫍩": "諣", - "𫍪": "誋", - "𫍫": "䛳", - "𫍬": "誷", - "𫍭": "𧩕", - "𫍮": "誳", - "𫍯": "諴", - "𫍰": "諰", - "𫍱": "諯", - "𫍲": "謏", - "𫍳": "諥", - "𫍴": "謱", - "𫍵": "謸", - "𫍶": "𧩼", - "𫍷": "謉", - "𫍸": "謆", - "𫍹": "謯", - "𫍺": "𧫝", - "𫍻": "譆", - "𫍼": "𧬤", - "𫍽": "譞", - "𫍾": "𧭈", - "𫍿": "譾", - "𫎆": "豵", - "𫎌": "貗", - "𫎦": "贚", - "𫎧": "䝭", - "𫎨": "𧸘", - "𫎩": "賝", - "𫎪": "䞋", - "𫎫": "贉", - "𫎬": "贑", - "𫎭": "䞓", - "𫎱": "䟐", - "𫎳": "䟆", - "𫎸": "𧽯", - "𫎺": "䟃", - "𫏃": "䠆", - "𫏆": "蹳", - "𫏋": "蹻", - "𫏌": "𨂐", - "𫏐": "蹔", - "𫏑": "𨇽", - "𫏕": "𨆪", - "𫏞": "𨇰", - "𫏨": "𨇤", - "𫐄": "軏", - "𫐅": "軕", - "𫐆": "轣", - "𫐇": "軜", - "𫐈": "軷", - "𫐉": "軨", - "𫐊": "軬", - "𫐋": "𨎌", - "𫐌": "軿", - "𫐍": "𨌈", - "𫐎": "輢", - "𫐏": "輖", - "𫐐": "輗", - "𫐑": "輨", - "𫐒": "輷", - "𫐓": "輮", - "𫐔": "𨍰", - "𫐕": "轊", - "𫐖": "轇", - "𫐗": "轐", - "𫐘": "轗", - "𫐙": "轠", - "𫐷": "遱", - "𫑘": "鄟", - "𫑡": "鄳", - "𫑷": "醶", - "𫓥": "釟", - "𫓦": "釨", - "𫓧": "鈇", - "𫓨": "鈛", - "𫓩": "鏦", - "𫓪": "鈆", - "𫓫": "𨥟", - "𫓬": "鉔", - "𫓭": "鉠", - "𫓮": "𨪕", - "𫓯": "銈", - "𫓰": "銊", - "𫓱": "鐈", - "𫓲": "銁", - "𫓳": "𨰋", - "𫓴": "鉾", - "𫓵": "鋠", - "𫓶": "鋗", - "𫓷": "𫒡", - "𫓸": "錽", - "𫓹": "錤", - "𫓺": "鐪", - "𫓻": "錜", - "𫓼": "𨨛", - "𫓽": "錝", - "𫓾": "錥", - "𫓿": "𨨢", - "𫔀": "鍊", - "𫔁": "鐼", - "𫔂": "鍉", - "𫔃": "𨰲", - "𫔄": "鍒", - "𫔅": "鎍", - "𫔆": "䥯", - "𫔇": "鎞", - "𫔈": "鎙", - "𫔉": "𨰃", - "𫔊": "鏥", - "𫔋": "䥗", - "𫔌": "鏾", - "𫔍": "鐇", - "𫔎": "鐍", - "𫔏": "𨬖", - "𫔐": "𨭸", - "𫔑": "𨭖", - "𫔒": "𨮳", - "𫔓": "𨯟", - "𫔔": "鑴", - "𫔕": "𨰥", - "𫔖": "𨲳", - "𫔭": "開", - "𫔮": "閒", - "𫔯": "閗", - "𫔰": "閞", - "𫔲": "𨴹", - "𫔴": "閵", - "𫔵": "䦯", - "𫔶": "闑", - "𫔽": "𨼳", - "𫕚": "𩀨", - "𫕥": "霣", - "𫕨": "𩅙", - "𫖃": "靧", - "𫖅": "䪊", - "𫖇": "鞾", - "𫖑": "𩎖", - "𫖒": "韠", - "𫖓": "𩏂", - "𫖔": "韛", - "𫖕": "韝", - "𫖖": "𩏠", - "𫖪": "𩑔", - "𫖫": "䪴", - "𫖬": "䪾", - "𫖭": "𩒎", - "𫖮": "顗", - "𫖯": "頫", - "𫖰": "䫂", - "𫖱": "䫀", - "𫖲": "䫟", - "𫖳": "頵", - "𫖴": "𩔳", - "𫖵": "𩓥", - "𫖶": "顅", - "𫖷": "𩔑", - "𫖸": "願", - "𫖹": "顣", - "𫖺": "䫶", - "𫗇": "䫻", - "𫗈": "𩗓", - "𫗉": "𩗴", - "𫗊": "䬓", - "𫗋": "飋", - "𫗚": "𩟗", - "𫗞": "飦", - "𫗟": "䬧", - "𫗠": "餦", - "𫗡": "𩚩", - "𫗢": "飵", - "𫗣": "飶", - "𫗤": "𩛌", - "𫗥": "餫", - "𫗦": "餔", - "𫗧": "餗", - "𫗨": "𩛡", - "𫗩": "饠", - "𫗪": "餧", - "𫗫": "餬", - "𫗬": "餪", - "𫗭": "餵", - "𫗮": "餭", - "𫗯": "餱", - "𫗰": "䭔", - "𫗱": "䭑", - "𫗳": "𩝽", - "𫗴": "饘", - "𫗵": "饟", - "𫘛": "馯", - "𫘜": "馼", - "𫘝": "駃", - "𫘞": "駞", - "𫘟": "駊", - "𫘠": "駤", - "𫘡": "駫", - "𫘣": "駻", - "𫘤": "騃", - "𫘥": "騉", - "𫘦": "騊", - "𫘧": "騄", - "𫘨": "騠", - "𫘩": "騜", - "𫘪": "騵", - "𫘫": "騴", - "𫘬": "騱", - "𫘭": "騻", - "𫘮": "䮰", - "𫘯": "驓", - "𫘰": "驙", - "𫘱": "驨", - "𫘽": "鬠", - "𫙂": "𩯁", - "𫚈": "鱮", - "𫚉": "魟", - "𫚊": "鰑", - "𫚋": "鱄", - "𫚌": "魦", - "𫚍": "魵", - "𫚎": "𩶁", - "𫚏": "䱁", - "𫚐": "䱀", - "𫚑": "鮅", - "𫚒": "鮄", - "𫚓": "鮤", - "𫚔": "鮰", - "𫚕": "鰤", - "𫚖": "鮆", - "𫚗": "鮯", - "𫚘": "𩻮", - "𫚙": "鯆", - "𫚚": "鮿", - "𫚛": "鮵", - "𫚜": "䲅", - "𫚝": "𩸄", - "𫚞": "鯬", - "𫚟": "𩸡", - "𫚠": "䱧", - "𫚡": "鯞", - "𫚢": "鰋", - "𫚣": "鯾", - "𫚤": "鰦", - "𫚥": "鰕", - "𫚦": "鰫", - "𫚧": "鰽", - "𫚨": "𩻗", - "𫚩": "𩻬", - "𫚪": "鱊", - "𫚫": "鱢", - "𫚬": "𩼶", - "𫚭": "鱲", - "𫛚": "鳽", - "𫛛": "鳷", - "𫛜": "鴀", - "𫛝": "鴅", - "𫛞": "鴃", - "𫛟": "鸗", - "𫛠": "𩿤", - "𫛡": "鴔", - "𫛢": "鸋", - "𫛣": "鴥", - "𫛤": "鴐", - "𫛥": "鵊", - "𫛦": "鴮", - "𫛧": "𪀖", - "𫛨": "鵧", - "𫛩": "鴳", - "𫛪": "鴽", - "𫛫": "鶰", - "𫛬": "䳜", - "𫛭": "鵟", - "𫛮": "䳤", - "𫛯": "鶭", - "𫛰": "䳢", - "𫛱": "鵫", - "𫛲": "鵰", - "𫛳": "鵩", - "𫛴": "鷤", - "𫛵": "鶌", - "𫛶": "鶒", - "𫛷": "鶦", - "𫛸": "鶗", - "𫛹": "𪃧", - "𫛺": "䳧", - "𫛻": "𪃒", - "𫛼": "䳫", - "𫛽": "鷅", - "𫛾": "𪆷", - "𫜀": "鷐", - "𫜁": "鷩", - "𫜂": "𪅂", - "𫜃": "鷣", - "𫜄": "鷷", - "𫜅": "䴋", - "𫜊": "𪉸", - "𫜑": "麷", - "𫜒": "䴱", - "𫜓": "𪌭", - "𫜔": "䴽", - "𫜕": "𪍠", - "𫜙": "䵴", - "𫜟": "𪓰", - "𫜨": "𪶕", - "𫜩": "齧", - "𫜪": "齩", - "𫜫": "𫜦", - "𫜬": "齰", - "𫜭": "齭", - "𫜮": "齴", - "𫜯": "𪙏", - "𫜰": "齾", - "𫜲": "龓", - "𫜳": "䶲", - "𫝈": "㑮", - "𫝋": "𠐊", - "𫝦": "㛝", - "𫝧": "㜐", - "𫝨": "媈", - "𫝩": "嬦", - "𫝪": "𡟫", - "𫝫": "婡", - "𫝬": "嬇", - "𫝭": "孆", - "𫝮": "孄", - "𫝵": "嶹", - "𫞅": "𦠅", - "𫞗": "潣", - "𫞚": "澬", - "𫞛": "㶆", - "𫞝": "灍", - "𫞠": "爧", - "𫞡": "爃", - "𫞢": "𤛱", - "𫞣": "㹽", - "𫞥": "珼", - "𫞦": "璾", - "𫞧": "𤩂", - "𫞨": "璼", - "𫞩": "璊", - "𫞷": "𥢶", - "𫟃": "絍", - "𫟄": "綋", - "𫟅": "綡", - "𫟆": "緟", - "𫟇": "𦆲", - "𫟑": "䖅", - "𫟕": "䕤", - "𫟞": "訨", - "𫟟": "詊", - "𫟠": "譂", - "𫟡": "誴", - "𫟢": "䜖", - "𫟤": "䡐", - "𫟥": "䡩", - "𫟦": "䡵", - "𫟫": "𨞺", - "𫟬": "𨟊", - "𫟲": "釚", - "𫟳": "釲", - "𫟴": "鈖", - "𫟵": "鈗", - "𫟶": "銏", - "𫟷": "鉝", - "𫟸": "鉽", - "𫟹": "鉷", - "𫟺": "䤤", - "𫟻": "銂", - "𫟼": "鐽", - "𫟽": "𨧰", - "𫟾": "𨩰", - "𫟿": "鎈", - "𫠀": "䥄", - "𫠁": "鑉", - "𫠂": "閝", - "𫠅": "韚", - "𫠆": "頍", - "𫠇": "𩖰", - "𫠈": "䫾", - "𫠊": "䮄", - "𫠋": "騼", - "𫠌": "𩦠", - "𫠏": "𩵦", - "𫠐": "魽", - "𫠑": "䱸", - "𫠒": "鱆", - "𫠖": "𩿅", - "𫠜": "齯", - "𫢸": "僤", - "𫧃": "𣍐", - "𫧮": "𪋿", - "𫫇": "噁", - "𫬐": "㘔", - "𫭟": "塸", - "𫭢": "埨", - "𫭼": "𡑍", - "𫮃": "墠", - "𫰛": "娙", - "𫵷": "㠣", - "𫶇": "嵽", - "𫷷": "廞", - "𫸩": "彄", - "𬀩": "暐", - "𬀪": "晛", - "𬂩": "梜", - "𬃊": "櫍", - "𬇕": "澫", - "𬇙": "浿", - "𬇹": "漍", - "𬉼": "熰", - "𬊈": "燖", - "𬊤": "燀", - "𬍛": "瓅", - "𬍡": "璗", - "𬍤": "璕", - "𬒈": "礐", - "𬒗": "𥗽", - "𬕂": "篢", - "𬘓": "紃", - "𬘘": "紞", - "𬘡": "絪", - "𬘩": "綎", - "𬘫": "綄", - "𬘬": "綪", - "𬘭": "綝", - "𬘯": "綧", - "𬙂": "縯", - "𬙊": "纆", - "𬙋": "纕", - "𬜬": "蔄", - "𬜯": "䓣", - "𬞟": "蘋", - "𬟁": "虉", - "𬟽": "蝀", - "𬣙": "訏", - "𬣞": "詝", - "𬣡": "諓", - "𬣳": "詪", - "𬤇": "諲", - "𬤊": "諟", - "𬤝": "譓", - "𬨂": "軝", - "𬨎": "輶", - "𬩽": "鄩", - "𬪩": "醲", - "𬬩": "釴", - "𬬭": "錀", - "𬬮": "鋹", - "𬬱": "釿", - "𬬸": "鉥", - "𬬹": "鉮", - "𬬻": "鑪", - "𬬿": "鉊", - "𬭁": "鉧", - "𬭊": "𨧀", - "𬭎": "鋐", - "𬭚": "錞", - "𬭛": "𨨏", - "𬭤": "鍭", - "𬭩": "鎓", - "𬭬": "鏏", - "𬭭": "鏚", - "𬭯": "䥕", - "𬭳": "𨭎", - "𬭶": "𨭆", - "𬭸": "鏻", - "𬭼": "鐩", - "𬮱": "闉", - "𬮿": "隑", - "𬯀": "隮", - "𬯎": "隤", - "𬱖": "頔", - "𬱟": "頠", - "𬳵": "駓", - "𬳶": "駉", - "𬳽": "駪", - "𬳿": "駼", - "𬴂": "騑", - "𬴃": "騞", - "𬴊": "驎", - "𬶋": "鮈", - "𬶍": "鮀", - "𬶏": "鮠", - "𬶐": "鮡", - "𬶟": "鯻", - "𬶠": "鰊", - "𬶨": "鱀", - "𬶭": "鰶", - "𬶮": "鱚", - "𬷕": "鵏", - "𬸘": "鶠", - "𬸚": "鸑", - "𬸣": "鶱", - "𬸦": "鷟", - "𬸪": "鷭", - "𬸯": "鷿", - "𬹼": "齘", - "𬺈": "齮", - "𬺓": "齼", - "𰬸": "繐", - "𰰨": "菕", - "𰶎": "譅", - "𰾄": "鋂", - "𰾭": "鑀", - "𱊜": "𪈼", -}; -const t2sData = { - 㑮: "𫝈", - 㑯: "㑔", - 㑳: "㑇", - 㑶: "㐹", - 㒓: "𠉂", - 㓄: "𪠟", - 㓨: "刾", - 㔋: "𪟎", - 㖮: "𪠵", - 㗲: "𠵾", - 㗿: "𪡛", - 㘉: "𠰱", - 㘓: "𪢌", - 㘔: "𫬐", - 㘚: "㘎", - 㛝: "𫝦", - 㜄: "㚯", - 㜏: "㛣", - 㜐: "𫝧", - 㜗: "𡞋", - 㜢: "𡞱", - 㜷: "𡝠", - 㞞: "𪨊", - 㟺: "𪩇", - 㠏: "㟆", - 㠣: "𫵷", - 㢗: "𪪑", - 㢝: "𢋈", - 㥮: "㤘", - 㦎: "𢛯", - 㦛: "𢗓", - 㦞: "𪫷", - 㨻: "𪮃", - 㩋: "𪮋", - 㩜: "㨫", - 㩳: "㧐", - 㩵: "擜", - 㪎: "𪯋", - 㯤: "𣘐", - 㰙: "𣗙", - 㵗: "𣳆", - 㵾: "𪷍", - 㶆: "𫞛", - 㷍: "𤆢", - 㷿: "𤈷", - 㸇: "𤎺", - 㹽: "𫞣", - 㺏: "𤠋", - 㺜: "𪺻", - 㻶: "𪼋", - 㿖: "𪽮", - 㿗: "𤻊", - 㿧: "𤽯", - 䀉: "𥁢", - 䀹: "𥅴", - 䁪: "𥇢", - 䁻: "䀥", - 䂎: "𥎝", - 䃮: "鿎", - 䅐: "𫀨", - 䅳: "𫀬", - 䆉: "𫁂", - 䉑: "𫁲", - 䉙: "𥬀", - 䉬: "𫂈", - 䉲: "𥮜", - 䉶: "𫁷", - 䊭: "𥺅", - 䊷: "䌶", - 䊺: "𫄚", - 䋃: "𫄜", - 䋔: "𫄞", - 䋙: "䌺", - 䋚: "䌻", - 䋦: "𫄩", - 䋹: "䌿", - 䋻: "䌾", - 䋼: "𫄮", - 䋿: "𦈓", - 䌈: "𦈖", - 䌋: "𦈘", - 䌖: "𦈜", - 䌝: "𦈟", - 䌟: "𦈞", - 䌥: "𦈠", - 䌰: "𦈙", - 䍤: "𫅅", - 䍦: "䍠", - 䍽: "𦍠", - 䎙: "𫅭", - 䎱: "䎬", - 䓣: "𬜯", - 䕤: "𫟕", - 䕳: "𦰴", - 䖅: "𫟑", - 䗅: "𫊪", - 䗿: "𧉞", - 䙔: "𫋲", - 䙡: "䙌", - 䙱: "𧜭", - 䚩: "𫌯", - 䛄: "𫍠", - 䛳: "𫍫", - 䜀: "䜧", - 䜖: "𫟢", - 䝭: "𫎧", - 䝻: "𧹕", - 䝼: "䞍", - 䞈: "𧹑", - 䞋: "𫎪", - 䞓: "𫎭", - 䟃: "𫎺", - 䟆: "𫎳", - 䟐: "𫎱", - 䠆: "𫏃", - 䠱: "𨅛", - 䡐: "𫟤", - 䡩: "𫟥", - 䡵: "𫟦", - 䢨: "𨑹", - 䤤: "𫟺", - 䥄: "𫠀", - 䥇: "䦂", - 䥑: "鿏", - 䥕: "𬭯", - 䥗: "𫔋", - 䥩: "𨱖", - 䥯: "𫔆", - 䥱: "䥾", - 䦘: "𨸄", - 䦛: "䦶", - 䦟: "䦷", - 䦯: "𫔵", - 䦳: "𨷿", - 䧢: "𨸟", - 䪊: "𫖅", - 䪏: "𩏼", - 䪗: "𩐀", - 䪘: "𩏿", - 䪴: "𫖫", - 䪾: "𫖬", - 䫀: "𫖱", - 䫂: "𫖰", - 䫟: "𫖲", - 䫴: "𩖗", - 䫶: "𫖺", - 䫻: "𫗇", - 䫾: "𫠈", - 䬓: "𫗊", - 䬘: "𩙮", - 䬝: "𩙯", - 䬞: "𩙧", - 䬧: "𫗟", - 䭀: "𩠇", - 䭃: "𩠈", - 䭑: "𫗱", - 䭔: "𫗰", - 䭿: "𩧭", - 䮄: "𫠊", - 䮝: "𩧰", - 䮞: "𩨁", - 䮠: "𩧿", - 䮫: "𩨇", - 䮰: "𫘮", - 䮳: "𩨏", - 䮾: "𩧪", - 䯀: "䯅", - 䯤: "𩩈", - 䰾: "鲃", - 䱀: "𫚐", - 䱁: "𫚏", - 䱙: "𩾈", - 䱧: "𫚠", - 䱬: "𩾊", - 䱰: "𩾋", - 䱷: "䲣", - 䱸: "𫠑", - 䱽: "䲝", - 䲁: "鳚", - 䲅: "𫚜", - 䲖: "𩾂", - 䲘: "鳤", - 䲰: "𪉂", - 䳜: "𫛬", - 䳢: "𫛰", - 䳤: "𫛮", - 䳧: "𫛺", - 䳫: "𫛼", - 䴉: "鹮", - 䴋: "𫜅", - 䴬: "𪎈", - 䴱: "𫜒", - 䴴: "𪎋", - 䴽: "𫜔", - 䵳: "𪑅", - 䵴: "𫜙", - 䶕: "𫜨", - 䶲: "𫜳", - 丟: "丢", - 並: "并", - 乾: "干", - 亂: "乱", - 亙: "亘", - 亞: "亚", - 佇: "伫", - 佈: "布", - 佔: "占", - 併: "并", - 來: "来", - 侖: "仑", - 侶: "侣", - 侷: "局", - 俁: "俣", - 係: "系", - 俓: "𠇹", - 俔: "伣", - 俠: "侠", - 俥: "伡", - 俬: "私", - 倀: "伥", - 倆: "俩", - 倈: "俫", - 倉: "仓", - 個: "个", - 們: "们", - 倖: "幸", - 倫: "伦", - 倲: "㑈", - 偉: "伟", - 偑: "㐽", - 側: "侧", - 偵: "侦", - 偽: "伪", - 傌: "㐷", - 傑: "杰", - 傖: "伧", - 傘: "伞", - 備: "备", - 傢: "家", - 傭: "佣", - 傯: "偬", - 傳: "传", - 傴: "伛", - 債: "债", - 傷: "伤", - 傾: "倾", - 僂: "偻", - 僅: "仅", - 僉: "佥", - 僑: "侨", - 僕: "仆", - 僞: "伪", - 僤: "𫢸", - 僥: "侥", - 僨: "偾", - 僱: "雇", - 價: "价", - 儀: "仪", - 儁: "俊", - 儂: "侬", - 億: "亿", - 儈: "侩", - 儉: "俭", - 儎: "", - 儐: "", - 儔: "俦", - 儕: "侪", - 儘: "尽", - 償: "偿", - 儣: "𠆲", - 優: "优", - 儭: "𠋆", - 儲: "储", - 儷: "俪", - 儸: "㑩", - 儺: "傩", - 儻: "傥", - 儼: "俨", - 兇: "凶", - 兌: "兑", - 兒: "儿", - 兗: "兖", - 內: "内", - 兩: "两", - 冊: "册", - 冑: "胄", - 冪: "幂", - 凈: "净", - 凍: "冻", - 凙: "𪞝", - 凜: "凛", - 凱: "凯", - 別: "别", - 刪: "删", - 剄: "刭", - 則: "则", - 剋: "克", - 剎: "刹", - 剗: "刬", - 剛: "刚", - 剝: "剥", - 剮: "剐", - 剴: "剀", - 創: "创", - 剷: "铲", - 剾: "𠛅", - 劃: "划", - 劇: "剧", - 劉: "刘", - 劊: "刽", - 劌: "刿", - 劍: "剑", - 劏: "㓥", - 劑: "剂", - 劚: "㔉", - 勁: "劲", - 勑: "𠡠", - 動: "动", - 務: "务", - 勛: "勋", - 勝: "胜", - 勞: "劳", - 勢: "势", - 勣: "𪟝", - 勩: "勚", - 勱: "劢", - 勳: "勋", - 勵: "励", - 勸: "劝", - 勻: "匀", - 匭: "匦", - 匯: "汇", - 匱: "匮", - 區: "区", - 協: "协", - 卹: "恤", - 卻: "却", - 卽: "即", - 厙: "厍", - 厠: "厕", - 厤: "历", - 厭: "厌", - 厲: "厉", - 厴: "厣", - 參: "参", - 叄: "叁", - 叢: "丛", - 吒: "咤", - 吳: "吴", - 吶: "呐", - 呂: "吕", - 咼: "呙", - 員: "员", - 哯: "𠯟", - 唄: "呗", - 唓: "𪠳", - 唸: "念", - 問: "问", - 啓: "启", - 啞: "哑", - 啟: "启", - 啢: "唡", - 喎: "㖞", - 喚: "唤", - 喪: "丧", - 喫: "吃", - 喬: "乔", - 單: "单", - 喲: "哟", - 嗆: "呛", - 嗇: "啬", - 嗊: "唝", - 嗎: "吗", - 嗚: "呜", - 嗩: "唢", - 嗰: "𠮶", - 嗶: "哔", - 嗹: "𪡏", - 嘆: "叹", - 嘍: "喽", - 嘓: "啯", - 嘔: "呕", - 嘖: "啧", - 嘗: "尝", - 嘜: "唛", - 嘩: "哗", - 嘪: "𪡃", - 嘮: "唠", - 嘯: "啸", - 嘰: "叽", - 嘳: "𪡞", - 嘵: "哓", - 嘸: "呒", - 嘺: "𪡀", - 嘽: "啴", - 噁: "恶", - 噅: "𠯠", - 噓: "嘘", - 噚: "㖊", - 噝: "咝", - 噞: "𪡋", - 噠: "哒", - 噥: "哝", - 噦: "哕", - 噯: "嗳", - 噲: "哙", - 噴: "喷", - 噸: "吨", - 噹: "当", - 嚀: "咛", - 嚇: "吓", - 嚌: "哜", - 嚐: "尝", - 嚕: "噜", - 嚙: "啮", - 嚛: "𪠸", - 嚥: "咽", - 嚦: "呖", - 嚧: "𠰷", - 嚨: "咙", - 嚮: "向", - 嚲: "亸", - 嚳: "喾", - 嚴: "严", - 嚶: "嘤", - 嚽: "𪢕", - 囀: "啭", - 囁: "嗫", - 囂: "嚣", - 囃: "𠱞", - 囅: "冁", - 囈: "呓", - 囉: "啰", - 囌: "苏", - 囑: "嘱", - 囒: "𪢠", - 囪: "囱", - 圇: "囵", - 國: "国", - 圍: "围", - 園: "园", - 圓: "圆", - 圖: "图", - 團: "团", - 圞: "𪢮", - 垻: "坝", - 埡: "垭", - 埨: "𫭢", - 埬: "𪣆", - 埰: "采", - 執: "执", - 堅: "坚", - 堊: "垩", - 堖: "垴", - 堚: "𪣒", - 堝: "埚", - 堯: "尧", - 報: "报", - 場: "场", - 塊: "块", - 塋: "茔", - 塏: "垲", - 塒: "埘", - 塗: "涂", - 塚: "冢", - 塢: "坞", - 塤: "埙", - 塵: "尘", - 塸: "𫭟", - 塹: "堑", - 塿: "𪣻", - 墊: "垫", - 墜: "坠", - 墠: "𫮃", - 墮: "堕", - 墰: "坛", - 墲: "𪢸", - 墳: "坟", - 墶: "垯", - 墻: "墙", - 墾: "垦", - 壇: "坛", - 壈: "𡒄", - 壋: "垱", - 壎: "埙", - 壓: "压", - 壗: "𡋤", - 壘: "垒", - 壙: "圹", - 壚: "垆", - 壜: "坛", - 壞: "坏", - 壟: "垄", - 壠: "垅", - 壢: "坜", - 壣: "𪤚", - 壩: "坝", - 壪: "塆", - 壯: "壮", - 壺: "壶", - 壼: "壸", - 壽: "寿", - 夠: "够", - 夢: "梦", - 夥: "伙", - 夾: "夹", - 奐: "奂", - 奧: "奥", - 奩: "奁", - 奪: "夺", - 奬: "奖", - 奮: "奋", - 奼: "姹", - 妝: "妆", - 姍: "姗", - 姦: "奸", - 娙: "𫰛", - 娛: "娱", - 婁: "娄", - 婡: "𫝫", - 婦: "妇", - 婭: "娅", - 媈: "𫝨", - 媧: "娲", - 媯: "妫", - 媰: "㛀", - 媼: "媪", - 媽: "妈", - 嫋: "袅", - 嫗: "妪", - 嫵: "妩", - 嫺: "娴", - 嫻: "娴", - 嫿: "婳", - 嬀: "妫", - 嬃: "媭", - 嬇: "𫝬", - 嬈: "娆", - 嬋: "婵", - 嬌: "娇", - 嬙: "嫱", - 嬡: "嫒", - 嬣: "𪥰", - 嬤: "嬷", - 嬦: "𫝩", - 嬪: "嫔", - 嬰: "婴", - 嬸: "婶", - 嬻: "𪥿", - 孃: "娘", - 孄: "𫝮", - 孆: "𫝭", - 孇: "𪥫", - 孋: "㛤", - 孌: "娈", - 孎: "𡠟", - 孫: "孙", - 學: "学", - 孻: "𡥧", - 孾: "𪧀", - 孿: "孪", - 宮: "宫", - 寀: "采", - 寠: "𪧘", - 寢: "寝", - 實: "实", - 寧: "宁", - 審: "审", - 寫: "写", - 寬: "宽", - 寵: "宠", - 寶: "宝", - 將: "将", - 專: "专", - 尋: "寻", - 對: "对", - 導: "导", - 尷: "尴", - 屆: "届", - 屍: "尸", - 屓: "屃", - 屜: "屉", - 屢: "屡", - 層: "层", - 屨: "屦", - 屩: "𪨗", - 屬: "属", - 岡: "冈", - 峯: "峰", - 峴: "岘", - 島: "岛", - 峽: "峡", - 崍: "崃", - 崑: "昆", - 崗: "岗", - 崙: "仑", - 崢: "峥", - 崬: "岽", - 嵐: "岚", - 嵗: "岁", - 嵼: "𡶴", - 嵽: "𫶇", - 嵾: "㟥", - 嶁: "嵝", - 嶄: "崭", - 嶇: "岖", - 嶈: "𡺃", - 嶔: "嵚", - 嶗: "崂", - 嶘: "𡺄", - 嶠: "峤", - 嶢: "峣", - 嶧: "峄", - 嶨: "峃", - 嶮: "崄", - 嶸: "嵘", - 嶹: "𫝵", - 嶺: "岭", - 嶼: "屿", - 嶽: "岳", - 巊: "𪩎", - 巋: "岿", - 巒: "峦", - 巔: "巅", - 巖: "岩", - 巗: "𪨷", - 巘: "𪩘", - 巰: "巯", - 巹: "卺", - 帥: "帅", - 師: "师", - 帳: "帐", - 帶: "带", - 幀: "帧", - 幃: "帏", - 幓: "㡎", - 幗: "帼", - 幘: "帻", - 幝: "𪩷", - 幟: "帜", - 幣: "币", - 幩: "𪩸", - 幫: "帮", - 幬: "帱", - 幹: "干", - 幾: "几", - 庫: "库", - 廁: "厕", - 廂: "厢", - 廄: "厩", - 廈: "厦", - 廎: "庼", - 廕: "荫", - 廚: "厨", - 廝: "厮", - 廞: "𫷷", - 廟: "庙", - 廠: "厂", - 廡: "庑", - 廢: "废", - 廣: "广", - 廧: "𪪞", - 廩: "廪", - 廬: "庐", - 廳: "厅", - 弒: "弑", - 弔: "吊", - 弳: "弪", - 張: "张", - 強: "强", - 彃: "𪪼", - 彄: "𫸩", - 彆: "别", - 彈: "弹", - 彌: "弥", - 彎: "弯", - 彔: "录", - 彙: "汇", - 彠: "彟", - 彥: "彦", - 彫: "雕", - 彲: "彨", - 彷: "彷", - 彿: "佛", - 後: "后", - 徑: "径", - 從: "从", - 徠: "徕", - 復: "复", - 徵: "征", - 徹: "彻", - 徿: "𪫌", - 恆: "恒", - 恥: "耻", - 悅: "悦", - 悞: "悮", - 悵: "怅", - 悶: "闷", - 悽: "凄", - 惡: "恶", - 惱: "恼", - 惲: "恽", - 惻: "恻", - 愛: "爱", - 愜: "惬", - 愨: "悫", - 愴: "怆", - 愷: "恺", - 愻: "𢙏", - 愾: "忾", - 慄: "栗", - 態: "态", - 慍: "愠", - 慘: "惨", - 慚: "惭", - 慟: "恸", - 慣: "惯", - 慤: "悫", - 慪: "怄", - 慫: "怂", - 慮: "虑", - 慳: "悭", - 慶: "庆", - 慺: "㥪", - 慼: "戚", - 慾: "欲", - 憂: "忧", - 憊: "惫", - 憐: "怜", - 憑: "凭", - 憒: "愦", - 憖: "慭", - 憚: "惮", - 憢: "𢙒", - 憤: "愤", - 憫: "悯", - 憮: "怃", - 憲: "宪", - 憶: "忆", - 憸: "𪫺", - 憹: "𢙐", - 懀: "𢙓", - 懇: "恳", - 應: "应", - 懌: "怿", - 懍: "懔", - 懎: "𢠁", - 懞: "蒙", - 懟: "怼", - 懣: "懑", - 懤: "㤽", - 懨: "恹", - 懲: "惩", - 懶: "懒", - 懷: "怀", - 懸: "悬", - 懺: "忏", - 懼: "惧", - 懾: "慑", - 戀: "恋", - 戇: "戆", - 戔: "戋", - 戧: "戗", - 戩: "戬", - 戰: "战", - 戱: "戯", - 戲: "戏", - 戶: "户", - 拋: "抛", - 挩: "捝", - 挱: "挲", - 挾: "挟", - 捨: "舍", - 捫: "扪", - 捱: "挨", - 捲: "卷", - 掃: "扫", - 掄: "抡", - 掆: "㧏", - 掗: "挜", - 掙: "挣", - 掚: "𪭵", - 掛: "挂", - 採: "采", - 揀: "拣", - 揚: "扬", - 換: "换", - 揮: "挥", - 揯: "搄", - 損: "损", - 搖: "摇", - 搗: "捣", - 搵: "揾", - 搶: "抢", - 摋: "𢫬", - 摐: "𪭢", - 摑: "掴", - 摜: "掼", - 摟: "搂", - 摯: "挚", - 摳: "抠", - 摶: "抟", - 摺: "折", - 摻: "掺", - 撈: "捞", - 撊: "𪭾", - 撏: "挦", - 撐: "撑", - 撓: "挠", - 撝: "㧑", - 撟: "挢", - 撣: "掸", - 撥: "拨", - 撧: "𪮖", - 撫: "抚", - 撲: "扑", - 撳: "揿", - 撻: "挞", - 撾: "挝", - 撿: "捡", - 擁: "拥", - 擄: "掳", - 擇: "择", - 擊: "击", - 擋: "挡", - 擓: "㧟", - 擔: "担", - 據: "据", - 擟: "𪭧", - 擠: "挤", - 擣: "捣", - 擫: "𢬍", - 擬: "拟", - 擯: "摈", - 擰: "拧", - 擱: "搁", - 擲: "掷", - 擴: "扩", - 擷: "撷", - 擺: "摆", - 擻: "擞", - 擼: "撸", - 擽: "㧰", - 擾: "扰", - 攄: "摅", - 攆: "撵", - 攋: "𪮶", - 攏: "拢", - 攔: "拦", - 攖: "撄", - 攙: "搀", - 攛: "撺", - 攜: "携", - 攝: "摄", - 攢: "攒", - 攣: "挛", - 攤: "摊", - 攪: "搅", - 攬: "揽", - 敎: "教", - 敓: "敚", - 敗: "败", - 敘: "叙", - 敵: "敌", - 數: "数", - 斂: "敛", - 斃: "毙", - 斅: "𢽾", - 斆: "敩", - 斕: "斓", - 斬: "斩", - 斷: "断", - 斸: "𣃁", - 於: "于", - 旂: "旗", - 旣: "既", - 昇: "升", - 時: "时", - 晉: "晋", - 晛: "𬀪", - 晝: "昼", - 暈: "晕", - 暉: "晖", - 暐: "𬀩", - 暘: "旸", - 暢: "畅", - 暫: "暂", - 曄: "晔", - 曆: "历", - 曇: "昙", - 曉: "晓", - 曊: "𪰶", - 曏: "向", - 曖: "暧", - 曠: "旷", - 曥: "𣆐", - 曨: "昽", - 曬: "晒", - 書: "书", - 會: "会", - 朥: "𦛨", - 朧: "胧", - 朮: "术", - 東: "东", - 枴: "拐", - 柵: "栅", - 柺: "拐", - 査: "查", - 桱: "𣐕", - 桿: "杆", - 梔: "栀", - 梖: "𪱷", - 梘: "枧", - 梜: "𬂩", - 條: "条", - 梟: "枭", - 梲: "棁", - 棄: "弃", - 棊: "棋", - 棖: "枨", - 棗: "枣", - 棟: "栋", - 棡: "㭎", - 棧: "栈", - 棲: "栖", - 棶: "梾", - 椏: "桠", - 椲: "㭏", - 楇: "𣒌", - 楊: "杨", - 楓: "枫", - 楨: "桢", - 業: "业", - 極: "极", - 榘: "矩", - 榦: "干", - 榪: "杩", - 榮: "荣", - 榲: "榅", - 榿: "桤", - 構: "构", - 槍: "枪", - 槓: "杠", - 槤: "梿", - 槧: "椠", - 槨: "椁", - 槫: "𣏢", - 槮: "椮", - 槳: "桨", - 槶: "椢", - 槼: "椝", - 樁: "桩", - 樂: "乐", - 樅: "枞", - 樑: "梁", - 樓: "楼", - 標: "标", - 樞: "枢", - 樠: "𣗊", - 樢: "㭤", - 樣: "样", - 樤: "𣔌", - 樧: "榝", - 樫: "㭴", - 樳: "桪", - 樸: "朴", - 樹: "树", - 樺: "桦", - 樿: "椫", - 橈: "桡", - 橋: "桥", - 機: "机", - 橢: "椭", - 橫: "横", - 橯: "𣓿", - 檁: "檩", - 檉: "柽", - 檔: "档", - 檜: "桧", - 檟: "槚", - 檢: "检", - 檣: "樯", - 檭: "𣘴", - 檮: "梼", - 檯: "台", - 檳: "槟", - 檵: "𪲛", - 檸: "柠", - 檻: "槛", - 櫃: "柜", - 櫅: "𪲎", - 櫍: "𬃊", - 櫓: "橹", - 櫚: "榈", - 櫛: "栉", - 櫝: "椟", - 櫞: "橼", - 櫟: "栎", - 櫠: "𪲮", - 櫥: "橱", - 櫧: "槠", - 櫨: "栌", - 櫪: "枥", - 櫫: "橥", - 櫬: "榇", - 櫱: "蘖", - 櫳: "栊", - 櫸: "榉", - 櫻: "樱", - 欄: "栏", - 欅: "榉", - 欇: "𪳍", - 權: "权", - 欍: "𣐤", - 欏: "椤", - 欐: "𪲔", - 欑: "𪴙", - 欒: "栾", - 欓: "𣗋", - 欖: "榄", - 欘: "𣚚", - 欞: "棂", - 欽: "钦", - 歎: "叹", - 歐: "欧", - 歟: "欤", - 歡: "欢", - 歲: "岁", - 歷: "历", - 歸: "归", - 歿: "殁", - 殘: "残", - 殞: "殒", - 殢: "𣨼", - 殤: "殇", - 殨: "㱮", - 殫: "殚", - 殭: "僵", - 殮: "殓", - 殯: "殡", - 殰: "㱩", - 殲: "歼", - 殺: "杀", - 殻: "壳", - 殼: "壳", - 毀: "毁", - 毆: "殴", - 毊: "𪵑", - 毿: "毵", - 氂: "牦", - 氈: "毡", - 氌: "氇", - 氣: "气", - 氫: "氢", - 氬: "氩", - 氭: "𣱝", - 氳: "氲", - 氾: "泛", - 汎: "泛", - 汙: "污", - 決: "决", - 沒: "没", - 沖: "冲", - 況: "况", - 泝: "溯", - 洩: "泄", - 洶: "汹", - 浹: "浃", - 浿: "𬇙", - 涇: "泾", - 涗: "涚", - 涼: "凉", - 淒: "凄", - 淚: "泪", - 淥: "渌", - 淨: "净", - 淩: "凌", - 淪: "沦", - 淵: "渊", - 淶: "涞", - 淺: "浅", - 渙: "涣", - 減: "减", - 渢: "沨", - 渦: "涡", - 測: "测", - 渾: "浑", - 湊: "凑", - 湋: "𣲗", - 湞: "浈", - 湧: "涌", - 湯: "汤", - 溈: "沩", - 準: "准", - 溝: "沟", - 溡: "𪶄", - 溫: "温", - 溮: "浉", - 溳: "涢", - 溼: "湿", - 滄: "沧", - 滅: "灭", - 滌: "涤", - 滎: "荥", - 滙: "汇", - 滬: "沪", - 滯: "滞", - 滲: "渗", - 滷: "卤", - 滸: "浒", - 滻: "浐", - 滾: "滚", - 滿: "满", - 漁: "渔", - 漊: "溇", - 漍: "𬇹", - 漚: "沤", - 漢: "汉", - 漣: "涟", - 漬: "渍", - 漲: "涨", - 漵: "溆", - 漸: "渐", - 漿: "浆", - 潁: "颍", - 潑: "泼", - 潔: "洁", - 潕: "𣲘", - 潙: "沩", - 潚: "㴋", - 潛: "潜", - 潣: "𫞗", - 潤: "润", - 潯: "浔", - 潰: "溃", - 潷: "滗", - 潿: "涠", - 澀: "涩", - 澅: "𣶩", - 澆: "浇", - 澇: "涝", - 澐: "沄", - 澗: "涧", - 澠: "渑", - 澤: "泽", - 澦: "滪", - 澩: "泶", - 澫: "𬇕", - 澬: "𫞚", - 澮: "浍", - 澱: "淀", - 澾: "㳠", - 濁: "浊", - 濃: "浓", - 濄: "㳡", - 濆: "𣸣", - 濕: "湿", - 濘: "泞", - 濚: "溁", - 濛: "蒙", - 濜: "浕", - 濟: "济", - 濤: "涛", - 濧: "㳔", - 濫: "滥", - 濰: "潍", - 濱: "滨", - 濺: "溅", - 濼: "泺", - 濾: "滤", - 濿: "𪵱", - 瀂: "澛", - 瀃: "𣽷", - 瀅: "滢", - 瀆: "渎", - 瀇: "㲿", - 瀉: "泻", - 瀋: "沈", - 瀏: "浏", - 瀕: "濒", - 瀘: "泸", - 瀝: "沥", - 瀟: "潇", - 瀠: "潆", - 瀦: "潴", - 瀧: "泷", - 瀨: "濑", - 瀰: "弥", - 瀲: "潋", - 瀾: "澜", - 灃: "沣", - 灄: "滠", - 灍: "𫞝", - 灑: "洒", - 灒: "𪷽", - 灕: "漓", - 灘: "滩", - 灙: "𣺼", - 灝: "灏", - 灡: "㳕", - 灣: "湾", - 灤: "滦", - 灧: "滟", - 灩: "滟", - 災: "灾", - 為: "为", - 烏: "乌", - 烴: "烃", - 無: "无", - 煇: "𪸩", - 煉: "炼", - 煒: "炜", - 煙: "烟", - 煢: "茕", - 煥: "焕", - 煩: "烦", - 煬: "炀", - 煱: "㶽", - 熂: "𪸕", - 熅: "煴", - 熉: "𤈶", - 熌: "𤇄", - 熒: "荧", - 熓: "𤆡", - 熗: "炝", - 熚: "𤇹", - 熡: "𤋏", - 熰: "𬉼", - 熱: "热", - 熲: "颎", - 熾: "炽", - 燀: "𬊤", - 燁: "烨", - 燈: "灯", - 燉: "炖", - 燒: "烧", - 燖: "𬊈", - 燙: "烫", - 燜: "焖", - 營: "营", - 燦: "灿", - 燬: "毁", - 燭: "烛", - 燴: "烩", - 燶: "㶶", - 燻: "熏", - 燼: "烬", - 燾: "焘", - 爃: "𫞡", - 爄: "𤇃", - 爇: "𦶟", - 爍: "烁", - 爐: "炉", - 爖: "𤇭", - 爛: "烂", - 爥: "𪹳", - 爧: "𫞠", - 爭: "争", - 爲: "为", - 爺: "爷", - 爾: "尔", - 牀: "床", - 牆: "墙", - 牘: "牍", - 牴: "牴", - 牽: "牵", - 犖: "荦", - 犛: "牦", - 犞: "𪺭", - 犢: "犊", - 犧: "牺", - 狀: "状", - 狹: "狭", - 狽: "狈", - 猌: "𪺽", - 猙: "狰", - 猶: "犹", - 猻: "狲", - 獁: "犸", - 獃: "呆", - 獄: "狱", - 獅: "狮", - 獊: "𪺷", - 獎: "奖", - 獨: "独", - 獩: "𤞃", - 獪: "狯", - 獫: "猃", - 獮: "狝", - 獰: "狞", - 獱: "㺍", - 獲: "获", - 獵: "猎", - 獷: "犷", - 獸: "兽", - 獺: "獭", - 獻: "献", - 獼: "猕", - 玀: "猡", - 玁: "𤞤", - 珼: "𫞥", - 現: "现", - 琱: "雕", - 琺: "珐", - 琿: "珲", - 瑋: "玮", - 瑒: "玚", - 瑣: "琐", - 瑤: "瑶", - 瑩: "莹", - 瑪: "玛", - 瑲: "玱", - 瑻: "𪻲", - 瑽: "𪻐", - 璉: "琏", - 璊: "𫞩", - 璕: "𬍤", - 璗: "𬍡", - 璝: "𪻺", - 璡: "琎", - 璣: "玑", - 璦: "瑷", - 璫: "珰", - 璯: "㻅", - 環: "环", - 璵: "玙", - 璸: "瑸", - 璼: "𫞨", - 璽: "玺", - 璾: "𫞦", - 璿: "璇", - 瓄: "𪻨", - 瓅: "𬍛", - 瓊: "琼", - 瓏: "珑", - 瓔: "璎", - 瓕: "𤦀", - 瓚: "瓒", - 瓛: "𤩽", - 甌: "瓯", - 甕: "瓮", - 產: "产", - 産: "产", - 甦: "苏", - 甯: "宁", - 畝: "亩", - 畢: "毕", - 畫: "画", - 異: "异", - 畵: "画", - 當: "当", - 畼: "𪽈", - 疇: "畴", - 疊: "叠", - 痙: "痉", - 痠: "酸", - 痮: "𪽪", - 痾: "疴", - 瘂: "痖", - 瘋: "疯", - 瘍: "疡", - 瘓: "痪", - 瘞: "瘗", - 瘡: "疮", - 瘧: "疟", - 瘮: "瘆", - 瘱: "𪽷", - 瘲: "疭", - 瘺: "瘘", - 瘻: "瘘", - 療: "疗", - 癆: "痨", - 癇: "痫", - 癉: "瘅", - 癐: "𤶊", - 癒: "愈", - 癘: "疠", - 癟: "瘪", - 癡: "痴", - 癢: "痒", - 癤: "疖", - 癥: "症", - 癧: "疬", - 癩: "癞", - 癬: "癣", - 癭: "瘿", - 癮: "瘾", - 癰: "痈", - 癱: "瘫", - 癲: "癫", - 發: "发", - 皁: "皂", - 皚: "皑", - 皟: "𤾀", - 皰: "疱", - 皸: "皲", - 皺: "皱", - 盃: "杯", - 盜: "盗", - 盞: "盏", - 盡: "尽", - 監: "监", - 盤: "盘", - 盧: "卢", - 盨: "𪾔", - 盪: "荡", - 眝: "𪾣", - 眞: "真", - 眥: "眦", - 眾: "众", - 睍: "𪾢", - 睏: "困", - 睜: "睁", - 睞: "睐", - 瞘: "眍", - 瞜: "䁖", - 瞞: "瞒", - 瞤: "𥆧", - 瞭: "瞭", - 瞶: "瞆", - 瞼: "睑", - 矇: "蒙", - 矉: "𪾸", - 矑: "𪾦", - 矓: "眬", - 矚: "瞩", - 矯: "矫", - 硃: "朱", - 硜: "硁", - 硤: "硖", - 硨: "砗", - 硯: "砚", - 碕: "埼", - 碙: "𥐻", - 碩: "硕", - 碭: "砀", - 碸: "砜", - 確: "确", - 碼: "码", - 碽: "䂵", - 磑: "硙", - 磚: "砖", - 磠: "硵", - 磣: "碜", - 磧: "碛", - 磯: "矶", - 磽: "硗", - 磾: "䃅", - 礄: "硚", - 礆: "硷", - 礎: "础", - 礐: "𬒈", - 礒: "𥐟", - 礙: "碍", - 礦: "矿", - 礪: "砺", - 礫: "砾", - 礬: "矾", - 礮: "𪿫", - 礱: "砻", - 祇: "祇", - 祕: "秘", - 祿: "禄", - 禍: "祸", - 禎: "祯", - 禕: "祎", - 禡: "祃", - 禦: "御", - 禪: "禅", - 禮: "礼", - 禰: "祢", - 禱: "祷", - 禿: "秃", - 秈: "籼", - 稅: "税", - 稈: "秆", - 稏: "䅉", - 稜: "棱", - 稟: "禀", - 種: "种", - 稱: "称", - 穀: "谷", - 穇: "䅟", - 穌: "稣", - 積: "积", - 穎: "颖", - 穠: "秾", - 穡: "穑", - 穢: "秽", - 穩: "稳", - 穫: "获", - 穭: "穞", - 窩: "窝", - 窪: "洼", - 窮: "穷", - 窯: "窑", - 窵: "窎", - 窶: "窭", - 窺: "窥", - 竄: "窜", - 竅: "窍", - 竇: "窦", - 竈: "灶", - 竊: "窃", - 竚: "𥩟", - 竪: "竖", - 竱: "𫁟", - 競: "竞", - 筆: "笔", - 筍: "笋", - 筧: "笕", - 筴: "䇲", - 箇: "个", - 箋: "笺", - 箏: "筝", - 節: "节", - 範: "范", - 築: "筑", - 篋: "箧", - 篔: "筼", - 篘: "𥬠", - 篠: "筿", - 篢: "𬕂", - 篤: "笃", - 篩: "筛", - 篳: "筚", - 篸: "𥮾", - 簀: "箦", - 簂: "𫂆", - 簍: "篓", - 簑: "蓑", - 簞: "箪", - 簡: "简", - 簢: "𫂃", - 簣: "篑", - 簫: "箫", - 簹: "筜", - 簽: "签", - 簾: "帘", - 籃: "篮", - 籅: "𥫣", - 籋: "𥬞", - 籌: "筹", - 籔: "䉤", - 籙: "箓", - 籛: "篯", - 籜: "箨", - 籟: "籁", - 籠: "笼", - 籤: "签", - 籩: "笾", - 籪: "簖", - 籬: "篱", - 籮: "箩", - 籲: "吁", - 粵: "粤", - 糉: "粽", - 糝: "糁", - 糞: "粪", - 糧: "粮", - 糰: "团", - 糲: "粝", - 糴: "籴", - 糶: "粜", - 糹: "纟", - 糺: "𫄙", - 糾: "纠", - 紀: "纪", - 紂: "纣", - 紃: "𬘓", - 約: "约", - 紅: "红", - 紆: "纡", - 紇: "纥", - 紈: "纨", - 紉: "纫", - 紋: "纹", - 納: "纳", - 紐: "纽", - 紓: "纾", - 純: "纯", - 紕: "纰", - 紖: "纼", - 紗: "纱", - 紘: "纮", - 紙: "纸", - 級: "级", - 紛: "纷", - 紜: "纭", - 紝: "纴", - 紞: "𬘘", - 紟: "𫄛", - 紡: "纺", - 紬: "䌷", - 紮: "扎", - 細: "细", - 紱: "绂", - 紲: "绁", - 紳: "绅", - 紵: "纻", - 紹: "绍", - 紺: "绀", - 紼: "绋", - 紿: "绐", - 絀: "绌", - 絁: "𫄟", - 終: "终", - 絃: "弦", - 組: "组", - 絅: "䌹", - 絆: "绊", - 絍: "𫟃", - 絎: "绗", - 結: "结", - 絕: "绝", - 絙: "𫄠", - 絛: "绦", - 絝: "绔", - 絞: "绞", - 絡: "络", - 絢: "绚", - 絥: "𫄢", - 給: "给", - 絧: "𫄡", - 絨: "绒", - 絪: "𬘡", - 絰: "绖", - 統: "统", - 絲: "丝", - 絳: "绛", - 絶: "绝", - 絹: "绢", - 絺: "𫄨", - 綀: "𦈌", - 綁: "绑", - 綃: "绡", - 綄: "𬘫", - 綆: "绠", - 綇: "𦈋", - 綈: "绨", - 綉: "绣", - 綋: "𫟄", - 綌: "绤", - 綎: "𬘩", - 綏: "绥", - 綐: "䌼", - 綑: "捆", - 經: "经", - 綖: "𫄧", - 綜: "综", - 綝: "𬘭", - 綞: "缍", - 綟: "𫄫", - 綠: "绿", - 綡: "𫟅", - 綢: "绸", - 綣: "绻", - 綧: "𬘯", - 綪: "𬘬", - 綫: "线", - 綬: "绶", - 維: "维", - 綯: "绹", - 綰: "绾", - 綱: "纲", - 網: "网", - 綳: "绷", - 綴: "缀", - 綵: "彩", - 綸: "纶", - 綹: "绺", - 綺: "绮", - 綻: "绽", - 綽: "绰", - 綾: "绫", - 綿: "绵", - 緄: "绲", - 緇: "缁", - 緊: "紧", - 緋: "绯", - 緍: "𦈏", - 緑: "绿", - 緒: "绪", - 緓: "绬", - 緔: "绱", - 緗: "缃", - 緘: "缄", - 緙: "缂", - 線: "线", - 緝: "缉", - 緞: "缎", - 緟: "𫟆", - 締: "缔", - 緡: "缗", - 緣: "缘", - 緤: "𫄬", - 緦: "缌", - 編: "编", - 緩: "缓", - 緬: "缅", - 緮: "𫄭", - 緯: "纬", - 緰: "𦈕", - 緱: "缑", - 緲: "缈", - 練: "练", - 緶: "缏", - 緷: "𦈉", - 緸: "𦈑", - 緹: "缇", - 緻: "致", - 緼: "缊", - 縈: "萦", - 縉: "缙", - 縊: "缢", - 縋: "缒", - 縍: "𫄰", - 縎: "𦈔", - 縐: "绉", - 縑: "缣", - 縕: "缊", - 縗: "缞", - 縛: "缚", - 縝: "缜", - 縞: "缟", - 縟: "缛", - 縣: "县", - 縧: "绦", - 縫: "缝", - 縬: "𦈚", - 縭: "缡", - 縮: "缩", - 縯: "𬙂", - 縰: "𫄳", - 縱: "纵", - 縲: "缧", - 縳: "䌸", - 縴: "纤", - 縵: "缦", - 縶: "絷", - 縷: "缕", - 縸: "𫄲", - 縹: "缥", - 縺: "𦈐", - 總: "总", - 績: "绩", - 繂: "𫄴", - 繃: "绷", - 繅: "缫", - 繆: "缪", - 繈: "𫄶", - 繏: "𦈝", - 繐: "𰬸", - 繒: "缯", - 繓: "𦈛", - 織: "织", - 繕: "缮", - 繚: "缭", - 繞: "绕", - 繟: "𦈎", - 繡: "绣", - 繢: "缋", - 繨: "𫄤", - 繩: "绳", - 繪: "绘", - 繫: "系", - 繬: "𫄱", - 繭: "茧", - 繮: "缰", - 繯: "缳", - 繰: "缲", - 繳: "缴", - 繶: "𫄷", - 繷: "𫄣", - 繸: "䍁", - 繹: "绎", - 繻: "𦈡", - 繼: "继", - 繽: "缤", - 繾: "缱", - 繿: "䍀", - 纁: "𫄸", - 纆: "𬙊", - 纇: "颣", - 纈: "缬", - 纊: "纩", - 續: "续", - 纍: "累", - 纏: "缠", - 纓: "缨", - 纔: "才", - 纕: "𬙋", - 纖: "纤", - 纗: "𫄹", - 纘: "缵", - 纚: "𫄥", - 纜: "缆", - 缽: "钵", - 罃: "䓨", - 罈: "坛", - 罌: "罂", - 罎: "坛", - 罰: "罚", - 罵: "骂", - 罷: "罢", - 羅: "罗", - 羆: "罴", - 羈: "羁", - 羋: "芈", - 羣: "群", - 羥: "羟", - 羨: "羡", - 義: "义", - 羵: "𫅗", - 羶: "膻", - 習: "习", - 翫: "玩", - 翬: "翚", - 翹: "翘", - 翽: "翙", - 耬: "耧", - 耮: "耢", - 聖: "圣", - 聞: "闻", - 聯: "联", - 聰: "聪", - 聲: "声", - 聳: "耸", - 聵: "聩", - 聶: "聂", - 職: "职", - 聹: "聍", - 聻: "𫆏", - 聽: "听", - 聾: "聋", - 肅: "肃", - 脅: "胁", - 脈: "脉", - 脛: "胫", - 脣: "唇", - 脥: "𣍰", - 脩: "修", - 脫: "脱", - 脹: "胀", - 腎: "肾", - 腖: "胨", - 腡: "脶", - 腦: "脑", - 腪: "𣍯", - 腫: "肿", - 腳: "脚", - 腸: "肠", - 膃: "腽", - 膕: "腘", - 膚: "肤", - 膞: "䏝", - 膠: "胶", - 膢: "𦝼", - 膩: "腻", - 膹: "𪱥", - 膽: "胆", - 膾: "脍", - 膿: "脓", - 臉: "脸", - 臍: "脐", - 臏: "膑", - 臗: "𣎑", - 臘: "腊", - 臚: "胪", - 臟: "脏", - 臠: "脔", - 臢: "臜", - 臥: "卧", - 臨: "临", - 臺: "台", - 與: "与", - 興: "兴", - 舉: "举", - 舊: "旧", - 舘: "馆", - 艙: "舱", - 艣: "𫇛", - 艤: "舣", - 艦: "舰", - 艫: "舻", - 艱: "艰", - 艷: "艳", - 芻: "刍", - 苧: "苎", - 茲: "兹", - 荊: "荆", - 莊: "庄", - 莖: "茎", - 莢: "荚", - 莧: "苋", - 菕: "𰰨", - 華: "华", - 菴: "庵", - 菸: "烟", - 萇: "苌", - 萊: "莱", - 萬: "万", - 萴: "荝", - 萵: "莴", - 葉: "叶", - 葒: "荭", - 葝: "𫈎", - 葤: "荮", - 葦: "苇", - 葯: "药", - 葷: "荤", - 蒍: "𫇭", - 蒐: "搜", - 蒓: "莼", - 蒔: "莳", - 蒕: "蒀", - 蒞: "莅", - 蒭: "𫇴", - 蒼: "苍", - 蓀: "荪", - 蓆: "席", - 蓋: "盖", - 蓧: "𦰏", - 蓮: "莲", - 蓯: "苁", - 蓴: "莼", - 蓽: "荜", - 蔄: "𬜬", - 蔔: "卜", - 蔘: "参", - 蔞: "蒌", - 蔣: "蒋", - 蔥: "葱", - 蔦: "茑", - 蔭: "荫", - 蔯: "𫈟", - 蔿: "𫇭", - 蕁: "荨", - 蕆: "蒇", - 蕎: "荞", - 蕒: "荬", - 蕓: "芸", - 蕕: "莸", - 蕘: "荛", - 蕝: "𫈵", - 蕢: "蒉", - 蕩: "荡", - 蕪: "芜", - 蕭: "萧", - 蕳: "𫈉", - 蕷: "蓣", - 蕽: "𫇽", - 薀: "蕰", - 薆: "𫉁", - 薈: "荟", - 薊: "蓟", - 薌: "芗", - 薑: "姜", - 薔: "蔷", - 薘: "荙", - 薟: "莶", - 薦: "荐", - 薩: "萨", - 薳: "䓕", - 薴: "苧", - 薵: "䓓", - 薹: "苔", - 薺: "荠", - 藉: "藉", - 藍: "蓝", - 藎: "荩", - 藝: "艺", - 藥: "药", - 藪: "薮", - 藭: "䓖", - 藴: "蕴", - 藶: "苈", - 藷: "𫉄", - 藹: "蔼", - 藺: "蔺", - 蘀: "萚", - 蘄: "蕲", - 蘆: "芦", - 蘇: "苏", - 蘊: "蕴", - 蘋: "苹", - 蘚: "藓", - 蘞: "蔹", - 蘟: "𦻕", - 蘢: "茏", - 蘭: "兰", - 蘺: "蓠", - 蘿: "萝", - 虆: "蔂", - 虉: "𬟁", - 處: "处", - 虛: "虚", - 虜: "虏", - 號: "号", - 虧: "亏", - 虯: "虬", - 蛺: "蛱", - 蛻: "蜕", - 蜆: "蚬", - 蝀: "𬟽", - 蝕: "蚀", - 蝟: "猬", - 蝦: "虾", - 蝨: "虱", - 蝸: "蜗", - 螄: "蛳", - 螞: "蚂", - 螢: "萤", - 螮: "䗖", - 螻: "蝼", - 螿: "螀", - 蟂: "𫋇", - 蟄: "蛰", - 蟈: "蝈", - 蟎: "螨", - 蟘: "𫋌", - 蟜: "𫊸", - 蟣: "虮", - 蟬: "蝉", - 蟯: "蛲", - 蟲: "虫", - 蟳: "𫊻", - 蟶: "蛏", - 蟻: "蚁", - 蠀: "𧏗", - 蠁: "蚃", - 蠅: "蝇", - 蠆: "虿", - 蠍: "蝎", - 蠐: "蛴", - 蠑: "蝾", - 蠔: "蚝", - 蠙: "𧏖", - 蠟: "蜡", - 蠣: "蛎", - 蠦: "𫊮", - 蠨: "蟏", - 蠱: "蛊", - 蠶: "蚕", - 蠻: "蛮", - 蠾: "𧑏", - 衆: "众", - 衊: "蔑", - 術: "术", - 衕: "同", - 衚: "胡", - 衛: "卫", - 衝: "冲", - 衹: "衹", - 袞: "衮", - 裊: "袅", - 裏: "里", - 補: "补", - 裝: "装", - 裡: "里", - 製: "制", - 複: "复", - 褌: "裈", - 褘: "袆", - 褲: "裤", - 褳: "裢", - 褸: "褛", - 褻: "亵", - 襀: "𫌀", - 襇: "裥", - 襉: "裥", - 襏: "袯", - 襓: "𫋹", - 襖: "袄", - 襗: "𫋷", - 襘: "𫋻", - 襝: "裣", - 襠: "裆", - 襤: "褴", - 襪: "袜", - 襬: "摆", - 襯: "衬", - 襰: "𧝝", - 襲: "袭", - 襴: "襕", - 襵: "𫌇", - 覆: "覆", - 覈: "核", - 見: "见", - 覎: "觃", - 規: "规", - 覓: "觅", - 視: "视", - 覘: "觇", - 覛: "𫌪", - 覡: "觋", - 覥: "觍", - 覦: "觎", - 親: "亲", - 覬: "觊", - 覯: "觏", - 覲: "觐", - 覷: "觑", - 覹: "𫌭", - 覺: "觉", - 覼: "𫌨", - 覽: "览", - 覿: "觌", - 觀: "观", - 觴: "觞", - 觶: "觯", - 觸: "触", - 訁: "讠", - 訂: "订", - 訃: "讣", - 計: "计", - 訊: "讯", - 訌: "讧", - 討: "讨", - 訏: "𬣙", - 訐: "讦", - 訑: "𫍙", - 訒: "讱", - 訓: "训", - 訕: "讪", - 訖: "讫", - 託: "托", - 記: "记", - 訛: "讹", - 訜: "𫍛", - 訝: "讶", - 訞: "𫍚", - 訟: "讼", - 訢: "䜣", - 訣: "诀", - 訥: "讷", - 訨: "𫟞", - 訩: "讻", - 訪: "访", - 設: "设", - 許: "许", - 訴: "诉", - 訶: "诃", - 診: "诊", - 註: "注", - 証: "证", - 詀: "𧮪", - 詁: "诂", - 詆: "诋", - 詊: "𫟟", - 詎: "讵", - 詐: "诈", - 詑: "𫍡", - 詒: "诒", - 詓: "𫍜", - 詔: "诏", - 評: "评", - 詖: "诐", - 詗: "诇", - 詘: "诎", - 詛: "诅", - 詝: "𬣞", - 詞: "词", - 詠: "咏", - 詡: "诩", - 詢: "询", - 詣: "诣", - 試: "试", - 詩: "诗", - 詪: "𬣳", - 詫: "诧", - 詬: "诟", - 詭: "诡", - 詮: "诠", - 詰: "诘", - 話: "话", - 該: "该", - 詳: "详", - 詵: "诜", - 詷: "𫍣", - 詼: "诙", - 詿: "诖", - 誂: "𫍥", - 誄: "诔", - 誅: "诛", - 誆: "诓", - 誇: "夸", - 誋: "𫍪", - 誌: "志", - 認: "认", - 誑: "诳", - 誒: "诶", - 誕: "诞", - 誘: "诱", - 誚: "诮", - 語: "语", - 誠: "诚", - 誡: "诫", - 誣: "诬", - 誤: "误", - 誥: "诰", - 誦: "诵", - 誨: "诲", - 說: "说", - 誫: "𫍨", - 説: "说", - 誰: "谁", - 課: "课", - 誳: "𫍮", - 誴: "𫟡", - 誶: "谇", - 誷: "𫍬", - 誹: "诽", - 誺: "𫍧", - 誼: "谊", - 誾: "訚", - 調: "调", - 諂: "谄", - 諄: "谆", - 談: "谈", - 諉: "诿", - 請: "请", - 諍: "诤", - 諏: "诹", - 諑: "诼", - 諒: "谅", - 諓: "𬣡", - 論: "论", - 諗: "谂", - 諛: "谀", - 諜: "谍", - 諝: "谞", - 諞: "谝", - 諟: "𬤊", - 諡: "谥", - 諢: "诨", - 諣: "𫍩", - 諤: "谔", - 諥: "𫍳", - 諦: "谛", - 諧: "谐", - 諫: "谏", - 諭: "谕", - 諮: "咨", - 諯: "𫍱", - 諰: "𫍰", - 諱: "讳", - 諲: "𬤇", - 諳: "谙", - 諴: "𫍯", - 諶: "谌", - 諷: "讽", - 諸: "诸", - 諺: "谚", - 諼: "谖", - 諾: "诺", - 謀: "谋", - 謁: "谒", - 謂: "谓", - 謄: "誊", - 謅: "诌", - 謆: "𫍸", - 謉: "𫍷", - 謊: "谎", - 謎: "谜", - 謏: "𫍲", - 謐: "谧", - 謔: "谑", - 謖: "谡", - 謗: "谤", - 謙: "谦", - 謚: "谥", - 講: "讲", - 謝: "谢", - 謠: "谣", - 謡: "谣", - 謨: "谟", - 謫: "谪", - 謬: "谬", - 謭: "谫", - 謯: "𫍹", - 謱: "𫍴", - 謳: "讴", - 謸: "𫍵", - 謹: "谨", - 謾: "谩", - 譁: "哗", - 譂: "𫟠", - 譅: "𰶎", - 譆: "𫍻", - 證: "证", - 譊: "𫍢", - 譎: "谲", - 譏: "讥", - 譑: "𫍤", - 譓: "𬤝", - 譖: "谮", - 識: "识", - 譙: "谯", - 譚: "谭", - 譜: "谱", - 譞: "𫍽", - 譟: "噪", - 譨: "𫍦", - 譫: "谵", - 譭: "毁", - 譯: "译", - 議: "议", - 譴: "谴", - 護: "护", - 譸: "诪", - 譽: "誉", - 譾: "谫", - 讀: "读", - 讅: "谉", - 變: "变", - 讋: "詟", - 讌: "䜩", - 讎: "雠", - 讒: "谗", - 讓: "让", - 讕: "谰", - 讖: "谶", - 讚: "赞", - 讜: "谠", - 讞: "谳", - 豈: "岂", - 豎: "竖", - 豐: "丰", - 豔: "艳", - 豬: "猪", - 豵: "𫎆", - 豶: "豮", - 貓: "猫", - 貗: "𫎌", - 貙: "䝙", - 貝: "贝", - 貞: "贞", - 貟: "贠", - 負: "负", - 財: "财", - 貢: "贡", - 貧: "贫", - 貨: "货", - 販: "贩", - 貪: "贪", - 貫: "贯", - 責: "责", - 貯: "贮", - 貰: "贳", - 貲: "赀", - 貳: "贰", - 貴: "贵", - 貶: "贬", - 買: "买", - 貸: "贷", - 貺: "贶", - 費: "费", - 貼: "贴", - 貽: "贻", - 貿: "贸", - 賀: "贺", - 賁: "贲", - 賂: "赂", - 賃: "赁", - 賄: "贿", - 賅: "赅", - 資: "资", - 賈: "贾", - 賊: "贼", - 賑: "赈", - 賒: "赊", - 賓: "宾", - 賕: "赇", - 賙: "赒", - 賚: "赉", - 賜: "赐", - 賝: "𫎩", - 賞: "赏", - 賟: "𧹖", - 賠: "赔", - 賡: "赓", - 賢: "贤", - 賣: "卖", - 賤: "贱", - 賦: "赋", - 賧: "赕", - 質: "质", - 賫: "赍", - 賬: "账", - 賭: "赌", - 賰: "䞐", - 賴: "赖", - 賵: "赗", - 賺: "赚", - 賻: "赙", - 購: "购", - 賽: "赛", - 賾: "赜", - 贃: "𧹗", - 贄: "贽", - 贅: "赘", - 贇: "赟", - 贈: "赠", - 贉: "𫎫", - 贊: "赞", - 贋: "赝", - 贍: "赡", - 贏: "赢", - 贐: "赆", - 贑: "𫎬", - 贓: "赃", - 贔: "赑", - 贖: "赎", - 贗: "赝", - 贚: "𫎦", - 贛: "赣", - 贜: "赃", - 赬: "赪", - 趕: "赶", - 趙: "赵", - 趨: "趋", - 趲: "趱", - 跡: "迹", - 踐: "践", - 踰: "逾", - 踴: "踊", - 蹌: "跄", - 蹔: "𫏐", - 蹕: "跸", - 蹟: "迹", - 蹠: "跖", - 蹣: "蹒", - 蹤: "踪", - 蹳: "𫏆", - 蹺: "跷", - 蹻: "𫏋", - 躂: "跶", - 躉: "趸", - 躊: "踌", - 躋: "跻", - 躍: "跃", - 躎: "䟢", - 躑: "踯", - 躒: "跞", - 躓: "踬", - 躕: "蹰", - 躘: "𨀁", - 躚: "跹", - 躝: "𨅬", - 躡: "蹑", - 躥: "蹿", - 躦: "躜", - 躪: "躏", - 軀: "躯", - 軉: "𨉗", - 車: "车", - 軋: "轧", - 軌: "轨", - 軍: "军", - 軏: "𫐄", - 軑: "轪", - 軒: "轩", - 軔: "轫", - 軕: "𫐅", - 軗: "𨐅", - 軛: "轭", - 軜: "𫐇", - 軝: "𬨂", - 軟: "软", - 軤: "轷", - 軨: "𫐉", - 軫: "轸", - 軬: "𫐊", - 軲: "轱", - 軷: "𫐈", - 軸: "轴", - 軹: "轵", - 軺: "轺", - 軻: "轲", - 軼: "轶", - 軾: "轼", - 軿: "𫐌", - 較: "较", - 輄: "𨐈", - 輅: "辂", - 輇: "辁", - 輈: "辀", - 載: "载", - 輊: "轾", - 輋: "𪨶", - 輒: "辄", - 輓: "挽", - 輔: "辅", - 輕: "轻", - 輖: "𫐏", - 輗: "𫐐", - 輛: "辆", - 輜: "辎", - 輝: "辉", - 輞: "辋", - 輟: "辍", - 輢: "𫐎", - 輥: "辊", - 輦: "辇", - 輨: "𫐑", - 輩: "辈", - 輪: "轮", - 輬: "辌", - 輮: "𫐓", - 輯: "辑", - 輳: "辏", - 輶: "𬨎", - 輷: "𫐒", - 輸: "输", - 輻: "辐", - 輼: "辒", - 輾: "辗", - 輿: "舆", - 轀: "辒", - 轂: "毂", - 轄: "辖", - 轅: "辕", - 轆: "辘", - 轇: "𫐖", - 轉: "转", - 轊: "𫐕", - 轍: "辙", - 轎: "轿", - 轐: "𫐗", - 轔: "辚", - 轗: "𫐘", - 轟: "轰", - 轠: "𫐙", - 轡: "辔", - 轢: "轹", - 轣: "𫐆", - 轤: "轳", - 辦: "办", - 辭: "辞", - 辮: "辫", - 辯: "辩", - 農: "农", - 迴: "回", - 逕: "迳", - 這: "这", - 連: "连", - 週: "周", - 進: "进", - 遊: "游", - 運: "运", - 過: "过", - 達: "达", - 違: "违", - 遙: "遥", - 遜: "逊", - 遞: "递", - 遠: "远", - 遡: "溯", - 適: "适", - 遱: "𫐷", - 遲: "迟", - 遷: "迁", - 選: "选", - 遺: "遗", - 遼: "辽", - 邁: "迈", - 還: "还", - 邇: "迩", - 邊: "边", - 邏: "逻", - 邐: "逦", - 郟: "郏", - 郵: "邮", - 鄆: "郓", - 鄉: "乡", - 鄒: "邹", - 鄔: "邬", - 鄖: "郧", - 鄟: "𫑘", - 鄧: "邓", - 鄩: "𬩽", - 鄭: "郑", - 鄰: "邻", - 鄲: "郸", - 鄳: "𫑡", - 鄴: "邺", - 鄶: "郐", - 鄺: "邝", - 酇: "酂", - 酈: "郦", - 醃: "腌", - 醖: "酝", - 醜: "丑", - 醞: "酝", - 醟: "蒏", - 醣: "糖", - 醫: "医", - 醬: "酱", - 醱: "酦", - 醲: "𬪩", - 醶: "𫑷", - 釀: "酿", - 釁: "衅", - 釃: "酾", - 釅: "酽", - 釋: "释", - 釐: "厘", - 釒: "钅", - 釓: "钆", - 釔: "钇", - 釕: "钌", - 釗: "钊", - 釘: "钉", - 釙: "钋", - 釚: "𫟲", - 針: "针", - 釟: "𫓥", - 釣: "钓", - 釤: "钐", - 釦: "扣", - 釧: "钏", - 釨: "𫓦", - 釩: "钒", - 釲: "𫟳", - 釳: "𨰿", - 釴: "𬬩", - 釵: "钗", - 釷: "钍", - 釹: "钕", - 釺: "钎", - 釾: "䥺", - 釿: "𬬱", - 鈀: "钯", - 鈁: "钫", - 鈃: "钘", - 鈄: "钭", - 鈅: "钥", - 鈆: "𫓪", - 鈇: "𫓧", - 鈈: "钚", - 鈉: "钠", - 鈋: "𨱂", - 鈍: "钝", - 鈎: "钩", - 鈐: "钤", - 鈑: "钣", - 鈒: "钑", - 鈔: "钞", - 鈕: "钮", - 鈖: "𫟴", - 鈗: "𫟵", - 鈛: "𫓨", - 鈞: "钧", - 鈠: "𨱁", - 鈡: "钟", - 鈣: "钙", - 鈥: "钬", - 鈦: "钛", - 鈧: "钪", - 鈮: "铌", - 鈯: "𨱄", - 鈰: "铈", - 鈲: "𨱃", - 鈳: "钶", - 鈴: "铃", - 鈷: "钴", - 鈸: "钹", - 鈹: "铍", - 鈺: "钰", - 鈽: "钸", - 鈾: "铀", - 鈿: "钿", - 鉀: "钾", - 鉁: "𨱅", - 鉅: "巨", - 鉆: "钻", - 鉈: "铊", - 鉉: "铉", - 鉊: "𬬿", - 鉋: "铇", - 鉍: "铋", - 鉑: "铂", - 鉔: "𫓬", - 鉕: "钷", - 鉗: "钳", - 鉚: "铆", - 鉛: "铅", - 鉝: "𫟷", - 鉞: "钺", - 鉠: "𫓭", - 鉢: "钵", - 鉤: "钩", - 鉥: "𬬸", - 鉦: "钲", - 鉧: "𬭁", - 鉬: "钼", - 鉭: "钽", - 鉮: "𬬹", - 鉳: "锫", - 鉶: "铏", - 鉷: "𫟹", - 鉸: "铰", - 鉺: "铒", - 鉻: "铬", - 鉽: "𫟸", - 鉾: "𫓴", - 鉿: "铪", - 銀: "银", - 銁: "𫓲", - 銂: "𫟻", - 銃: "铳", - 銅: "铜", - 銈: "𫓯", - 銊: "𫓰", - 銍: "铚", - 銏: "𫟶", - 銑: "铣", - 銓: "铨", - 銖: "铢", - 銘: "铭", - 銚: "铫", - 銛: "铦", - 銜: "衔", - 銠: "铑", - 銣: "铷", - 銥: "铱", - 銦: "铟", - 銨: "铵", - 銩: "铥", - 銪: "铕", - 銫: "铯", - 銬: "铐", - 銱: "铞", - 銳: "锐", - 銶: "𨱇", - 銷: "销", - 銹: "锈", - 銻: "锑", - 銼: "锉", - 鋁: "铝", - 鋂: "𰾄", - 鋃: "锒", - 鋅: "锌", - 鋇: "钡", - 鋉: "𨱈", - 鋌: "铤", - 鋏: "铗", - 鋐: "𬭎", - 鋒: "锋", - 鋗: "𫓶", - 鋙: "铻", - 鋝: "锊", - 鋟: "锓", - 鋠: "𫓵", - 鋣: "铘", - 鋤: "锄", - 鋥: "锃", - 鋦: "锔", - 鋨: "锇", - 鋩: "铓", - 鋪: "铺", - 鋭: "锐", - 鋮: "铖", - 鋯: "锆", - 鋰: "锂", - 鋱: "铽", - 鋶: "锍", - 鋸: "锯", - 鋹: "𬬮", - 鋼: "钢", - 錀: "𬬭", - 錁: "锞", - 錂: "𨱋", - 錄: "录", - 錆: "锖", - 錇: "锫", - 錈: "锩", - 錏: "铔", - 錐: "锥", - 錒: "锕", - 錕: "锟", - 錘: "锤", - 錙: "锱", - 錚: "铮", - 錛: "锛", - 錜: "𫓻", - 錝: "𫓽", - 錞: "𬭚", - 錟: "锬", - 錠: "锭", - 錡: "锜", - 錢: "钱", - 錤: "𫓹", - 錥: "𫓾", - 錦: "锦", - 錨: "锚", - 錩: "锠", - 錫: "锡", - 錮: "锢", - 錯: "错", - 録: "录", - 錳: "锰", - 錶: "表", - 錸: "铼", - 錼: "镎", - 錽: "𫓸", - 鍀: "锝", - 鍁: "锨", - 鍃: "锪", - 鍄: "𨱉", - 鍅: "钫", - 鍆: "钔", - 鍇: "锴", - 鍈: "锳", - 鍉: "𫔂", - 鍊: "炼", - 鍋: "锅", - 鍍: "镀", - 鍒: "𫔄", - 鍔: "锷", - 鍘: "铡", - 鍚: "钖", - 鍛: "锻", - 鍠: "锽", - 鍤: "锸", - 鍥: "锲", - 鍩: "锘", - 鍬: "锹", - 鍭: "𬭤", - 鍮: "𨱎", - 鍰: "锾", - 鍵: "键", - 鍶: "锶", - 鍺: "锗", - 鍼: "针", - 鍾: "钟", - 鎂: "镁", - 鎄: "锿", - 鎇: "镅", - 鎈: "𫟿", - 鎊: "镑", - 鎌: "镰", - 鎍: "𫔅", - 鎓: "𬭩", - 鎔: "镕", - 鎖: "锁", - 鎘: "镉", - 鎙: "𫔈", - 鎚: "锤", - 鎛: "镈", - 鎝: "𨱏", - 鎞: "𫔇", - 鎡: "镃", - 鎢: "钨", - 鎣: "蓥", - 鎦: "镏", - 鎧: "铠", - 鎩: "铩", - 鎪: "锼", - 鎬: "镐", - 鎭: "镇", - 鎮: "镇", - 鎯: "𨱍", - 鎰: "镒", - 鎲: "镋", - 鎳: "镍", - 鎵: "镓", - 鎶: "鿔", - 鎷: "𨰾", - 鎸: "镌", - 鎿: "镎", - 鏃: "镞", - 鏆: "𨱌", - 鏇: "旋", - 鏈: "链", - 鏉: "𨱒", - 鏌: "镆", - 鏍: "镙", - 鏏: "𬭬", - 鏐: "镠", - 鏑: "镝", - 鏗: "铿", - 鏘: "锵", - 鏚: "𬭭", - 鏜: "镗", - 鏝: "镘", - 鏞: "镛", - 鏟: "铲", - 鏡: "镜", - 鏢: "镖", - 鏤: "镂", - 鏥: "𫔊", - 鏦: "𫓩", - 鏨: "錾", - 鏰: "镚", - 鏵: "铧", - 鏷: "镤", - 鏹: "镪", - 鏺: "䥽", - 鏻: "𬭸", - 鏽: "锈", - 鏾: "𫔌", - 鐃: "铙", - 鐄: "𨱑", - 鐇: "𫔍", - 鐈: "𫓱", - 鐋: "铴", - 鐍: "𫔎", - 鐎: "𨱓", - 鐏: "𨱔", - 鐐: "镣", - 鐒: "铹", - 鐓: "镦", - 鐔: "镡", - 鐘: "钟", - 鐙: "镫", - 鐝: "镢", - 鐠: "镨", - 鐥: "䦅", - 鐦: "锎", - 鐧: "锏", - 鐨: "镄", - 鐩: "𬭼", - 鐪: "𫓺", - 鐫: "镌", - 鐮: "镰", - 鐯: "䦃", - 鐲: "镯", - 鐳: "镭", - 鐵: "铁", - 鐶: "镮", - 鐸: "铎", - 鐺: "铛", - 鐼: "𫔁", - 鐽: "𫟼", - 鐿: "镱", - 鑀: "𰾭", - 鑄: "铸", - 鑉: "𫠁", - 鑊: "镬", - 鑌: "镔", - 鑑: "鉴", - 鑒: "鉴", - 鑔: "镲", - 鑕: "锧", - 鑞: "镴", - 鑠: "铄", - 鑣: "镳", - 鑥: "镥", - 鑪: "𬬻", - 鑭: "镧", - 鑰: "钥", - 鑱: "镵", - 鑲: "镶", - 鑴: "𫔔", - 鑷: "镊", - 鑹: "镩", - 鑼: "锣", - 鑽: "钻", - 鑾: "銮", - 鑿: "凿", - 钁: "镢", - 钂: "镋", - 長: "长", - 門: "门", - 閂: "闩", - 閃: "闪", - 閆: "闫", - 閈: "闬", - 閉: "闭", - 開: "开", - 閌: "闶", - 閍: "𨸂", - 閎: "闳", - 閏: "闰", - 閐: "𨸃", - 閑: "闲", - 閒: "闲", - 間: "间", - 閔: "闵", - 閗: "𫔯", - 閘: "闸", - 閝: "𫠂", - 閞: "𫔰", - 閡: "阂", - 閣: "阁", - 閤: "合", - 閥: "阀", - 閨: "闺", - 閩: "闽", - 閫: "阃", - 閬: "阆", - 閭: "闾", - 閱: "阅", - 閲: "阅", - 閵: "𫔴", - 閶: "阊", - 閹: "阉", - 閻: "阎", - 閼: "阏", - 閽: "阍", - 閾: "阈", - 閿: "阌", - 闃: "阒", - 闆: "板", - 闇: "暗", - 闈: "闱", - 闉: "𬮱", - 闊: "阔", - 闋: "阕", - 闌: "阑", - 闍: "阇", - 闐: "阗", - 闑: "𫔶", - 闒: "阘", - 闓: "闿", - 闔: "阖", - 闕: "阙", - 闖: "闯", - 關: "关", - 闞: "阚", - 闠: "阓", - 闡: "阐", - 闢: "辟", - 闤: "阛", - 闥: "闼", - 阪: "阪", - 陘: "陉", - 陝: "陕", - 陞: "升", - 陣: "阵", - 陰: "阴", - 陳: "陈", - 陸: "陆", - 陽: "阳", - 隉: "陧", - 隊: "队", - 階: "阶", - 隑: "𬮿", - 隕: "陨", - 際: "际", - 隤: "𬯎", - 隨: "随", - 險: "险", - 隮: "𬯀", - 隯: "陦", - 隱: "隐", - 隴: "陇", - 隸: "隶", - 隻: "只", - 雋: "隽", - 雖: "虽", - 雙: "双", - 雛: "雏", - 雜: "杂", - 雞: "鸡", - 離: "离", - 難: "难", - 雲: "云", - 電: "电", - 霑: "沾", - 霢: "霡", - 霣: "𫕥", - 霧: "雾", - 霼: "𪵣", - 霽: "霁", - 靂: "雳", - 靄: "霭", - 靆: "叇", - 靈: "灵", - 靉: "叆", - 靚: "靓", - 靜: "静", - 靝: "靔", - 靦: "腼", - 靧: "𫖃", - 靨: "靥", - 鞏: "巩", - 鞝: "绱", - 鞦: "秋", - 鞽: "鞒", - 鞾: "𫖇", - 韁: "缰", - 韃: "鞑", - 韆: "千", - 韉: "鞯", - 韋: "韦", - 韌: "韧", - 韍: "韨", - 韓: "韩", - 韙: "韪", - 韚: "𫠅", - 韛: "𫖔", - 韜: "韬", - 韝: "鞲", - 韞: "韫", - 韠: "𫖒", - 韻: "韵", - 響: "响", - 頁: "页", - 頂: "顶", - 頃: "顷", - 項: "项", - 順: "顺", - 頇: "顸", - 須: "须", - 頊: "顼", - 頌: "颂", - 頍: "𫠆", - 頎: "颀", - 頏: "颃", - 預: "预", - 頑: "顽", - 頒: "颁", - 頓: "顿", - 頔: "𬱖", - 頗: "颇", - 領: "领", - 頜: "颌", - 頠: "𬱟", - 頡: "颉", - 頤: "颐", - 頦: "颏", - 頫: "𫖯", - 頭: "头", - 頮: "颒", - 頰: "颊", - 頲: "颋", - 頴: "颕", - 頵: "𫖳", - 頷: "颔", - 頸: "颈", - 頹: "颓", - 頻: "频", - 頽: "颓", - 顂: "𩓋", - 顃: "𩖖", - 顅: "𫖶", - 顆: "颗", - 題: "题", - 額: "额", - 顎: "颚", - 顏: "颜", - 顒: "颙", - 顓: "颛", - 顔: "颜", - 顗: "𫖮", - 願: "愿", - 顙: "颡", - 顛: "颠", - 類: "类", - 顢: "颟", - 顣: "𫖹", - 顥: "颢", - 顧: "顾", - 顫: "颤", - 顬: "颥", - 顯: "显", - 顰: "颦", - 顱: "颅", - 顳: "颞", - 顴: "颧", - 風: "风", - 颭: "飐", - 颮: "飑", - 颯: "飒", - 颰: "𩙥", - 颱: "台", - 颳: "刮", - 颶: "飓", - 颷: "𩙪", - 颸: "飔", - 颺: "飏", - 颻: "飖", - 颼: "飕", - 颾: "𩙫", - 飀: "飗", - 飄: "飘", - 飆: "飙", - 飈: "飚", - 飋: "𫗋", - 飛: "飞", - 飠: "饣", - 飢: "饥", - 飣: "饤", - 飥: "饦", - 飦: "𫗞", - 飩: "饨", - 飪: "饪", - 飫: "饫", - 飭: "饬", - 飯: "饭", - 飱: "飧", - 飲: "饮", - 飴: "饴", - 飵: "𫗢", - 飶: "𫗣", - 飼: "饲", - 飽: "饱", - 飾: "饰", - 飿: "饳", - 餃: "饺", - 餄: "饸", - 餅: "饼", - 餈: "糍", - 餉: "饷", - 養: "养", - 餌: "饵", - 餎: "饹", - 餏: "饻", - 餑: "饽", - 餒: "馁", - 餓: "饿", - 餔: "𫗦", - 餕: "馂", - 餖: "饾", - 餗: "𫗧", - 餘: "余", - 餚: "肴", - 餛: "馄", - 餜: "馃", - 餞: "饯", - 餡: "馅", - 餦: "𫗠", - 餧: "𫗪", - 館: "馆", - 餪: "𫗬", - 餫: "𫗥", - 餬: "糊", - 餭: "𫗮", - 餱: "糇", - 餳: "饧", - 餵: "喂", - 餶: "馉", - 餷: "馇", - 餸: "𩠌", - 餺: "馎", - 餼: "饩", - 餾: "馏", - 餿: "馊", - 饁: "馌", - 饃: "馍", - 饅: "馒", - 饈: "馐", - 饉: "馑", - 饊: "馓", - 饋: "馈", - 饌: "馔", - 饑: "饥", - 饒: "饶", - 饗: "飨", - 饘: "𫗴", - 饜: "餍", - 饞: "馋", - 饟: "𫗵", - 饠: "𫗩", - 饢: "馕", - 馬: "马", - 馭: "驭", - 馮: "冯", - 馯: "𫘛", - 馱: "驮", - 馳: "驰", - 馴: "驯", - 馹: "驲", - 馼: "𫘜", - 駁: "驳", - 駃: "𫘝", - 駉: "𬳶", - 駊: "𫘟", - 駎: "𩧨", - 駐: "驻", - 駑: "驽", - 駒: "驹", - 駓: "𬳵", - 駔: "驵", - 駕: "驾", - 駘: "骀", - 駙: "驸", - 駚: "𩧫", - 駛: "驶", - 駝: "驼", - 駞: "𫘞", - 駟: "驷", - 駡: "骂", - 駢: "骈", - 駤: "𫘠", - 駧: "𩧲", - 駩: "𩧴", - 駪: "𬳽", - 駫: "𫘡", - 駭: "骇", - 駰: "骃", - 駱: "骆", - 駶: "𩧺", - 駸: "骎", - 駻: "𫘣", - 駼: "𬳿", - 駿: "骏", - 騁: "骋", - 騂: "骍", - 騃: "𫘤", - 騄: "𫘧", - 騅: "骓", - 騉: "𫘥", - 騊: "𫘦", - 騌: "骔", - 騍: "骒", - 騎: "骑", - 騏: "骐", - 騑: "𬴂", - 騔: "𩨀", - 騖: "骛", - 騙: "骗", - 騚: "𩨊", - 騜: "𫘩", - 騝: "𩨃", - 騞: "𬴃", - 騟: "𩨈", - 騠: "𫘨", - 騤: "骙", - 騧: "䯄", - 騪: "𩨄", - 騫: "骞", - 騭: "骘", - 騮: "骝", - 騰: "腾", - 騱: "𫘬", - 騴: "𫘫", - 騵: "𫘪", - 騶: "驺", - 騷: "骚", - 騸: "骟", - 騻: "𫘭", - 騼: "𫠋", - 騾: "骡", - 驀: "蓦", - 驁: "骜", - 驂: "骖", - 驃: "骠", - 驄: "骢", - 驅: "驱", - 驊: "骅", - 驋: "𩧯", - 驌: "骕", - 驍: "骁", - 驎: "𬴊", - 驏: "骣", - 驓: "𫘯", - 驕: "骄", - 驗: "验", - 驙: "𫘰", - 驚: "惊", - 驛: "驿", - 驟: "骤", - 驢: "驴", - 驤: "骧", - 驥: "骥", - 驦: "骦", - 驨: "𫘱", - 驪: "骊", - 驫: "骉", - 骯: "肮", - 髏: "髅", - 髒: "脏", - 體: "体", - 髕: "髌", - 髖: "髋", - 髮: "发", - 鬆: "松", - 鬍: "胡", - 鬖: "𩭹", - 鬚: "须", - 鬠: "𫘽", - 鬢: "鬓", - 鬥: "斗", - 鬧: "闹", - 鬨: "哄", - 鬩: "阋", - 鬮: "阄", - 鬱: "郁", - 鬹: "鬶", - 魎: "魉", - 魘: "魇", - 魚: "鱼", - 魛: "鱽", - 魟: "𫚉", - 魢: "鱾", - 魥: "𩽹", - 魦: "𫚌", - 魨: "鲀", - 魯: "鲁", - 魴: "鲂", - 魵: "𫚍", - 魷: "鱿", - 魺: "鲄", - 魽: "𫠐", - 鮀: "𬶍", - 鮁: "鲅", - 鮃: "鲆", - 鮄: "𫚒", - 鮅: "𫚑", - 鮆: "𫚖", - 鮈: "𬶋", - 鮊: "鲌", - 鮋: "鲉", - 鮍: "鲏", - 鮎: "鲇", - 鮐: "鲐", - 鮑: "鲍", - 鮒: "鲋", - 鮓: "鲊", - 鮚: "鲒", - 鮜: "鲘", - 鮝: "鲞", - 鮞: "鲕", - 鮟: "𩽾", - 鮠: "𬶏", - 鮡: "𬶐", - 鮣: "䲟", - 鮤: "𫚓", - 鮦: "鲖", - 鮪: "鲔", - 鮫: "鲛", - 鮭: "鲑", - 鮮: "鲜", - 鮯: "𫚗", - 鮰: "𫚔", - 鮳: "鲓", - 鮵: "𫚛", - 鮶: "鲪", - 鮸: "3", - 鮺: "鲝", - 鮿: "𫚚", - 鯀: "鲧", - 鯁: "鲠", - 鯄: "𩾁", - 鯆: "𫚙", - 鯇: "鲩", - 鯉: "鲤", - 鯊: "鲨", - 鯒: "鲬", - 鯔: "鲻", - 鯕: "鲯", - 鯖: "鲭", - 鯗: "鲞", - 鯛: "鲷", - 鯝: "鲴", - 鯞: "𫚡", - 鯡: "鲱", - 鯢: "鲵", - 鯤: "鲲", - 鯧: "鲳", - 鯨: "鲸", - 鯪: "鲮", - 鯫: "鲰", - 鯬: "𫚞", - 鯰: "鲶", - 鯱: "𩾇", - 鯴: "鲺", - 鯶: "𩽼", - 鯷: "鳀", - 鯻: "𬶟", - 鯽: "鲫", - 鯾: "𫚣", - 鯿: "鳊", - 鰁: "鳈", - 鰂: "鲗", - 鰃: "鳂", - 鰆: "䲠", - 鰈: "鲽", - 鰉: "鳇", - 鰊: "𬶠", - 鰋: "𫚢", - 鰌: "䲡", - 鰍: "鳅", - 鰏: "鲾", - 鰐: "鳄", - 鰑: "𫚊", - 鰒: "鳆", - 鰓: "鳃", - 鰕: "𫚥", - 鰛: "鳁", - 鰜: "鳒", - 鰟: "鳑", - 鰠: "鳋", - 鰣: "鲥", - 鰤: "𫚕", - 鰥: "鳏", - 鰦: "𫚤", - 鰧: "䲢", - 鰨: "鳎", - 鰩: "鳐", - 鰫: "𫚦", - 鰭: "鳍", - 鰮: "鳁", - 鰱: "鲢", - 鰲: "鳌", - 鰳: "鳓", - 鰵: "鳘", - 鰶: "𬶭", - 鰷: "鲦", - 鰹: "鲣", - 鰺: "鲹", - 鰻: "鳗", - 鰼: "鳛", - 鰽: "𫚧", - 鰾: "鳔", - 鱀: "𬶨", - 鱂: "鳉", - 鱄: "𫚋", - 鱅: "鳙", - 鱆: "𫠒", - 鱇: "𩾌", - 鱈: "鳕", - 鱉: "鳖", - 鱊: "𫚪", - 鱒: "鳟", - 鱔: "鳝", - 鱖: "鳜", - 鱗: "鳞", - 鱘: "鲟", - 鱚: "𬶮", - 鱝: "鲼", - 鱟: "鲎", - 鱠: "鲙", - 鱢: "𫚫", - 鱣: "鳣", - 鱤: "鳡", - 鱧: "鳢", - 鱨: "鲿", - 鱭: "鲚", - 鱮: "𫚈", - 鱯: "鳠", - 鱲: "𫚭", - 鱷: "鳄", - 鱸: "鲈", - 鱺: "鲡", - 鳥: "鸟", - 鳧: "凫", - 鳩: "鸠", - 鳬: "凫", - 鳲: "鸤", - 鳳: "凤", - 鳴: "鸣", - 鳶: "鸢", - 鳷: "𫛛", - 鳼: "𪉃", - 鳽: "𫛚", - 鳾: "䴓", - 鴀: "𫛜", - 鴃: "𫛞", - 鴅: "𫛝", - 鴆: "鸩", - 鴇: "鸨", - 鴉: "鸦", - 鴐: "𫛤", - 鴒: "鸰", - 鴔: "𫛡", - 鴕: "鸵", - 鴗: "𫁡", - 鴛: "鸳", - 鴜: "𪉈", - 鴝: "鸲", - 鴞: "鸮", - 鴟: "鸱", - 鴣: "鸪", - 鴥: "𫛣", - 鴦: "鸯", - 鴨: "鸭", - 鴮: "𫛦", - 鴯: "鸸", - 鴰: "鸹", - 鴲: "𪉆", - 鴳: "𫛩", - 鴴: "鸻", - 鴷: "䴕", - 鴻: "鸿", - 鴽: "𫛪", - 鴿: "鸽", - 鵁: "䴔", - 鵂: "鸺", - 鵃: "鸼", - 鵊: "𫛥", - 鵏: "𬷕", - 鵐: "鹀", - 鵑: "鹃", - 鵒: "鹆", - 鵓: "鹁", - 鵚: "𪉍", - 鵜: "鹈", - 鵝: "鹅", - 鵟: "𫛭", - 鵠: "鹄", - 鵡: "鹉", - 鵧: "𫛨", - 鵩: "𫛳", - 鵪: "鹌", - 鵫: "𫛱", - 鵬: "鹏", - 鵮: "鹐", - 鵯: "鹎", - 鵰: "雕", - 鵲: "鹊", - 鵷: "鹓", - 鵾: "鹍", - 鶄: "䴖", - 鶇: "鸫", - 鶉: "鹑", - 鶊: "鹒", - 鶌: "𫛵", - 鶒: "𫛶", - 鶓: "鹋", - 鶖: "鹙", - 鶗: "𫛸", - 鶘: "鹕", - 鶚: "鹗", - 鶠: "𬸘", - 鶡: "鹖", - 鶥: "鹛", - 鶦: "𫛷", - 鶩: "鹜", - 鶪: "䴗", - 鶬: "鸧", - 鶭: "𫛯", - 鶯: "莺", - 鶰: "𫛫", - 鶱: "𬸣", - 鶲: "鹟", - 鶴: "鹤", - 鶹: "鹠", - 鶺: "鹡", - 鶻: "鹘", - 鶼: "鹣", - 鶿: "鹚", - 鷀: "鹚", - 鷁: "鹢", - 鷂: "鹞", - 鷄: "鸡", - 鷅: "𫛽", - 鷉: "䴘", - 鷊: "鹝", - 鷐: "𫜀", - 鷓: "鹧", - 鷔: "𪉑", - 鷖: "鹥", - 鷗: "鸥", - 鷙: "鸷", - 鷚: "鹨", - 鷟: "𬸦", - 鷣: "𫜃", - 鷤: "𫛴", - 鷥: "鸶", - 鷦: "鹪", - 鷨: "𪉊", - 鷩: "𫜁", - 鷫: "鹔", - 鷭: "𬸪", - 鷯: "鹩", - 鷲: "鹫", - 鷳: "鹇", - 鷴: "鹇", - 鷷: "𫜄", - 鷸: "鹬", - 鷹: "鹰", - 鷺: "鹭", - 鷽: "鸴", - 鷿: "𬸯", - 鸂: "㶉", - 鸇: "鹯", - 鸊: "䴙", - 鸋: "𫛢", - 鸌: "鹱", - 鸏: "鹲", - 鸑: "𬸚", - 鸕: "鸬", - 鸗: "𫛟", - 鸘: "鹴", - 鸚: "鹦", - 鸛: "鹳", - 鸝: "鹂", - 鸞: "鸾", - 鹵: "卤", - 鹹: "咸", - 鹺: "鹾", - 鹼: "碱", - 鹽: "盐", - 麗: "丽", - 麥: "麦", - 麨: "𪎊", - 麩: "麸", - 麪: "面", - 麫: "面", - 麬: "𤿲", - 麯: "曲", - 麲: "𪎉", - 麳: "𪎌", - 麴: "曲", - 麵: "面", - 麷: "𫜑", - 麼: "么", - 麽: "么", - 黃: "黄", - 黌: "黉", - 點: "点", - 黨: "党", - 黲: "黪", - 黴: "霉", - 黶: "黡", - 黷: "黩", - 黽: "黾", - 黿: "鼋", - 鼂: "鼌", - 鼉: "鼍", - 鼕: "冬", - 鼴: "鼹", - 齊: "齐", - 齋: "斋", - 齎: "赍", - 齏: "齑", - 齒: "齿", - 齔: "龀", - 齕: "龁", - 齗: "龂", - 齘: "𬹼", - 齙: "龅", - 齜: "龇", - 齟: "龃", - 齠: "龆", - 齡: "龄", - 齣: "出", - 齦: "龈", - 齧: "啮", - 齩: "𫜪", - 齪: "龊", - 齬: "龉", - 齭: "𫜭", - 齮: "𬺈", - 齯: "𫠜", - 齰: "𫜬", - 齲: "龋", - 齴: "𫜮", - 齶: "腭", - 齷: "龌", - 齼: "𬺓", - 齾: "𫜰", - 龍: "龙", - 龎: "厐", - 龐: "庞", - 龑: "䶮", - 龓: "𫜲", - 龔: "龚", - 龕: "龛", - 龜: "龟", - 龭: "𩨎", - 龯: "𨱆", - 鿁: "䜤", - 鿓: "鿒", - "𠁞": "𠀾", - "𠌥": "𠆿", - "𠏢": "𠉗", - "𠐊": "𫝋", - "𠗣": "㓆", - "𠞆": "𠛆", - "𠠎": "𠚳", - "𠬙": "𪠡", - "𠽃": "𪠺", - "𠿕": "𪜎", - "𡂡": "𪢒", - "𡃄": "𪡺", - "𡃕": "𠴛", - "𡃤": "𪢐", - "𡄔": "𠴢", - "𡄣": "𠵸", - "𡅏": "𠲥", - "𡅯": "𪢖", - "𡑍": "𫭼", - "𡑭": "𡋗", - "𡓁": "𪤄", - "𡓾": "𡋀", - "𡔖": "𡍣", - "𡞵": "㛟", - "𡟫": "𫝪", - "𡠹": "㛿", - "𡢃": "㛠", - "𡮉": "𡭜", - "𡮣": "𡭬", - "𡳳": "𡳃", - "𡸗": "𪨩", - "𡹬": "𪨹", - "𡻕": "岁", - "𡽗": "𡸃", - "𡾱": "㟜", - "𡿖": "𪩛", - "𢍰": "𪪴", - "𢠼": "𢙑", - "𢣐": "𪬚", - "𢣚": "𢘝", - "𢣭": "𢘞", - "𢤩": "𪫡", - "𢤱": "𢘙", - "𢤿": "𪬯", - "𢯷": "𪭝", - "𢶒": "𪭯", - "𢶫": "𢫞", - "𢷮": "𢫊", - "𢹿": "𢬦", - "𢺳": "𪮳", - "𣈶": "暅", - "𣋋": "𣈣", - "𣍐": "𫧃", - "𣙎": "㭣", - "𣜬": "𪳗", - "𣝕": "𣘷", - "𣞻": "𣘓", - "𣠩": "𣞎", - "𣠲": "𣑶", - "𣯩": "𣯣", - "𣯴": "𣭤", - "𣯶": "毶", - "𣽏": "𪶮", - "𣾷": "㳢", - "𣿉": "𣶫", - "𤁣": "𣺽", - "𤄷": "𪶒", - "𤅶": "𣷷", - "𤑳": "𤎻", - "𤑹": "𪹀", - "𤒎": "𤊀", - "𤒻": "𪹹", - "𤓌": "𪹠", - "𤓎": "𤎺", - "𤓩": "𤊰", - "𤘀": "𪺣", - "𤛮": "𤙯", - "𤛱": "𫞢", - "𤜆": "𪺪", - "𤠮": "𪺸", - "𤢟": "𤝢", - "𤢻": "𢢐", - "𤩂": "𫞧", - "𤪺": "㻘", - "𤫩": "㻏", - "𤬅": "𪼴", - "𤳷": "𪽝", - "𤳸": "𤳄", - "𤷃": "𪽭", - "𤸫": "𤶧", - "𤺔": "𪽴", - "𥊝": "𥅿", - "𥌃": "𥅘", - "𥏝": "𪿊", - "𥕥": "𥐰", - "𥖅": "𥐯", - "𥖲": "𪿞", - "𥗇": "𪿵", - "𥗽": "𬒗", - "𥜐": "𫀓", - "𥜰": "𫀌", - "𥞵": "𥞦", - "𥢢": "䅪", - "𥢶": "𫞷", - "𥢷": "𫀮", - "𥨐": "𥧂", - "𥪂": "𥩺", - "𥯤": "𫁳", - "𥴨": "𫂖", - "𥴼": "𫁺", - "𥵃": "𥱔", - "𥵊": "𥭉", - "𥶽": "𫁱", - "𥸠": "𥮋", - "𥻦": "𫂿", - "𥼽": "𥹥", - "𥽖": "𥺇", - "𥾯": "𫄝", - "𥿊": "𦈈", - "𦀖": "𫄦", - "𦂅": "𦈒", - "𦃄": "𦈗", - "𦃩": "𫄯", - "𦅇": "𫄪", - "𦅈": "𫄵", - "𦆲": "𫟇", - "𦒀": "𫅥", - "𦔖": "𫅼", - "𦘧": "𡳒", - "𦟼": "𫆝", - "𦠅": "𫞅", - "𦡝": "𫆫", - "𦢈": "𣍨", - "𦣎": "𦟗", - "𦧺": "𫇘", - "𦪙": "䑽", - "𦪽": "𦨩", - "𦱌": "𫇪", - "𦾟": "𦶻", - "𧎈": "𧌥", - "𧒯": "𫊹", - "𧔥": "𧒭", - "𧕟": "𧉐", - "𧜗": "䘞", - "𧜵": "䙊", - "𧝞": "䘛", - "𧞫": "𫌋", - "𧟀": "𧝧", - "𧡴": "𫌫", - "𧢄": "𫌬", - "𧦝": "𫍞", - "𧦧": "𫍟", - "𧩕": "𫍭", - "𧩙": "𬣥", - "𧩼": "𫍶", - "𧫝": "𫍺", - "𧬤": "𫍼", - "𧭈": "𫍾", - "𧭹": "𫍐", - "𧳟": "𧳕", - "𧵳": "䞌", - "𧶔": "𧹓", - "𧶧": "䞎", - "𧷎": "𪠀", - "𧸘": "𫎨", - "𧹈": "𪥠", - "𧽯": "𫎸", - "𨂐": "𫏌", - "𨄣": "𨀱", - "𨅍": "𨁴", - "𨆪": "𫏕", - "𨇁": "𧿈", - "𨇞": "𨅫", - "𨇤": "𫏨", - "𨇰": "𫏞", - "𨇽": "𫏑", - "𨈊": "𨂺", - "𨈌": "𨄄", - "𨊰": "䢀", - "𨊸": "䢁", - "𨊻": "𨐆", - "𨋢": "䢂", - "𨌈": "𫐍", - "𨍰": "𫐔", - "𨎌": "𫐋", - "𨎮": "𨐉", - "𨏠": "𨐇", - "𨏥": "𨐊", - "𨞺": "𫟫", - "𨟊": "𫟬", - "𨢿": "𨡙", - "𨣈": "𨡺", - "𨣞": "𨟳", - "𨣧": "𨠨", - "𨤻": "𨤰", - "𨥛": "𨱀", - "𨥟": "𫓫", - "𨦫": "䦀", - "𨧀": "𬭊", - "𨧜": "䦁", - "𨧰": "𫟽", - "𨧱": "𨱊", - "𨨏": "𬭛", - "𨨛": "𫓼", - "𨨢": "𫓽", - "𨩰": "𫟾", - "𨪕": "𫓮", - "𨫒": "𨱐", - "𨬖": "𫔏", - "𨭆": "𬭶", - "𨭎": "𬭳", - "𨭖": "𫔑", - "𨭸": "𫔐", - "𨮂": "𨱕", - "𨮳": "𫔒", - "𨯅": "䥿", - "𨯟": "𫔓", - "𨰃": "𫔉", - "𨰋": "𫓳", - "𨰥": "𫔕", - "𨰲": "𫔃", - "𨲳": "𫔖", - "𨳑": "𨸁", - "𨳕": "𨸀", - "𨴗": "𨸅", - "𨴹": "𫔲", - "𨵩": "𨸆", - "𨵸": "𨸇", - "𨶀": "𨸉", - "𨶏": "𨸊", - "𨶮": "𨸌", - "𨶲": "𨸋", - "𨷲": "𨸎", - "𨼳": "𫔽", - "𨽏": "𨸘", - "𩀨": "𫕚", - "𩅙": "𫕨", - "𩎖": "𫖑", - "𩎢": "𩏾", - "𩏂": "𫖓", - "𩏠": "𫖖", - "𩏪": "𩏽", - "𩏷": "𫃗", - "𩑔": "𫖪", - "𩒎": "𫖭", - "𩓣": "𩖕", - "𩓥": "𫖵", - "𩔑": "𫖷", - "𩔳": "𫖴", - "𩖰": "𫠇", - "𩗀": "𩙦", - "𩗓": "𫗈", - "𩗴": "𫗉", - "𩘀": "𩙩", - "𩘝": "𩙭", - "𩘹": "𩙨", - "𩘺": "𩙬", - "𩙈": "𩙰", - "𩚛": "𩟿", - "𩚥": "𩠀", - "𩚩": "𫗡", - "𩚵": "𩠁", - "𩛆": "𩠂", - "𩛌": "𫗤", - "𩛡": "𫗨", - "𩛩": "𩠃", - "𩜇": "𩠉", - "𩜦": "𩠆", - "𩜵": "𩠊", - "𩝔": "𩠋", - "𩝽": "𫗳", - "𩞄": "𩠎", - "𩞦": "𩠏", - "𩞯": "䭪", - "𩟐": "𩠅", - "𩟗": "𫗚", - "𩠴": "𩠠", - "𩡣": "𩡖", - "𩡺": "𩧦", - "𩢡": "𩧬", - "𩢴": "𩧵", - "𩢸": "𩧳", - "𩢾": "𩧮", - "𩣏": "𩧶", - "𩣑": "䯃", - "𩣫": "𩧸", - "𩣵": "𩧻", - "𩣺": "𩧼", - "𩤊": "𩧩", - "𩤙": "𩨆", - "𩤲": "𩨉", - "𩤸": "𩨅", - "𩥄": "𩨋", - "𩥇": "𩨍", - "𩥉": "𩧱", - "𩥑": "𩨌", - "𩦠": "𫠌", - "𩧆": "𩨐", - "𩭙": "𩬣", - "𩯁": "𫙂", - "𩯳": "𩯒", - "𩰀": "𩬤", - "𩰹": "𩰰", - "𩳤": "𩲒", - "𩴵": "𩴌", - "𩵦": "𫠏", - "𩵩": "𩽺", - "𩵹": "𩽻", - "𩶁": "𫚎", - "𩶘": "䲞", - "𩶰": "𩽿", - "𩶱": "𩽽", - "𩷰": "𩾄", - "𩸃": "𩾅", - "𩸄": "𫚝", - "𩸡": "𫚟", - "𩸦": "𩾆", - "𩻗": "𫚨", - "𩻬": "𫚩", - "𩻮": "𫚘", - "𩼶": "𫚬", - "𩽇": "𩾎", - "𩿅": "𫠖", - "𩿤": "𫛠", - "𩿪": "𪉄", - "𪀖": "𫛧", - "𪀦": "𪉅", - "𪀾": "𪉋", - "𪁈": "𪉉", - "𪁖": "𪉌", - "𪂆": "𪉎", - "𪃍": "𪉐", - "𪃏": "𪉏", - "𪃒": "𫛻", - "𪃧": "𫛹", - "𪄆": "𪉔", - "𪄕": "𪉒", - "𪅂": "𫜂", - "𪆷": "𫛾", - "𪇳": "𪉕", - "𪈼": "𱊜", - "𪉸": "𫜊", - "𪋿": "𫧮", - "𪌭": "𫜓", - "𪍠": "𫜕", - "𪓰": "𫜟", - "𪔵": "𪔭", - "𪘀": "𪚏", - "𪘯": "𪚐", - "𪙏": "𫜯", - "𪟖": "𠛾", - "𪷓": "𣶭", - "𫒡": "𫓷", - "𫜦": "𫜫", -}; -// 数据来源:https://github.com/BYVoid/OpenCC/tree/master/data/dictionary/STCharacters.txt -/** - * 转换文本 - * @param {String} str - 待转换的文本 - * @param {Boolean} toT - 是否转换成繁体 - * @returns {String} - 转换结果 - */ -function tranStr(str, toT) { - var i; - var letter; - var code; - var isChinese; - var src; - var result = ""; - if (toT) { - src = s2tData; - } - else { - src = t2sData; - } - if (typeof str !== "string") { - return str; - } - for (i = 0; i < str.length; i++) { - letter = str.charAt(i); - code = str.charCodeAt(i); - // 根据字符的 Unicode 判断是否为汉字,以提高性能 - isChinese = - (code > 0x3400 && code < 0x9fc3) || (code > 0xf900 && code < 0xfa6a); - if (!isChinese) { - result += letter; - continue; - } - let target = src[letter]; - if (target) { - result += target; - } - else { - result += letter; - } - } - return result; -} -var Chinese = { - s2t: function (str) { - return tranStr(str, true); - }, - t2s: function (str) { - return tranStr(str, false); - }, -}; - -let keywords = [ - "章", - "节", - "回", - "節", - "卷", - "部", - "輯", - "辑", - "話", - "集", - "话", - "篇", - " ", - " ", -]; -let containChars = []; -// let containChars = ["[", "。", ";", ";"]; -let startWithChars = [ - "CHAPTER", - "Chapter", - "序章", - "前言", - "声明", - "写在前面的话", - "后记", - "楔子", - "后序", - "章节目录", - "尾声", - "聲明", - "寫在前面的話", - "後記", - "後序", - "章節目錄", - "尾聲", -]; -const txtToHtml = (text, parserRegex, bookLocation) => { - let lines = text.split("\n"); - if (lines.length === 1) { - lines = text.split("\r"); - } - const htmlParts = []; // Use an array to store HTML parts - let isRefresh = false; - if (bookLocation && bookLocation.refresh) { - isRefresh = true; - } - if (lines.length > 10000 && !isRefresh) { - if (!bookLocation || !bookLocation.text) { - bookLocation = { - text: lines[0], - chapterTitle: "", - chapterDocIndex: 0, - }; - } - // --- Slicing and Title Identification Logic --- - let targetLineIndex = lines.findIndex((item) => { - // Optimization: cleanText called only once here if needed often - return cleanText(item) === cleanText(bookLocation.text); - }); - if (targetLineIndex === -1) { - targetLineIndex = 0; - } - // Slice the lines array - const startIndex = Math.max(targetLineIndex - 1000, 0); - const endIndex = Math.min(targetLineIndex + 1000, lines.length); - const relevantLines = lines.slice(startIndex, endIndex); // Process only the relevant slice - // Identify potential titles within the relevant slice - const titlesInSlice = relevantLines.filter((item) => { - const cleaned = cleanText(item); // Clean once - return cleaned && isTitle(cleaned, parserRegex); - }); - // Create a Set of cleaned titles for fast lookup - const cleanedTitlesSet = new Set(titlesInSlice.map((title) => cleanText(title))); - let targetTitleIndex = titlesInSlice.findIndex((item) => { - // Optimization: cleanText called only once here - return cleanText(item) === cleanText(bookLocation.chapterTitle); - }); - if (targetTitleIndex === -1) { - targetTitleIndex = 0; - } - // --- Prepending Logic (if needed) --- - // This part seems related to chapter indexing, ensure it uses the correct indices based on the slice - if (targetTitleIndex < parseInt(bookLocation.chapterDocIndex || "0") - 1) { - let prependLength = parseInt(bookLocation.chapterDocIndex || "0") - targetTitleIndex; - if (prependLength > 0) { - for (let i = 0; i < prependLength; i++) { - // Push to array instead of concatenating - htmlParts.push(`

Chapter ${i}

`); - htmlParts.push(`

Chapter ${i}

`); - } - } - } - // --- Main Loop for Relevant Lines --- - for (const item of relevantLines) { - // Iterate over the sliced array - const cleanedItem = cleanText(item); // Clean once per line - // Use the Set for O(1) average lookup - if (cleanedItem && cleanedTitlesSet.has(cleanedItem)) { - htmlParts.push(`

${cleanedItem}

`); // Push to array - } - else { - // Avoid cleaning again if not necessary, use original item for content - htmlParts.push(`

${item}

`); // Push to array - } - } - } - else { - // --- Loop for Full File (if not large or no bookLocation) --- - for (const item of lines) { - const cleanedItem = cleanText(item); // Clean once per line - if (cleanedItem && isTitle(cleanedItem, parserRegex)) { - htmlParts.push(`

${cleanedItem}

`); // Push to array - } - else { - htmlParts.push(`

${item}

`); // Push to array - } - } - } - // Join the array at the end - const finalHtml = htmlParts.join(""); - if (finalHtml) { - return finalHtml; - } - else { - // Fallback if no HTML was generated - return `

Title

${text}

`; - } -}; -const cleanText = (str) => { - return str - .trim() - .replace(/(\r\n|\n|\r|\t)/gm, "") - .substring(0, 100) - .split("") - .filter((item) => item !== "=" && item !== "-" && item !== "_" && item !== "+") - .join(""); -}; -const isTitle = (line, parserRegex = "") => { - if (parserRegex) { - return new RegExp(parserRegex).test(line); - } - return (line && - line.length < 40 && - !isContain(line) && - (isStartWithChars(line) || - (line.startsWith("第") && startWithDI(line)) || - (line.startsWith("卷") && startWithJUAN(line)) || - (line.indexOf("第") > -1 && - line.lastIndexOf("第") < 7 && - startWithDI(line.substr(line.indexOf("第")))))); -}; -const isContain = (line) => { - return containChars.filter((item) => line.indexOf(item) > -1).length > 0; -}; -const isStartWithChars = (line) => { - return startWithChars.filter((item) => line.startsWith(item)).length > 0; -}; -const startWithDI = (line) => { - let flag = false; - for (let i = 0; i < keywords.length; i++) { - if (/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(line.substring(1, line.indexOf(keywords[i])).trim()) || - /^\d+$/.test(line.substring(1, line.indexOf(keywords[i])).trim())) { - flag = true; - } - if (flag) - break; - } - return flag; -}; -const startWithJUAN = (line) => { - if (/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(line.substring(1, line.indexOf(" "))) || - /^\d+$/.test(line.substring(1, line.indexOf(" ")))) - return true; - if (/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(line.substring(1, line.indexOf(" "))) || - /^\d+$/.test(line.substring(1, line.indexOf(" ")))) - return true; - if (/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(line.substring(1)) || - /^\d+$/.test(line.substring(1))) - return true; - return false; -}; - -let lock = false; -const getBlockElement = (Element) => { - return Array.from(Element.querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,li,dt,dd,pre,blockquote,address,kookitmarker")); -}; -const handleScrollPage = (element, animation, delta, doc, flipToNextPage, flipToPrevPage, isMobile) => __awaiter(void 0, void 0, void 0, function* () { - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - const width = element.clientWidth; - if (animation === "mimical" && isMobile !== "yes") { - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "block"; - if (delta > 0) { - flipToPrevPage(); - } - else if (delta < 0) { - flipToNextPage(); - } - setTimeout(() => { - if (!bookDiv) - return {}; - bookDiv.style.display = "none"; - }, 1000); - } - } - const currentScrollLeft = doc.body.scrollLeft; - const scrollDistance = width + gap; - if (delta > 0) { - // previous page - 计算当前页数并减1 - const currentPage = Math.round(currentScrollLeft / scrollDistance); - const targetPage = Math.max(0, currentPage - 1); - const targetScrollLeft = targetPage * scrollDistance; - doc.body.scrollTo({ - top: 0, - left: targetScrollLeft, - behavior: animation === "sliding" && isMobile !== "yes" ? "smooth" : "auto", - }); - } - else if (delta < 0) { - // next page - 计算当前页数并加1 - const currentPage = Math.round(currentScrollLeft / scrollDistance); - const targetPage = currentPage + 1; - const targetScrollLeft = targetPage * scrollDistance; - doc.body.scrollTo({ - top: 0, - left: targetScrollLeft, - behavior: animation === "sliding" && isMobile !== "yes" ? "smooth" : "auto", - }); - } -}); -const findValidChapter = (chapterDocIndex, chapterHref, chapterDocList, flag) => { - let currentChapterIndex = _.findLastIndex(chapterDocList, (chapter) => { - return (chapter.href === chapterHref || - (chapter.href && - chapter.href.includes("#") && - chapter.href.includes(chapterHref))); - }); - if (chapterHref && - _.findLastIndex(chapterDocList, (chapter) => { - return (chapter.href === chapterHref || - (chapter.href && - chapter.href.includes("#") && - chapter.href.includes(chapterHref))); - }) > -1) ; - else { - currentChapterIndex = chapterDocIndex; - } - if (flag === "prev") { - return Object.assign(Object.assign({}, chapterDocList[currentChapterIndex - 1]), { index: currentChapterIndex - 1 }); - } - else { - return Object.assign(Object.assign({}, chapterDocList[currentChapterIndex + 1]), { index: currentChapterIndex + 1 }); - } -}; -const handlePrevChapter = (element, flattenChapters, chapterDocList, readerMode, format, tempLocation, doc, iframe) => __awaiter(void 0, void 0, void 0, function* () { - let chapterDocIndex = parseInt(tempLocation.chapterDocIndex || "0"); - let chapterHref = tempLocation.chapterHref || ""; - if (chapterDocIndex === 0) { - return; - } - let prevChapter = findValidChapter(chapterDocIndex, chapterHref, chapterDocList, "prev"); - if (!prevChapter) - return; - tempLocation.text = "prevChapter"; - tempLocation.page = ""; - yield handleRenderChapter(prevChapter.index, prevChapter.label, prevChapter.href, chapterDocList, element, readerMode, format, tempLocation, doc, iframe); -}); -const handleRenderChapter = (chapterDocIndex, chapterTitle, chapterHref, chapterDocList, element, readerMode, format, tempLocation, doc, iframe) => __awaiter(void 0, void 0, void 0, function* () { - doc.body.innerHTML = ""; - iframe.height = 0 + "px"; - doc.body.scrollTo(0, 0); - if ((chapterTitle && !chapterDocIndex) || - (chapterDocList[chapterDocIndex] && - chapterDocList[chapterDocIndex].label && - chapterTitle && - chapterTitle !== chapterDocList[chapterDocIndex].label && - chapterHref.indexOf("#") === -1)) { - let tempChapterDocIndex = _.findLastIndex(chapterDocList, { - label: chapterTitle, - }); - if (tempChapterDocIndex !== -1) { - chapterDocIndex = tempChapterDocIndex; - } - } - if (chapterDocIndex === -1 && chapterHref.indexOf("#") > -1) { - let href = chapterHref.split("#")[0]; - let tempChapterDocIndex = _.findLastIndex(chapterDocList, (chapter) => { - return (chapter.href === href || - (chapter.href && - chapter.href.includes("#") && - chapter.href.includes(href))); - }); - if (tempChapterDocIndex !== -1) { - chapterDocIndex = tempChapterDocIndex; - } - } - if (chapterDocIndex === -1 || chapterDocIndex > chapterDocList.length - 1) { - chapterDocIndex = 0; - } - let chapterText = yield handleOneChapterDoc(chapterDocList[chapterDocIndex].text, false); - let bodyAttrs = getBodyAttributes(chapterText); - //get viewport width from chapterText - doc.body.innerHTML = chapterText; - if (bodyAttrs["style"]) { - doc.body.setAttribute("style", doc.body.getAttribute("style") || ""); - } - else if (bodyAttrs["class"]) { - doc.body.setAttribute("class", bodyAttrs["class"]); - } - else if (bodyAttrs["id"]) { - doc.body.setAttribute("id", bodyAttrs["id"]); - } - else if (!bodyAttrs["class"]) { - doc.body.removeAttribute("class"); - } - else if (!bodyAttrs["id"]) { - doc.body.removeAttribute("id"); - } - yield handleCssLink(doc); - tempLocation.chapterTitle = chapterTitle; - tempLocation.chapterHref = chapterHref; - tempLocation.chapterDocIndex = chapterDocIndex + ""; - tempLocation.percentage = - chapterDocList - .slice(0, chapterDocIndex) - .map((item) => (item.text ? item.text.size || 1 : 1)) - .reduce((a, b) => a + b, 0) / - chapterDocList - .map((item) => (item.text ? item.text.size || 1 : 1)) - .reduce((a, b) => a + b, 0) + - ""; - tempLocation.text = ""; - yield handleIframeHeight(element, readerMode, format, iframe, doc); - yield handleScrollPosition(element, readerMode, "", "", "", "", doc); -}); -function getBodyAttributes(htmlStr) { - // 匹配 开始标签(忽略大小写) - const bodyTagMatch = htmlStr.match(/]*)>/i); - if (!bodyTagMatch) - return {}; - // 提取属性字符串(如 'id="main" class=dark') - const attrStr = bodyTagMatch[1]; - const attributes = {}; - // 匹配属性键值对 - const attrRegex = /([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^>\s]+))/g; - let match; - while ((match = attrRegex.exec(attrStr)) !== null) { - const value = match[2] || match[3] || match[4] || ""; - attributes[match[1]] = value; - } - return attributes; -} -const handleCssLink = (doc) => __awaiter(void 0, void 0, void 0, function* () { - let linkList = Array.from(doc.getElementsByTagName("link")); - if (linkList.length === 0) { - return; - } - let styleSheetPromises = []; - for (let index = 0; index < linkList.length; index++) { - const link = linkList[index]; - if (!link.href.endsWith("null")) { - styleSheetPromises.push(new Promise((resolve, reject) => { - link.addEventListener("load", resolve); - })); - } - } - try { - yield Promise.race([ - Promise.all(styleSheetPromises), - new Promise((resolve, reject) => { - setTimeout(() => { - // reject(new Error("Timeout")); - resolve("css load timeout"); - }, 10); - }), - ]); - } - catch (err) { - console.error(err); - } -}); -const handleScrollPosition = (element, readerMode, text, count, href, page, doc) => __awaiter(void 0, void 0, void 0, function* () { - let left = 0; - let targetNode = doc.body; - if (page && readerMode !== "scroll") { - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - const width = convertComputedNum(getComputedStyle(element).width); - let pageWidth = width + gap; - left = pageWidth * (parseInt(page) - 1); - } - else if (text) { - let nodeList = getBlockElement(doc.body); - let targetNodeList = nodeList.filter((s, index) => { - return (cleanText(s.textContent) && - (cleanText(s.textContent) === cleanText(text) || - cleanText(s.textContent) === - Chinese.t2s(cleanText(text)) || - cleanText(s.textContent) === - Chinese.s2t(cleanText(text))) && - (Math.abs(index - parseInt(count)) < 2 || - count === "search" || - count === "ignore" || - count === "next")); - }); - if (targetNodeList.length === 0) { - return; - } - targetNode = getCloestBlock(targetNodeList[0], element, readerMode); - left = targetNode - ? convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)) - : text === "prevChapter" - ? doc.body.scrollWidth - : 0; - } - else if (href && href.indexOf("#") > -1) { - let id = CSS.escape(href.split("#").reverse()[0]); - if (!doc.body.querySelector("#" + id)) { - return; - } - targetNode = getCloestBlock(doc.body.querySelector("#" + id) || doc.body, element, readerMode); - left = targetNode - ? convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)) - : 0; - } - if (readerMode !== "scroll") { - doc.body.scrollTo(left, 0); - } - else { - targetNode.scrollIntoView(); - } -}); -const getCloestBlock = (targetNode, element, readerMode) => { - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let offsetLeft = convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)); - if (readerMode === "scroll") { - return targetNode; - } - else if (readerMode !== "scroll" && - checkDivisibleInRange(parseInt(offsetLeft + ""), (element.clientWidth + gap) / 2)) { - return targetNode; - } - else if (targetNode.parentElement) { - return getCloestBlock(targetNode.parentElement, element, readerMode); - } - else { - return targetNode; - } -}; -const checkDivisibleInRange = (x, y) => { - for (let i = x - 10; i <= x + 10; i++) { - if (i % y === 0) { - return true; - } - } - return false; -}; -const handleRecord = (element, readerMode, flattenChapters, chapterDocList, tempLocation, doc, targetNode) => __awaiter(void 0, void 0, void 0, function* () { - var _a, _b; - if (lock) - return; - let nodeList = getBlockElement(doc.body); - let visibleNode = nodeList.filter((s) => isScrolledIntoView(element, s, readerMode) && - (s.textContent || "").trim()); - let firstVisibleNode = visibleNode[0]; - if (targetNode) { - firstVisibleNode = targetNode; - } - let count = 0; - for (let i = 0; i < nodeList.length; i++) { - if (isScrolledIntoView(element, nodeList[i], readerMode) && - firstVisibleNode && - nodeList[i].innerHTML === firstVisibleNode.innerHTML) { - count = i; - break; - } - } - handleHashChapter(visibleNode, flattenChapters, tempLocation); - if (firstVisibleNode && - !isCurrentNodeFarFromParrent(firstVisibleNode, element, readerMode)) { - tempLocation.text = firstVisibleNode.textContent || ""; - tempLocation.count = count + ""; - tempLocation.page = ""; - let totalSize = chapterDocList - .map((item) => (item.text ? item.text.size || 1 : 1)) - .reduce((a, b) => a + b, 0); - tempLocation.percentage = - chapterDocList - .slice(0, parseInt(tempLocation.chapterDocIndex)) - .map((item) => (item.text ? item.text.size || 1 : 1)) - .reduce((a, b) => a + b, 0) / - totalSize + - ((((_a = chapterDocList.find((_item, index) => index === parseInt(tempLocation.chapterDocIndex))) === null || _a === void 0 ? void 0 : _a.text.size) || 0) / - totalSize) * - (count / nodeList.length) + - ""; - } - else { - tempLocation.page = - ((_b = (yield progressInfo(readerMode, doc, element))) === null || _b === void 0 ? void 0 : _b.currentPage) + ""; - } - lock = true; - setTimeout(() => { - lock = false; - }, 100); -}); -const isCurrentNodeFarFromParrent = (targetNode, element, readerMode) => { - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - if (Math.abs(targetNode.offsetLeft - - getCloestBlock(targetNode, element, readerMode).offsetLeft) > - (element.clientWidth + gap) / 2) { - return true; - } - else { - return false; - } -}; -const handleHashChapter = (visibleNode, flattenChapters, tempLocation) => { - let chapterHref = tempLocation.chapterHref || ""; - let lastIndexOfHash = chapterHref.lastIndexOf("#"); - let beforeHash = ""; - if (lastIndexOfHash === -1) { - beforeHash = chapterHref; - } - else { - beforeHash = chapterHref.substring(0, lastIndexOfHash); - } - for (let index = 0; index < visibleNode.length; index++) { - const element = visibleNode[index]; - if (element.id) { - let newHref = beforeHash + "#" + element.id; - let newIndex = _.findLastIndex(flattenChapters, { - href: newHref, - }); - if (newIndex > -1) { - tempLocation.chapterHref = newHref; - tempLocation.chapterTitle = flattenChapters[newIndex].label; - } - } - } -}; -const handleNextChapter = (element, flattenChapters, chapterDocList, readerMode, format, tempLocation, doc, iframe) => __awaiter(void 0, void 0, void 0, function* () { - let chapterDocIndex = parseInt(tempLocation.chapterDocIndex || "0"); - let chapterHref = tempLocation.chapterHref || ""; - if (chapterDocIndex >= chapterDocList.length - 1) { - tempLocation.percentage = "1"; - return; - } - let nextChapter = findValidChapter(chapterDocIndex, chapterHref, chapterDocList, "next"); - if (!nextChapter) - return; - tempLocation.page = ""; - yield handleRenderChapter(nextChapter.index, nextChapter.label, nextChapter.href, chapterDocList, element, readerMode, format, tempLocation, doc, iframe); -}); -const getAudioText = (element, readerMode, doc) => { - let nodeList = getBlockElement(doc.body).filter((item) => !isParentBlock(item)); - let audioNode = nodeList.filter((s) => { - // 检查文本内容是否存在且不为空 - if (!(s.textContent || "").trim()) { - return false; - } - // 检查是否有父级块元素(排除body) - let parent = s.parentElement; - while (parent && parent !== doc.body) { - // 如果父级元素也在nodeList中,说明当前元素是嵌套的 - if (nodeList.includes(parent)) { - return false; - } - parent = parent.parentElement; - } - return true; - }); - let audioText = audioNode - .filter((item) => { var _a; return item.textContent !== "img" && !((_a = item.textContent) === null || _a === void 0 ? void 0 : _a.startsWith("img")); }) - .map((item) => item.textContent); - let firstSliceIndex = 0; - let visibleText = getVisibleText(element, readerMode, doc); - if (visibleText && visibleText.length > 0) { - let firstVisibleText = visibleText[0]; - firstSliceIndex = audioText.indexOf(firstVisibleText); - } - return audioText.slice(firstSliceIndex); -}; -const getVisibleText = (element, readerMode, doc) => { - let nodeList = getBlockElement(doc.body).filter((item) => !isParentBlock(item)); - let visibleNode = nodeList.filter((s) => isScrolledIntoView(element, s, readerMode) && - (s.textContent || "").trim()); - visibleNode = visibleNode.filter((s) => { - // 检查文本内容是否存在且不为空 - if (!(s.textContent || "").trim()) { - return false; - } - // 检查是否有父级块元素(排除body) - let parent = s.parentElement; - while (parent && parent !== doc.body) { - // 如果父级元素也在nodeList中,说明当前元素是嵌套的 - if (nodeList.includes(parent)) { - return false; - } - parent = parent.parentElement; - } - return true; - }); - return visibleNode - .filter((item) => { var _a; return item.textContent !== "img" && !((_a = item.textContent) === null || _a === void 0 ? void 0 : _a.startsWith("img")); }) - .map((item) => item.textContent); -}; -const handleHighlightSearchNode = (text, style, doc) => { - // First remove any existing highlights - const existingHighlights = doc.querySelectorAll(`span[data-highlight="true"]`); - existingHighlights.forEach((highlight) => { - const parent = highlight.parentNode; - if (parent) { - parent.replaceChild(doc.createTextNode(highlight.textContent || ""), highlight); - } - }); - if (!text.trim()) - return; - // Get block elements and find those containing the target text - let nodeList = Array.from(doc.body.querySelectorAll("span, p, div, h1, h2, h3, h4, h5, h6 ")); - let nodes = nodeList.filter((node) => { - const content = node.textContent || ""; - return content.trim() && content.indexOf(text) > -1; - }); - // For the first matching node, highlight the text - if (nodes.length > 0) { - // Function to process text nodes - const processNode = (node) => { - var _a; - if (node.nodeType === Node.TEXT_NODE) { - const content = node.textContent || ""; - const index = content.indexOf(text); - if (index > -1) { - // Split the text node and insert the highlight - const before = content.substring(0, index); - const after = content.substring(index + text.length); - // Create span with the specified style - const highlightSpan = doc.createElement("span"); - highlightSpan.setAttribute("style", style); - highlightSpan.setAttribute("data-highlight", "true"); - highlightSpan.textContent = text; - // Replace the original text node with three new nodes - const fragment = doc.createDocumentFragment(); - if (before) - fragment.appendChild(doc.createTextNode(before)); - fragment.appendChild(highlightSpan); - if (after) - fragment.appendChild(doc.createTextNode(after)); - (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(fragment, node); - return true; // Text was found and highlighted - } - } - return false; // No match in this node - }; - // Process all child nodes recursively until we find a match - const walkAndProcess = (node) => { - if (processNode(node)) - return true; - // Process children if this node didn't contain the text - const childNodes = Array.from(node.childNodes); - for (const child of childNodes) { - if (walkAndProcess(child)) - return true; - } - return false; - }; - for (let i = 0; i < nodes.length; i++) { - walkAndProcess(nodes[i]); - } - } -}; -const handleHighlightAudioNode = (text, style, doc, element, readerMode) => { - // First remove any existing highlights - const existingHighlights = doc.querySelectorAll(`span[data-highlight="true"]`); - existingHighlights.forEach((highlight) => { - const parent = highlight.parentNode; - if (parent) { - parent.replaceChild(doc.createTextNode(highlight.textContent || ""), highlight); - } - }); - if (!text.trim()) - return; - // Get block elements and find those containing the target text - let nodeList = getBlockElement(doc.body).filter((s) => isScrolledIntoView(element, s, readerMode) && - (s.textContent || "").trim()); - let nodes = nodeList.filter((node) => { - const content = node.textContent || ""; - return content.trim() && content.indexOf(text) > -1; - }); - // For the first matching node, highlight the text - if (nodes.length > 0) { - // Function to process text nodes - const processNode = (node) => { - var _a; - if (node.nodeType === Node.TEXT_NODE) { - const content = node.textContent || ""; - const index = content.indexOf(text); - if (index > -1) { - // Split the text node and insert the highlight - const before = content.substring(0, index); - const after = content.substring(index + text.length); - // Create span with the specified style - const highlightSpan = doc.createElement("span"); - highlightSpan.setAttribute("style", style); - highlightSpan.setAttribute("data-highlight", "true"); - highlightSpan.textContent = text; - // Replace the original text node with three new nodes - const fragment = doc.createDocumentFragment(); - if (before) - fragment.appendChild(doc.createTextNode(before)); - fragment.appendChild(highlightSpan); - if (after) - fragment.appendChild(doc.createTextNode(after)); - (_a = node.parentNode) === null || _a === void 0 ? void 0 : _a.replaceChild(fragment, node); - return true; // Text was found and highlighted - } - } - return false; // No match in this node - }; - // Process all child nodes recursively until we find a match - const walkAndProcess = (node) => { - if (processNode(node)) - return true; - // Process children if this node didn't contain the text - const childNodes = Array.from(node.childNodes); - for (const child of childNodes) { - if (walkAndProcess(child)) - return true; - } - return false; - }; - walkAndProcess(nodes[0]); - } -}; -const getSearchResult = (keyword, chapterDocList) => __awaiter(void 0, void 0, void 0, function* () { - var _d; - let searchResult = []; - for (let i = 0; i < chapterDocList.length; i++) { - let chapterDoc = new DOMParser().parseFromString(yield handleOneChapterDoc(chapterDocList[i].text, true), "text/html"); - let nodeList = getBlockElement(chapterDoc.body).filter((item) => !isParentBlock(item)); - for (let j = 0; j < nodeList.length; j++) { - let keyWordIndex = (nodeList[j].textContent || "").indexOf(keyword); - if (keyWordIndex > -1) { - searchResult.push({ - excerpt: ((_d = nodeList[j].textContent) === null || _d === void 0 ? void 0 : _d.substring(keyWordIndex - 100, keyWordIndex + 100)) || "", - cfi: JSON.stringify({ - text: nodeList[j].textContent, - chapterTitle: chapterDocList[i].label, - chapterDocIndex: i, - chapterHref: chapterDocList[i].href, - count: "search", - percentage: i / chapterDocList.length, - keyword: keyword, - }), - }); - } - } - } - return _.uniq(searchResult, "excerpt"); -}); -const isParentBlock = (myDiv) => { - var children = myDiv.children; - let flag = false; - var blockRegex = /^(address|kookitmarker|section|blockquote|body|center|dir|div|dl|fieldset|form|h[1-6]|hr|isindex|menu|noframes|noscript|ol|p|pre|table|ul|dd|dt|frameset|li|tbody|td|tfoot|th|thead|tr|html)$/i; - let blockElementList = Array.from(children).filter((item) => blockRegex.test(item.nodeName)); - // some elements might contain image and image subtitle - if (blockElementList.length < 3) { - return false; - } - for (var i = 0; i < children.length; i++) { - if (blockRegex.test(children[i].nodeName)) { - flag = true; - break; - } - } - return flag; -}; -const isScrolledIntoView = (element, el, readerMode) => { - var isVisible = false; - var rect = el.getBoundingClientRect(); - if (readerMode !== "scroll" && el.textContent && el.textContent.trim()) { - let elemLeft = rect.left; - isVisible = elemLeft > -10 && elemLeft <= element.clientWidth; - } - else if (readerMode === "scroll" && - el.textContent && - el.textContent.trim()) { - let elemTop = rect.top; - isVisible = - elemTop >= element.scrollTop && - elemTop <= element.scrollTop + element.clientHeight; - } - else if (readerMode !== "scroll") { - let elemLeft = rect.left; - isVisible = elemLeft >= 0 && elemLeft <= element.clientWidth; - } - return isVisible; -}; - -class EventEmitter { - constructor() { - this.callbacks = {}; - this.callbacks.base = {}; - } - /** - * On - */ - on(_names, callback) { - const that = this; - // Errors - if (typeof _names === "undefined" || _names === "") { - console.warn("wrong names"); - return false; - } - if (typeof callback === "undefined") { - console.warn("wrong callback"); - return false; - } - // Resolve names - const names = this.resolveNames(_names); - // Each name - names.forEach(function (_name) { - // Resolve name - const name = that.resolveName(_name); - // Create namespace if not exist - if (!(that.callbacks[name.namespace] instanceof Object)) - that.callbacks[name.namespace] = {}; - // Create callback if not exist - if (!(that.callbacks[name.namespace][name.value] instanceof Array)) - that.callbacks[name.namespace][name.value] = []; - // Add callback - that.callbacks[name.namespace][name.value].push(callback); - }); - return this; - } - /** - * Off - */ - off(_names) { - const that = this; - // Errors - if (typeof _names === "undefined" || _names === "") { - console.warn("wrong name"); - return false; - } - // Resolve names - const names = this.resolveNames(_names); - // Each name - names.forEach(function (_name) { - // Resolve name - const name = that.resolveName(_name); - // Remove namespace - if (name.namespace !== "base" && name.value === "") { - delete that.callbacks[name.namespace]; - } - // Remove specific callback in namespace - else { - // Default - if (name.namespace === "base") { - // Try to remove from each namespace - for (const namespace in that.callbacks) { - if (that.callbacks[namespace] instanceof Object && - that.callbacks[namespace][name.value] instanceof Array) { - delete that.callbacks[namespace][name.value]; - // Remove namespace if empty - if (Object.keys(that.callbacks[namespace]).length === 0) - delete that.callbacks[namespace]; - } - } - } - // Specified namespace - else if (that.callbacks[name.namespace] instanceof Object && - that.callbacks[name.namespace][name.value] instanceof Array) { - delete that.callbacks[name.namespace][name.value]; - // Remove namespace if empty - if (Object.keys(that.callbacks[name.namespace]).length === 0) - delete that.callbacks[name.namespace]; - } - } - }); - return this; - } - /** - * Trigger - */ - trigger(_name, _args = []) { - // Errors - if (typeof _name === "undefined" || _name === "") { - console.warn("wrong name"); - return false; - } - const that = this; - let finalResult = null; - // Default args - const args = !(_args instanceof Array) ? [] : _args; - // Resolve names (should on have one event) - let name = this.resolveNames(_name); - // Resolve name - name = this.resolveName(name[0]); - setTimeout(() => { - if (name.namespace === "base") { - // Try to find callback in each namespace - for (const namespace in that.callbacks) { - if (that.callbacks[namespace] instanceof Object && - that.callbacks[namespace][name.value] instanceof Array && - that.callbacks[namespace][name.value]) { - that.callbacks[namespace][name.value].forEach(function (callback) { - callback.apply(that, args); - }); - } - else if (this.callbacks[name.namespace] instanceof Object && - that.callbacks[name.namespace][name.value]) { - if (name.value === "") { - console.warn("wrong name"); - return this; - } - that.callbacks[name.namespace][name.value].forEach(function (callback) { - callback.apply(that, args); - }); - } - return finalResult; - } - } - }, 100); - // Default namespace - // Specified namespace - } - /** - * Resolve names - */ - resolveNames(_names) { - let names = _names; - names = names.replace(/[^a-zA-Z0-9 ,/.]/g, ""); - names = names.replace(/[,/]+/g, " "); - names = names.split(" "); - return names; - } - /** - * Resolve name - */ - resolveName(name) { - const newName = {}; - const parts = name.split("."); - newName.original = name; - newName.value = parts[0]; - newName.namespace = "base"; // Base namespace - // Specified namespace - if (parts.length > 1 && parts[1] !== "") { - newName.namespace = parts[1]; - } - return newName; - } -} - -/** - * @see https://github.com/fread-ink/epub-cfi-resolver - * @latest a0d7e4e39d5b4adc9150e006e0b6d7af9513ae27 - */ -const ELEMENT_NODE = Node.ELEMENT_NODE; -const TEXT_NODE = Node.TEXT_NODE; -const CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE; -function cfiEscape(str) { - return str.replace(/[\[\]\^,();]/g, `^$&`); -} -// Get indices of all matches of regExp in str -// if `add` is non-null, add it to the matched indices -function matchAll(str, regExp, add) { - add = add || 0; - const matches = []; - let offset = 0; - let m; - do { - m = str.match(regExp); - if (!m) - break; - matches.push(m.index + add); - // @ts-ignore - offset += m.index + m.length; - // @ts-ignore - str = str.slice(m.index + m.length); - } while (offset < str.length); - return matches; -} -// Get the number in a that has the smallest diff to n -function closest(a, n) { - let minDiff; - let closest; - let i, diff; - for (i = 0; i < a.length; i++) { - diff = Math.abs(a[i] - n); - // @ts-ignore - if (!i || diff < minDiff) { - diff = minDiff; - closest = a[i]; - } - } - return closest; -} -// Given a set of nodes that are all children -// and a reference to one of those nodes -// calculate the count/index of the node -// according to the CFI spec. -// Also re-calculate offset if supplied and relevant -function calcSiblingCount(nodes, n, offset) { - let count = 0; - let lastWasElement; - let prevOffset = 0; - let firstNode = true; - let i, node; - for (i = 0; i < nodes.length; i++) { - node = nodes[i]; - // @ts-ignore - if (node.nodeType === ELEMENT_NODE) { - if (lastWasElement || firstNode) { - count += 2; - firstNode = false; - } - else { - count++; - } - // @ts-ignore - if (n === node) { - // @ts-ignore - if (node.tagName.toLowerCase() === `img`) { - return { count, offset }; - } - else { - return { count }; - } - } - prevOffset = 0; - lastWasElement = true; - } - else if ((node === null || node === void 0 ? void 0 : node.nodeType) === TEXT_NODE || - (node === null || node === void 0 ? void 0 : node.nodeType) === CDATA_SECTION_NODE) { - if (lastWasElement || firstNode) { - count++; - firstNode = false; - } - // @ts-ignore - if (n === node) { - return { count, offset: offset + prevOffset }; - } - // @ts-ignore - prevOffset += node.textContent.length; - lastWasElement = false; - } - else { - continue; - } - } - throw new Error(`The specified node was not found in the array of siblings`); -} -function compareTemporal(a, b) { - const isA = typeof a === `number`; - const isB = typeof b === `number`; - if (!isA && !isB) - return 0; - if (!isA && isB) - return -1; - if (isA && !isB) - return 1; - return (a || 0.0) - (b || 0.0); -} -function compareSpatial(a, b) { - if (!a && !b) - return 0; - if (!a && b) - return -1; - if (a && !b) - return 1; - const diff = (a.y || 0) - (b.y || 0); - if (diff) - return diff; - return (a.x || 0) - (b.x || 0); -} -class CFI { - constructor(str, opts) { - this.isRange = false; - this.opts = Object.assign({ - // If CFI is a Simple Range, pretend it isn't - // by parsing only the start of the range - flattenRange: false, - // Strip temporal, spatial, offset and textLocationAssertion - // from places where they don't make sense - stricter: true, - }, opts || {}); - this.cfi = str; - this.parts = []; - const isCFI = /^epubcfi\((.*)\)$/; - str = str.trim(); - const m = str.match(isCFI); - if (!m) - throw new Error(`Not a valid CFI`); - if (m.length < 2) - return; // Empty CFI - str = m[1] || ``; - let parsed, offset, newDoc; - let subParts = []; - let sawComma = 0; - while (str.length) { - ({ parsed, offset, newDoc } = this.parse(str)); - if (!parsed || offset === null) - throw new Error(`Parsing failed`); - if (sawComma && newDoc) - throw new Error(`CFI is a range that spans multiple documents. This is not allowed`); - subParts.push(parsed); - // Handle end of string - if (newDoc || str.length - offset <= 0) { - // Handle end if this was a range - if (sawComma === 2) { - // @ts-ignore - this.to = subParts; - } - else { - // not a range - this.parts.push(subParts); - } - subParts = []; - } - str = str.slice(offset); - // Handle Simple Ranges - if (str[0] === `,`) { - if (sawComma === 0) { - if (subParts.length) { - this.parts.push(subParts); - } - subParts = []; - } - else if (sawComma === 1) { - if (subParts.length) { - // @ts-ignore - this.from = subParts; - } - subParts = []; - } - str = str.slice(1); - sawComma++; - } - } - // @ts-ignore - if (this.from && this.from.length) { - // @ts-ignore - if (this.opts.flattenRange || !this.to || !this.to.length) { - // @ts-ignore - this.parts = this.parts.concat(this.from); - // @ts-ignore - delete this.from; - // @ts-ignore - delete this.to; - } - else { - this.isRange = true; - } - } - // @ts-ignore - if (this.opts.stricter) { - // @ts-ignore - this.removeIllegalOpts(); - } - } - removeIllegalOpts(parts) { - if (!parts) { - // @ts-ignore - if (this.from) { - // @ts-ignore - this.removeIllegalOpts(this.from); - // @ts-ignore - if (!this.to) - return; - // @ts-ignore - parts = this.to; - } - else { - parts = this.parts; - } - } - let i, j, part, subpart; - for (i = 0; i < parts.length; i++) { - part = parts[i]; - for (j = 0; j < part.length - 1; j++) { - subpart = part[j]; - delete subpart.temporal; - delete subpart.spatial; - delete subpart.offset; - delete subpart.textLocationAssertion; - } - } - } - static generatePart(node, offset, extra) { - let cfi = ``; - let o; - while (node.parentNode) { - // @ts-ignore - o = calcSiblingCount(node.parentNode.childNodes, node, offset); - if (!cfi && o.offset) - cfi = `:` + o.offset; - // @ts-ignore - cfi = - `/` + - o.count + - (node.id ? `[` + cfiEscape(node.id) + `]` : ``) + - cfi; - // debugger - node = node.parentNode; - } - return cfi; - } - static generate(node, offset, extra) { - let cfi; - if (Array.isArray(node)) { - const strs = []; - for (const o of node) { - strs.push(this.generatePart(o.node, o.offset, extra)); - } - cfi = strs.join(`!`); - } - else { - cfi = this.generatePart(node, offset, extra); - } - if (extra) - cfi += extra; - return `epubcfi(` + cfi + `)`; - } - static toParsed(cfi) { - if (cfi.isRange) { - return cfi.getFrom(); - } - else { - return cfi.get(); - } - } - // Takes two CFI paths and compares them - static comparePath(a, b) { - const max = Math.max(a.length, b.length); - let i, cA, cB, diff; - for (i = 0; i < max; i++) { - cA = a[i]; - cB = b[i]; - if (!cA) - return -1; - if (!cB) - return 1; - diff = this.compareParts(cA, cB); - if (diff) - return diff; - } - return 0; - } - // Sort an array of CFI objects - static sort(a) { - // @ts-ignore - a.sort((a, b) => { - return this.compare(a, b); - }); - } - // Takes two CFI objects and compares them. - static compare(a, b) { - let oA = a.get(); - let oB = b.get(); - if (a.isRange || b.isRange) { - if (a.isRange && b.isRange) { - const diff = this.comparePath(oA.from, oB.from); - if (diff) - return diff; - return this.comparePath(oA.to, oB.to); - } - if (a.isRange) - oA = oA.from; - if (b.isRange) - oB = oB.from; - return this.comparePath(oA, oB); - } - else { - // neither a nor b is a range - return this.comparePath(oA, oB); - } - } - // Takes two parsed path parts (assuming path is split on '!') and compares them. - static compareParts(a, b) { - const max = Math.max(a.length, b.length); - let i, cA, cB, diff; - for (i = 0; i < max; i++) { - cA = a[i]; - cB = b[i]; - if (!cA) - return -1; - if (!cB) - return 1; - diff = cA.nodeIndex - cB.nodeIndex; - if (diff) - return diff; - // The paths must be equal if the "before the first node" syntax is used - // and this must be the last subpart (assuming a valid CFI) - if (cA.nodeIndex === 0) { - return 0; - } - // Don't bother comparing offsets, temporals or spatials - // unless we're on the last element, since they're not - // supposed to be on elements other than the last - if (i < max - 1) - continue; - // Only compare spatials or temporals for element nodes - if (cA.nodeIndex % 2 === 0) { - diff = compareTemporal(cA.temporal, cB.temporal); - if (diff) - return diff; - diff = compareSpatial(cA.spatial, cB.spatial); - if (diff) - return diff; - } - diff = (cA.offset || 0) - (cB.offset || 0); - if (diff) - return diff; - } - return 0; - } - decodeEntities(dom, str) { - try { - const el = dom.createElement(`textarea`); - el.innerHTML = str; - return el.value || ``; - } - catch (err) { - // TODO fall back to simpler decode? - // e.g. regex match for stuff like   and   - return str; - } - } - // decode HTML/XML entities and compute length - trueLength(dom, str) { - return this.decodeEntities(dom, str).length; - } - getFrom() { - if (!this.isRange) - throw new Error(`Trying to get beginning of non-range CFI`); - // @ts-ignore - if (!this.from) { - return this.deepClone(this.parts); - } - const parts = this.deepClone(this.parts); - // @ts-ignore - parts[parts.length - 1] = parts[parts.length - 1].concat(this.from); - return parts; - } - getTo() { - if (!this.isRange) - throw new Error(`Trying to get end of non-range CFI`); - const parts = this.deepClone(this.parts); - // @ts-ignore - parts[parts.length - 1] = parts[parts.length - 1].concat(this.to); - return parts; - } - get() { - if (this.isRange) { - return { - from: this.getFrom(), - to: this.getTo(), - isRange: true, - }; - } - return this.deepClone(this.parts); - } - parseSideBias(o, loc) { - if (!loc) - return; - const m = loc.trim().match(/^(.*);s=([ba])$/); - if (!m || m.length < 3) { - if (typeof o.textLocationAssertion === `object`) { - o.textLocationAssertion.post = loc; - } - else { - o.textLocationAssertion = loc; - } - return; - } - if (m[1]) { - if (typeof o.textLocationAssertion === `object`) { - o.textLocationAssertion.post = m[1]; - } - else { - o.textLocationAssertion = m[1]; - } - } - if (m[2] === `a`) { - o.sideBias = `after`; - } - else { - o.sideBias = `before`; - } - } - parseSpatialRange(range) { - if (!range) - return undefined; - const m = range.trim().match(/^([\d\.]+):([\d\.]+)$/); - if (!m || m.length < 3) - return undefined; - const o = { - x: parseInt(m[1]), - y: parseInt(m[2]), - }; - if (typeof o.x !== `number` || typeof o.y !== `number`) { - return undefined; - } - return o; - } - parse(cfi) { - const o = {}; - const isNumber = /[\d]/; - let f; - let state; - let prevState; - let cur, escape; - let seenColon = false; - let seenSlash = false; - let i; - for (i = 0; i <= cfi.length; i++) { - if (i < cfi.length) { - cur = cfi[i]; - } - else { - cur = ``; - } - if (cur === `^` && !escape) { - escape = true; - continue; - } - if (state === `/`) { - if (cur.match(isNumber)) { - if (!f) { - f = cur; - } - else { - f += cur; - } - escape = false; - continue; - } - else { - if (f) { - // @ts-ignore - o.nodeIndex = parseInt(f); - f = null; - } - prevState = state; - state = null; - } - } - if (state === `:`) { - if (cur.match(isNumber)) { - if (!f) { - f = cur; - } - else { - f += cur; - } - escape = false; - continue; - } - else { - if (f) { - // @ts-ignore - o.offset = parseInt(f); - f = null; - } - prevState = state; - state = null; - } - } - if (state === `@`) { - let done = false; - if (cur.match(isNumber) || cur === `.` || cur === `:`) { - if (cur === `:`) { - if (!seenColon) { - seenColon = true; - } - else { - done = true; - } - } - } - else { - done = true; - } - if (!done) { - if (!f) { - f = cur; - } - else { - f += cur; - } - escape = false; - continue; - } - else { - prevState = state; - state = null; - // @ts-ignore - if (f && seenColon) - o.spatial = this.parseSpatialRange(f); - f = null; - } - } - if (state === `~`) { - if (cur.match(isNumber) || cur === `.`) { - if (!f) { - f = cur; - } - else { - f += cur; - } - escape = false; - continue; - } - else { - if (f) { - // @ts-ignore - o.temporal = parseFloat(f); - } - prevState = state; - state = null; - f = null; - } - } - if (!state) { - if (cur === `!`) { - i++; - state = cur; - break; - } - if (cur === `,`) { - break; - } - if (cur === `/`) { - if (seenSlash) { - break; - } - else { - seenSlash = true; - prevState = state; - state = cur; - escape = false; - continue; - } - } - if (cur === `:` || cur === `~` || cur === `@`) { - // @ts-ignore - if (this.opts.stricter) { - // We've already had a temporal or spatial indicator - // and offset does not make sense and the same time - // @ts-ignore - if (cur === `:` && - (typeof o.temporal !== `undefined` || - typeof o.spatial !== `undefined`)) { - break; - } - // We've already had an offset - // and temporal or spatial do not make sense at the same time - // @ts-ignore - if ((cur === `~` || cur === `@`) && - typeof o.offset !== `undefined`) { - break; - } - } - prevState = state; - state = cur; - escape = false; - seenColon = false; // only relevant for '@' - continue; - } - if (cur === `[` && !escape && prevState === `:`) { - prevState = state; - state = `[`; - escape = false; - continue; - } - if (cur === `[` && !escape && prevState === `/`) { - prevState = state; - state = `nodeID`; - escape = false; - continue; - } - } - if (state === `[`) { - if (cur === `]` && !escape) { - prevState = state; - state = null; - this.parseSideBias(o, f); - f = null; - } - else if (cur === `,` && !escape) { - // @ts-ignore - o.textLocationAssertion = {}; - if (f) { - // @ts-ignore - o.textLocationAssertion.pre = f; - } - f = null; - } - else { - if (!f) { - f = cur; - } - else { - f += cur; - } - } - escape = false; - continue; - } - if (state === `nodeID`) { - if (cur === `]` && !escape) { - prevState = state; - state = null; - // @ts-ignore - o.nodeID = f; - f = null; - } - else { - if (!f) { - f = cur; - } - else { - f += cur; - } - } - escape = false; - continue; - } - escape = false; - } - // @ts-ignore - if (!o.nodeIndex && o.nodeIndex !== 0) - throw new Error(`Missing child node index in CFI`); - return { parsed: o, offset: i, newDoc: state === `!` }; - } - // The CFI counts child nodes differently from the DOM - // Retrieve the child of parentNode at the specified index - // according to the CFI standard way of counting - getChildNodeByCFIIndex(dom, parentNode, index, offset) { - // console.log(`getChildNodeByCFIIndex`, { parentNode, index, offset }) - const children = parentNode.childNodes; - if (!children.length) - return { node: parentNode, offset: 0 }; - // index is pointing to the virtual node before the first node - // as defined in the CFI spec - if (index <= 0) { - return { node: children[0], relativeToNode: `before`, offset: 0 }; - } - let cfiCount = 0; - let lastChild; - let i, child; - // console.log(children, children.length) - for (i = 0; i < children.length; i++) { - child = children[i]; - // @ts-ignore - switch (child.nodeType) { - case ELEMENT_NODE: - // If the previous node was also an element node - // then we have to pretend there was a text node in between - // the current and previous nodes (according to the CFI spec) - // so we increment cfiCount by two - if (cfiCount % 2 === 0) { - cfiCount += 2; - if (cfiCount >= index) { - // @ts-ignore - if (child.tagName.toLowerCase() === `img` && offset) { - return { node: child, offset }; - } - return { node: child, offset: 0 }; - } - } - else { - // Previous node was a text node - cfiCount += 1; - if (cfiCount === index) { - // @ts-ignore - if (child.tagName.toLowerCase() === `img` && offset) { - return { node: child, offset }; - } - return { node: child, offset: 0 }; - // This happens when offset into the previous text node was greater - // than the number of characters in that text node - // So we return a position at the end of the previous text node - } - else if (cfiCount > index) { - if (!lastChild) { - return { node: parentNode, offset: 0 }; - } - // @ts-ignore - return { - node: lastChild, - offset: this.trueLength(dom, lastChild.textContent), - }; - } - } - lastChild = child; - break; - case TEXT_NODE: - case CDATA_SECTION_NODE: - // console.log('TEXT') - // If this is the first node or the previous node was an element node - if (cfiCount === 0 || cfiCount % 2 === 0) { - cfiCount += 1; - } - if (cfiCount === index) { - // If offset is greater than the length of the current text node - // then we assume that the next node will also be a text node - // and that we'll be combining them with the current node - // @ts-ignore - const trueLength = this.trueLength(dom, child.textContent); - if (offset >= trueLength) { - offset -= trueLength; - } - else { - return { node: child, offset: offset }; - } - } - lastChild = child; - break; - default: - continue; - } - } - // console.log(lastChild, index, cfiCount) - // index is pointing to the virtual node after the last child - // as defined in the CFI spec - if (index > cfiCount) { - const o = { relativeToNode: `after`, offset: 0 }; - if (!lastChild) { - // @ts-ignore - o.node = parentNode; - } - else { - // @ts-ignore - o.node = lastChild; - } - // @ts-ignore - if (this.isTextNode(o.node)) { - // @ts-ignore - o.offset = this.trueLength(dom, o.node.textContent.length); - } - return o; - } - } - isTextNode(node) { - if (!node) - return false; - if (node.nodeType === TEXT_NODE || node.nodeType === CDATA_SECTION_NODE) { - return true; - } - return false; - } - // Use a Text Location Assertion to correct and offset - correctOffset(dom, node, offset, assertion) { - let curNode = node; - let matchStr; - if (typeof assertion === `string`) { - matchStr = this.decodeEntities(dom, assertion); - } - else { - assertion.pre = this.decodeEntities(dom, assertion.pre); - assertion.post = this.decodeEntities(dom, assertion.post); - matchStr = assertion.pre + `.` + assertion.post; - } - if (!this.isTextNode(node)) { - return { node, offset: 0 }; - } - // @ts-ignore - while (this.isTextNode(curNode.previousSibling)) { - // @ts-ignore - curNode = curNode.previousSibling; - } - const startNode = curNode; - let str; - const nodeLengths = []; - let txt = ``; - let i = 0; - while (this.isTextNode(curNode)) { - // @ts-ignore - str = this.decodeEntities(dom, curNode.textContent); - nodeLengths[i] = str.length; - txt += str; - if (!curNode.nextSibling) - break; - // @ts-ignore - curNode = curNode.nextSibling; - i++; - } - // Find all matches to the Text Location Assertion - const matchOffset = assertion.pre ? assertion.pre.length : 0; - const m = matchAll(txt, new RegExp(matchStr), matchOffset); - if (!m.length) - return { node, offset }; - // Get the match that has the closest offset to the existing offset - let newOffset = closest(m, offset); - if (curNode === node && newOffset === offset) { - return { node, offset }; - } - i = 0; - curNode = startNode; - // @ts-ignore - while (newOffset >= nodeLengths[i]) { - // @ts-ignore - newOffset -= nodeLengths[i]; - if (newOffset < 0) - return { node, offset }; - const nodeOffsets = []; // added because original code has nodeOffsets undefined. @see https://github.com/fread-ink/epub-cfi-resolver/blob/master/index.js#L826 - if (!curNode.nextSibling || i + 1 >= nodeOffsets.length) - return { node, offset }; - i++; - // @ts-ignore - curNode = curNode.nextSibling; - } - return { node: curNode, offset: newOffset }; - } - resolveNode(index, subparts, dom, opts) { - opts = Object.assign({}, opts || {}); - if (!dom) - throw new Error(`Missing DOM argument`); - // Traverse backwards until a subpart with a valid ID is found - // or the first subpart is reached - let startNode; - if (index === 0) { - startNode = dom.querySelector(`package`); - } - if (!startNode) { - for (const n of dom.childNodes) { - if (n.nodeType === ELEMENT_NODE) { - // if (n.nodeType === Node.DOCUMENT_NODE) { - startNode = n; - break; - } - } - } - // custom - startNode = dom; - // debugger - if (!startNode) - throw new Error(`Document incompatible with CFIs`); - let node = startNode; - let startFrom = 0; - let i; - let subpart; - for (i = subparts.length - 1; i >= 0; i--) { - subpart = subparts[i]; - // @ts-ignore - if (!opts.ignoreIDs && - subpart.nodeID && - (node = dom.getElementById(subpart.nodeID))) { - startFrom = i + 1; - break; - } - } - // console.log(startNode, startFrom) - if (!node) { - node = startNode; - } - let o = { node, offset: 0 }; - for (i = startFrom; i < subparts.length; i++) { - subpart = subparts[i]; - if (subpart) { - // console.log(o, dom, o.node, subpart.nodeIndex, subpart.offset) - // @ts-ignore - o = this.getChildNodeByCFIIndex(dom, o.node, subpart.nodeIndex, subpart.offset); - // @ts-ignore - if (subpart.textLocationAssertion) { - // console.log(subparts, subpart, o) - // @ts-ignore - o = this.correctOffset(dom, o.node, subpart.offset, subpart.textLocationAssertion); - } - } - } - return o; - } - // Each part of a CFI (as separated by '!') - // references a separate HTML/XHTML/XML document. - // This function takes an index specifying the part - // of the CFI and the appropriate Document or XMLDocument - // that is referenced by the specified part of the CFI - // and returns the URI for the document referenced by - // the next part of the CFI - // If the opt `ignoreIDs` is true then IDs - // will not be used while resolving - resolveURI(index, dom, opts) { - opts = opts || {}; - if (index < 0 || index > this.parts.length - 2) { - throw new Error(`index is out of bounds`); - } - const subparts = this.parts[index]; - if (!subparts) - throw new Error(`Missing CFI part for index: ` + index); - // @ts-ignore - const o = this.resolveNode(index, subparts, dom, opts); - // debugger - let node = o.node; - // @ts-ignore - const tagName = node.tagName.toLowerCase(); - if (tagName === `itemref` && - // @ts-ignore - node.parentNode.tagName.toLowerCase() === `spine`) { - // @ts-ignore - const idref = node.getAttribute(`idref`); - if (!idref) - throw new Error(`Referenced node had not 'idref' attribute`); - // @ts-ignore - node = dom.getElementById(idref); - if (!node) - throw new Error(`Specified node is missing from manifest`); - // @ts-ignore - const href = node.getAttribute(`href`); - if (!href) - throw new Error(`Manifest item is missing href attribute`); - return href; - } - if (tagName === `iframe` || tagName === `embed`) { - // @ts-ignore - const src = node.getAttribute(`src`); - if (!src) - throw new Error(tagName + ` element is missing 'src' attribute`); - return src; - } - if (tagName === `object`) { - // @ts-ignore - const data = node.getAttribute(`data`); - if (!data) - throw new Error(tagName + ` element is missing 'data' attribute`); - return data; - } - if (tagName === `image` || tagName === `use`) { - // @ts-ignore - const href = node.getAttribute(`xlink:href`); - if (!href) - throw new Error(tagName + ` element is missing 'xlink:href' attribute`); - return href; - } - throw new Error(`No URI found`); - } - deepClone(o) { - return JSON.parse(JSON.stringify(o)); - } - resolveLocation(dom, parts) { - const index = parts.length - 1; - const subparts = parts[index]; - if (!subparts) - throw new Error(`Missing CFI part for index: ` + index); - // @ts-ignore - const o = this.resolveNode(index, subparts, dom); - // @ts-ignore - const lastPart = this.deepClone(subparts[subparts.length - 1]); - delete lastPart.nodeIndex; - // @ts-ignore - if (!lastPart.offset) - delete o.offset; - return Object.assign(Object.assign({}, lastPart), o); - } - // Takes the Document or XMLDocument for the final - // document referenced by the CFI - // and returns the node and offset into that node - resolveLast(dom, opts) { - opts = Object.assign({ - range: false, - }, opts || {}); - if (!this.isRange) { - return this.resolveLocation(dom, this.parts); - } - // @ts-ignore - if (opts.range) { - const range = dom.createRange(); - const from = this.getFrom(); - if (from.relativeToNode === `before`) { - // @ts-ignore - range.setStartBefore(from.node, from.offset); - } - else if (from.relativeToNode === `after`) { - // @ts-ignore - range.setStartAfter(from.node, from.offset); - } - else { - range.setStart(from.node, from.offset); - } - const to = this.getTo(); - if (to.relativeToNode === `before`) { - // @ts-ignore - range.setEndBefore(to.node, to.offset); - } - else if (to.relativeToNode === `after`) { - // @ts-ignore - range.setEndAfter(to.node, to.offset); - } - else { - range.setEnd(to.node, to.offset); - } - return range; - } - return { - from: this.resolveLocation(dom, this.getFrom()), - to: this.resolveLocation(dom, this.getTo()), - isRange: true, - }; - } - resolve(doc, opts) { - // @ts-ignore - return this.resolveLast(doc, opts); - } -} - -const classes = [ - "color-0", - "color-1", - "color-2", - "color-3", - "line-0", - "line-1", - "line-2", - "line-3", -]; -const colors = ["#FEF3CD", "#FBFACC", "#CEFACD", "#CDE9FA"]; -const lines = ["#FF0000", "#000080", "#0000FF", "#2EFF2E"]; -const pdfColors = ["#fac106", "#ebe702", "#0be603", "#0493e6"]; -const showNoteHighlight = (range, colorIndex, noteKey, handleNoteClick, doc, iframe) => { - var _a, _b; - let colorCode = classes[colorIndex]; - let iWin = iframe.contentWindow || ((_a = iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.defaultView); - let temp = range; - temp = [temp]; - // sleep(500); - let selection = rangy.getSelection(iframe); - selection.restoreCharacterRanges(doc, temp); - let newRange = selection.getRangeAt(0); - highlightRange(newRange, colorCode, noteKey, handleNoteClick, doc); - if (!iWin || !iWin.getSelection()) - return; - (_b = iWin.getSelection()) === null || _b === void 0 ? void 0 : _b.empty(); -}; -const showPDFHighlight = (selected, colorIndex, noteKey, handleNoteClick, page, scale, doc) => { - let colorCode = classes[colorIndex]; - let pageElement = doc.querySelector(".noteLayer"); - let docLayer = doc.querySelector("#koodoPDFLayer"); - var viewport = page.getViewport({ scale: scale }); - let rects = []; - //convertToViewportRectangle - for (let i = 0; i < selected.coords.length; i++) { - const rect = selected.coords[i]; - var bounds = viewport.convertToViewportRectangle(rect); - let width = Math.abs(bounds[0] - bounds[2]); - let height = Math.abs(bounds[1] - bounds[3]); - let top = Math.min(bounds[1], bounds[3]); - let left = Math.min(bounds[0], bounds[2]); - let bottom = top + height; - let right = left + width; - if (Math.abs(height - viewport.height) < 10 || - Math.abs(width - viewport.width) < 10 || - width === 0 || - height === 0) { - continue; - } - rects.push({ width, height, top, left, bottom, right }); - } - //获取最小的高度 - let minHeight = 10000; - rects.forEach((rect) => { - if (rect.height < minHeight) { - minHeight = rect.height; - } - }); - // 按宽度从小到大排序 - const sortedRects = rects.sort((a, b) => a.width - b.width); - // 去除bottom相差小于5且宽度更小的rect,保留宽度最大的rect - const filteredRects = []; - for (let i = 0; i < sortedRects.length; i++) { - const currentRect = sortedRects[i]; - const currentBottom = currentRect.bottom; - // 检查是否有bottom相差小于5且宽度更大的rect - const hasSimilarBottomWithLargerWidth = sortedRects.some((otherRect, otherIndex) => { - if (otherIndex === i) - return false; - const otherBottom = otherRect.bottom; - return (Math.abs(currentBottom - otherBottom) < minHeight && - ((otherRect.left <= currentRect.left && - otherRect.right >= currentRect.right) || - (otherRect.left <= currentRect.left && - Math.abs(otherRect.right - currentRect.right) < 5) || - (Math.abs(otherRect.left - currentRect.left) < 5 && - otherRect.right >= currentRect.right))); - }); - // 如果没有找到bottom相差小于5且宽度更大的rect,则保留当前rect - if (!hasSimilarBottomWithLargerWidth) { - filteredRects.push(currentRect); - } - } - for (let i = 0; i < filteredRects.length; i++) { - const rect = filteredRects[i]; - var newNode = document.createElement("div"); - if (!docLayer) { - continue; - } - newNode === null || newNode === void 0 ? void 0 : newNode.setAttribute("style", "position: absolute;" + - (colorCode.indexOf("color") > -1 - ? "background-color: " - : "border-bottom: ") + - (colorCode.indexOf("color") > -1 - ? pdfColors[colorCode.split("-")[1]] - : `2px solid ${lines[colorCode.split("-")[1]]}`) + - "; left:" + - (rect.left + parseFloat(getComputedStyle(docLayer).marginLeft)) + - "px; top:" + - rect.top + - "px;" + - "width:" + - rect.width + - "px; height:" + - rect.height + - "px; z-index: 1; cursor: pointer; opacity: " + - (colorCode.indexOf("color") > -1 ? 0.3 : 1) + - ";"); - newNode === null || newNode === void 0 ? void 0 : newNode.setAttribute("data-key", noteKey); - newNode === null || newNode === void 0 ? void 0 : newNode.setAttribute("class", "kookit-note"); - newNode === null || newNode === void 0 ? void 0 : newNode.addEventListener("click", (event) => { - if (event && event.target) { - if (event.target.dataset && - event.target.dataset.key) { - handleNoteClick(event); - } - } - }); - newNode.ontouchend = (event) => { - if (window.isSwiping) { - return; - } - if (event && event.target) { - if (event.target.dataset && - event.target.dataset.key) { - handleNoteClick(event); - } - } - event.preventDefault(); - event.stopPropagation(); - }; - pageElement.appendChild(newNode); - } -}; -const clearHighlight = (doc) => { - const elements = doc.querySelectorAll(".kookit-note"); - for (let index = 0; index < elements.length; index++) { - const element = elements[index]; - element.parentNode.removeChild(element); - } -}; -const highlightRange = (range, colorCode, noteKey, handleNoteClick, doc) => { - const rects = range.nativeRange.getClientRects(); - const validRects = []; - // 将rects转换为数组并按宽度从小到大排序 - const sortedRects = Array.from(rects).sort((a, b) => a.width - b.width); - // 获取所有rects中的最大宽度 - const maxWidth = sortedRects.length - ? Math.max(...Array.from(rects).map((rect) => rect.width)) - : 0; - // 过滤重复和无效的矩形 - for (let index = 0; index < sortedRects.length; index++) { - const rect = sortedRects[index]; - // 过滤掉宽度或高度为0的矩形 - if (rect.width <= 0 || rect.height <= 0) { - continue; - } - // 检查是否与已有矩形重叠 - const isOverlapping = validRects.some((validRect) => { - return (Math.abs(rect.bottom - validRect.bottom) < 5 && rect.width === maxWidth); - }); - if (!isOverlapping) { - validRects.push(rect); - } - } - for (let index = 0; index < validRects.length; index++) { - const rect = validRects[index]; - var newNode = document.createElement("span"); - newNode === null || newNode === void 0 ? void 0 : newNode.setAttribute("style", "position: absolute;" + - (colorCode.indexOf("color") > -1 - ? "background-color: " - : "border-bottom: ") + - (colorCode.indexOf("color") > -1 - ? colors[colorCode.split("-")[1]] + ";opacity: 1" - : `2px solid ${lines[colorCode.split("-")[1]]}`) + - ";left:" + - (Math.min(rect.left, rect.x) + doc.body.scrollLeft) + - "px; top:" + - (Math.min(rect.top, rect.y) + doc.body.scrollTop) + - "px;" + - "width:" + - rect.width + - "px; height:" + - rect.height + - "px; z-index:-1;opacity: " + - (colorCode.indexOf("color") > -1 ? 0.8 : 1) + - "; cursor: pointer;"); - newNode.setAttribute("class", " kookit-note"); - newNode.setAttribute("data-key", noteKey); - // newNode.setAttribute("onclick", `window.handleNoteClick()`); - doc.body.appendChild(newNode); - var clickNode = document.createElement("span"); - clickNode === null || clickNode === void 0 ? void 0 : clickNode.setAttribute("style", "position: absolute;" + - "left:" + - (Math.min(rect.left, rect.x) + doc.body.scrollLeft) + - "px; top:" + - (Math.min(rect.top, rect.y) + doc.body.scrollTop) + - "px;" + - "width:" + - rect.width + - "px; height:" + - rect.height + - "px; z-index:1;"); - clickNode.setAttribute("class", " kookit-note"); - clickNode.setAttribute("data-key", noteKey); - clickNode.addEventListener("click", (event) => { - if (event && event.target) { - if (event.target.dataset && - event.target.dataset.key) { - handleNoteClick(event); - } - } - }); - clickNode.ontouchend = (event) => { - if (window.isSwiping) { - return; - } - if (event && event.target) { - if (event.target.dataset && - event.target.dataset.key) { - handleNoteClick(event); - } - } - event.preventDefault(); - event.stopPropagation(); - }; - doc.body.appendChild(clickNode); - } -}; - -function createBookElement(sectionCount) { - let tempDiv = document.getElementById("book"); - if (tempDiv) { - tempDiv.remove(); - } - // Create the book div - const bookDiv = document.createElement("div"); - bookDiv.id = "book"; - // Create the canvas element - const canvas = document.createElement("canvas"); - canvas.id = "pageflip-canvas"; - // Create the pages div - const pagesDiv = document.createElement("div"); - pagesDiv.id = "pages"; - // Create the section elements based on sectionCount - for (let i = 0; i < sectionCount; i++) { - const section = document.createElement("section"); - pagesDiv.appendChild(section); - } - // Append the canvas and pages div to the book div - bookDiv.appendChild(canvas); - bookDiv.appendChild(pagesDiv); - // Append the book div to the body - document.body.appendChild(bookDiv); - let css = ` - #book { - position: fixed; - width: 200vw; - height: 100vh; - left: -100vw; - top: 0vh; - float: left; - margin: 0; - display: none; - } - - #pages section { - display: block; - width: 100vw; - height: 100vh; - position: absolute; - left: 100vw; - top: 0px; - overflow: hidden; - margin: 0; - } - - #pageflip-canvas { - position: absolute; - z-index: 100; - margin: 0; - } - `; - let style = document.createElement("style"); - style.innerHTML = css; - document.head.appendChild(style); -} -const addPageAnimation = (totalPage, isDarkMode, backgroundColor) => { - // Example usage: create a book element with 3 sections - createBookElement(totalPage + 1); - var WINDOW_WIDTH = window.innerWidth; - var WINDOW_HEIGHT = window.innerHeight; - // The canvas size equals to the book dimensions + this padding - var CANVAS_PADDING = 0; - // Dimensions of the whole book - var BOOK_WIDTH = 2 * WINDOW_WIDTH - CANVAS_PADDING; - var BOOK_HEIGHT = WINDOW_HEIGHT - CANVAS_PADDING; - // Dimensions of one page in the book - var PAGE_WIDTH = WINDOW_WIDTH; - var PAGE_HEIGHT = WINDOW_HEIGHT; - var touchStartX = 0; - var touchEndX = 0; - // Vertical spacing between the top edge of the book and the papers - var PAGE_Y = (BOOK_HEIGHT - PAGE_HEIGHT) / 2; - var pageNum = 0; - var canvas = document.getElementById("pageflip-canvas"); - if (!canvas) - return; - var context = canvas.getContext("2d"); - var mouse = { x: 0, y: 0 }; - var flips = []; - var book = document.getElementById("book"); - if (!book) - return; - // List of all the page elements in the DOM - var pages = book.getElementsByTagName("section"); - // Organize the depth of our pages and create the flip definitions - for (var i = 0, len = pages.length; i < len; i++) { - pages[i].style.zIndex = len - i + ""; - flips.push({ - // Current progress of the flip (left -1 to right +1) - progress: 1, - // The target value towards which progress is always moving - target: 1, - // The page DOM element related to this flip - page: pages[i], - // True while the page is being dragged - dragging: false, - }); - } - // Resize the canvas to match the book size - canvas.width = BOOK_WIDTH + CANVAS_PADDING * 2; - canvas.height = BOOK_HEIGHT + CANVAS_PADDING * 2; - // Offset the canvas so that it's padding is evenly spread around the book - canvas.style.top = -CANVAS_PADDING + "px"; - canvas.style.left = -CANVAS_PADDING + "px"; - // Render the page flip 60 times a second - setInterval(render, 1800 / 60); - document.addEventListener("mousemove", mouseMoveHandler, false); - document.addEventListener("mousedown", mouseDownHandler, false); - document.addEventListener("mouseup", mouseUpHandler, false); - book.addEventListener("touchmove", mouseMoveHandler, false); - book.addEventListener("touchstart", mouseDownHandler, false); - book.addEventListener("touchend", mouseUpHandler, false); - function mouseMoveHandler(event) { - if (!book) - return; - // Offset mouse position so that the top of the spine is 0,0 - // mouse.x = event.clientX - book.offsetLeft - BOOK_WIDTH / 2; - // mouse.y = event.clientY - book.offsetTop; - const touch = event.touches[0]; - const touchCurrentX = touch.screenX; - const touchCurrentY = touch.screenY; - mouse.x = touchCurrentX - book.offsetLeft - BOOK_WIDTH / 2; - mouse.y = touchCurrentY - book.offsetTop; - } - function mouseDownHandler(event) { - const touch = event.touches[0]; - // flips[page].dragging = true; - touchStartX = touch.screenX; - if (touch.screenX < window.screen.width / 2 && pageNum - 1 >= 0) { - flips[pageNum - 1].dragging = true; - } - else if (touch.screenX > window.screen.width / 2 && - pageNum + 1 < flips.length) { - flips[pageNum].dragging = true; - } - // Prevents the text selection cursor from appearing when dragging - event.preventDefault(); - } - function mouseUpHandler(event) { - const touch = event.changedTouches[0]; - touchEndX = touch.screenX; - for (var i = 0; i < flips.length; i++) { - // If this flip was being dragged we animate to its destination - if (flips[i].dragging) { - // Figure out which page we should go to next depending on the flip direction - if (mouse.x < (PAGE_WIDTH / 4) * 3 && touchEndX - touchStartX < 0) { - flips[i].target = -1; - pageNum = Math.min(pageNum + 1, flips.length); - } - else if (mouse.x > (PAGE_WIDTH / 4) * 1 && - touchEndX - touchStartX > 0) { - flips[i].target = 1; - pageNum = Math.max(pageNum - 1, 0); - } - else { - //实现当不满足以上条件时将拖拽的页面恢复到原来的位置 - if (i === pageNum) { - // Page was being dragged forward attempt - flips[i].target = 1; - } - else if (i === pageNum - 1) { - // Page was being dragged backward attempt - flips[i].target = -1; - } - } - } - flips[i].dragging = false; - } - } - function render() { - context.clearRect(0, 0, canvas.width, canvas.height); - for (var i = 0; i < flips.length; i++) { - var flip = flips[i]; - if (flip.dragging) { - flip.target = Math.max(Math.min(mouse.x / PAGE_WIDTH, 1), -1); - } - flip.progress += (flip.target - flip.progress) * 0.2; - // If the flip is being dragged or is somewhere in the middle of the book, render it - if (flip.dragging || Math.abs(flip.progress) < 0.997) { - drawFlip(flip); - } - } - } - function drawFlip(flip) { - // Strength of the fold is strongest in the middle of the book - var strength = 1 - Math.abs(flip.progress); - // Width of the folded paper - var foldWidth = PAGE_WIDTH * 0.5 * (1 - flip.progress); - // X position of the folded paper - var foldX = PAGE_WIDTH * flip.progress + foldWidth; - // How far the page should outdent vertically due to perspective - var verticalOutdent = 20 * strength; - // The maximum width of the left and right side shadows - var paperShadowWidth = PAGE_WIDTH * 0.5 * Math.max(Math.min(1 - flip.progress, 0.5), 0); - var rightShadowWidth = PAGE_WIDTH * 0.5 * Math.max(Math.min(strength, 0.5), 0); - var leftShadowWidth = PAGE_WIDTH * 0.5 * Math.max(Math.min(strength, 0.5), 0); - // Change page element width to match the x position of the fold - flip.page.style.width = Math.max(foldX, 0) + "px"; - context.save(); - context.translate(CANVAS_PADDING + BOOK_WIDTH / 2, PAGE_Y + CANVAS_PADDING); - // Draw a sharp shadow on the left side of the page - context.strokeStyle = - (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + - 0.05 * strength + - ")"; - context.lineWidth = 30 * strength; - context.beginPath(); - context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5); - context.lineTo(foldX - foldWidth, PAGE_HEIGHT + verticalOutdent * 0.5); - context.stroke(); - // Right side drop shadow - var rightShadowGradient = context.createLinearGradient(foldX, 0, foldX + rightShadowWidth, 0); - rightShadowGradient.addColorStop(0, (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + - strength * 0.2 + - ")"); - rightShadowGradient.addColorStop(0.8, (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + "0.0)"); - context.fillStyle = rightShadowGradient; - context.beginPath(); - context.moveTo(foldX, 0); - context.lineTo(foldX + rightShadowWidth, 0); - context.lineTo(foldX + rightShadowWidth, PAGE_HEIGHT); - context.lineTo(foldX, PAGE_HEIGHT); - context.fill(); - // Left side drop shadow - var leftShadowGradient = context.createLinearGradient(foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0); - leftShadowGradient.addColorStop(0, (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + "0.0)"); - leftShadowGradient.addColorStop(1, (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + - +strength * 0.15 + - ")"); - context.fillStyle = leftShadowGradient; - context.beginPath(); - context.moveTo(foldX - foldWidth - leftShadowWidth, 0); - context.lineTo(foldX - foldWidth, 0); - context.lineTo(foldX - foldWidth, PAGE_HEIGHT); - context.lineTo(foldX - foldWidth - leftShadowWidth, PAGE_HEIGHT); - context.fill(); - // Gradient applied to the folded paper (highlights & shadows) - var foldGradient = context.createLinearGradient(foldX - paperShadowWidth, 0, foldX, 0); - if (backgroundColor) { - foldGradient.addColorStop(0.35, backgroundColor); - foldGradient.addColorStop(0.73, backgroundColor); - foldGradient.addColorStop(0.9, backgroundColor); - foldGradient.addColorStop(1.0, backgroundColor); - } - else if (isDarkMode === "no") { - foldGradient.addColorStop(0.35, "#fafafa"); - foldGradient.addColorStop(0.73, "#eeeeee"); - foldGradient.addColorStop(0.9, "#fafafa"); - foldGradient.addColorStop(1.0, "#e2e2e2"); - } - else { - foldGradient.addColorStop(0.35, "#333"); - foldGradient.addColorStop(0.73, "#444"); - foldGradient.addColorStop(0.9, "#333"); - foldGradient.addColorStop(1.0, "#444"); - } - context.fillStyle = foldGradient; - context.strokeStyle = - (isDarkMode === "no" ? "rgba(0,0,0," : "rgba(255,255,255,") + "0.06)"; - context.lineWidth = 0.5; - // Draw the folded piece of paper - context.beginPath(); - context.moveTo(foldX, 0); - context.lineTo(foldX, PAGE_HEIGHT); - context.quadraticCurveTo(foldX, PAGE_HEIGHT + verticalOutdent * 2, foldX - foldWidth, PAGE_HEIGHT + verticalOutdent); - context.lineTo(foldX - foldWidth, -verticalOutdent); - context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0); - context.fill(); - context.stroke(); - context.restore(); - } - return { - flipToNextPage: () => { - if (pageNum + 1 < flips.length) { - flips[pageNum].target = -1; - pageNum = Math.min(pageNum + 1, flips.length); - } - }, - flipToPrevPage: () => { - if (pageNum - 1 >= 0) { - flips[pageNum - 1].target = 1; - pageNum = Math.max(pageNum - 1, 0); - } - }, - mouseDownHandler, - mouseUpHandler, - mouseMoveHandler, - }; -}; - -const getPdfScale = (element, readerMode, chapterDocList, chapterDocIndex, doc) => __awaiter(void 0, void 0, void 0, function* () { - let { width, height } = yield chapterDocList[chapterDocIndex].text.getDimension(); - let viewWidth = doc.body.clientWidth; - let viewHeight = element.clientHeight; - let scale = Math.min(viewWidth / width, viewHeight / height); - if (readerMode === "scroll") { - scale = viewWidth / width; - } - return scale; -}); -const handlePDFLayout = (element, readerMode, doc) => { - if (readerMode === "scroll") - return; - let scale = readerMode === "double" ? 2 : 1; - let section = Math.floor(doc.body.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - doc.body.setAttribute("style", element.getAttribute("style") + - `height: 100%;overflow-y: hidden;overflow-X: hidden;padding-left: 0px;padding-right: 0px;margin: 0px;box-sizing: border-box;touch-action: manipulation; overscroll-behavior: none;max-width: inherit;column-fill: auto;column-gap: ${gap}px; column-width: ${(doc.body.clientWidth - gap) / scale}px;`); -}; -const createPDFContainer = (element, chapterDocList, viewport, readerMode) => { - for (let index = 0; index < chapterDocList.length; index++) { - // Create container with aspect ratio - const iframeContainer = document.createElement("div"); - iframeContainer.style.position = "relative"; - iframeContainer.style.width = "100%"; - iframeContainer.id = "pdf-container-" + index; - iframeContainer.className = "pdf-container"; - if (readerMode === "single") { - iframeContainer.style.paddingTop = element.clientHeight + "px"; - } - else { - // Set aspect ratio based on PDF page dimensions - const aspectRatio = (viewport === null || viewport === void 0 ? void 0 : viewport.width) / (viewport === null || viewport === void 0 ? void 0 : viewport.height) || 0.75; // Default to 3:4 if viewport unknown - iframeContainer.style.paddingTop = `${(1 / aspectRatio) * 100}%`; - } - if (readerMode === "double") { - //break-inside: avoid; - iframeContainer.style.breakInside = "avoid"; - } - element.appendChild(iframeContainer); - } -}; -const createPDFIframe = (chapterDocIndex, doc) => { - var _a; - const iframeContainer = doc.getElementById("pdf-container-" + chapterDocIndex); - if (!iframeContainer) - return; - // Create iframe with absolute positioning - let iframe = document.createElement("iframe"); - iframe.style.position = "absolute"; - iframe.style.top = "0"; - iframe.style.left = "0"; - iframe.style.width = "100%"; - iframe.style.height = "100%"; - iframe.style.border = "0"; - iframe.style.margin = "0"; - iframe.style.padding = "0"; - iframe.style.fontSize = "100%"; - iframe.style.font = "inherit"; - iframe.scrolling = "no"; - iframe.tabIndex = 0; - iframe.id = "pdf-iframe-" + chapterDocIndex; - // Add style element - let style = document.createElement("style"); - style.id = "default-style"; - style.textContent = - "p,empty-line{display: inherit;margin-block-start: inherit;margin-block-end: inherit;margin-inline-start: inherit;margin-inline-end: inherit;}body{margin: 0px}"; - // Append iframe to container, then container to parent - iframeContainer.appendChild(iframe); - // Add style to iframe after it's in the DOM - (_a = iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.head.appendChild(style); - return iframe; -}; -const handleScrollPDFPosition = (chapterDocIndex, readerMode, doc) => __awaiter(void 0, void 0, void 0, function* () { - let targetNode = doc.getElementById("pdf-container-" + chapterDocIndex); - if (!targetNode) - return; - if (readerMode !== "scroll") { - let left = targetNode - ? convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)) - : 0; - doc.body.scrollTo(left, 0); - } - else { - targetNode.scrollIntoView(); - } - targetNode.scrollIntoView(); -}); -const isPDFScrolledIntoView = (element, el, readerMode, doc) => { - var isVisible = false; - var rect = el.getBoundingClientRect(); - if (readerMode !== "scroll") { - let elemLeft = rect.left; - isVisible = elemLeft > -10 && elemLeft <= doc.body.clientWidth; - } - else { - let elemTop = rect.top; - let elemBottom = rect.bottom; - isVisible = - (elemTop - 10 >= element.scrollTop && - elemTop + 10 <= element.scrollTop + element.clientHeight) || - (elemBottom - 10 >= element.scrollTop && - elemBottom + 10 <= element.scrollTop + element.clientHeight) || - (elemTop + 10 <= element.scrollTop && - elemBottom - 10 >= element.scrollTop + element.clientHeight); - } - return isVisible; -}; -const getPDFVisibleText = (chapterDocIndex, chapterDocList, readerMode) => __awaiter(void 0, void 0, void 0, function* () { - let textContent = yield chapterDocList[chapterDocIndex].text.getTextContent(); - let textList = textContent.items.map((item) => { - return item.str; - }); - if (readerMode === "double") { - let nextTextContent = yield chapterDocList[chapterDocIndex + 1].text.getTextContent(); - let nextTextList = nextTextContent.items.map((item) => { - return item.str; - }); - textList = textList.concat(nextTextList); - } - return textList; -}); -const handleHighlightPDFNode = (text, style, doc) => { - // First remove any existing highlights - const existingHighlights = doc.querySelectorAll(`span[data-highlight="true"]`); - existingHighlights.forEach((highlight) => { - // Remove only background style from the element - const style = highlight.getAttribute("style") || ""; - // Remove background or background-color properties using regex - const newStyle = style - .replace(/background(?:-color)?\s*:[^;]+;?/gi, "") - .trim(); - if (newStyle) { - highlight.setAttribute("style", newStyle); - } - else { - highlight.removeAttribute("style"); - } - highlight.removeAttribute("data-highlight"); - }); - if (!text.trim()) - return; - let nodeList = doc.querySelectorAll("p,span"); - let nodes = Array.from(nodeList).filter((s, index) => { - return ((s.textContent || "").trim() && - s.textContent === text); - }); - if (nodes.length > 0) { - nodes[0].setAttribute("style", (nodes[0].getAttribute("style") || "") + style); - nodes[0].setAttribute("data-highlight", "true"); - } -}; -const getPDFSearchResult = (keyword, chapterDocList) => __awaiter(void 0, void 0, void 0, function* () { - let searchResult = []; - for (let i = 0; i < chapterDocList.length; i++) { - let textContent = yield chapterDocList[i].text.getTextContent(); - textContent.items.forEach((item, itemIndex) => { - if (item.str.indexOf(keyword) > -1) { - searchResult.push({ - excerpt: item.str, - cfi: JSON.stringify({ - text: item.str + "#" + i + "#" + itemIndex, - chapterTitle: chapterDocList[i].label, - chapterDocIndex: i, - chapterHref: chapterDocList[i].href, - count: "search", - percentage: i / chapterDocList.length, - keyword: keyword, - }), - }); - } - }); - } - return _.uniq(searchResult, "excerpt"); -}); -const handleIOSScrollPage = (element, animation, delta, doc, flipToNextPage, flipToPrevPage, isMobile, chapterDocIndex, readerMode) => __awaiter(void 0, void 0, void 0, function* () { - let section = Math.floor(doc.body.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - const width = doc.body.clientWidth; - if (animation === "mimical" && isMobile !== "yes") { - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "block"; - if (delta > 0) { - flipToPrevPage(); - } - else if (delta < 0) { - flipToNextPage(); - } - setTimeout(() => { - if (!bookDiv) - return {}; - bookDiv.style.display = "none"; - }, 1000); - } - } - if (delta > 0) { - // previous page - if (readerMode === "single") { - let subContainer = doc.querySelector("#pdf-container-" + (chapterDocIndex - 1)); - if (subContainer) { - subContainer.scrollIntoView(); - } - } - else { - doc.body.scrollBy(-(width + gap) / 2, 0); - } - } - else if (delta < 0) { - // next page - if (readerMode === "single") { - let subContainer = doc.querySelector("#pdf-container-" + (chapterDocIndex + 1)); - if (subContainer) { - subContainer.scrollIntoView(); - } - } - else { - doc.body.scrollBy((width + gap) / 2, 0); - } - } -}); -const convertPageToImage = (page) => __awaiter(void 0, void 0, void 0, function* () { - const desiredWidth = 800; - const viewport = page.getViewport({ scale: 1 }); - const canvas = document.createElement("canvas"); - const context = canvas.getContext("2d"); - canvas.width = desiredWidth; - canvas.height = (desiredWidth / viewport.width) * viewport.height; - const renderContext = { - canvasContext: context, - viewport: page.getViewport({ scale: desiredWidth / viewport.width }), - }; - yield page.render(renderContext).promise; - const imageURL = canvas.toDataURL("image/jpeg", 0.8); - const size = calculateSize(imageURL); - return { imageURL, size }; -}); -function calculateSize(imageURL) { - const base64Length = imageURL.length - "data:image/jpeg;base64,".length; - const sizeInBytes = Math.ceil(base64Length * 0.75); - return sizeInBytes; -} -const isElectron$1 = () => { - // Renderer process - if (typeof window !== "undefined" && - typeof window.process === "object" && - window.process.type === "renderer") { - return true; - } - // Main process - if (typeof process !== "undefined" && - typeof process.versions === "object" && - !!process.versions.electron) { - return true; - } - // Detect the user agent when the `nodeIntegration` option is set to true - if (typeof navigator === "object" && - typeof navigator.userAgent === "string" && - navigator.userAgent.indexOf("Electron") >= 0) { - return true; - } - return false; -}; -const showOCRProgress = (progress) => { - let bar = document.getElementById("ocr-progress-bar"); - if (!bar) { - bar = document.createElement("progress"); - bar.id = "ocr-progress-bar"; - bar.max = 1; - bar.value = 0; - bar.style.position = "fixed"; - bar.style.top = "10px"; - bar.style.left = "50%"; - bar.style.transform = "translateX(-50%)"; - bar.style.width = "300px"; - bar.style.zIndex = "9999"; - document.body.appendChild(bar); - } - bar.value = progress; - if (progress >= 1) { - setTimeout(() => { - bar.remove(); - }, 1000); - } -}; - -function blobUrlToBase64(blobUrl) { - return __awaiter(this, void 0, void 0, function* () { - try { - // 1. 获取Blob数据 - const response = yield fetch(blobUrl); - const blob = yield response.blob(); - // 2. 将Blob转换为Base64 - const base64 = yield new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onloadend = () => resolve(reader.result); // 成功时返回Base64字符串 - reader.onerror = reject; // 失败时拒绝Promise - reader.readAsDataURL(blob); // 开始读取 - }); - return base64; // 返回形如 "data:image/png;base64,..." 的字符串 - } - catch (error) { - console.error("转换失败:", error); - throw error; // 抛出错误 - } - }); -} -function getScreenLeftOffset() { - if (window.visualViewport) { - return window.visualViewport.offsetLeft; - } - else { - // 回退到滚动偏移量,注意可能不准确 - return window.pageXOffset || document.documentElement.scrollLeft || 0; - } -} -function getScreenTopOffset() { - if (window.visualViewport) { - return window.visualViewport.offsetTop; - } - else { - // 回退到滚动偏移量,注意可能不准确 - return window.pageYOffset || document.documentElement.scrollTop || 0; - } -} -const preventLinkNavigation = (event, doc, render) => __awaiter(void 0, void 0, void 0, function* () { - const target = event.target; - if (!target) - return; - const linkElement = findLinkElement(target); - if (linkElement) { - event.preventDefault(); - event.stopPropagation(); - // Get href from the link - let href = linkElement.getAttribute("href"); - if (href && href.startsWith("kindle:")) { - let chapterInfo = render.resolveChapter(href); - if (chapterInfo) { - yield render.goToChapter(chapterInfo.index, chapterInfo.href, chapterInfo.label); - return true; - } - let result = yield render.resolveHref(href); - href = "#" + result.id; - } - let footnote = ""; - if (href && href.indexOf("#") > -1) { - let id = href.split("#").reverse()[0]; - let node = doc.body.querySelector("#" + id); - if (!node) { - if (href.indexOf("filepos") > -1) { - let chapterInfo = render.resolveChapter(href); - yield render.goToChapter(chapterInfo.index, chapterInfo.href, chapterInfo.label); - return true; - } - if (href.indexOf("#") !== 0) { - while (href.startsWith(".")) { - href = href.substring(1); - } - let chapterInfo = render.resolveChapter(href); - if (chapterInfo) { - yield render.goToChapter(chapterInfo.index, chapterInfo.href, chapterInfo.label); - } - } - node = doc.body.querySelector("#" + CSS.escape(id)); - if (!node) { - return false; - } - yield render.goToNode(doc.body.querySelector("#" + CSS.escape(id))); - } - if ((node.textContent.trim() === event.target.textContent.trim() || - !node.textContent.trim()) && - node.parentElement) { - if (node.parentElement.tagName !== "BODY") { - node = node.parentElement; - } - else { - return false; - } - } - //将html代码中的img标签由blob转换为base64 - footnote = node.textContent; - } - else if (href) { - let chapterInfo = render.resolveChapter(href); - if (chapterInfo) { - yield render.goToChapter(chapterInfo.index, chapterInfo.href, chapterInfo.label); - } - } - // Send message to React Native with link details - window.ReactNativeWebView.postMessage(JSON.stringify({ - event: "link-clicked", - href: href, - footnote: footnote, - })); - return false; - } -}); -function findLinkElement(element) { - // Check if the element itself is a link - if (element.tagName === "A") { - return element; - } - // Traverse up the DOM tree to check for parent links - // This handles cases where the click target is a child element inside a link - let currentElement = element; - while (currentElement && currentElement.tagName !== "BODY") { - if (currentElement.tagName === "A") { - return currentElement; - } - currentElement = currentElement.parentElement; - } - return null; -} -function getTouchAction(col, row, touchControlRule) { - //根据col和row获取对应的区域编号,从左到右,从上到下,1-9 - const areaIndex = row * 3 + col + 1; // 1-9 - //如果区域编号在touchControlRule中存在,则返回对应的action - if (touchControlRule.layout["A"].area.includes(areaIndex)) { - return touchControlRule["touchControlA"]; - } - else if (touchControlRule.layout["B"].area.includes(areaIndex)) { - return touchControlRule["touchControlB"]; - } - else if (touchControlRule.layout["C"].area.includes(areaIndex)) { - return touchControlRule["touchControlC"]; - } - return "right"; -} -const addAndroidTouchEvent = (doc, iframe, element, readerMode, animation, format, touchControlRule, render) => { - var _a; - let iWin = iframe.contentWindow || ((_a = iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.defaultView); - let outerDoc = render.getDocument(); - let touchStartTime = 0; - let touchStartX = 0; - let touchStartY = 0; - let lastTouchEnd = 0; - const swipeThreshold = 30; // Minimum distance in pixels to be considered a swipe - const timeThreshold = 500; // Maximum time in milliseconds to be considered a tap - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let pageWidth = element.clientWidth + gap; - let pageChangeDebounceTimer = null; - let onTouchEnd = function (event) { - window.isSwiping = false; - window.isTouchNavigation = true; - if (pageChangeDebounceTimer) { - clearTimeout(pageChangeDebounceTimer); - } - pageChangeDebounceTimer = setTimeout(() => { - window.isTouchNavigation = false; - pageChangeDebounceTimer = null; - }, 4000); - let now = new Date().getTime(); - if (now - lastTouchEnd <= 300) { - event.preventDefault(); - return; - } - lastTouchEnd = now; - const touch = event.changedTouches[0]; - const touchEndTime = Date.now(); - let touchEndX = touch.screenX; - let touchEndY = touch.screenY; - const timeDiff = touchEndTime - touchStartTime; - const distX = touchEndX - touchStartX; - const distY = touchEndY - touchStartY; - if (isDragging && animation === "mimical" && readerMode !== "scroll") { - isDragging = false; - render.mouseUpHandler(event); - if (touch.screenX < (window.screen.width / 4) * 3 && - touchEndX - touchStartX < 0) { - render.next(); - isDragging = false; - } - else if (touch.screenX > (window.screen.width / 4) * 1 && - touchEndX - touchStartX > 0) { - render.prev(); - isDragging = false; - } - setTimeout(() => { - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "none"; - } - }, 400); - return; - } - // Replace the scrollTo implementation with this optimized version - if (isDragging && animation === "sliding" && readerMode !== "scroll") { - let tempDoc = format === "PDF" ? outerDoc : doc; - // Clean up any existing animation - if (window.scrollAnimationId) { - cancelAnimationFrame(window.scrollAnimationId); - } - if (Math.abs(tempDoc.body.scrollWidth - - tempDoc.body.scrollLeft - - element.clientWidth) < 10) { - if (selectionTimeout) { - clearTimeout(selectionTimeout); - } - selectionTimeout = setTimeout(() => { - render.next(); - isDragging = false; - }, 300); // Debounce selection events - return; - } - if (tempDoc.body.scrollLeft === 0) { - if (selectionTimeout) { - clearTimeout(selectionTimeout); - } - selectionTimeout = setTimeout(() => { - render.prev(); - isDragging = false; - }, 300); // Debounce selection events - return; - } - tempDoc.body.style.transform = ""; - let scrollLeft = tempDoc.body.scrollLeft; - // Improved snapping logic - let snapX; - const currentPage = Math.round(scrollLeft / pageWidth); - const dragPercentage = Math.abs(distX) / window.screen.width; - const dragThreshold = 0.1; // Only 10% drag needed to change page - if (distX > 0 && dragPercentage > dragThreshold) { - // Dragged right (go to previous page) - snapX = (currentPage - 1) * pageWidth; - } - else if (distX < 0 && dragPercentage > dragThreshold) { - // Dragged left (go to next page) - snapX = (currentPage + 1) * pageWidth; - } - else { - // Stay on current page - snapX = currentPage * pageWidth; - } - // Ensure we don't go out of bounds - snapX = Math.max(0, Math.min(snapX, tempDoc.body.scrollWidth - pageWidth)); - if (tempDoc.body.scrollWidth - snapX < pageWidth + gap) { - snapX = tempDoc.body.scrollWidth; - } - // Use custom smooth scrolling with requestAnimationFrame instead of browser's scrollTo - const startTime = performance.now(); - const startLeft = tempDoc.body.scrollLeft; - const distance = snapX - startLeft; - const duration = 300; // milliseconds - // Apply hardware acceleration before animation starts - tempDoc.body.style.willChange = "scroll-position"; - // Custom easing function for natural movement - const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3); - function animateScroll(currentTime) { - const elapsedTime = currentTime - startTime; - if (elapsedTime >= duration) { - // Animation complete - set final position - tempDoc.body.scrollLeft = snapX; - // Clean up acceleration hints - tempDoc.body.style.willChange = "auto"; - render.record(); - isDragging = false; - return; - } - // Calculate new position using easing - const progress = easeOutCubic(elapsedTime / duration); - const newLeft = startLeft + distance * progress; - // Update scroll position - tempDoc.body.scrollLeft = newLeft; - // Continue animation - window.scrollAnimationId = requestAnimationFrame(animateScroll); - } - // Start animation - window.scrollAnimationId = requestAnimationFrame(animateScroll); - return; - } - var selectedText = iWin.getSelection().toString(); - var isSwiping = Math.abs(distX) >= swipeThreshold || Math.abs(distY) >= swipeThreshold; - if (selectedText && - (format !== "PDF" || (format === "PDF" && !isSwiping))) { - window.ReactNativeWebView.postMessage(JSON.stringify({ - event: "select-text-after-touch", - selectedText: selectedText, - })); - return; - } - if (timeDiff > timeThreshold) { - const target = event.target; - if (!target) - return; - if (target.tagName === "IMG" || target.tagName === "image") { - const imgSrc = target.src || target.getAttribute("xlink:href"); - //blob to base64 - if (imgSrc.startsWith("blob:")) { - blobUrlToBase64(imgSrc).then((base64) => { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "view-image", imgSrc: base64 })); - }); - } - return; - } - } - if (timeDiff < timeThreshold && - Math.abs(distX) < swipeThreshold && - Math.abs(distY) < swipeThreshold) { - var width = window.screen.width; - var height = window.screen.height; - var cellWidth = width / 3; - var cellHeight = height / 3; - var col = Math.floor(touchEndX / cellWidth); - var row = Math.floor(touchEndY / cellHeight); - var result = getTouchAction(col, row, touchControlRule); - window.ReactNativeWebView.postMessage(JSON.stringify({ event: result })); - } - else if (Math.abs(distX) >= swipeThreshold || - Math.abs(distY) >= swipeThreshold) { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "swipe" })); - if (readerMode === "scroll" && - Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) < 10) { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "scroll-bottom" })); - } - if (readerMode === "scroll" && element.scrollTop === 0) { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "scroll-top" })); - } - } - }; - let onTouchStart = function (event) { - touchStartTime = Date.now(); - const target = event.target; - if (!target) - return; - const linkElement = findLinkElement(target); - if (linkElement) { - return; - } - if (event.touches.length > 1) { - event.preventDefault(); - } - const touch = event.touches[0]; - touchStartX = touch.screenX; - touchStartY = touch.screenY; - }; - let isDragging = false; - let lastTouchX = 0; - let onTouchMove = function (event) { - // Skip handling if not dragging yet and still determining direction - if (!isDragging && Math.abs(event.touches[0].screenX - touchStartX) <= 10) { - return; - } - // Prevent default to stop browser scroll behavior - event.preventDefault(); - if (window.visualViewport.scale > 1 && format === "PDF") { - event.preventDefault(); - return; - } - const touch = event.touches[0]; - const touchCurrentX = touch.screenX; - const touchCurrentY = touch.screenY; - // Calculate distance moved - const distX = touchCurrentX - touchStartX; - const distY = touchCurrentY - touchStartY; - if (Math.abs(distX) > 10 || Math.abs(distY) > 10) { - window.isSwiping = true; - } - // Only start dragging if horizontal movement is greater than vertical - if (!isDragging && - Math.abs(distX) > Math.abs(distY) && - Math.abs(distX) > 10) { - isDragging = true; - lastTouchX = touchCurrentX; - // Apply hardware acceleration to the body - doc.body.style.transform = "translateZ(0)"; - if (animation === "mimical" && readerMode !== "scroll") { - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "block"; - render.mouseDownHandler(event); - } - } - return; - } - if (isDragging && animation === "mimical" && readerMode !== "scroll") { - render.mouseMoveHandler(event); - } - // If we're in dragging mode, apply direct transform for better performance - if (isDragging && animation === "sliding" && readerMode !== "scroll") { - let tempDoc = format === "PDF" ? outerDoc : doc; - // Calculate the delta since last move event - const deltaX = touchCurrentX - lastTouchX; - const currentScrollLeft = tempDoc.body.scrollLeft; - tempDoc.body.scrollLeft = currentScrollLeft - deltaX; - // Update last position - lastTouchX = touchCurrentX; - // Request animation frame for smoother updates (optional) - requestAnimationFrame(() => { - // Additional visual feedback can be added here - }); - } - }; - doc.addEventListener("touchend", onTouchEnd, false); - doc.addEventListener("touchstart", onTouchStart, false); - doc.addEventListener("touchmove", onTouchMove, false); - // Add this with the other event listeners - doc.addEventListener("click", (event) => { - preventLinkNavigation(event, doc, render); - }, true); // Use capturing phase - let selectionTimeout = null; - let startSelectionTime = 0; - let selectionCount = 0; - let triggerSelectionMenu = (event) => __awaiter(void 0, void 0, void 0, function* () { - var _b, _c; - const selectedText = iWin.getSelection().toString().trim(); - if (selectedText) { - var range = iWin.getSelection().getRangeAt(0); - let pageSize = render.getPageSize(); - var rect = range.getBoundingClientRect(); - if (format === "PDF") { - let clientRects = range.getClientRects(); - if (clientRects.length > 0) { - //combine all the rects - clientRects = Array.from(clientRects).filter((item) => { - return (Math.abs(item.height - pageSize.sectionHeight) > 10 && - Math.abs(item.width - pageSize.sectionWidth) > 10 && - item.height > 0 && - item.width > 0); - }); - let minTop = Infinity; - let minLeft = Infinity; - let maxBottom = -Infinity; - let maxRight = -Infinity; - for (let i = 0; i < clientRects.length; i++) { - const rect = clientRects[i]; - minTop = Math.min(minTop, rect.top); - minLeft = Math.min(minLeft, rect.left); - maxBottom = Math.max(maxBottom, rect.bottom); - maxRight = Math.max(maxRight, rect.right); - } - // Create the combined rectangle object - const combinedRect = { - top: minTop, - left: minLeft, - bottom: maxBottom, - right: maxRight, - width: maxRight - minLeft, - height: maxBottom - minTop, - }; - rect = combinedRect; - } - } - var position = { - top: rect.top - element.scrollTop, - left: rect.left, - width: rect.width, - height: rect.height, - screenWidth: window.innerWidth, - screenHeight: window.innerHeight, - sectionHeight: pageSize.sectionHeight, - sectionWidth: pageSize.sectionWidth, - gap: pageSize.gap, - scale: window.visualViewport.scale, - offsetLeft: offsetLeft, - offsetTop: offsetTop, - }; - rangy.init(); - let charRange = null; - if (format === "PDF") { - try { - let target = event.target; - let targetIframe = (_c = (_b = target.ownerDocument) === null || _b === void 0 ? void 0 : _b.defaultView) === null || _c === void 0 ? void 0 : _c.frameElement; - let id = (targetIframe === null || targetIframe === void 0 ? void 0 : targetIframe.getAttribute("id")) || ""; - let chapterDocIndex = id ? parseInt(id.split("-").reverse()[0]) : 0; - charRange = yield render.getHightlightCoords(chapterDocIndex); - position.chapterDocIndex = chapterDocIndex; - let subContainer = targetIframe.parentElement; - if (subContainer) { - position.top = - position.top + parseFloat(getComputedStyle(subContainer).top); - } - } - catch (error) { - console.error("Error getting highlight coords:", error); - } - } - else { - charRange = yield render.getHightlightCoords(); - } - window.ReactNativeWebView.postMessage(JSON.stringify({ - event: "select-text", - selectedText: selectedText, - position: position, - range: charRange, - })); - } - }); - doc.body.oncontextmenu = function (event) { - const target = event.target; - if (!target) - return; - if (target.tagName === "IMG" || target.tagName === "image") { - const imgSrc = target.src || target.getAttribute("xlink:href"); - //blob to base64 - if (imgSrc.startsWith("blob:")) { - blobUrlToBase64(imgSrc).then((base64) => { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "view-image", imgSrc: base64 })); - }); - } - return; - } - if (Date.now() - startSelectionTime < 100) { - setTimeout(() => { - if (selectionCount === 1) { - triggerSelectionMenu(event); - } - }, 600); - } - else { - triggerSelectionMenu(event); - } - event.preventDefault(); - event.stopPropagation(); - return false; - }; - let scrollLeft = 0; - let offsetLeft = 0; - let offsetTop = 0; - doc.addEventListener("selectstart", (event) => { - selectionCount = 0; - startSelectionTime = Date.now(); - offsetLeft = getScreenLeftOffset(); - offsetTop = getScreenTopOffset(); - if (readerMode === "scroll") - return; - scrollLeft = doc.body.scrollLeft; - //prevent doc.body from scrolling - }, false); - let lastSelectionChangeTime = 0; - const SELECTION_THROTTLE_DELAY = 3000; // 3秒 - doc.addEventListener("selectionchange", (event) => { - //检查选择文字是否为空 - const selectedText = iWin.getSelection().toString().trim(); - if (!selectedText) { - return; - } - if (scrollLeft > 0) { - doc.body.scrollLeft = scrollLeft; - } - selectionCount++; - const now = Date.now(); - // 检查是否超过3秒间隔 - if (now - lastSelectionChangeTime >= SELECTION_THROTTLE_DELAY) { - // 更新最后触发时间 - lastSelectionChangeTime = now; - // 执行原有逻辑 - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "selection-change" })); - } - }, false); -}; -const addAppleTouchEvent = (doc, iframe, element, readerMode, animation, format, touchControlRules, render) => { - var _a; - let iWin = iframe.contentWindow || ((_a = iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.defaultView); - let outerDoc = render.getDocument(); - let touchStartTime = 0; - let touchStartX = 0; - let touchStartY = 0; - let lastTouchEnd = 0; - let swipeThreshold = 30; // Minimum distance in pixels to be considered a swipe - const timeThreshold = 500; // Maximum time in milliseconds to be considered a tap - let section = Math.floor(element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let pageChangeDebounceTimer = null; - let onTouchEnd = function (event) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - window.isSwiping = false; - window.isTouchNavigation = true; - if (pageChangeDebounceTimer) { - clearTimeout(pageChangeDebounceTimer); - } - pageChangeDebounceTimer = setTimeout(() => { - window.isTouchNavigation = false; - pageChangeDebounceTimer = null; - }, 4000); - let now = new Date().getTime(); - if (now - lastTouchEnd <= 300) { - event.preventDefault(); - return; - } - lastTouchEnd = now; - const touch = event.changedTouches[0]; - const touchEndTime = Date.now(); - const touchEndX = touch.screenX; - const touchEndY = touch.screenY; - const timeDiff = touchEndTime - touchStartTime; - const distX = touchEndX - touchStartX; - const distY = touchEndY - touchStartY; - if (isDragging && animation === "mimical" && readerMode !== "scroll") { - isDragging = false; - render.mouseUpHandler(event); - if (touchEndX < (window.screen.width / 4) * 3 && - touchEndX - touchStartX < 0) { - render.next(); - isDragging = false; - } - else if (touchEndX > (window.screen.width / 4) * 1 && - touchEndX - touchStartX > 0) { - render.prev(); - isDragging = false; - } - setTimeout(() => { - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "none"; - } - }, 400); - return; - } - // Replace the scrollTo implementation with this optimized version - if (isDragging && animation === "sliding" && readerMode !== "scroll") { - let tempDoc = format === "PDF" ? outerDoc : doc; - // Clean up any existing animation - if (window.scrollAnimationId) { - cancelAnimationFrame(window.scrollAnimationId); - } - if (Math.abs(tempDoc.body.scrollWidth - - tempDoc.body.scrollLeft - - element.clientWidth) < 10) { - if (selectionTimeout) { - clearTimeout(selectionTimeout); - } - selectionTimeout = setTimeout(() => { - render.next(); - isDragging = false; - }, 300); // Debounce selection events - return; - } - if (tempDoc.body.scrollLeft === 0) { - if (selectionTimeout) { - clearTimeout(selectionTimeout); - } - selectionTimeout = setTimeout(() => { - render.prev(); - isDragging = false; - }, 300); // Debounce selection events - return; - } - tempDoc.body.style.transform = ""; - let pageWidth = element.clientWidth + gap; - let scrollLeft = tempDoc.body.scrollLeft; - // Improved snapping logic - let snapX; - const currentPage = Math.round(scrollLeft / pageWidth); - const dragPercentage = Math.abs(distX) / window.screen.width; - const dragThreshold = 0.1; // Only 10% drag needed to change page - if (distX > 0 && dragPercentage > dragThreshold) { - // Dragged right (go to previous page) - snapX = (currentPage - 1) * pageWidth; - } - else if (distX < 0 && dragPercentage > dragThreshold) { - // Dragged left (go to next page) - snapX = (currentPage + 1) * pageWidth; - } - else { - // Stay on current page - snapX = currentPage * pageWidth; - } - // Ensure we don't go out of bounds - snapX = Math.max(0, Math.min(snapX, tempDoc.body.scrollWidth - pageWidth)); - if (tempDoc.body.scrollWidth - snapX < pageWidth + gap) { - snapX = tempDoc.body.scrollWidth; - } - // Use custom smooth scrolling with requestAnimationFrame instead of browser's scrollTo - const startTime = performance.now(); - const startLeft = tempDoc.body.scrollLeft; - const distance = snapX - startLeft; - const duration = 300; // milliseconds - // Apply hardware acceleration before animation starts - tempDoc.body.style.willChange = "scroll-position"; - // Custom easing function for natural movement - const easeOutCubic = (t) => 1 - Math.pow(1 - t, 3); - function animateScroll(currentTime) { - const elapsedTime = currentTime - startTime; - if (elapsedTime >= duration) { - // Animation complete - set final position - tempDoc.body.scrollLeft = snapX; - // Clean up acceleration hints - tempDoc.body.style.willChange = "auto"; - render.record(); - isDragging = false; - return; - } - // Calculate new position using easing - const progress = easeOutCubic(elapsedTime / duration); - const newLeft = startLeft + distance * progress; - // Update scroll position - tempDoc.body.scrollLeft = newLeft; - // Continue animation - window.scrollAnimationId = requestAnimationFrame(animateScroll); - } - // Start animation - window.scrollAnimationId = requestAnimationFrame(animateScroll); - return; - } - const selectedText = iWin.getSelection().toString().trim(); - if (selectedText) { - var range = iWin.getSelection().getRangeAt(0); - var rect = range.getBoundingClientRect(); - var pageSize = render.getPageSize(); - var position = { - top: rect.top - element.scrollTop, - left: rect.left, - width: rect.width, - height: rect.height, - screenWidth: window.innerWidth, - screenHeight: window.innerHeight, - sectionHeight: pageSize.sectionHeight, - sectionWidth: pageSize.sectionWidth, - gap: pageSize.gap, - scale: window.visualViewport.scale, - offsetLeft: getScreenLeftOffset(), - offsetTop: getScreenTopOffset(), - }; - rangy.init(); - let charRange = null; - if (format === "PDF") { - let target = event.target; - let ownerDoc = target.ownerDocument; - let targetIframe = (_a = ownerDoc === null || ownerDoc === void 0 ? void 0 : ownerDoc.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement; - let id = (targetIframe === null || targetIframe === void 0 ? void 0 : targetIframe.getAttribute("id")) || ""; - let chapterDocIndex = id ? parseInt(id.split("-").reverse()[0]) : 0; - position.chapterDocIndex = chapterDocIndex; - charRange = yield render.getHightlightCoords(chapterDocIndex); - let subContainer = targetIframe.parentElement; - if (subContainer) { - position.top = - position.top + parseFloat(getComputedStyle(subContainer).top); - } - } - else { - charRange = yield render.getHightlightCoords(); - } - window.ReactNativeWebView.postMessage(JSON.stringify({ - event: "select-text", - selectedText: selectedText, - position: position, - range: charRange, - })); - return; - } - if (timeDiff > timeThreshold) { - const target = event.target; - if (!target) - return; - if (target.tagName === "IMG" || target.tagName === "image") { - const imgSrc = target.src || target.getAttribute("xlink:href"); - //blob to base64 - if (imgSrc.startsWith("blob:")) { - blobUrlToBase64(imgSrc).then((base64) => { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "view-image", imgSrc: base64 })); - }); - } - return; - } - } - if (timeDiff < timeThreshold && - Math.abs(distX) < swipeThreshold && - Math.abs(distY) < swipeThreshold) { - const width = document.documentElement.clientWidth; - const height = document.documentElement.clientHeight; - let normalizedX = Math.min(Math.max(touchEndX, 0), width); - let normalizedY = Math.min(Math.max(touchEndY, 0), height); - if (format === "PDF" && readerMode === "double") { - let target = event.target; - let ownerDoc = target.ownerDocument; - let targetIframe = (_b = ownerDoc === null || ownerDoc === void 0 ? void 0 : ownerDoc.defaultView) === null || _b === void 0 ? void 0 : _b.frameElement; - let id = (targetIframe === null || targetIframe === void 0 ? void 0 : targetIframe.getAttribute("id")) || ""; - let chapterDocIndex = id ? parseInt(id.split("-").reverse()[0]) : 0; - if (chapterDocIndex % 2 === 1) { - normalizedX = normalizedX + width / 2; - } - } - // For pagination mode: keep original 3x3 grid - const cellWidth = width / 3; - const cellHeight = height / 3; - const col = Math.min(Math.floor(normalizedX / cellWidth), 2); - const row = Math.min(Math.floor(normalizedY / cellHeight), 2); - let result = getTouchAction(col, row, touchControlRules); - window.ReactNativeWebView.postMessage(JSON.stringify({ event: result })); - } - else if (Math.abs(distX) >= swipeThreshold || - Math.abs(distY) >= swipeThreshold) { - window.ReactNativeWebView.postMessage(JSON.stringify({ - event: "swipe", - })); - if (readerMode === "scroll" && - Math.abs(element.scrollHeight - element.scrollTop - element.clientHeight) < 10) { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "scroll-bottom" })); - } - if (readerMode === "scroll" && element.scrollTop === 0) { - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "scroll-top" })); - } - } - }); - }; - let onTouchStart = function (event) { - const target = event.target; - if (!target) - return; - const linkElement = findLinkElement(target); - if (linkElement) { - return; - } - //// 注释掉解决无法双指缩放pdf的问题 - // if (event.touches.length > 1) { - // event.preventDefault(); - // } - const touch = event.touches[0]; - touchStartTime = Date.now(); - touchStartX = touch.screenX; - touchStartY = touch.screenY; - }; - let isDragging = false; - let lastTouchX = 0; - let onTouchMove = function (event) { - const selectedText = iWin.getSelection().toString().trim(); - // Skip handling if not dragging yet and still determining direction - if ((!isDragging && Math.abs(event.touches[0].screenX - touchStartX) <= 10) || - selectedText) { - return; - } - if (window.visualViewport.scale > 1 && format === "PDF") { - return; - } - const touch = event.touches[0]; - const touchCurrentX = touch.screenX; - const touchCurrentY = touch.screenY; - // Calculate distance moved - const distX = touchCurrentX - touchStartX; - const distY = touchCurrentY - touchStartY; - // Only start dragging if horizontal movement is greater than vertical - if (!isDragging && - Math.abs(distX) > Math.abs(distY) && - Math.abs(distX) > 10) { - isDragging = true; - lastTouchX = touchCurrentX; - if (animation === "mimical" && readerMode !== "scroll") { - window.isSwiping = true; - let bookDiv = document.getElementById("book"); - if (bookDiv) { - bookDiv.style.display = "block"; - render.mouseDownHandler(event); - } - } - return; - } - if (isDragging && animation === "mimical" && readerMode !== "scroll") { - render.mouseMoveHandler(event); - } - // If we're in dragging mode, apply direct transform for better performance - if (isDragging && animation === "sliding" && readerMode !== "scroll") { - window.isSwiping = true; - let tempDoc = format === "PDF" ? outerDoc : doc; - // Calculate the delta since last move event - const deltaX = touchCurrentX - lastTouchX; - // Use transform instead of scrollBy for smoother rendering - const currentScrollLeft = tempDoc.body.scrollLeft; - tempDoc.body.scrollLeft = currentScrollLeft - deltaX; - // Update last position - lastTouchX = touchCurrentX; - // Request animation frame for smoother updates (optional) - requestAnimationFrame(() => { - // Additional visual feedback can be added here - }); - } - }; - // Add passive: false to ensure preventDefault works - doc.addEventListener("touchend", onTouchEnd, { passive: false }); - doc.addEventListener("touchstart", onTouchStart, { passive: false }); - doc.addEventListener("touchmove", onTouchMove, { passive: false }); - // Add this with the other event listeners - doc.addEventListener("click", (event) => { - preventLinkNavigation(event, doc, render); - }, true); // Use capturing phase - let selectionTimeout = null; - doc.body.oncontextmenu = function (event) { - event.preventDefault(); - event.stopPropagation(); - return false; - }; - let lastSelectionChangeTime = 0; - const SELECTION_THROTTLE_DELAY = 3000; // 3秒 - doc.addEventListener("selectionchange", (event) => { - //检查选择文字是否为空 - const selectedText = iWin.getSelection().toString().trim(); - if (!selectedText) { - return; - } - const now = Date.now(); - // 检查是否超过3秒间隔 - if (now - lastSelectionChangeTime >= SELECTION_THROTTLE_DELAY) { - // 更新最后触发时间 - lastSelectionChangeTime = now; - // 执行原有逻辑 - window.ReactNativeWebView.postMessage(JSON.stringify({ event: "selection-change" })); - } - }, { passive: false }); -}; - -class GeneralRender extends EventEmitter { - constructor(config) { - super(); - this.tranformText = () => { - let doc = this.getDocument(); - if (!doc) - return; - if (this.convertChinese === "Simplified To Traditional") { - doc - .querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker") - .forEach((item) => { - item.innerHTML = item.innerHTML - .split("") - .map((item) => Chinese.s2t(item)) - .join(""); - }); - } - else if (this.convertChinese === "Traditional To Simplified") { - doc - .querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker") - .forEach((item) => { - item.innerHTML = item.innerHTML - .split("") - .map((item) => Chinese.t2s(item)) - .join(""); - }); - } - //确保页面完全加载完毕之后,在修改缩进 - if (this.isIndent === "yes") { - doc - .querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address") - .forEach((item) => { - for (let node of item.childNodes) { - if (node.nodeType === Node.TEXT_NODE) { - // 将前导空格替换为零宽度字符,保留原始内容但不显示 - const text = node.nodeValue || ""; - const leadingSpaces = text.match(/^(\s+)/); - if (leadingSpaces) { - //覆盖父元素的text-indent css - item.setAttribute("style", (item.getAttribute("style") || "") + - "text-indent: 0em !important;"); - } - // 只处理第一个,退出循环 - break; - } - //如果子元素为img则也缩进设为0 - if (node.nodeType === Node.ELEMENT_NODE && - node.tagName.toLowerCase() === "img") { - item.setAttribute("style", (item.getAttribute("style") || "") + - "text-indent: 0em !important;"); - break; - } - } - }); - } - }; - this.addPageAnimation = (backgroundColor) => { - if (this.animation === "mimical") { - let progressInfo = this.getProgress(); - if (!progressInfo) - return; - const pageAnimation = addPageAnimation(progressInfo.totalPage, this.isDarkMode, backgroundColor); - if (pageAnimation) { - this.flipToNextPage = pageAnimation.flipToNextPage; - this.flipToPrevPage = pageAnimation.flipToPrevPage; - this.mouseDownHandler = pageAnimation.mouseDownHandler; - this.mouseUpHandler = pageAnimation.mouseUpHandler; - this.mouseMoveHandler = pageAnimation.mouseMoveHandler; - } - } - }; - this.readerMode = config.readerMode; - this.animation = config.animation; - this.format = config.format; - this.convertChinese = config.convertChinese; - this.isIndent = config.isIndent; - this.isDarkMode = config.isDarkMode; - this.isMobile = config.isMobile; - this.chapterList = []; - this.chapterDocList = []; - this.flattenChapters = []; - this.book = ""; - this.element = ""; - this.tempLocation = {}; - this.flipToNextPage = () => { }; - this.flipToPrevPage = () => { }; - this.mouseDownHandler = () => { }; - this.mouseUpHandler = () => { }; - this.mouseMoveHandler = (event) => { }; - this.touchEventSet = {}; - if (this.isMobile === "yes") { - console.log = function (...args) { - window.ReactNativeWebView.postMessage(args.map((arg) => String(arg)).join(", ")); - }; - console.info = function (...args) { - window.ReactNativeWebView.postMessage(args.map((arg) => String(arg)).join(", ")); - }; - console.error = function (...args) { - window.ReactNativeWebView.postMessage(args.map((arg) => String(arg)).join(", ")); - }; - } - } - getPageSize() { - let scale = this.readerMode === "double" ? 2 : 1; - let section = Math.floor(this.element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let iframe = this.getIframe(); - if (!iframe) - return; - let iframeHeight = iframe === null || iframe === void 0 ? void 0 : iframe.getBoundingClientRect().height; - return { - width: this.element.clientWidth, - height: this.element.clientHeight, - left: this.element.offsetLeft, - top: this.element.offsetTop, - scrollTop: this.element.scrollTop, - sectionWidth: (this.element.clientWidth - gap) / scale, - sectionHeight: iframeHeight, - gap: gap, - }; - } - scrollToText(text) { - let doc = this.getDocument(); - if (!doc) - return; - let nodeList = getBlockElement(doc.body).filter((item) => !isParentBlock(item)); - let audioNodes = nodeList.filter((s) => (s.textContent || "").indexOf(text) > -1); - if (audioNodes.length > 0) { - let targetNode = audioNodes[0]; - let left = targetNode - ? convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)) - : 0; - let top = targetNode - ? convertStyleNum(targetNode.offsetTop) - - convertStyleNum(targetNode.marginTop || - parseFloat(getComputedStyle(targetNode).marginTop)) - : 0; - if (this.readerMode !== "scroll") { - doc.body.scrollTo(left, 0); - } - else { - this.element.scrollTo(0, top); - } - } - } - goToPage(targetPage) { - return __awaiter(this, void 0, void 0, function* () { - if (this.readerMode === "scroll") { - if (targetPage < 0) { - targetPage = 1; - } - let top = (targetPage - 1) * (this.element.clientHeight - 50); - this.element.scrollTo(0, top); - } - else { - let doc = this.getDocument(); - if (!doc) - return; - let section = Math.floor(this.element.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - const width = this.element.clientWidth; - const scrollDistance = width + gap; - if (this.readerMode === "double") { - targetPage = - (targetPage % 2 === 0 ? targetPage - 2 : targetPage - 1) / 2; - } - else { - targetPage = targetPage - 1; - } - if (targetPage < 0) { - targetPage = 0; - } - const targetScrollLeft = targetPage * scrollDistance; - doc.body.scrollTo({ - top: 0, - left: targetScrollLeft, - behavior: this.animation === "sliding" && this.isMobile !== "yes" - ? "smooth" - : "auto", - }); - } - yield this.record(); - }); - } - resolveChapter(href) { - let path = href; - let chapterIndex = -1; - for (let index = 0; index < this.flattenChapters.length; index++) { - if (this.flattenChapters[index].href.includes(path)) { - chapterIndex = index; - break; - } - } - if (chapterIndex > -1) { - return this.flattenChapters[chapterIndex]; - } - else { - let pathWithoutHash = href.split("#")[0]; - for (let index = 0; index < this.flattenChapters.length; index++) { - if (this.flattenChapters[index].href.includes(pathWithoutHash.substring(1))) { - chapterIndex = index; - break; - } - } - if (chapterIndex > -1) { - return this.flattenChapters[chapterIndex]; - } - else { - for (let index = 0; index < this.chapterDocList.length; index++) { - if (this.chapterDocList[index].text && - this.chapterDocList[index].text.id && - (this.chapterDocList[index].text.id + "").includes(path)) { - chapterIndex = index; - break; - } - } - if (chapterIndex > -1) { - return { label: "", href: "", index: chapterIndex }; - } - else { - return null; - } - } - } - } - flatChapter(chapters) { - let newChapter = []; - for (let i = 0; i < chapters.length; i++) { - if (chapters[i].subitems && chapters[i].subitems.length > 0) { - newChapter.push(chapters[i]); - newChapter = newChapter.concat(this.flatChapter(chapters[i].subitems)); - } - else { - newChapter.push(chapters[i]); - } - } - this.flattenChapters = newChapter; - return newChapter; - } - getChapter() { - return this.chapterList; - } - getChapterDoc() { - return this.chapterDocList; - } - goToPercentage(percentage) { - return __awaiter(this, void 0, void 0, function* () { - if (this.flattenChapters.length > 0) { - let chapterIndex = percentage === 1 - ? this.flattenChapters.length - 1 - : Math.floor(this.flattenChapters.length * percentage); - yield this.goToChapter(this.flattenChapters[chapterIndex].index.toString(), this.flattenChapters[chapterIndex].href, this.flattenChapters[chapterIndex].label); - } - }); - } - goToChapterIndex(targetChapterIndex) { - return __awaiter(this, void 0, void 0, function* () { - if (this.flattenChapters.length > 0) { - yield this.goToChapter(this.flattenChapters[targetChapterIndex].index, this.flattenChapters[targetChapterIndex].href, this.flattenChapters[targetChapterIndex].label); - } - }); - } - goToChapterDocIndex(chapterDocIndex) { - return __awaiter(this, void 0, void 0, function* () { - if (this.chapterDocList.length > 0) { - yield this.goToChapter(chapterDocIndex, this.chapterDocList[chapterDocIndex].href, this.chapterDocList[chapterDocIndex].label); - } - }); - } - goToChapter(chapterDocIndex, chapterHref, chapterTitle) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - yield handleRenderChapter(parseInt(chapterDocIndex), chapterTitle, chapterHref, this.chapterDocList, this.element, this.readerMode, this.format, this.tempLocation, doc, iframe); - if (chapterHref && chapterHref.indexOf("#") > -1) { - yield handleScrollPosition(this.element, this.readerMode, "", "", chapterHref, "", doc); - } - yield this.record(); - this.trigger("rendered"); - }); - } - goToPosition(bookLocationStr) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - let bookLocation = JSON.parse(bookLocationStr); - this.tempLocation = { - text: bookLocation.text, - chapterTitle: bookLocation.chapterTitle, - chapterDocIndex: bookLocation.chapterDocIndex, - chapterHref: bookLocation.chapterHref, - count: bookLocation.count, - page: bookLocation.page, - percentage: bookLocation.percentage, - }; - let { text, chapterTitle, chapterDocIndex, chapterHref, count, page, cfi } = bookLocation; - yield handleRenderChapter(parseInt(chapterDocIndex), chapterTitle, chapterHref, this.chapterDocList, this.element, this.readerMode, this.format, this.tempLocation, doc, iframe); - if (cfi) { - const cfiInfo = new CFI(cfi, {}); - let doc = this.getDocument(); - if (!doc) { - return; - } - const { node, offset } = cfiInfo.resolve(doc, {}); - if (node) { - let element = null; - let currentNode = node; - while (currentNode) { - const temp = currentNode; - if (temp.tagName && - "h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker".indexOf(temp.tagName.toLowerCase()) > -1) { - element = temp; - break; - } - currentNode = currentNode.parentNode; - } - if (element) { - count = "ignore"; - text = element.textContent; - } - } - } - yield handleScrollPosition(this.element, this.readerMode, text, count, "", page, doc); - rangy.init(); - yield this.record(); - this.trigger("rendered"); - // this.addPageAnimation(); - }); - } - getDocument() { - let pageArea = document.getElementById("page-area"); - if (!pageArea) - return null; - let iframe = pageArea.getElementsByTagName("iframe")[0]; - if (!iframe) - return null; - let doc = iframe.contentDocument; - if (!doc) { - return null; - } - return doc; - } - getIframe() { - let pageArea = document.getElementById("page-area"); - if (!pageArea) - return null; - let iframe = pageArea.getElementsByTagName("iframe")[0]; - if (!iframe) - return null; - return iframe; - } - goToNode(node) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) { - return; - } - let targetNode = getCloestBlock(node, this.element, this.readerMode); - let left = targetNode - ? convertStyleNum(targetNode.offsetLeft) - - convertStyleNum(targetNode.marginLeft || - parseFloat(getComputedStyle(targetNode).marginLeft)) - : 0; - let top = targetNode - ? convertStyleNum(targetNode.offsetTop) - - convertStyleNum(targetNode.marginTop || - parseFloat(getComputedStyle(targetNode).marginTop)) - : 0; - if (this.readerMode !== "scroll") { - doc.body.scrollTo(left, 0); - } - else { - this.element.scrollTo(0, top); - } - yield this.record(); - this.trigger("rendered"); - }); - } - removeContent() { - this.element.innerHTML = ""; - } - prev() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) { - return; - } - if ((this.readerMode === "scroll" && - convertStyleNum(this.element.scrollTop) === 0) || - (this.readerMode !== "scroll" && - convertStyleNum(doc.body.scrollLeft) === 0)) { - if (this.tempLocation.chapterDocIndex === "0") { - return; - } - if (this.animation === "mimical" && - this.readerMode !== "scroll" && - this.isMobile === "yes") { - //sleep 1s prevent animation stuck - yield new Promise((r) => setTimeout(r, 500)); - } - yield handlePrevChapter(this.element, this.flatChapter(this.chapterList), this.chapterDocList, this.readerMode, this.format, this.tempLocation, doc, iframe); - let chapterDocIndex = parseInt(this.tempLocation.chapterDocIndex || "-1"); - if (chapterDocIndex > -1) { - if (this.readerMode === "scroll") { - this.element.scrollTo(0, doc.body.scrollHeight); - } - else { - doc.body.scrollTo(doc.body.scrollWidth, 0); - } - } - this.trigger("rendered"); - } - else if (this.readerMode === "scroll") { - // scroll readerMode under normal condition - this.element.scrollBy({ - left: 0, - top: -(this.element.clientHeight - 50), - behavior: "smooth", - }); - } - else { - yield handleScrollPage(this.element, this.animation, 1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile); - } - yield this.record(); - }); - } - next() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) { - return; - } - if ((Math.abs(doc.body.scrollWidth - - convertStyleNum(doc.body.scrollLeft) - - doc.body.clientWidth) < 50 && - this.readerMode !== "scroll") || - (Math.abs(this.element.scrollHeight - - convertStyleNum(this.element.scrollTop) - - this.element.clientHeight) < 20 && - this.readerMode === "scroll")) { - if (this.animation === "mimical" && - this.readerMode !== "scroll" && - this.isMobile === "yes") { - //sleep 1s prevent animation stuck - yield new Promise((r) => setTimeout(r, 500)); - } - // if the last page - yield handleNextChapter(this.element, this.flatChapter(this.chapterList), this.chapterDocList, this.readerMode, this.format, this.tempLocation, doc, iframe); - this.trigger("rendered"); - } - else if (this.readerMode === "scroll") { - // scroll readerMode under normal condition - if (Math.abs(this.element.scrollHeight - - convertStyleNum(this.element.scrollTop) - - this.element.clientHeight) - - (this.element.clientHeight - 50) < - 20 && - Math.abs(this.element.scrollHeight - - convertStyleNum(this.element.scrollTop) - - this.element.clientHeight) > 20) { - this.element.scrollTo({ - left: 0, - top: this.element.scrollHeight - 20, - behavior: "smooth", - }); - } - else { - this.element.scrollBy({ - left: 0, - top: this.element.clientHeight - 50, - behavior: "smooth", - }); - } - } - else { - // single and double readerMode under normal condition - yield handleScrollPage(this.element, this.animation, -1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile); - } - yield this.record(); - }); - } - prevChapter() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - yield handlePrevChapter(this.element, this.flatChapter(this.chapterList), this.chapterDocList, this.readerMode, this.format, this.tempLocation, doc, iframe); - yield this.record(); - this.trigger("rendered"); - }); - } - nextChapter() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - yield handleNextChapter(this.element, this.flatChapter(this.chapterList), this.chapterDocList, this.readerMode, this.format, this.tempLocation, doc, iframe); - yield this.record(); - this.trigger("rendered"); - }); - } - visibleText() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return ""; - return getVisibleText(this.element, this.readerMode, doc); - }); - } - audioText() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return ""; - return getAudioText(this.element, this.readerMode, doc); - }); - } - chapterText() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return ""; - return doc.body.textContent || ""; - }); - } - autoScroll(rate, isStart) { - let doc = this.getDocument(); - if (!doc) - return; - if (this.scrollTimer) { - cancelAnimationFrame(this.scrollTimer); - this.scrollTimer = null; - } - if (this.recordTimer) { - clearInterval(this.recordTimer); - this.recordTimer = null; - } - if (isStart === "no" || this.readerMode !== "scroll") { - return; - } - let accumulatedScroll = 0; // 累积滚动量 - let frameCount = 0; // 帧计数器 - const scrollStep = () => { - accumulatedScroll += rate; - frameCount++; - // 对于慢速滚动,使用更精细的控制 - if (Math.abs(rate) < 1) { - // 每隔一定帧数或累积到足够像素时滚动 - const shouldScroll = Math.abs(accumulatedScroll) >= 0.5 || - frameCount % Math.max(1, Math.floor(30 / Math.abs(rate))) === 0; - if (shouldScroll && Math.abs(accumulatedScroll) >= 0.1) { - const scrollAmount = Math.round(accumulatedScroll * 10) / 10; // 保留一位小数 - this.element.scrollBy({ - left: 0, - top: scrollAmount, - behavior: "auto", - }); - accumulatedScroll = 0; // 重置累积量 - frameCount = 0; // 重置帧计数 - } - } - else { - // 快速滚动时保持原有逻辑 - if (Math.abs(accumulatedScroll) >= 1) { - const scrollAmount = Math.floor(accumulatedScroll); - this.element.scrollBy({ - left: 0, - top: scrollAmount, - behavior: "auto", - }); - accumulatedScroll -= scrollAmount; // 减去已滚动的量 - } - } - this.scrollTimer = requestAnimationFrame(scrollStep); - }; - this.scrollTimer = requestAnimationFrame(scrollStep); - this.recordTimer = setInterval(() => { - if (this.readerMode === "scroll" && - Math.abs(this.element.scrollHeight - - this.element.scrollTop - - this.element.clientHeight) < 10) { - this.nextChapter(); - } - this.record(); - }, 3000); - } - autoScrollIOS(rate, isStart) { - let doc = this.getDocument(); - if (!doc) - return; - if (this.scrollTimer) { - clearInterval(this.scrollTimer); - this.scrollTimer = null; - } - if (this.recordTimer) { - clearInterval(this.recordTimer); - this.recordTimer = null; - } - if (isStart === "no" || this.readerMode !== "scroll") { - return; - } - let accumulatedScroll = 0; // 累积滚动量 - let realScrollTop = this.element.scrollTop; // 记录真实滚动位置 - // this.scrollTimer = requestAnimationFrame(scrollStep); - this.scrollTimer = setInterval(() => { - accumulatedScroll += rate; - if (doc) { - doc.body.style.transform = `translateY(-${accumulatedScroll}px)`; - // 每隔一定距离同步真实滚动位置,避免transform累积过大 - if (Math.abs(accumulatedScroll) >= 50) { - // 重置transform - doc.body.style.transform = "translateY(0px)"; - // 更新真实滚动位置 - realScrollTop += accumulatedScroll; - this.element.scrollTo({ - left: 0, - top: realScrollTop, - behavior: "auto", - }); - // 重置累积量 - accumulatedScroll = 0; - } - } - }, 30); - this.recordTimer = setInterval(() => { - if (this.readerMode === "scroll" && - Math.abs(this.element.scrollHeight - - this.element.scrollTop - - this.element.clientHeight) < 10) { - this.nextChapter(); - } - this.record(); - }, 3000); - } - highlightSearchNode(text, style) { - let doc = this.getDocument(); - if (!doc) - return; - handleHighlightSearchNode(text, style, doc); - } - highlightAudioNode(text, style) { - let doc = this.getDocument(); - if (!doc) - return; - handleHighlightAudioNode(text, style, doc, this.element, this.readerMode); - } - doSearch(keyword) { - return __awaiter(this, void 0, void 0, function* () { - if (this.format === "PDF") { - return yield getPDFSearchResult(keyword, this.chapterDocList); - } - else { - return yield getSearchResult(keyword, this.chapterDocList); - } - }); - } - getProgress() { - let doc = this.getDocument(); - if (!doc) - return; - return progressInfo(this.readerMode, doc, this.element); - } - record() { - return __awaiter(this, void 0, void 0, function* () { - if (this.animation !== "") { - yield new Promise((r) => setTimeout(r, 1000)); - } - let doc = this.getDocument(); - if (!doc) - return; - yield handleRecord(this.element, this.readerMode, this.flatChapter(this.chapterList), this.chapterDocList, this.tempLocation, doc, null); - this.trigger("page-changed"); - }); - } - getPosition() { - return this.tempLocation; - } - getNotePosition() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return; - let selectedElement = getSelectedElement(doc); - if (!selectedElement) - return; - yield handleRecord(this.element, this.readerMode, this.flatChapter(this.chapterList), this.chapterDocList, this.tempLocation, doc, selectedElement); - return this.tempLocation; - }); - } - setStyle(css) { - let doc = this.getDocument(); - if (!doc) - return; - var defaultStyle = document.createElement("style"); - defaultStyle.innerHTML = css; - doc.head.appendChild(defaultStyle); - } - getHightlightCoords() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - let charRange = rangy.getSelection(iframe).saveCharacterRanges(doc.body)[0]; - return charRange; - }); - } - renderHighlighters(notes, handleNoteClick) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - clearHighlight(doc); - // if more than 20 notes, render one by one to avoid blocking the UI thread - for (let index = 0; index < notes.length; index++) { - const item = notes[index]; - try { - yield new Promise((r) => setTimeout(r, 5)); - showNoteHighlight(JSON.parse(item.range), item.color, item.key, handleNoteClick, doc, iframe); - // highlighter.highlightSelection(classes[item.color]); - } - catch (e) { - console.error(e, "Exception has been caught when restore character ranges."); - return; - } - } - }); - } - removeOneNote(key, chapterDocIndex) { - let doc = this.getDocument(); - if (!doc) - return; - const elements = doc.querySelectorAll(".kookit-note"); - for (let index = 0; index < elements.length; index++) { - const element = elements[index]; - const dataKey = element.getAttribute("data-key"); - if (dataKey === key) { - element.parentNode.removeChild(element); - } - } - } - createOneNote(item, handleNoteClick) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - showNoteHighlight(JSON.parse(item.range), item.color, item.key, handleNoteClick, doc, iframe); - }); - } - displayFontBase64(fontName, fontBase64, fontFormat, fontType) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc || fontBase64.length === 0) - return; - const font = new FontFace(fontName, `url(data:font/${fontType};charset=utf-8;base64,${fontBase64})`); - let loadedFont = yield font.load(); - // 将加载的字体添加到文档的字体集合中 - document.fonts.add(loadedFont); - const fontFaceCSS = "@font-face {" + - " font-family: '" + - fontName + - "';" + - " src: url('data:font/" + - fontType + - ";charset=utf-8;base64," + - fontBase64 + - "') format('" + - fontFormat + - "');" + - "}"; - const styleElement = document.createElement("style"); - styleElement.type = "text/css"; - styleElement.appendChild(document.createTextNode(fontFaceCSS)); - doc.head.appendChild(styleElement); - }); - } - displayFontUrl(fontName, fontUrl) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc || fontUrl.length === 0) - return; - // 使用 FontFace API 创建字体 - const font = new FontFace(fontName, `url(${fontUrl})`); - // 加载字体并监听加载完成事件 - let loadedFont = yield font.load(); - // 将加载的字体添加到文档的字体集合中 - document.fonts.add(loadedFont); - const fontFaceCSS = "@font-face {" + - " font-family: '" + - fontName + - "';" + - " src: url('" + - fontUrl + - "') format('truetype');" + - "}"; - const styleElement = document.createElement("style"); - styleElement.type = "text/css"; - styleElement.appendChild(document.createTextNode(fontFaceCSS)); - doc.head.appendChild(styleElement); - }); - } - getAllDocuments() { - let doc = this.getDocument(); - if (!doc) - return []; - if (this.format !== "PDF") { - return [doc]; - } - let iframes = doc.querySelectorAll("iframe"); - let documents = []; - iframes.forEach((iframe) => { - let iframeDoc = iframe.contentDocument; - if (iframeDoc) { - documents.push(iframeDoc); - } - }); - return [doc, ...documents]; - } - getAllIframes() { - let iframe = this.getIframe(); - if (!iframe) - return []; - if (this.format !== "PDF") { - return [iframe]; - } - let doc = this.getDocument(); - if (!doc) - return []; - let iframes = doc.querySelectorAll("iframe"); - let iframeElements = []; - iframes.forEach((iframe) => { - let iframeElement = iframe; - iframeElements.push(iframeElement); - }); - return [iframe, ...iframeElements]; - } - addTouchEvent(isAndroid, touchControlRule) { - let docs = this.getAllDocuments(); - let iframes = this.getAllIframes(); - for (let index = 0; index < docs.length; index++) { - const doc = docs[index]; - const iframe = iframes[index]; - if (!doc || !iframe) - continue; - let iframeId = iframe.id; - if (this.touchEventSet[iframeId]) { - continue; - } - this.touchEventSet[iframeId] = true; - if (isAndroid === "yes") { - addAndroidTouchEvent(doc, iframe, this.element, this.readerMode, this.animation, this.format, touchControlRule, this); - } - else { - addAppleTouchEvent(doc, iframe, this.element, this.readerMode, this.animation, this.format, touchControlRule, this); - } - } - } - clearSelection() { - var _a, _b; - let iframes = this.getAllIframes(); - for (let index = 0; index < iframes.length; index++) { - const iframe = iframes[index]; - if (!iframe) - continue; - let iWin = iframe.contentWindow || ((_a = iframe.contentDocument) === null || _a === void 0 ? void 0 : _a.defaultView); - if (!iWin || !iWin.getSelection()) - return; - (_b = iWin.getSelection()) === null || _b === void 0 ? void 0 : _b.empty(); - } - } - restoreSelectionClearHighlight() { - if (this.format === "PDF") { - return; - } - let doc = this.getDocument(); - if (!doc) - return; - let tempHighlights = doc.querySelectorAll("#temp-highlight"); - tempHighlights.forEach((element) => { - var _a; - (_a = element.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(element); - }); - let iframe = this.getIframe(); - if (!iframe) - return; - let charRange = window.charRange; - if (!charRange) - return; - rangy.getSelection(iframe).restoreCharacterRanges(doc, [charRange]); - } -} - -const mimetype = { - svg: "image/svg+xml", - png: "image/png", - jpg: "image/jpeg", - jpeg: "image/jpeg", - gif: "image/gif", - webp: "image/webp", - zip: "application/zip", - rar: "application/x-rar-compressed", - "7z": "application/x-7z-compressed", - tar: "application/x-tar", - html: "text/html", - htm: "text/html", - xml: "text/xml", - xhtml: "application/xhtml+xml", - css: "text/css", -}; -const mimetypeReverse = { - "image/svg+xml": "svg", - "image/png": "png", - "image/jpeg": "jpg", - "image/gif": "gif", - "image/webp": "webp", - "application/zip": "zip", - "application/x-rar-compressed": "rar", - "application/x-7z-compressed": "7z", - "application/x-tar": "tar", - "text/html": "html", - "text/xml": "xml", - "application/xhtml+xml": "xhtml", - "text/css": "css", -}; - -const makeCacheBook = (bookBuffer) => __awaiter(void 0, void 0, void 0, function* () { - let zip = yield JSZip.loadAsync(bookBuffer); - var tocZip = zip.file("toc.json"); - let toc = []; - if (tocZip) { - toc = JSON.parse(yield tocZip.async("string")); - } - var sectionsZip = zip.file("sections.json"); - let sections = []; - if (sectionsZip) { - sections = JSON.parse(yield sectionsZip.async("string")); - } - const load = (index) => __awaiter(void 0, void 0, void 0, function* () { - var chapterZip = zip.file("chapters/" + index + ".html"); - let chapter = ""; - if (chapterZip) { - chapter = yield chapterZip.async("string"); - } - return URL.createObjectURL(new Blob([chapter], { type: "text/html" })); - }); - const unload = (index) => { }; - const book = {}; - book.getCover = () => ""; - const loadAsset = (url) => __awaiter(void 0, void 0, void 0, function* () { - var assetZip = zip.file(url); - let asset; - if (assetZip) { - asset = yield assetZip.async("arraybuffer"); - } - return URL.createObjectURL(new Blob([asset], { type: mimetype[url.split(".").reverse()[0]] })); - }); - book.sections = sections.map((item, index) => ({ - id: item.href, - load: () => load(index), - unload: () => unload(), - loadAsset: (url) => loadAsset(url), - })); - book.toc = toc.map((item) => ({ - label: item.label, - href: item.href, - subitems: item.subitems, - })); - book.rendition = { layout: "pre-paginated" }; - book.resolveHref = (href) => { - return { index: _.findLastIndex(sections, { href }) }; - }; - book.splitTOCHref = (href) => [href, null]; - book.getTOCFragment = (doc) => doc.documentElement; - return book; -}); -const getCache = (book) => { - return new Promise((resolve, reject) => __awaiter(void 0, void 0, void 0, function* () { - let parser = new GeneralParser(book); - let chapterList = yield parser.getChapter(book.toc); - let chapterDocList = yield parser.getChapterDoc(); - let toc = chapterList; - let sections = chapterDocList.map((item) => { - return { href: item.href, label: item.label }; - }); - let chapterTexts = yield Promise.all(chapterDocList.map((item) => __awaiter(void 0, void 0, void 0, function* () { - let chapterText = ""; - if (item.text && item.text.load) { - let blob = yield fetch(yield item.text.load()).then((r) => r.blob()); - chapterText = yield blob.text(); - } - return chapterText; - }))); - let zip = new JSZip(); - zip.file("toc.json", JSON.stringify(toc)); - zip.file("sections.json", JSON.stringify(sections)); - let chapters = []; - //todo get css, fonts and images blob - for (let index = 0; index < chapterTexts.length; index++) { - let chapterDoc = new DOMParser().parseFromString(chapterTexts[index], "text/html"); - let imgDomList = getImageElement(chapterDoc); - for (let subindex = 0; subindex < imgDomList.length; subindex++) { - let subImgZip = zip.folder("imgs/" + index); - if (!subImgZip) { - break; - } - let imageUrl = imgDomList[subindex].getAttribute("src") || - imgDomList[subindex].getAttribute("xlink:href"); - if (imageUrl) { - try { - let blob = yield fetch(yield imageUrl).then((r) => r.blob()); - subImgZip.file(subindex + "." + mimetypeReverse[blob.type], blob); - let newUrl = "imgs/" + - index + - "/" + - subindex + - "." + - mimetypeReverse[blob.type]; - imgDomList[subindex].src = newUrl; - if (imgDomList[subindex].getAttribute("xlink:href")) { - imgDomList[subindex].setAttribute("xlink:href", newUrl); - } - } - catch (error) { - console.error(error); - } - } - } - let linkList = Array.from(chapterDoc.getElementsByTagName("link")); - for (let subindex = 0; subindex < linkList.length; subindex++) { - let link = linkList[subindex]; - let subCssZip = zip.folder("css/" + index); - if (!subCssZip) { - break; - } - if (link.getAttribute("href")) { - try { - let blob = yield fetch(yield link.getAttribute("href")).then((r) => r.blob()); - subCssZip.file(subindex + "." + mimetypeReverse[blob.type], blob); - link.href = - "css/" + - index + - "/" + - subindex + - "." + - mimetypeReverse[blob.type]; - } - catch (error) { - console.error(error); - } - } - } - chapters.push(chapterDoc.documentElement.innerHTML); - } - let configZip = zip.folder("chapters"); - if (!configZip) { - return; - } - for (let index = 0; index < chapters.length; index++) { - configZip.file(index + ".html", chapters[index]); - } - zip - .generateAsync({ type: "blob" }) - .then((blob) => __awaiter(void 0, void 0, void 0, function* () { - resolve(yield new Response(blob).arrayBuffer()); - })) - .catch((err) => { - resolve("err"); - }); - })); -}; - -class EpubRender extends GeneralRender { - constructor(epubBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "EPUB" })); - this.epubBuffer = epubBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - let blob = new Blob([this.epubBuffer]); - let file = new File([blob], "book", { - lastModified: new Date().getTime(), - type: blob.type, - }); - try { - const loader = yield this.makeZipLoader(file); - this.book = yield new EPUB(loader).init(); - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - } - catch (error) { - return ""; - } - }); - } - makeZipLoader(file) { - return __awaiter(this, void 0, void 0, function* () { - let zip = yield JSZip.loadAsync(file); - const entries = zip.files; - const loadText = (name) => __awaiter(this, void 0, void 0, function* () { - let entry = zip.file(name); - if (entry) { - return entry.async("string"); - } - return ""; - }); - const loadBlob = (name) => __awaiter(this, void 0, void 0, function* () { - let entry = zip.file(name); - if (entry) { - let buffer = yield entry.async("arraybuffer"); - return new Blob([buffer]); - } - return new Blob([new ArrayBuffer(0)]); - }); - const getSize = (name) => { - let entry = zip.file(name); - if (entry) { - return entry._data.uncompressedSize || 0; - } - }; - return { - entries: Object.values(entries).map((item) => { - return { filename: item.name }; - }), - loadText, - loadBlob, - getSize, - }; - }); - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - return yield parser.getMetadata(); - } - catch (error) { - console.error(error, "error"); - throw error; - } - }); - } -} - -const unescapeHTML = (str) => { - if (!str) return ""; - const textarea = document.createElement("textarea"); - textarea.innerHTML = str; - return textarea.value; -}; - -const MIME$1 = { - XML: "application/xml", - XHTML: "application/xhtml+xml", - HTML: "text/html", - CSS: "text/css", - SVG: "image/svg+xml", -}; - -const PDB_HEADER = { - name: [0, 32, "string"], - type: [60, 4, "string"], - creator: [64, 4, "string"], - numRecords: [76, 2, "uint"], -}; - -const PALMDOC_HEADER = { - compression: [0, 2, "uint"], - numTextRecords: [8, 2, "uint"], - recordSize: [10, 2, "uint"], - encryption: [12, 2, "uint"], -}; - -const MOBI_HEADER = { - magic: [16, 4, "string"], - length: [20, 4, "uint"], - type: [24, 4, "uint"], - encoding: [28, 4, "uint"], - uid: [32, 4, "uint"], - version: [36, 4, "uint"], - titleOffset: [84, 4, "uint"], - titleLength: [88, 4, "uint"], - localeRegion: [94, 1, "uint"], - localeLanguage: [95, 1, "uint"], - resourceStart: [108, 4, "uint"], - huffcdic: [112, 4, "uint"], - numHuffcdic: [116, 4, "uint"], - exthFlag: [128, 4, "uint"], - trailingFlags: [240, 4, "uint"], - indx: [244, 4, "uint"], -}; - -const KF8_HEADER = { - resourceStart: [108, 4, "uint"], - fdst: [192, 4, "uint"], - numFdst: [196, 4, "uint"], - frag: [248, 4, "uint"], - skel: [252, 4, "uint"], - guide: [260, 4, "uint"], -}; - -const EXTH_HEADER = { - magic: [0, 4, "string"], - length: [4, 4, "uint"], - count: [8, 4, "uint"], -}; - -const INDX_HEADER = { - magic: [0, 4, "string"], - length: [4, 4, "uint"], - type: [8, 4, "uint"], - idxt: [20, 4, "uint"], - numRecords: [24, 4, "uint"], - encoding: [28, 4, "uint"], - language: [32, 4, "uint"], - total: [36, 4, "uint"], - ordt: [40, 4, "uint"], - ligt: [44, 4, "uint"], - numLigt: [48, 4, "uint"], - numCncx: [52, 4, "uint"], -}; - -const TAGX_HEADER = { - magic: [0, 4, "string"], - length: [4, 4, "uint"], - numControlBytes: [8, 4, "uint"], -}; - -const HUFF_HEADER = { - magic: [0, 4, "string"], - offset1: [8, 4, "uint"], - offset2: [12, 4, "uint"], -}; - -const CDIC_HEADER = { - magic: [0, 4, "string"], - length: [4, 4, "uint"], - numEntries: [8, 4, "uint"], - codeLength: [12, 4, "uint"], -}; - -const FDST_HEADER = { - magic: [0, 4, "string"], - numEntries: [8, 4, "uint"], -}; - -const FONT_HEADER = { - flags: [8, 4, "uint"], - dataStart: [12, 4, "uint"], - keyLength: [16, 4, "uint"], - keyStart: [20, 4, "uint"], -}; - -const MOBI_ENCODING = { - 1252: "windows-1252", - 65001: "utf-8", -}; - -const EXTH_RECORD_TYPE = { - 100: ["creator", "string", true], - 101: ["publisher"], - 103: ["description"], - 104: ["isbn"], - 105: ["subject", "string", true], - 106: ["date"], - 108: ["contributor", "string", true], - 109: ["rights"], - 110: ["subjectCode", "string", true], - 112: ["source", "string", true], - 113: ["asin"], - 121: ["boundary", "uint"], - 122: ["fixedLayout"], - 125: ["numResources", "uint"], - 126: ["originalResolution"], - 127: ["zeroGutter"], - 128: ["zeroMargin"], - 129: ["coverURI"], - 132: ["regionMagnification"], - 201: ["coverOffset", "uint"], - 202: ["thumbnailOffset", "uint"], - 503: ["title"], - 524: ["language", "string", true], - 527: ["pageProgressionDirection"], -}; - -const MOBI_LANG = { - 1: [ - "ar", - "ar-SA", - "ar-IQ", - "ar-EG", - "ar-LY", - "ar-DZ", - "ar-MA", - "ar-TN", - "ar-OM", - "ar-YE", - "ar-SY", - "ar-JO", - "ar-LB", - "ar-KW", - "ar-AE", - "ar-BH", - "ar-QA", - ], - 2: ["bg"], - 3: ["ca"], - 4: ["zh", "zh-TW", "zh-CN", "zh-HK", "zh-SG"], - 5: ["cs"], - 6: ["da"], - 7: ["de", "de-DE", "de-CH", "de-AT", "de-LU", "de-LI"], - 8: ["el"], - 9: [ - "en", - "en-US", - "en-GB", - "en-AU", - "en-CA", - "en-NZ", - "en-IE", - "en-ZA", - "en-JM", - null, - "en-BZ", - "en-TT", - "en-ZW", - "en-PH", - ], - 10: [ - "es", - "es-ES", - "es-MX", - null, - "es-GT", - "es-CR", - "es-PA", - "es-DO", - "es-VE", - "es-CO", - "es-PE", - "es-AR", - "es-EC", - "es-CL", - "es-UY", - "es-PY", - "es-BO", - "es-SV", - "es-HN", - "es-NI", - "es-PR", - ], - 11: ["fi"], - 12: ["fr", "fr-FR", "fr-BE", "fr-CA", "fr-CH", "fr-LU", "fr-MC"], - 13: ["he"], - 14: ["hu"], - 15: ["is"], - 16: ["it", "it-IT", "it-CH"], - 17: ["ja"], - 18: ["ko"], - 19: ["nl", "nl-NL", "nl-BE"], - 20: ["no", "nb", "nn"], - 21: ["pl"], - 22: ["pt", "pt-BR", "pt-PT"], - 23: ["rm"], - 24: ["ro"], - 25: ["ru"], - 26: ["hr", null, "sr"], - 27: ["sk"], - 28: ["sq"], - 29: ["sv", "sv-SE", "sv-FI"], - 30: ["th"], - 31: ["tr"], - 32: ["ur"], - 33: ["id"], - 34: ["uk"], - 35: ["be"], - 36: ["sl"], - 37: ["et"], - 38: ["lv"], - 39: ["lt"], - 41: ["fa"], - 42: ["vi"], - 43: ["hy"], - 44: ["az"], - 45: ["eu"], - 46: ["hsb"], - 47: ["mk"], - 48: ["st"], - 49: ["ts"], - 50: ["tn"], - 52: ["xh"], - 53: ["zu"], - 54: ["af"], - 55: ["ka"], - 56: ["fo"], - 57: ["hi"], - 58: ["mt"], - 59: ["se"], - 62: ["ms"], - 63: ["kk"], - 65: ["sw"], - 67: ["uz", null, "uz-UZ"], - 68: ["tt"], - 69: ["bn"], - 70: ["pa"], - 71: ["gu"], - 72: ["or"], - 73: ["ta"], - 74: ["te"], - 75: ["kn"], - 76: ["ml"], - 77: ["as"], - 78: ["mr"], - 79: ["sa"], - 82: ["cy", "cy-GB"], - 83: ["gl", "gl-ES"], - 87: ["kok"], - 97: ["ne"], - 98: ["fy"], -}; - -const concatTypedArray = (a, b) => { - const result = new a.constructor(a.length + b.length); - result.set(a); - result.set(b, a.length); - return result; -}; -const concatTypedArray3 = (a, b, c) => { - const result = new a.constructor(a.length + b.length + c.length); - result.set(a); - result.set(b, a.length); - result.set(c, a.length + b.length); - return result; -}; - -const decoder = new TextDecoder(); -const getString = (buffer) => decoder.decode(buffer); -const getUint = (buffer) => { - if (!buffer) return; - const l = buffer.byteLength; - const func = l === 4 ? "getUint32" : l === 2 ? "getUint16" : "getUint8"; - return new DataView(buffer)[func](0); -}; -const getStruct = (def, buffer) => - Object.fromEntries( - Array.from(Object.entries(def)).map(([key, [start, len, type]]) => [ - key, - (type === "string" ? getString : getUint)( - buffer.slice(start, start + len) - ), - ]) - ); - -const getDecoder = (x) => new TextDecoder(MOBI_ENCODING[x]); - -const getVarLen = (byteArray, i = 0) => { - let value = 0, - length = 0; - for (const byte of byteArray.subarray(i, i + 4)) { - value = (value << 7) | ((byte & 0b111_1111) >>> 0); - length++; - if (byte & 0b1000_0000) break; - } - return { value, length }; -}; - -// variable-length quantity, but read from the end of data -const getVarLenFromEnd = (byteArray) => { - let value = 0; - for (const byte of byteArray.subarray(-4)) { - // `byte & 0b1000_0000` indicates the start of value - if (byte & 0b1000_0000) value = 0; - value = (value << 7) | (byte & 0b111_1111); - } - return value; -}; - -const countBitsSet = (x) => { - let count = 0; - for (; x > 0; x = x >> 1) if ((x & 1) === 1) count++; - return count; -}; - -const countUnsetEnd = (x) => { - let count = 0; - while ((x & 1) === 0) (x = x >> 1), count++; - return count; -}; - -const decompressPalmDOC = (array) => { - let output = []; - for (let i = 0; i < array.length; i++) { - const byte = array[i]; - if (byte === 0) output.push(0); // uncompressed literal, just copy it - else if (byte <= 8) - // copy next 1-8 bytes - for (const x of array.subarray(i + 1, (i += byte) + 1)) output.push(x); - else if (byte <= 0b0111_1111) output.push(byte); // uncompressed literal - else if (byte <= 0b1011_1111) { - // 1st and 2nd bits are 10, meaning this is a length-distance pair - // read next byte and combine it with current byte - const bytes = (byte << 8) | array[i++ + 1]; - // the 3rd to 13th bits encode distance - const distance = (bytes & 0b0011_1111_1111_1111) >>> 3; - // the last 3 bits, plus 3, is the length to copy - const length = (bytes & 0b111) + 3; - for (let j = 0; j < length; j++) - output.push(output[output.length - distance]); - } - // compressed from space plus char - else output.push(32, byte ^ 0b1000_0000); - } - return Uint8Array.from(output); -}; - -const read32Bits = (byteArray, from) => { - const startByte = from >> 3; - const end = from + 32; - const endByte = end >> 3; - let bits = 0n; - for (let i = startByte; i <= endByte; i++) - bits = (bits << 8n) | BigInt(byteArray[i] ?? 0); - return (bits >> (8n - BigInt(end & 7))) & 0xffffffffn; -}; - -const huffcdic = async (mobi, loadRecord) => { - const huffRecord = await loadRecord(mobi.huffcdic); - const { magic, offset1, offset2 } = getStruct(HUFF_HEADER, huffRecord); - if (magic !== "HUFF") throw new Error("Invalid HUFF record"); - - // table1 is indexed by byte value - const table1 = Array.from({ length: 256 }, (_, i) => offset1 + i * 4) - .map((offset) => getUint(huffRecord.slice(offset, offset + 4))) - .map((x) => [x & 0b1000_0000, x & 0b1_1111, x >>> 8]); - - // table2 is indexed by code length - const table2 = [null].concat( - Array.from({ length: 32 }, (_, i) => offset2 + i * 8).map((offset) => [ - getUint(huffRecord.slice(offset, offset + 4)), - getUint(huffRecord.slice(offset + 4, offset + 8)), - ]) - ); - - const dictionary = []; - for (let i = 1; i < mobi.numHuffcdic; i++) { - const record = await loadRecord(mobi.huffcdic + i); - const cdic = getStruct(CDIC_HEADER, record); - if (cdic.magic !== "CDIC") throw new Error("Invalid CDIC record"); - // `numEntries` is the total number of dictionary data across CDIC records - // so `n` here is the number of entries in *this* record - const n = Math.min( - 1 << cdic.codeLength, - cdic.numEntries - dictionary.length - ); - const buffer = record.slice(cdic.length); - for (let i = 0; i < n; i++) { - const offset = getUint(buffer.slice(i * 2, i * 2 + 2)); - const x = getUint(buffer.slice(offset, offset + 2)); - const length = x & 0x7fff; - const decompressed = x & 0x8000; - const value = new Uint8Array( - buffer.slice(offset + 2, offset + 2 + length) - ); - dictionary.push([value, decompressed]); - } - } - - const decompress = (byteArray) => { - let output = new Uint8Array(); - const bitLength = byteArray.byteLength * 8; - for (let i = 0; i < bitLength;) { - const bits = Number(read32Bits(byteArray, i)); - let [found, codeLength, value] = table1[bits >>> 24]; - if (!found) { - while (bits >>> (32 - codeLength) < table2[codeLength][0]) - codeLength += 1; - value = table2[codeLength][1]; - } - if ((i += codeLength) > bitLength) break; - - const code = value - (bits >>> (32 - codeLength)); - let [result, decompressed] = dictionary[code]; - if (!decompressed) { - // the result is itself compressed - result = decompress(result); - // cache the result for next time - dictionary[code] = [result, true]; - } - output = concatTypedArray(output, result); - } - return output; - }; - return decompress; -}; - -const getIndexData = async (indxIndex, loadRecord) => { - const indxRecord = await loadRecord(indxIndex); - const indx = getStruct(INDX_HEADER, indxRecord); - if (indx.magic !== "INDX") throw new Error("Invalid INDX record"); - const decoder = getDecoder(indx.encoding); - - const tagxBuffer = indxRecord.slice(indx.length); - const tagx = getStruct(TAGX_HEADER, tagxBuffer); - if (tagx.magic !== "TAGX") throw new Error("Invalid TAGX section"); - const numTags = (tagx.length - 12) / 4; - const tagTable = Array.from( - { length: numTags }, - (_, i) => new Uint8Array(tagxBuffer.slice(12 + i * 4, 12 + i * 4 + 4)) - ); - - const cncx = {}; - let cncxRecordOffset = 0; - for (let i = 0; i < indx.numCncx; i++) { - const record = await loadRecord(indxIndex + indx.numRecords + i + 1); - const array = new Uint8Array(record); - for (let pos = 0; pos < array.byteLength;) { - const index = pos; - const { value, length } = getVarLen(array, pos); - pos += length; - const result = record.slice(pos, pos + value); - pos += value; - cncx[cncxRecordOffset + index] = decoder.decode(result); - } - cncxRecordOffset += 0x10000; - } - - const table = []; - for (let i = 0; i < indx.numRecords; i++) { - const record = await loadRecord(indxIndex + 1 + i); - const array = new Uint8Array(record); - const indx = getStruct(INDX_HEADER, record); - if (indx.magic !== "INDX") throw new Error("Invalid INDX record"); - for (let j = 0; j < indx.numRecords; j++) { - const offsetOffset = indx.idxt + 4 + 2 * j; - const offset = getUint(record.slice(offsetOffset, offsetOffset + 2)); - - const length = getUint(record.slice(offset, offset + 1)); - const name = getString(record.slice(offset + 1, offset + 1 + length)); - - const tags = []; - const startPos = offset + 1 + length; - let controlByteIndex = 0; - let pos = startPos + tagx.numControlBytes; - for (const [tag, numValues, mask, end] of tagTable) { - if (end & 1) { - controlByteIndex++; - continue; - } - const offset = startPos + controlByteIndex; - const value = getUint(record.slice(offset, offset + 1)) & mask; - if (value === mask) { - if (countBitsSet(mask) > 1) { - const { value, length } = getVarLen(array, pos); - tags.push([tag, null, value, numValues]); - pos += length; - } else tags.push([tag, 1, null, numValues]); - } else tags.push([tag, value >> countUnsetEnd(mask), null, numValues]); - } - - const tagMap = {}; - for (const [tag, valueCount, valueBytes, numValues] of tags) { - const values = []; - if (valueCount != null) { - for (let i = 0; i < valueCount * numValues; i++) { - const { value, length } = getVarLen(array, pos); - values.push(value); - pos += length; - } - } else { - let count = 0; - while (count < valueBytes) { - const { value, length } = getVarLen(array, pos); - values.push(value); - pos += length; - count += length; - } - } - tagMap[tag] = values; - } - table.push({ name, tagMap }); - } - } - return { table, cncx }; -}; - -const getNCX = async (indxIndex, loadRecord) => { - const { table, cncx } = await getIndexData(indxIndex, loadRecord); - const items = table.map(({ tagMap }, index) => ({ - index, - offset: tagMap[1]?.[0], - size: tagMap[2]?.[0], - label: cncx[tagMap[3]] ?? "", - headingLevel: tagMap[4]?.[0], - pos: tagMap[6], - parent: tagMap[21]?.[0], - firstChild: tagMap[22]?.[0], - lastChild: tagMap[23]?.[0], - })); - const getChildren = (item) => { - if (item.firstChild == null) return item; - item.children = items - .filter((x) => x.parent === item.index) - .map(getChildren); - return item; - }; - return items.filter((item) => item.headingLevel === 0).map(getChildren); -}; - -const getEXTH = (buf, encoding) => { - const { magic, count } = getStruct(EXTH_HEADER, buf); - if (magic !== "EXTH") throw new Error("Invalid EXTH header"); - const decoder = getDecoder(encoding); - const results = {}; - let offset = 12; - for (let i = 0; i < count; i++) { - const type = getUint(buf.slice(offset, offset + 4)); - const length = getUint(buf.slice(offset + 4, offset + 8)); - if (type in EXTH_RECORD_TYPE) { - const [name, typ, many] = EXTH_RECORD_TYPE[type]; - const data = buf.slice(offset + 8, offset + length); - const value = typ === "uint" ? getUint(data) : decoder.decode(data); - if (many) { - results[name] ??= []; - results[name].push(value); - } else results[name] = value; - } - offset += length; - } - return results; -}; - -const getFont = async (buf, unzlib) => { - const { flags, dataStart, keyLength, keyStart } = getStruct(FONT_HEADER, buf); - const array = new Uint8Array(buf.slice(dataStart)); - // deobfuscate font - if (flags & 0b10) { - const bytes = keyLength === 16 ? 1024 : 1040; - const key = new Uint8Array(buf.slice(keyStart, keyStart + keyLength)); - const length = Math.min(bytes, array.length); - for (var i = 0; i < length; i++) array[i] = array[i] ^ key[i % key.length]; - } - // decompress font - if (flags & 1) - try { - return await unzlib(array); - } catch (e) { - console.warn(e); - console.warn("Failed to decompress font"); - } - return array; -}; - -const isMOBI = async (file) => { - const magic = getString(await file.slice(60, 68).arrayBuffer()); - return magic === "BOOKMOBI"; // || magic === 'TEXtREAd' -}; - -class PDB { - #file; - #offsets; - pdb; - async open(file) { - this.#file = file; - const pdb = getStruct(PDB_HEADER, await file.slice(0, 78).arrayBuffer()); - this.pdb = pdb; - const buffer = await file.slice(78, 78 + pdb.numRecords * 8).arrayBuffer(); - // get start and end offsets for each record - this.#offsets = Array.from({ length: pdb.numRecords }, (_, i) => - getUint(buffer.slice(i * 8, i * 8 + 4)) - ).map((x, i, a) => [x, a[i + 1]]); - } - loadRecord(index) { - const offsets = this.#offsets[index]; - if (!offsets) throw new RangeError("Record index out of bounds"); - return this.#file.slice(...offsets).arrayBuffer(); - } - async loadMagic(index) { - const start = this.#offsets[index][0]; - return getString(await this.#file.slice(start, start + 4).arrayBuffer()); - } -} - -class MOBI extends PDB { - #start = 0; - #resourceStart; - #decoder; - #encoder; - #decompress; - #removeTrailingEntries; - constructor({ unzlib }) { - super(); - this.unzlib = unzlib; - } - async open(file) { - await super.open(file); - // TODO: if (this.pdb.type === 'TEXt') - this.headers = this.#getHeaders(await super.loadRecord(0)); - this.#resourceStart = this.headers.mobi.resourceStart; - let isKF8 = this.headers.mobi.version >= 8; - if (!isKF8) { - const boundary = this.headers.exth?.boundary; - if (boundary < 0xffffffff) - try { - // it's a "combo" MOBI/KF8 file; try to open the KF8 part - this.headers = this.#getHeaders(await super.loadRecord(boundary)); - this.#start = boundary; - isKF8 = true; - } catch (e) { - console.warn(e); - console.warn("Failed to open KF8; falling back to MOBI"); - } - } - await this.#setup(); - return isKF8 ? new KF8(this).init() : new MOBI6(this).init(); - } - #getHeaders(buf) { - const palmdoc = getStruct(PALMDOC_HEADER, buf); - const mobi = getStruct(MOBI_HEADER, buf); - if (mobi.magic !== "MOBI") throw new Error("Missing MOBI header"); - - const { titleOffset, titleLength, localeLanguage, localeRegion } = mobi; - mobi.title = buf.slice(titleOffset, titleOffset + titleLength); - const lang = MOBI_LANG[localeLanguage]; - mobi.language = lang?.[localeRegion >> 2] ?? lang?.[0]; - - const exth = - mobi.exthFlag & 0b100_0000 - ? getEXTH(buf.slice(mobi.length + 16), mobi.encoding) - : null; - const kf8 = mobi.version >= 8 ? getStruct(KF8_HEADER, buf) : null; - return { palmdoc, mobi, exth, kf8 }; - } - async #setup() { - const { palmdoc, mobi } = this.headers; - this.#decoder = getDecoder(mobi.encoding); - // `TextEncoder` only supports UTF-8 - // we are only encoding ASCII anyway, so I think it's fine - this.#encoder = new TextEncoder(); - - // set up decompressor - const { compression } = palmdoc; - this.#decompress = - compression === 1 - ? (f) => f - : compression === 2 - ? decompressPalmDOC - : compression === 17480 - ? await huffcdic(mobi, this.loadRecord.bind(this)) - : null; - if (!this.#decompress) throw new Error("Unknown compression type"); - - // set up function for removing trailing bytes - const { trailingFlags } = mobi; - const multibyte = trailingFlags & 1; - const numTrailingEntries = countBitsSet(trailingFlags >>> 1); - this.#removeTrailingEntries = (array) => { - for (let i = 0; i < numTrailingEntries; i++) { - const length = getVarLenFromEnd(array); - array = array.subarray(0, -length); - } - if (multibyte) { - const length = (array[array.length - 1] & 0b11) + 1; - array = array.subarray(0, -length); - } - return array; - }; - } - decode(...args) { - return this.#decoder.decode(...args); - } - encode(...args) { - return this.#encoder.encode(...args); - } - loadRecord(index) { - return super.loadRecord(this.#start + index); - } - loadMagic(index) { - return super.loadMagic(this.#start + index); - } - loadText(index) { - return this.loadRecord(index + 1) - .then((buf) => new Uint8Array(buf)) - .then(this.#removeTrailingEntries) - .then(this.#decompress); - } - async loadResource(index) { - const buf = await super.loadRecord(this.#resourceStart + index); - const magic = getString(buf.slice(0, 4)); - if (magic === "FONT") return getFont(buf, this.unzlib); - if (magic === "VIDE" || magic === "AUDI") return buf.slice(12); - return buf; - } - getNCX() { - const index = this.headers.mobi.indx; - if (index < 0xffffffff) return getNCX(index, this.loadRecord.bind(this)); - } - getMetadata() { - const { mobi, exth } = this.headers; - return { - identifier: mobi.uid.toString(), - title: unescapeHTML(exth?.title || this.decode(mobi.title)), - author: exth?.creator?.map(unescapeHTML), - publisher: unescapeHTML(exth?.publisher), - language: exth?.language ?? mobi.language, - published: exth?.date, - description: unescapeHTML(exth?.description), - subject: exth?.subject?.map(unescapeHTML), - rights: unescapeHTML(exth?.rights), - }; - } - async getCover() { - const { exth } = this.headers; - const offset = - exth?.coverOffset < 0xffffffff - ? exth?.coverOffset - : exth?.thumbnailOffset < 0xffffffff - ? exth?.thumbnailOffset - : null; - if (offset != null) { - const buf = await this.loadResource(offset); - return new Blob([buf]); - } - } -} - -const mbpPagebreakRegex = /<\s*(?:mbp:)?pagebreak[^>]*>/gi; -const fileposRegex = /<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>/gi; - -const getIndent = (el) => { - let x = 0; - while (el) { - const parent = el.parentElement; - if (parent) { - const tag = parent.tagName.toLowerCase(); - if (tag === "p") x += 1.5; - else if (tag === "blockquote") x += 2; - } - el = parent; - } - return x; -}; - -class MOBI6 { - parser = new DOMParser(); - serializer = new XMLSerializer(); - #resourceCache = new Map(); - #textCache = new Map(); - #cache = new Map(); - #sections; - #fileposList = []; - #type = MIME$1.HTML; - constructor(mobi) { - this.mobi = mobi; - } - async init() { - // load all text records in an array - let array = new Uint8Array(); - for (let i = 0; i < this.mobi.headers.palmdoc.numTextRecords; i++) - array = concatTypedArray(array, await this.mobi.loadText(i)); - - // convert to string so we can use regex - // note that `filepos` are byte offsets - // so it needs to preserve each byte as a separate character - // (see https://stackoverflow.com/q/50198017) - const str = Array.from(new Uint8Array(array), (c) => - String.fromCharCode(c) - ).join(""); - - // split content into sections at each `` - this.#sections = [0] - .concat(Array.from(str.matchAll(mbpPagebreakRegex), (m) => m.index)) - .map((x, i, a) => str.slice(x, a[i + 1])) - // recover the original raw bytes - .map((str) => Uint8Array.from(str, (x) => x.charCodeAt(0))) - .map((raw) => ({ book: this, raw })) - // get start and end filepos for each section - .reduce((arr, x) => { - const last = arr[arr.length - 1]; - x.start = last?.end ?? 0; - x.end = x.start + x.raw.byteLength; - return arr.concat(x); - }, []); - - this.sections = this.#sections.map((section, index) => ({ - id: index, - load: () => this.loadSection(section), - createDocument: () => this.createDocument(section), - resolveHref: (href) => this.resolveHref(href), - size: section.end - section.start, - })); - - try { - this.landmarks = await this.getGuide(); - const tocHref = this.landmarks.find(({ type }) => - type?.includes("toc") - )?.href; - if (tocHref) { - const { index } = this.resolveHref(tocHref); - const doc = await this.sections[index].createDocument(); - let lastItem; - let lastLevel = 0; - let lastIndent = 0; - const lastLevelOfIndent = new Map(); - const lastParentOfLevel = new Map(); - this.toc = Array.from(doc.querySelectorAll("a[filepos]")).reduce( - (arr, a) => { - const indent = getIndent(a); - const item = { - label: a.innerText?.trim(), - href: `#filepos${a.getAttribute("filepos")}`, - }; - const level = - indent > lastIndent - ? lastLevel + 1 - : indent === lastIndent - ? lastLevel - : lastLevelOfIndent.get(indent) ?? Math.max(0, lastLevel - 1); - if (level > lastLevel) { - if (lastItem) { - lastItem.subitems ??= []; - lastItem.subitems.push(item); - lastParentOfLevel.set(level, lastItem); - } else arr.push(item); - } else { - const parent = lastParentOfLevel.get(level); - if (parent) parent.subitems.push(item); - else arr.push(item); - } - lastItem = item; - lastLevel = level; - lastIndent = indent; - lastLevelOfIndent.set(indent, level); - return arr; - }, - [] - ); - } - } catch (e) { - console.warn(e); - } - - // get list of all `filepos` references in the book, - // which will be used to insert anchor elements - // because only then can they be referenced in the DOM - this.#fileposList = [ - ...new Set(Array.from(str.matchAll(fileposRegex), (m) => m[1])), - ] - .map((filepos) => ({ filepos, number: Number(filepos) })) - .sort((a, b) => a.number - b.number); - - this.metadata = this.mobi.getMetadata(); - this.getCover = this.mobi.getCover.bind(this.mobi); - return this; - } - async getGuide() { - const doc = await this.createDocument(this.#sections[0]); - return Array.from(doc.getElementsByTagName("reference"), (ref) => ({ - label: ref.getAttribute("title"), - type: ref.getAttribute("type")?.split(/\s/), - href: `#filepos${ref.getAttribute("filepos")}`, - })); - } - async loadResource(index) { - if (this.#resourceCache.has(index)) return this.#resourceCache.get(index); - const raw = await this.mobi.loadResource(index); - const url = URL.createObjectURL(new Blob([raw])); - this.#resourceCache.set(index, url); - return url; - } - async loadRecindex(recindex) { - return this.loadResource(Number(recindex) - 1); - } - async replaceResources(doc) { - for (const img of doc.querySelectorAll("img[recindex]")) { - const recindex = img.getAttribute("recindex"); - try { - img.src = await this.loadRecindex(recindex); - } catch (e) { - console.warn(`Failed to load image ${recindex}`); - } - } - for (const media of doc.querySelectorAll("[mediarecindex]")) { - const mediarecindex = media.getAttribute("mediarecindex"); - const recindex = media.getAttribute("recindex"); - try { - media.src = await this.loadRecindex(mediarecindex); - if (recindex) media.poster = await this.loadRecindex(recindex); - } catch (e) { - console.warn(`Failed to load media ${mediarecindex}`); - } - } - for (const a of doc.querySelectorAll("[filepos]")) { - const filepos = a.getAttribute("filepos"); - a.href = `#filepos${filepos}`; - } - } - async loadText(section) { - if (this.#textCache.has(section)) return this.#textCache.get(section); - const { raw } = section; - - // insert anchor elements for each `filepos` - const fileposList = this.#fileposList - .filter(({ number }) => number >= section.start && number < section.end) - .map((obj) => ({ ...obj, offset: obj.number - section.start })); - let arr = raw; - if (fileposList.length) { - arr = raw.subarray(0, fileposList[0].offset); - fileposList.forEach(({ filepos, offset }, i) => { - const next = fileposList[i + 1]; - const a = this.mobi.encode(``); - arr = concatTypedArray3(arr, a, raw.subarray(offset, next?.offset)); - }); - } - const str = this.mobi.decode(arr).replaceAll(mbpPagebreakRegex, ""); - this.#textCache.set(section, str); - return str; - } - async createDocument(section) { - const str = await this.loadText(section); - return this.parser.parseFromString(str, this.#type); - } - async loadSection(section) { - if (this.#cache.has(section)) return this.#cache.get(section); - const doc = await this.createDocument(section); - - // inject default stylesheet - const style = doc.createElement("style"); - doc.head.append(style); - // blockquotes in MOBI seem to have only a small left margin by default - // many books seem to rely on this, as it's the only way to set margin - // (since there's no CSS) - style.append( - doc.createTextNode(`blockquote { - margin-block-start: 0; - margin-block-end: 0; - margin-inline-start: 1em; - margin-inline-end: 0; - }`) - ); - - await this.replaceResources(doc); - const result = this.serializer.serializeToString(doc); - const url = URL.createObjectURL(new Blob([result], { type: this.#type })); - this.#cache.set(section, url); - return url; - } - resolveHref(href) { - const filepos = href.match(/#filepos(.*)/)[1]; - const number = Number(filepos); - const index = this.#sections.findIndex((section) => section.end > number); - const anchor = (doc) => doc.getElementById(`filepos${filepos}`); - return { index, anchor }; - } - splitTOCHref(href) { - const filepos = href.match(/#filepos(.*)/)[1]; - const number = Number(filepos); - const index = this.#sections.findIndex((section) => section.end > number); - return [index, `filepos${filepos}`]; - } - getTOCFragment(doc, id) { - return doc.getElementById(id); - } - isExternal(uri) { - return /^(?!blob|filepos)\w+:/i.test(uri); - } - destroy() { - for (const url of this.#resourceCache.values()) URL.revokeObjectURL(url); - for (const url of this.#cache.values()) URL.revokeObjectURL(url); - } -} - -// handlers for `kindle:` uris -const kindleResourceRegex = - /kindle:(flow|embed):(\w+)(?:\?mime=(\w+\/[-+.\w]+))?/; -const kindlePosRegex = /kindle:pos:fid:(\w+):off:(\w+)/; -const parseResourceURI = (str) => { - const [resourceType, id, type] = str.match(kindleResourceRegex).slice(1); - return { resourceType, id: parseInt(id, 32), type }; -}; -const parsePosURI = (str) => { - const [fid, off] = str.match(kindlePosRegex).slice(1); - return { fid: parseInt(fid, 32), off: parseInt(off, 32) }; -}; -const makePosURI = (fid = 0, off = 0) => - `kindle:pos:fid:${fid.toString(32).toUpperCase().padStart(4, "0")}:off:${off - .toString(32) - .toUpperCase() - .padStart(10, "0")}`; - -// `kindle:pos:` links are originally links that contain fragments identifiers -// so there should exist an element with `id` or `name` -// otherwise try to find one with an `aid` attribute -const getFragmentSelector = (str) => { - const match = str.match(/\s(id|name|aid)\s*=\s*['"]([^'"]*)['"]/i); - if (!match) return; - const [, attr, value] = match; - return `[${attr}="${CSS.escape(value)}"]`; -}; - -// replace asynchronously and sequentially -const replaceSeries = async (str, regex, f) => { - const matches = []; - str.replace(regex, (...args) => (matches.push(args), null)); - const results = []; - for (const args of matches) results.push(await f(...args)); - return str.replace(regex, () => results.shift()); -}; - -const getPageSpread = (properties) => { - for (const p of properties) { - if (p === "page-spread-left" || p === "rendition:page-spread-left") - return "left"; - if (p === "page-spread-right" || p === "rendition:page-spread-right") - return "right"; - if (p === "rendition:page-spread-center") return "center"; - } -}; - -class KF8 { - parser = new DOMParser(); - serializer = new XMLSerializer(); - #cache = new Map(); - #fragmentOffsets = new Map(); - #fragmentSelectors = new Map(); - #tables = {}; - #sections; - #fullRawLength; - #rawHead = new Uint8Array(); - #rawTail = new Uint8Array(); - #lastLoadedHead = -1; - #lastLoadedTail = -1; - #type = MIME$1.XHTML; - #inlineMap = new Map(); - constructor(mobi) { - this.mobi = mobi; - } - async init() { - const loadRecord = this.mobi.loadRecord.bind(this.mobi); - const { kf8 } = this.mobi.headers; - - try { - const fdstBuffer = await loadRecord(kf8.fdst); - const fdst = getStruct(FDST_HEADER, fdstBuffer); - if (fdst.magic !== "FDST") throw new Error("Missing FDST record"); - const fdstTable = Array.from( - { length: fdst.numEntries }, - (_, i) => 12 + i * 8 - ).map((offset) => [ - getUint(fdstBuffer.slice(offset, offset + 4)), - getUint(fdstBuffer.slice(offset + 4, offset + 8)), - ]); - this.#tables.fdstTable = fdstTable; - this.#fullRawLength = fdstTable[fdstTable.length - 1][1]; - } catch { } - - const skelTable = (await getIndexData(kf8.skel, loadRecord)).table.map( - ({ name, tagMap }, index) => ({ - index, - name, - numFrag: tagMap[1][0], - offset: tagMap[6][0], - length: tagMap[6][1], - }) - ); - const fragData = await getIndexData(kf8.frag, loadRecord); - const fragTable = fragData.table.map(({ name, tagMap }) => ({ - insertOffset: parseInt(name), - selector: fragData.cncx[tagMap[2][0]], - index: tagMap[4][0], - offset: tagMap[6][0], - length: tagMap[6][1], - })); - this.#tables.skelTable = skelTable; - this.#tables.fragTable = fragTable; - - this.#sections = skelTable.reduce((arr, skel) => { - const last = arr[arr.length - 1]; - const fragStart = last?.fragEnd ?? 0, - fragEnd = fragStart + skel.numFrag; - const frags = fragTable.slice(fragStart, fragEnd); - const length = - skel.length + frags.map((f) => f.length).reduce((a, b) => a + b); - const totalLength = (last?.totalLength ?? 0) + length; - return arr.concat({ skel, frags, fragEnd, length, totalLength }); - }, []); - - const resources = await this.getResourcesByMagic(["RESC", "PAGE"]); - const pageSpreads = new Map(); - if (resources.RESC) { - const buf = await this.mobi.loadRecord(resources.RESC); - const str = this.mobi.decode(buf.slice(16)).replace(/\0/g, ""); - // the RESC record lacks the root `` element - // but seem to be otherwise valid XML - const index = str.search(/\?>/); - const xmlStr = `${str.slice(index)}`; - const opf = this.parser.parseFromString(xmlStr, MIME$1.XML); - for (const $itemref of opf.querySelectorAll("spine > itemref")) { - const i = parseInt($itemref.getAttribute("skelid")); - pageSpreads.set( - i, - getPageSpread($itemref.getAttribute("properties")?.split(" ") ?? []) - ); - } - } - - this.sections = this.#sections.map((section, index) => - section.frags.length - ? { - id: index, - load: () => this.loadSection(section), - createDocument: () => this.createDocument(section), - resolveHref: (href) => this.resolveHref(href), - size: section.length, - pageSpread: pageSpreads.get(index), - } - : { linear: "no" } - ); - - try { - const ncx = await this.mobi.getNCX(); - const map = ({ label, pos, children }) => { - const [fid, off] = pos; - const href = makePosURI(fid, off); - const arr = this.#fragmentOffsets.get(fid); - if (arr) arr.push(off); - else this.#fragmentOffsets.set(fid, [off]); - return { - label: unescapeHTML(label), - href, - subitems: children?.map(map), - }; - }; - this.toc = ncx?.map(map); - this.landmarks = await this.getGuide(); - } catch (e) { - console.warn(e); - } - - const { exth } = this.mobi.headers; - this.dir = exth.pageProgressionDirection; - this.rendition = { - layout: exth.fixedLayout === "true" ? "pre-paginated" : "reflowable", - viewport: Object.fromEntries( - exth.originalResolution - ?.split("x") - ?.slice(0, 2) - ?.map((x, i) => [i ? "height" : "width", x]) ?? [] - ), - }; - - this.metadata = this.mobi.getMetadata(); - this.getCover = this.mobi.getCover.bind(this.mobi); - return this; - } - // is this really the only way of getting to RESC, PAGE, etc.? - async getResourcesByMagic(keys) { - const results = {}; - const start = this.mobi.headers.kf8.resourceStart; - const end = this.mobi.pdb.numRecords; - for (let i = start; i < end; i++) { - try { - const magic = await this.mobi.loadMagic(i); - const match = keys.find((key) => key === magic); - if (match) results[match] = i; - } catch { } - } - return results; - } - async getGuide() { - const index = this.mobi.headers.kf8.guide; - if (index < 0xffffffff) { - const loadRecord = this.mobi.loadRecord.bind(this.mobi); - const { table, cncx } = await getIndexData(index, loadRecord); - return table.map(({ name, tagMap }) => ({ - label: cncx[tagMap[1][0]] ?? "", - type: name?.split(/\s/), - href: makePosURI(tagMap[6]?.[0] ?? tagMap[3]?.[0]), - })); - } - } - async loadResourceBlob(str) { - let { resourceType, id, type } = parseResourceURI(str); - if (type === "image/jpg") { - type = "image/jpeg"; - } - const raw = - resourceType === "flow" - ? await this.loadFlow(id) - : await this.mobi.loadResource(id - 1); - const result = [MIME$1.XHTML, MIME$1.HTML, MIME$1.CSS, MIME$1.SVG].includes(type) - ? await this.replaceResources(this.mobi.decode(raw)) - : raw; - const doc = - type === MIME$1.SVG ? this.parser.parseFromString(result, type) : null; - return [ - new Blob([result], { type }), - // SVG wrappers need to be inlined - // as browsers don't allow external resources when loading SVG as an image - doc?.getElementsByTagNameNS("http://www.w3.org/2000/svg", "image")?.length - ? doc.documentElement - : null, - ]; - } - async loadResource(str) { - if (this.#cache.has(str)) return this.#cache.get(str); - const [blob, inline] = await this.loadResourceBlob(str); - const url = inline ? str : URL.createObjectURL(blob); - if (inline) this.#inlineMap.set(url, inline); - this.#cache.set(str, url); - return url; - } - replaceResources(str) { - const regex = new RegExp(kindleResourceRegex, "g"); - return replaceSeries(str, regex, this.loadResource.bind(this)); - } - // NOTE: there doesn't seem to be a way to access text randomly? - // how to know the decompressed size of the records without decompressing? - // 4096 is just the maximum size - async loadRaw(start, end) { - // here we load either from the front or back until we have reached the - // required offsets; at worst you'd have to load half the book at once - const distanceHead = end - this.#rawHead.length; - const distanceEnd = - this.#fullRawLength == null - ? Infinity - : this.#fullRawLength - this.#rawTail.length - start; - // load from the start - if (distanceHead < 0 || distanceHead < distanceEnd) { - while (this.#rawHead.length < end) { - const index = ++this.#lastLoadedHead; - const data = await this.mobi.loadText(index); - this.#rawHead = concatTypedArray(this.#rawHead, data); - } - return this.#rawHead.slice(start, end); - } - // load from the end - while (this.#fullRawLength - this.#rawTail.length > start) { - const index = - this.mobi.headers.palmdoc.numTextRecords - 1 - ++this.#lastLoadedTail; - const data = await this.mobi.loadText(index); - this.#rawTail = concatTypedArray(data, this.#rawTail); - } - const rawTailStart = this.#fullRawLength - this.#rawTail.length; - return this.#rawTail.slice(start - rawTailStart, end - rawTailStart); - } - loadFlow(index) { - if (index < 0xffffffff) - return this.loadRaw(...this.#tables.fdstTable[index]); - } - async loadText(section) { - const { skel, frags, length } = section; - const raw = await this.loadRaw(skel.offset, skel.offset + length); - let skeleton = raw.slice(0, skel.length); - for (const frag of frags) { - const insertOffset = frag.insertOffset - skel.offset; - const offset = skel.length + frag.offset; - const fragRaw = raw.slice(offset, offset + frag.length); - skeleton = concatTypedArray3( - skeleton.slice(0, insertOffset), - fragRaw, - skeleton.slice(insertOffset) - ); - - const offsets = this.#fragmentOffsets.get(frag.index); - if (offsets) - for (const offset of offsets) { - const str = this.mobi.decode(fragRaw).slice(offset); - const selector = getFragmentSelector(str); - this.#setFragmentSelector(frag.index, offset, selector); - } - } - return this.mobi.decode(skeleton); - } - async createDocument(section) { - const str = await this.loadText(section); - return this.parser.parseFromString(str, this.#type); - } - async loadSection(section) { - if (this.#cache.has(section)) return this.#cache.get(section); - const str = await this.loadText(section); - const replaced = await this.replaceResources(str); - - // by default, type is XHTML; change to HTML if it's not valid XHTML - let doc = this.parser.parseFromString(replaced, this.#type); - if (doc.querySelector("parsererror")) { - this.#type = MIME$1.HTML; - doc = this.parser.parseFromString(replaced, this.#type); - } - for (const [url, node] of this.#inlineMap) { - for (const el of doc.querySelectorAll(`img[src="${url}"]`)) - el.replaceWith(node); - } - const url = URL.createObjectURL( - new Blob([this.serializer.serializeToString(doc)], { type: this.#type }) - ); - this.#cache.set(section, url); - return url; - } - getIndexByFID(fid) { - return this.#sections.findIndex((section) => - section.frags.some((frag) => frag.index === fid) - ); - } - #setFragmentSelector(id, offset, selector) { - const map = this.#fragmentSelectors.get(id); - if (map) map.set(offset, selector); - else { - const map = new Map(); - this.#fragmentSelectors.set(id, map); - map.set(offset, selector); - } - } - async resolveHref(href) { - const { fid, off } = parsePosURI(href); - const index = this.getIndexByFID(fid); - if (index < 0) return; - - const saved = this.#fragmentSelectors.get(fid)?.get(off); - if (saved) return { index, anchor: (doc) => doc.querySelector(saved) }; - - const { skel, frags } = this.#sections[index]; - const frag = frags.find((frag) => frag.index === fid); - const offset = skel.offset + skel.length + frag.offset; - const fragRaw = await this.loadRaw(offset, offset + frag.length); - const str = this.mobi.decode(fragRaw.slice(off)); - const selector = getFragmentSelector(str); - this.#setFragmentSelector(fid, off, selector); - const anchor = (doc) => doc.querySelector(selector); - return { index, anchor }; - } - splitTOCHref(href) { - const pos = parsePosURI(href); - const index = this.getIndexByFID(pos.fid); - return [index, pos]; - } - getTOCFragment(doc, { fid, off }) { - const selector = this.#fragmentSelectors.get(fid)?.get(off); - return doc.querySelector(selector); - } - isExternal(uri) { - return /^(?!blob|kindle)\w+:/i.test(uri); - } - destroy() { - for (const url of this.#cache.values()) URL.revokeObjectURL(url); - } -} - -class MobiRender extends GeneralRender { - constructor(mobiBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "MOBI" })); - this.mobiBuffer = mobiBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - resolveHref(href) { - return __awaiter(this, void 0, void 0, function* () { - let chapterDocIndex = this.tempLocation.chapterDocIndex; - let chapterDoc = this.chapterDocList[chapterDocIndex]; - if (chapterDoc) { - let result = yield chapterDoc.text.resolveHref(href); - if (!result) - return {}; - if (result.index === parseInt(chapterDocIndex)) { - let doc = this.getDocument(); - if (!doc) - return result; - let element = result.anchor(doc); - if (!element) - return result; - let id = element.getAttribute("id") || ""; - return Object.assign(Object.assign({}, result), { id }); - } - return result; - } - return {}; - }); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - try { - let blob = new Blob([this.mobiBuffer]); - let file = new File([blob], "book", { - lastModified: new Date().getTime(), - type: blob.type, - }); - if (yield isMOBI(file)) { - this.book = yield new MOBI({ unzlib: unzlibSync }).open(file); - } - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - return yield parser.getMetadata(); - } - catch (error) { - console.error(error); - throw error; - } - }); - } -} - -const pdfjsPath = path => `${isElectron() ? "." : ""}/lib/pdfjs/${path}`; - -const pdfjsLib = window.pdfjsLib; - -const fetchText$1 = async url => await (await fetch(url)).text(); -const isElectron = () => { - // Renderer process - if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') { - return true; - } - // Main process - if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) { - return true; - } - // Detect the user agent when the `nodeIntegration` option is set to true - if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) { - return true; - } - - return false; -}; -function vexPromptAsync(message, placeholder = '', value = '') { - return new Promise((resolve) => { - vex.dialog.prompt({ - message, - placeholder, - value, - callback: function (input) { - resolve(input); - } - }); - }); -} -// https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/text_layer_builder.css -const textLayerBuilderCSS = async () => await fetchText$1(pdfjsPath('text_layer_builder.css')); -// https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/annotation_layer_builder.css -const annotationLayerBuilderCSS = async () => await fetchText$1(pdfjsPath('annotation_layer_builder.css')); - -const render = async (page, doc, zoom, isMobile) => { - let devicePixelRatio = window.devicePixelRatio * (isMobile === "yes" ? (1 / zoom) * 1.5 : 1); - const scale = zoom * devicePixelRatio; - let docLayer = doc.querySelector('#koodoPDFLayer'); - docLayer.style.visibility = 'hidden'; - docLayer.style.transform = `scale(${1 / devicePixelRatio})`; - docLayer.style.transformOrigin = 'top left'; - docLayer.style.setProperty('--scale-factor', scale); - const viewport = page.getViewport({ scale }); - - - // the canvas must be in the `PDFDocument`'s `ownerDocument` - // (`globalThis.document` by default); that's where the fonts are loaded - const canvas = document.createElement('canvas'); - docLayer.style.width = `${viewport.width}px`; - docLayer.style.height = `${viewport.height}px`; - canvas.height = viewport.height; - canvas.width = viewport.width; - const canvasContext = canvas.getContext('2d'); - await page.render({ canvasContext, viewport, background: 'rgba(0,0,0,0)', }).promise; - doc.querySelector('#canvas').replaceChildren(doc.adoptNode(canvas)); - docLayer.style.overflow = 'hidden'; - const container = doc.querySelector('#textLayer'); - const textLayer = new pdfjsLib.TextLayer({ - textContentSource: await page.streamTextContent(), - container, viewport, - }); - await textLayer.render(); - - // hide "offscreen" canvases appended to docuemnt when rendering text layer - // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/pdf_viewer.css#L51-L58 - for (const canvas of document.querySelectorAll('.hiddenCanvasElement')) - Object.assign(canvas.style, { - position: 'absolute', - top: '0', - left: '0', - width: '0', - height: '0', - display: 'none', - }); - - // fix text selection - // https://github.com/mozilla/pdf.js/blob/642b9a5ae67ef642b9a8808fd9efd447e8c350e2/web/text_layer_builder.js#L105-L107 - const endOfContent = document.createElement('div'); - endOfContent.className = 'endOfContent'; - container.append(endOfContent); - let isSelecting = false; - let closestElement = null; - // TODO: this only works in Firefox; see https://github.com/mozilla/pdf.js/pull/17923 - container.onpointerdown = () => { - let iWin = doc?.defaultView; - const selectedText = iWin.getSelection().toString().trim(); - if (selectedText.length > 0) { - // if there is already selected text, do not start selecting - container.classList.remove('selecting'); - isSelecting = false; - endOfContent.remove(); - container.append(endOfContent); - return - } - container.classList.add('selecting'); - isSelecting = true; - }; - if (isMobile !== "yes") { - container.onpointerup = () => { - container.classList.remove('selecting'); - isSelecting = false; - endOfContent.remove(); - container.append(endOfContent); - }; - container.onpointermove = (e) => { - if (!isSelecting) return - let element = e.target.closest('.textLayer > span'); - // Check if the target or any of its parents is a span element within the text layer - const isText = element !== null; - container.style.cursor = isText ? 'text' : 'default'; - //if not, insert end of content element next to closest element - //remove end of content element from container - if (isText) { - closestElement = element; - } - - endOfContent.remove(); - container.insertBefore(endOfContent, closestElement); - - }; - } else { - //adapt to touch screen - doc.addEventListener('selectionchange', (e) => { - if (!isSelecting) return - // get the end element of the current selection - let iWin = doc?.defaultView; - var range = iWin.getSelection().getRangeAt(0); - // get the end element of the current range - var endNode = range.endContainer; - // Get the parent HTMLElement. If endNode is a Text node, parentNode is the element. - // If endNode is already an element (less common for endContainer), use it directly. - let element = endNode.nodeType === Node.TEXT_NODE ? endNode.parentNode : endNode; - element = element.closest('.textLayer > span'); - // Check if the target or any of its parents is a span element within the text layer - const isText = element !== null; - container.style.cursor = isText ? 'text' : 'default'; - //if not, insert end of content element next to closest element - //remove end of content element from container - if (isText) { - closestElement = element; - } - endOfContent.remove(); - container.insertBefore(endOfContent, closestElement.nextSibling ? closestElement.nextSibling : closestElement); - }); - } - - - - const div = doc.querySelector('#annotationLayer'); - try { - await new pdfjsLib.AnnotationLayer({ page, viewport, div }).render({ - annotations: await page.getAnnotations(), - linkService: { - goToDestination: () => { }, - getDestinationHash: dest => JSON.stringify(dest), - addLinkAttributes: (link, url) => link.href = url, - }, - }); - } catch (error) { - console.error(error); - } - - - - -}; - -const renderPage = async (page, getImageBlob) => { - const viewport = page.getViewport({ scale: 1 }); - if (getImageBlob) { - const canvas = document.createElement('canvas'); - canvas.height = viewport.height; - canvas.width = viewport.width; - const canvasContext = canvas.getContext('2d'); - await page.render({ canvasContext, viewport }).promise; - return new Promise(resolve => canvas.toBlob(resolve)) - } - const src = URL.createObjectURL(new Blob([` - - - - - -
-
-
-
-
-
- `], { type: 'text/html' })); - return src -}; - -const makeTOCItem = item => ({ - label: item.title, - href: item.dest ? JSON.stringify(item.dest) : null, - subitems: item.items.length ? item.items.map(makeTOCItem) : null, -}); -function getPasswordPrompt(type = "need") { - const lang = navigator.language?.toLowerCase() || "en"; - if (lang.startsWith("zh")) { - return type === "need" - ? "请输入PDF密码:" - : "密码错误,请重新输入:"; - } - // 可扩展更多语言 - return type === "need" - ? "Need password to open this PDF:" - : "Incorrect password, please try again:"; -} -const makePDF = async (file, password) => { - let pdf; - while (true) { - // 每次都新建 transport,避免 no PDFDataTransportStreamRangeReader instance found 错误 - const transport = new pdfjsLib.PDFDataRangeTransport(file.size, []); - transport.requestDataRange = (begin, end) => { - file.slice(begin, end).arrayBuffer().then(chunk => { - transport.onDataRange(begin, chunk); - }); - }; - try { - pdf = await pdfjsLib.getDocument({ - range: transport, - cMapUrl: pdfjsPath('cmaps/'), - standardFontDataUrl: pdfjsPath('standard_fonts/'), - isEvalSupported: false, - password, - }).promise; - break; // 成功加载,跳出循环 - } catch (e) { - if (e.name === 'PasswordException') { - if (e.code === pdfjsLib.PasswordResponses.NEED_PASSWORD) { - // 如果是 Electron 环境,使用 electron-prompt 获取密码 - if (isElectron()) { - password = await vexPromptAsync(getPasswordPrompt("need"), '', ''); - vex.closeAll(); // 关闭对话框 - } else { - password = prompt(getPasswordPrompt("need")); - } - - } else if (e.code === pdfjsLib.PasswordResponses.INCORRECT_PASSWORD) { - if (isElectron()) { - password = await vexPromptAsync(getPasswordPrompt("incorrect"), '', ''); - vex.closeAll(); // 关闭对话框 - } else { - password = prompt(getPasswordPrompt("incorrect")); - } - - } - if (!password) { - throw new Error('PDF loading failed: no password provided'); - } - } else { - throw e; - } - } - } - let isScannedPdf = false; - - let testedPage = pdf.numPages > 0 ? await pdf.getPage(Math.floor(pdf.numPages / 2) + 1) : null; - if (testedPage) { - const textContent = await testedPage.getTextContent(); - isScannedPdf = textContent.items.length === 0; - // 进一步检查文本有效性(避免误判带OCR的扫描件) - if (textContent.items.length > 0) { - const totalChars = textContent.items.reduce( - (sum, item) => sum + item.str.trim().length, 0 - ); - // 阈值策略:字符少于50或文本覆盖率过低 - isScannedPdf = totalChars < 40; - } - testedPage.cleanup(); - } - - - const book = { rendition: { layout: 'pre-paginated' } }; - - const { metadata, info } = await pdf.getMetadata() ?? {}; - // TODO: for better results, parse `metadata.getRaw()` - book.metadata = { - title: metadata?.get('dc:title') ?? info?.Title, - author: metadata?.get('dc:creator') ?? info?.Author, - contributor: metadata?.get('dc:contributor'), - description: metadata?.get('dc:description') ?? info?.Subject, - language: metadata?.get('dc:language'), - publisher: metadata?.get('dc:publisher'), - subject: metadata?.get('dc:subject'), - identifier: metadata?.get('dc:identifier'), - source: metadata?.get('dc:source'), - rights: metadata?.get('dc:rights'), - }; - book.metadata.description = (book.metadata.description ? book.metadata.description : "") + - (isScannedPdf ? "\nscanned PDF" : "") + (password ? ("\nprotected PDF: #" + password + "#") : ""); - - const outline = await pdf.getOutline(); - book.toc = outline?.map(makeTOCItem); - - const cache = new Map(); - book.sections = Array.from({ length: pdf.numPages }).map((_, i) => ({ - id: i, - load: async () => { - const cached = cache.get(i); - if (cached) return cached - const url = await renderPage(await pdf.getPage(i + 1)); - cache.set(i, url); - return url - }, - unload: async () => { - let page = await pdf.getPage(i + 1); - page.cleanup(); - }, - render: async (doc, scale, isMobile, isDarkMode) => { - await render(await pdf.getPage(i + 1), doc, scale, isMobile); - }, - getTextContent: async () => { - const page = await pdf.getPage(i + 1); - const textContent = await page.getTextContent(); - return textContent - // return textContent.items.map(item => item.str).join('\n') - }, - size: 1000, - getDimension: async () => { - let viewport = (await pdf.getPage(i + 1)).getViewport({ scale: 1 }); - return { width: viewport.width, height: viewport.height } - }, - getPage: async () => { - return await pdf.getPage(i + 1) - } - })); - book.isExternal = uri => /^\w+:/i.test(uri); - book.resolveHref = async href => { - const parsed = JSON.parse(href); - const dest = typeof parsed === 'string' - ? await pdf.getDestination(parsed) : parsed; - const index = await pdf.getPageIndex(dest[0]); - return { index } - }; - book.splitTOCHref = async href => { - const parsed = JSON.parse(href); - const dest = typeof parsed === 'string' - ? await pdf.getDestination(parsed) : parsed; - const index = await pdf.getPageIndex(dest[0]); - return [index, null] - }; - book.getTOCFragment = doc => doc.documentElement; - book.getCover = async () => renderPage(await pdf.getPage(1), true); - book.destroy = () => pdf.destroy(); - return book -}; -const isPDF = async file => { - const arr = new Uint8Array(await file.slice(0, 5).arrayBuffer()); - return arr[0] === 0x25 - && arr[1] === 0x50 && arr[2] === 0x44 && arr[3] === 0x46 - && arr[4] === 0x2d -}; - -class PdfRender extends GeneralRender { - constructor(pdfBuffer, config) { - super(Object.assign(Object.assign({}, config), { convertChinese: "Default", format: "PDF" })); - this.isStartFromEven = "no"; - this.password = ""; - this.scale = 1; - this.pdfBuffer = pdfBuffer; - this.isStartFromEven = config.isStartFromEven || "no"; - this.password = config.password || ""; - this.scale = config.scale || 1; - this.backgroundColor = config.backgroundColor || "#ffffff"; - this.isScannedPDF = config.isScannedPDF || "no"; - this.platform = config.platform || "web"; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - if (this.isStartFromEven === "yes") { - this.chapterDocList = [ - { - label: "", - text: { - load: () => __awaiter(this, void 0, void 0, function* () { return ""; }), - render: () => __awaiter(this, void 0, void 0, function* () { }), - unload: () => __awaiter(this, void 0, void 0, function* () { }), - getPage: () => __awaiter(this, void 0, void 0, function* () { return null; }), - getDimension: () => __awaiter(this, void 0, void 0, function* () { return ({ width: 0, height: 0 }); }), - getScale: () => __awaiter(this, void 0, void 0, function* () { return 1; }), - getPageCount: () => __awaiter(this, void 0, void 0, function* () { return 0; }), - }, - href: "", - }, - ...this.chapterDocList, - ]; - } - if (document.body.clientWidth * Math.abs(this.scale) - - document.body.clientWidth * 0.4 > - document.body.clientWidth && - this.readerMode !== "double") { - createIframe(element, this.scale); - } - else { - createIframe(element); - } - const viewport = yield this.chapterDocList[Math.floor(this.chapterDocList.length / 2)].text.getDimension(); - let doc = this.getDocument(); - if (!doc) - return; - createPDFContainer(doc.body || doc.documentElement, this.chapterDocList, viewport, this.readerMode); - let scrollTimeout = null; - if (this.readerMode === "scroll") { - this.element.addEventListener("scroll", (e) => { - if (scrollTimeout) { - clearTimeout(scrollTimeout); - } - scrollTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () { - yield this.handlePDFScrollEvent(doc); - yield this.record(); - }), 100); // Debounce selection events - }); - } - else { - doc.addEventListener("scroll", (e) => { - if (scrollTimeout) { - clearTimeout(scrollTimeout); - } - scrollTimeout = setTimeout(() => __awaiter(this, void 0, void 0, function* () { - yield this.handlePDFScrollEvent(doc); - yield this.record(); - }), 200); // Debounce selection events - }); - } - handlePDFLayout(element, this.readerMode, doc); - resolve(); - })); - } - handlePDFScrollEvent(doc) { - return __awaiter(this, void 0, void 0, function* () { - let subContainers = doc.querySelectorAll(".pdf-container"); - for (let index = 0; index < subContainers.length; index++) { - let subContainer = subContainers[index]; - let id = subContainer.getAttribute("id"); - if (!id) - continue; - let chapterDocIndex = parseInt(id.split("-").reverse()[0]); - if (isPDFScrolledIntoView(this.element, subContainer, this.readerMode, doc)) { - yield this.renderPdfPage(chapterDocIndex, doc); - } - } - }); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - try { - let blob = new Blob([this.pdfBuffer]); - let file = new File([blob], "book", { - lastModified: new Date().getTime(), - type: blob.type, - }); - if (yield isPDF(file)) { - this.book = yield makePDF(file, this.password); - } - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - return ""; - // if (!this.book) { - // await this.parse(); - // } - // return await getCache(this.book); - }); - } - goToChapterIndex(targetChapterIndex) { - return __awaiter(this, void 0, void 0, function* () { - if (this.chapterDocList.length > 0) { - yield this.goToChapter(targetChapterIndex, this.chapterDocList[targetChapterIndex].href, this.chapterDocList[targetChapterIndex].label); - } - }); - } - getPageSize() { - let doc = this.getDocument(); - if (!doc) - return; - let scale = this.readerMode === "double" ? 2 : 1; - let section = Math.floor(doc.body.clientWidth / 12); - let gap = section % 2 === 0 ? section : section - 1; - let subIframe = doc.querySelectorAll("iframe")[0]; - let iframeHeight = subIframe === null || subIframe === void 0 ? void 0 : subIframe.getBoundingClientRect().height; - return { - width: doc.body.clientWidth, - height: this.element.clientHeight, - left: this.element.offsetLeft, - top: this.element.offsetTop, - scrollTop: this.element.scrollTop, - sectionWidth: (doc.body.clientWidth - gap) / scale, - sectionHeight: iframeHeight, - gap: gap, - }; - } - goToChapter(chapterDocIndex, chapterHref, chapterTitle) { - return __awaiter(this, void 0, void 0, function* () { - if (this.readerMode === "double" && chapterDocIndex % 2 == 1) { - chapterDocIndex--; - } - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - yield this.renderPdfPage(chapterDocIndex, doc); - yield handleScrollPDFPosition(parseInt(chapterDocIndex), this.readerMode, doc); - yield this.recordByChapter(chapterDocIndex); - }); - } - getPositionByChapter(chapterDocIndex) { - return { - percentage: chapterDocIndex / this.chapterDocList.length, - chapterDocIndex: chapterDocIndex + "", - chapterHref: this.chapterDocList[chapterDocIndex].href, - chapterTitle: this.chapterDocList[chapterDocIndex].label, - text: "", - }; - } - goToPercentage(percentage) { - return __awaiter(this, void 0, void 0, function* () { - if (this.chapterDocList.length > 0) { - let chapterIndex = percentage === 1 - ? this.chapterDocList.length - 1 - : Math.floor(this.chapterDocList.length * percentage); - yield this.goToChapter(chapterIndex, this.chapterDocList[chapterIndex].href, this.chapterDocList[chapterIndex].label); - } - }); - } - goToPosition(bookLocationStr) { - var _a; - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) - return; - let bookLocation = JSON.parse(bookLocationStr); - if (bookLocation.chapterDocIndex === undefined) { - bookLocation.chapterDocIndex = 0; - } - this.tempLocation = { - text: bookLocation.text, - chapterTitle: bookLocation.chapterTitle, - chapterDocIndex: bookLocation.chapterDocIndex, - chapterHref: bookLocation.chapterHref, - count: bookLocation.count, - page: bookLocation.page, - percentage: bookLocation.percentage, - }; - let { chapterTitle, chapterDocIndex, chapterHref } = bookLocation; - if (this.readerMode === "double" && chapterDocIndex % 2 == 1) { - chapterDocIndex--; - } - yield this.renderPdfPage(parseInt(chapterDocIndex), doc); - if (this.readerMode === "scroll") { - let subIframe = this.getSubIframe(chapterDocIndex !== undefined - ? chapterDocIndex - : parseInt(this.tempLocation.chapterDocIndex)); - if (!subIframe) - return; - let iframeHeight = ((_a = subIframe.parentElement) === null || _a === void 0 ? void 0 : _a.getBoundingClientRect().height) || 0; - iframe.style.height = iframeHeight * this.chapterDocList.length + "px"; - } - yield handleScrollPDFPosition(parseInt(chapterDocIndex), this.readerMode, doc); - rangy.init(); - yield this.recordByChapter(parseInt(chapterDocIndex)); - }); - } - prev(platform) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) { - return; - } - if (this.readerMode === "scroll") { - // scroll readerMode under normal condition - this.element.scrollBy({ - left: 0, - top: -(this.element.clientHeight - 50), - behavior: "smooth", - }); - } - else { - if (platform === "ios") { - yield handleIOSScrollPage(this.element, this.animation, 1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile, parseInt(this.tempLocation.chapterDocIndex || "0"), this.readerMode); - } - else { - yield handleScrollPage(this.element, this.animation, 1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile); - } - yield this.renderPdfPage(parseInt(this.tempLocation.chapterDocIndex) - - (this.readerMode === "double" ? 2 : 1), doc); - } - yield this.record(); - }); - } - next(platform) { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - let iframe = this.getIframe(); - if (!doc || !iframe) { - return; - } - if (this.readerMode === "scroll") { - // scroll readerMode under normal condition - this.element.scrollBy({ - left: 0, - top: this.element.clientHeight - 50, - behavior: "smooth", - }); - } - else { - // single and double readerMode under normal condition - if (platform === "ios") { - yield handleIOSScrollPage(this.element, this.animation, -1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile, parseInt(this.tempLocation.chapterDocIndex || "0"), this.readerMode); - } - else { - yield handleScrollPage(this.element, this.animation, -1, doc, this.flipToNextPage, this.flipToPrevPage, this.isMobile); - } - yield this.renderPdfPage(parseInt(this.tempLocation.chapterDocIndex) + - (this.readerMode === "double" ? 2 : 1), doc); - } - yield this.record(); - }); - } - prevChapter() { - return __awaiter(this, void 0, void 0, function* () { - yield this.prev(); - }); - } - nextChapter() { - return __awaiter(this, void 0, void 0, function* () { - yield this.next(); - }); - } - goToPage(targetPage) { - return __awaiter(this, void 0, void 0, function* () { - let chapterDocIndex = Math.floor(targetPage - 1); - if (chapterDocIndex >= this.chapterDocList.length) { - chapterDocIndex = this.chapterDocList.length - 1; - } - if (chapterDocIndex < 0) { - chapterDocIndex = 0; - } - yield this.goToChapter(chapterDocIndex, this.chapterDocList[chapterDocIndex].href, this.chapterDocList[chapterDocIndex].label); - }); - } - visibleText() { - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return ""; - return yield getPDFVisibleText(parseInt(this.tempLocation.chapterDocIndex || "0"), this.chapterDocList, this.readerMode); - }); - } - audioText() { - return __awaiter(this, void 0, void 0, function* () { - return yield this.visibleText(); - }); - } - chapterText() { - return __awaiter(this, void 0, void 0, function* () { - return (yield this.visibleText()).join(" "); - }); - } - record() { - return __awaiter(this, void 0, void 0, function* () { - if (this.animation !== "") { - yield new Promise((r) => setTimeout(r, 1000)); - } - let doc = this.getDocument(); - if (!doc) - return; - yield this.handlePDFRecord(doc); - }); - } - recordByChapter(chapterDocIndex) { - return __awaiter(this, void 0, void 0, function* () { - if (this.animation !== "") { - yield new Promise((r) => setTimeout(r, 1000)); - } - if (chapterDocIndex >= this.chapterDocList.length || chapterDocIndex < 0) { - return; - } - this.tempLocation.chapterDocIndex = chapterDocIndex + ""; - this.tempLocation.percentage = - chapterDocIndex / (this.chapterDocList.length - 1) + ""; - this.tempLocation.chapterHref = this.chapterDocList[chapterDocIndex].href; - this.tempLocation.chapterTitle = this.chapterDocList[chapterDocIndex].label; - this.tempLocation.text = ""; - this.trigger("page-changed"); - }); - } - handlePDFRecord(doc) { - return __awaiter(this, void 0, void 0, function* () { - let subContainers = doc.querySelectorAll(".pdf-container"); - if (subContainers.length > 0 && - isPDFScrolledIntoView(this.element, subContainers[subContainers.length - 1], this.readerMode, doc)) { - this.handleRecord(subContainers[subContainers.length - 1]); - return; - } - for (let index = 0; index < subContainers.length; index++) { - let subContainer = subContainers[index]; - if (isPDFScrolledIntoView(this.element, subContainer, this.readerMode, doc)) { - this.handleRecord(subContainer); - break; - } - } - }); - } - handleRecord(subContainer) { - let id = subContainer.getAttribute("id"); - if (!id) - return; - let chapterDocIndex = parseInt(id.split("-").reverse()[0]); - if (chapterDocIndex !== parseInt(this.tempLocation.chapterDocIndex)) { - this.tempLocation.chapterDocIndex = chapterDocIndex + ""; - this.tempLocation.percentage = - chapterDocIndex / (this.chapterDocList.length - 1) + ""; - this.tempLocation.chapterHref = this.chapterDocList[chapterDocIndex].href; - this.tempLocation.chapterTitle = - this.chapterDocList[chapterDocIndex].label; - this.tempLocation.text = ""; - this.trigger("page-changed"); - } - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - let metadata = yield parser.getMetadata(); - return metadata; - } - catch (error) { - console.error(error); - throw error; - } - }); - } - highlightAudioNode(text, style) { - let pageIndex = parseInt(this.tempLocation.chapterDocIndex); - let doc = this.getSubDocument(pageIndex); - if (!doc) - return; - handleHighlightPDFNode(text, style, doc); - if (this.readerMode === "double") { - let doc = this.getSubDocument(pageIndex + 1); - if (!doc) - return; - handleHighlightPDFNode(text, style, doc); - } - } - highlightSearchNode(text, style) { - let pageIndex = parseInt(this.tempLocation.chapterDocIndex); - let doc = this.getSubDocument(pageIndex); - if (!doc) - return; - handleHighlightSearchNode(text, style, doc); - } - getProgress() { - return { - totalPage: this.chapterDocList.length, - currentPage: parseInt(this.tempLocation.chapterDocIndex || "0") + 1, - }; - } - getNotePosition() { - var _a; - return __awaiter(this, void 0, void 0, function* () { - let doc = this.getDocument(); - if (!doc) - return; - let selectedElement = getSelectedElement(doc); - if (!selectedElement) - return; - let ownerDoc = selectedElement.ownerDocument; - let targetIframe = (_a = ownerDoc === null || ownerDoc === void 0 ? void 0 : ownerDoc.defaultView) === null || _a === void 0 ? void 0 : _a.frameElement; - let id = (targetIframe === null || targetIframe === void 0 ? void 0 : targetIframe.getAttribute("id")) || ""; - let chapterDocIndex = id ? parseInt(id.split("-").reverse()[0]) : 0; - return Object.assign(Object.assign({}, this.tempLocation), { chapterDocIndex }); - }); - } - getSubDocument(chapterDocIndex) { - let pageArea = document.getElementById("page-area"); - if (!pageArea) - return null; - let iframe = pageArea.getElementsByTagName("iframe")[0]; - if (!iframe) - return null; - let doc = iframe.contentDocument; - if (!doc) { - return null; - } - let subIframe = doc.getElementById("pdf-iframe-" + chapterDocIndex); - if (!subIframe) { - createPDFIframe(chapterDocIndex || 0, doc); - subIframe = doc.getElementById("pdf-iframe-" + chapterDocIndex); - } - return subIframe.contentDocument; - } - getSubIframe(chapterDocIndex) { - let pageArea = document.getElementById("page-area"); - if (!pageArea) - return null; - let iframe = pageArea.getElementsByTagName("iframe")[0]; - if (!iframe) - return null; - let doc = iframe.contentDocument; - if (!doc) { - return null; - } - iframe = doc.getElementById("pdf-iframe-" + chapterDocIndex); - if (!iframe) { - createPDFIframe(chapterDocIndex || 0, doc); - iframe = doc.getElementById("pdf-iframe-" + chapterDocIndex); - } - return iframe; - } - getHightlightCoords(chapterDocIndex) { - return __awaiter(this, void 0, void 0, function* () { - let pageIndex = chapterDocIndex !== undefined - ? chapterDocIndex - : parseInt(this.tempLocation.chapterDocIndex); - let subDoc = this.getSubDocument(chapterDocIndex); - if (!subDoc) - return; - var selectionRects = subDoc.getSelection().getRangeAt(0).getClientRects(); - let page = yield this.chapterDocList[pageIndex].text.getPage(); - let scale = yield getPdfScale(this.element, this.readerMode, this.chapterDocList, pageIndex, subDoc); - var viewport = page.getViewport({ scale: scale }); - let canvas = subDoc.querySelector("canvas"); - var pageRect = canvas === null || canvas === void 0 ? void 0 : canvas.getClientRects()[0]; - let tempRect = []; - for (let i = 0; i < selectionRects.length; i++) { - if (i === 0) { - tempRect.push({ - bottom: selectionRects[i].bottom, - top: selectionRects[i].top, - left: selectionRects[i].left, - right: selectionRects[i].right, - }); - } - else if (Math.abs(tempRect[tempRect.length - 1].bottom - selectionRects[i].bottom) < 5) { - if (tempRect[tempRect.length - 1].left > selectionRects[i].left) { - tempRect[tempRect.length - 1].left = selectionRects[i].left; - } - if (tempRect[tempRect.length - 1].right < selectionRects[i].right) { - tempRect[tempRect.length - 1].right = selectionRects[i].right; - } - } - else { - tempRect.push({ - bottom: selectionRects[i].bottom, - top: selectionRects[i].top, - left: selectionRects[i].left, - right: selectionRects[i].right, - }); - } - } - var selected = tempRect.map(function (r) { - return viewport - .convertToPdfPoint(r.left - pageRect.x, r.top - pageRect.y) - .concat(viewport.convertToPdfPoint(r.right - pageRect.x, r.bottom - pageRect.y)); - }); - return { page: pageIndex, coords: selected, readerMode: this.readerMode }; - }); - } - renderHighlighters(notes, handleNoteClick) { - var _a, _b; - return __awaiter(this, void 0, void 0, function* () { - if (notes.length === 0) - return; - let chapterIndex = notes[0].chapterIndex; - let subIframe = this.getSubIframe(chapterIndex); - let subDoc = this.getSubDocument(chapterIndex); - if (!subDoc || !subIframe) - return; - clearHighlight(subDoc); - let iWin = subIframe.contentWindow || ((_a = subIframe.contentDocument) === null || _a === void 0 ? void 0 : _a.defaultView); - for (let index = 0; index < notes.length; index++) { - const item = notes[index]; - let selected = JSON.parse(item.range); - var pageIndex = parseInt(selected.page + ""); - if (pageIndex !== chapterIndex) { - continue; - } - let page = yield this.chapterDocList[pageIndex].text.getPage(); - let scale = yield getPdfScale(this.element, this.readerMode, this.chapterDocList, pageIndex, subDoc); - try { - showPDFHighlight(selected, item.color, item.key, handleNoteClick, page, scale, subDoc); - } - catch (e) { - console.warn(e, "Exception has been caught when restore character ranges."); - return; - } - if (!iWin || !iWin.getSelection()) - return; - (_b = iWin.getSelection()) === null || _b === void 0 ? void 0 : _b.empty(); - } - }); - } - removeOneNote(key, chapterDocIndex) { - let doc = this.getSubDocument(chapterDocIndex !== undefined - ? chapterDocIndex - : parseInt(this.tempLocation.chapterDocIndex)); - if (!doc) - return; - const elements = doc.querySelectorAll(".kookit-note"); - for (let index = 0; index < elements.length; index++) { - const element = elements[index]; - const dataKey = element.getAttribute("data-key"); - if (dataKey === key) { - element.parentNode.removeChild(element); - } - } - } - createOneNote(item, handleNoteClick) { - return __awaiter(this, void 0, void 0, function* () { - let iframe = this.getSubIframe(item.chapterIndex); - let subDoc = this.getSubDocument(item.chapterIndex); - if (!subDoc || !iframe) - return; - let selected = JSON.parse(item.range); - var pageIndex = parseInt(selected.page + ""); - let page = yield this.chapterDocList[pageIndex].text.getPage(); - let scale = yield getPdfScale(this.element, this.readerMode, this.chapterDocList, pageIndex, subDoc); - showPDFHighlight(selected, item.color, item.key, handleNoteClick, page, scale, subDoc); - this.clearSelection(); - }); - } - handleRenderPDFChapter(chapterDocIndex, doc) { - return __awaiter(this, void 0, void 0, function* () { - if (chapterDocIndex >= this.chapterDocList.length || chapterDocIndex < 0) { - return; - } - let subIframe = doc.getElementById("pdf-iframe-" + chapterDocIndex); - if (!subIframe) { - subIframe = createPDFIframe(chapterDocIndex, doc); - } - let subDoc = subIframe === null || subIframe === void 0 ? void 0 : subIframe.contentDocument; - if (!subDoc) - return; - if (subDoc.body.innerHTML) { - return; - } - subDoc.body.innerHTML = ""; - let blob = yield fetch(yield this.chapterDocList[chapterDocIndex].text.load()).then((r) => r.blob()); - let chapterText = yield blob.text(); - subDoc.body.innerHTML = chapterText; - let scale = yield getPdfScale(this.element, this.readerMode, this.chapterDocList, chapterDocIndex, subDoc); - yield this.chapterDocList[chapterDocIndex].text.render(subDoc, scale, this.isMobile); - let docLayer = subDoc.querySelector("#koodoPDFLayer"); - if (!docLayer) { - return; - } - if (this.isDarkMode === "yes") { - docLayer.style.filter = "invert(1) hue-rotate(180deg) contrast(0.95)"; - } - if (this.backgroundColor === "rgba(233, 216, 188,1)" && - this.isScannedPDF === "yes") { - docLayer.style.filter = "sepia(100%) contrast(0.95) brightness(0.95)"; - } - if (this.backgroundColor === "rgba(197, 231, 207,1)" && - this.isScannedPDF === "yes") { - docLayer.style.filter = - "sepia(30%) hue-rotate(60deg) saturate(120%) brightness(95%)"; - } - if (this.readerMode === "single" || this.readerMode === "double") { - let additionalHeight = this.element.clientHeight / 2 - - docLayer.getBoundingClientRect().height / 2; - docLayer.style.marginTop = additionalHeight + "px"; - subIframe.style.height = - docLayer.getBoundingClientRect().height + additionalHeight + "px"; - let noteLayer = subDoc.querySelector(".noteLayer"); - if (noteLayer) { - noteLayer.style.position = "relative"; - } - } - if (this.readerMode !== "scroll") { - docLayer.style.marginLeft = `calc(50% - ${docLayer.getBoundingClientRect().width / 2}px)`; - } - docLayer.style.visibility = "visible"; - window.chapterDocIndex = chapterDocIndex; - this.trigger("rendered"); - }); - } - handleUnloadPDFChapter(chapterDocIndex, doc) { - return __awaiter(this, void 0, void 0, function* () { - if (chapterDocIndex >= this.chapterDocList.length || chapterDocIndex < 0) { - return; - } - let subDoc = this.getSubDocument(chapterDocIndex); - if (!subDoc) - return; - if (subDoc.body.innerHTML === "") { - return; - } - yield this.chapterDocList[chapterDocIndex].text.unload(); - subDoc.body.innerHTML = ""; - }); - } - renderPdfPage(chapterDocIndex, doc) { - return __awaiter(this, void 0, void 0, function* () { - if (chapterDocIndex >= this.chapterDocList.length || chapterDocIndex < 0) { - return; - } - else if (chapterDocIndex > 2) { - yield this.handleUnloadPDFChapter(chapterDocIndex - 3, doc); - } - yield this.handleRenderPDFChapter(chapterDocIndex, doc); - yield this.handleRenderPDFChapter(chapterDocIndex + 1, doc); - }); - } -} - -const fetchText = (url) => __awaiter(void 0, void 0, void 0, function* () { return yield (yield fetch(url)).text(); }); -class PdfTextRender extends GeneralRender { - constructor(pdfBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "PDFTEXT" })); - this.password = ""; - this.ocrLang = "chi_sim"; // 默认OCR语言为简体中文 - this.paraSpacingValue = 1.5; // 段落间距 - this.titleSizeValue = 1.2; // 标题大小倍数 - this.isFinishOCR = false; - this.performOCR = (imageUrl) => __awaiter(this, void 0, void 0, function* () { - try { - if (this.ocrEngine === "tesseract") { - const result = yield this.worker.recognize(imageUrl); - // await this.worker.terminate(); - return result.data.text; - } - else if (this.ocrEngine === "system") { - } - } - catch (error) { - console.error("OCR Error:", error); - throw error; - } - }); - this.pdfBuffer = pdfBuffer; - this.password = config.password || ""; - this.isScannedPDF = config.isScannedPDF || "no"; - this.ocrLang = config.ocrLang || "chi_sim"; // 支持配置OCR语言 - this.paraSpacingValue = parseFloat(config.paraSpacingValue) || 1.5; // 支持配置段落间距 - this.titleSizeValue = parseFloat(config.titleSizeValue) || 1.2; // 支持配置标题大小倍数 - this.cache = {}; - this.serverRegion = config.serverRegion || "global"; - this.processingPromises = new Map(); - this.ocrEngine = config.ocrEngine || "tesseract"; // 支持配置OCR引擎 - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - for (let index = 0; index < this.chapterDocList.length; index++) { - let chapterDoc = this.chapterDocList[index]; - chapterDoc.text.load = () => __awaiter(this, void 0, void 0, function* () { - if (this.cache[index]) { - // 即使缓存存在,也要检查后续章节 - if (this.isScannedPDF === "yes") { - this.preProcessNextChapters(index); - } - return this.cache[index]; - } - let src = ""; - if (this.isScannedPDF === "yes") { - // 优先处理当前章节 - src = yield this.processCurrentChapter(index); - // 异步处理后续章节 - this.preProcessNextChapters(index); - } - else { - src = yield this.getTextFromDoc(chapterDoc); - this.cache[index] = src; - } - return src; - }); - } - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - // 优先处理当前章节 - processCurrentChapter(index) { - return __awaiter(this, void 0, void 0, function* () { - if (this.cache[index]) { - return this.cache[index]; - } - // 如果当前章节正在处理,等待完成 - if (this.processingPromises.has(index)) { - yield this.processingPromises.get(index); - return this.cache[index]; - } - const chapterDoc = this.chapterDocList[index]; - const src = yield this.getTextByOCR(chapterDoc); - this.cache[index] = src; - return src; - }); - } - // 异步预处理后续章节 - preProcessNextChapters(currentIndex) { - const maxIndex = Math.min(currentIndex + 3, this.chapterDocList.length - 1); - for (let i = currentIndex + 1; i <= maxIndex; i++) { - // 只处理未缓存且未在处理中的章节 - if (!this.cache[i] && !this.processingPromises.has(i)) { - const promise = this.processChapterOCR(i); - this.processingPromises.set(i, promise); - // 处理完成后清理 Promise 记录 - promise.finally(() => { - this.processingPromises.delete(i); - }); - } - } - } - // 处理单个章节的OCR - processChapterOCR(index) { - return __awaiter(this, void 0, void 0, function* () { - try { - const chapterDoc = this.chapterDocList[index]; - const src = yield this.getTextByOCR(chapterDoc); - this.cache[index] = src; - } - catch (error) { - console.error(`Failed to process OCR for chapter ${index}:`, error); - } - }); - } - getTextByOCR(chapterDoc) { - return __awaiter(this, void 0, void 0, function* () { - let page = yield chapterDoc.text.getPage(); - let { imageURL } = yield convertPageToImage(page); - const textContent = yield this.performOCR(imageURL); - let paraList = textContent.split("\n").filter((para) => para.trim() !== ""); - const src = URL.createObjectURL(new Blob([ - ` - - - - -
${paraList.map((para) => `

${para}

`).join("")}
- `, - ], { type: "text/html" })); - return src; - }); - } - getTextFromDoc(chapterDoc) { - return __awaiter(this, void 0, void 0, function* () { - let textContent = yield chapterDoc.text.getTextContent(); - let paraList = []; - if (textContent && textContent.items && Array.isArray(textContent.items)) { - // 先收集所有字体大小,确定基础大小和最大大小 - // 先收集所有字体大小,确定基础大小和最大大小 - const fontSizes = textContent.items - .filter((item) => item.str && item.transform) - .map((item) => item.transform[3]); - let baseFontSize = 10; - if (fontSizes.length > 0) { - // 计算字体大小的众数(出现频率最高的值) - const fontSizeCount = fontSizes.reduce((acc, size) => { - acc[size] = (acc[size] || 0) + 1; - return acc; - }, {}); - baseFontSize = Object.keys(fontSizeCount) - .map(Number) - .reduce((a, b) => (fontSizeCount[a] > fontSizeCount[b] ? a : b)); - } - // const maxFontSize = Math.max(...fontSizes); - // const fontSizeRange = maxFontSize - Number(baseFontSize); - let currentPara = { - text: "", - styles: new Set(), - y: 0, - tag: "p", - }; - let lastY = 0; - textContent.items.forEach((item) => { - if (item.str) { - // 检测段落分隔(基于Y坐标变化) - const yDiff = Math.abs(item.transform[5] - lastY); - const fontSize = item.transform[3]; - // 根据字体大小确定样式,都用p标签,大字体用bold - let tag = "p"; - let isBold = fontSize > Number(baseFontSize) * this.titleSizeValue; - // 如果Y坐标变化较大,认为是新段落 - if (yDiff > item.height * this.paraSpacingValue && - currentPara.text.trim()) { - paraList.push(currentPara); - currentPara = { - text: "", - styles: new Set(), - y: item.transform[5], - tag: tag, - isBold: isBold, - }; - } - else if (!currentPara.hasOwnProperty("isBold")) { - // 如果当前段落还没有确定样式,使用当前item的样式 - currentPara.isBold = isBold; - } - // 包装文本 - const wrappedText = item.str; - // 换行时用空格连接,而不是分段 - if (item.hasEOL) { - // 如果是用了连接符(如连字符),直接拼接,不加空格 - if (wrappedText.endsWith("-")) { - currentPara.text += wrappedText.slice(0, -1); - } - else { - currentPara.text += wrappedText + " "; - } - } - else { - currentPara.text += wrappedText; - } - lastY = item.transform[5]; - } - }); - // 添加最后一个段落 - if (currentPara.text.trim()) { - paraList.push(currentPara); - } - } - const src = URL.createObjectURL(new Blob([ - ` - - - - -
${paraList.length > 0 - ? paraList - .map((para) => `

${para.text.trim()}

`) - .join("") - : "Empty"}
- `, - ], { type: "text/html" })); - return src; - }); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - try { - let blob = new Blob([this.pdfBuffer]); - let file = new File([blob], "book", { - lastModified: new Date().getTime(), - type: blob.type, - }); - if (yield isPDF(file)) { - this.book = yield makePDF(file, this.password); - } - if (this.isScannedPDF === "yes" && this.ocrEngine === "tesseract") { - let workerScript = yield fetchText(`${isElectron$1() ? "." : ""}/lib/tesseractjs/worker.min.js`); - let workerUrl = URL.createObjectURL(new Blob([workerScript], { type: "application/javascript" })); - const worker = yield window.Tesseract.createWorker([this.ocrLang], 1, { - workerPath: workerUrl, - corePath: `https://${this.serverRegion === "global" - ? "storage.koodoreader.com" - : "storage.koodoreader.cn"}/tesseractjs/tesseract-core`, - langPath: `https://${this.serverRegion === "global" - ? "storage.koodoreader.com" - : "storage.koodoreader.cn"}/tesseractjs/4.0.0-fast`, - logger: (m) => { - if (m.status === "recognizing text" && - typeof m.progress === "number" && - !this.isFinishOCR) { - showOCRProgress(m.progress); - if (m.progress === 1) { - this.isFinishOCR = true; - } - } - }, - }); - yield worker.load(); - this.worker = worker; - } - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - return yield parser.getMetadata(); - } - catch (error) { - console.error(error); - throw error; - } - }); - } -} - -const makeHtmlBook = (bookStr, isTxt = false, parserRegex = "", bookLocation) => { - const bookDoc = new DOMParser().parseFromString(isTxt ? txtToHtml(bookStr, parserRegex, bookLocation) : bookStr, "text/html"); - let chapterDomList = getTitleElement(bookDoc); - if (chapterDomList.length === 0) { - chapterDomList = getTitlefromText(bookDoc); - } - for (let i = 0; i < chapterDomList.length; i++) { - // this.chapterDomList[i].id = this.chapterList[i].id; - var newItem = document.createElement("kookitmarker"); - var textnode = document.createTextNode(" "); - newItem.appendChild(textnode); - chapterDomList[i].parentNode && - chapterDomList[i].parentNode.insertBefore(newItem, chapterDomList[i]); - } - const chapterList = getChapterDoc(bookDoc.body.innerHTML); - const load = (index) => __awaiter(void 0, void 0, void 0, function* () { - const page = URL.createObjectURL(new Blob([chapterList[index].text], { type: "text/html" })); - return page; - }); - const unload = (index) => { }; - const book = {}; - book.getCover = () => ""; - book.sections = chapterList.map((item) => ({ - id: item.index, - load: () => load(item.index), - unload: () => unload(item.index), - size: chapterList[item.index].text.length, - })); - book.toc = chapterList - .map((item) => ({ - label: item.label, - href: "title" + item.index, - })) - .filter((item) => item.label !== ""); - book.rendition = { layout: "pre-paginated" }; - book.resolveHref = (href) => { - return { index: parseInt(href.substring(5, href.length)) }; - }; - book.splitTOCHref = (href) => [href, null]; - book.getTOCFragment = (doc) => doc.documentElement; - return book; -}; -const getTitleElement = (Element) => { - return Array.from(Element.querySelectorAll("h1,h2,h3,h4,h5,h6,title")); -}; -const getChapterDoc = (bookStr) => { - let chapterDocList = []; - let chapterStrList = bookStr - .split(" ") - .filter((item) => item.trim() !== ""); - let titleList = chapterStrList.map((item) => { - return getHFromStr(item) || getTitleFromStr(item); - }); - chapterDocList = chapterStrList.map((item, index) => { - return { - index: index, - label: titleList[index], - text: item, - href: "title" + index, - }; - }); - return chapterDocList; -}; -const getHFromStr = (str) => { - var _a; - // Create temporary DOM element - const tempDoc = new DOMParser().parseFromString(str, "text/html"); - // Find first heading tag - const headingTag = tempDoc.querySelector("h1, h2, h3, h4, h5, h6"); - // Return content if found, otherwise empty string - return headingTag ? ((_a = headingTag.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || "" : ""; -}; -const getTitleFromStr = (str) => { - var _a; - // Create temporary DOM element - const tempDoc = new DOMParser().parseFromString(str, "text/html"); - // Find first heading tag - const headingTag = tempDoc.querySelector("title"); - // Return content if found, otherwise empty string - return headingTag ? ((_a = headingTag.textContent) === null || _a === void 0 ? void 0 : _a.trim()) || "" : ""; -}; -const getTitlefromText = (bookDoc) => { - let elements = bookDoc.getElementsByTagName("*"); - let titleElements = Array.from(elements).filter((item) => { - return (item.childNodes.length === 1 && - item.childNodes[0].nodeType === Node.TEXT_NODE && - isTitle(cleanText(item.textContent))); - }); - let h1TitleElements = []; - for (let index = 0; index < titleElements.length; index++) { - const oldElement = titleElements[index]; - const newElement = document.createElement("h1"); - newElement.innerHTML = oldElement.innerText; - oldElement.parentNode.replaceChild(newElement, oldElement); - h1TitleElements.push(newElement); - } - return h1TitleElements; -}; - -class TxtRender extends GeneralRender { - constructor(txtBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "TXT" })); - this.txtBuffer = txtBuffer; - this.charset = config.charset; - this.parserRegex = config.parserRegex; - } - renderTo(element, bookLocation) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(bookLocation); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse(bookLocation) { - return __awaiter(this, void 0, void 0, function* () { - try { - const textDecoder = new TextDecoder(this.charset); - const bytes = new Uint8Array(this.txtBuffer); - let text = textDecoder.decode(bytes); - this.book = makeHtmlBook(text, true, this.parserRegex, bookLocation); - } - catch (error) { - console.error(error); - throw error; - } - }); - } - refreshContent() { - return __awaiter(this, void 0, void 0, function* () { - yield this.parse({ refresh: true }); - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - return this.chapterList; - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse({ refresh: true }); - } - return yield getCache(this.book); - }); - } - getMetadata(txtBuffer) { - return __awaiter(this, void 0, void 0, function* () { - try { - // Define the size of the chunk to read for detection (e.g., 4KB) - const CHUNK_SIZE = 4096; - const bufferLength = txtBuffer.byteLength; - // Determine the actual size to read (up to CHUNK_SIZE or the file size if smaller) - const sizeToRead = Math.min(bufferLength, CHUNK_SIZE); - // Create a Uint8Array from the beginning chunk of the buffer - const chunkArray = new Uint8Array(txtBuffer, 0, sizeToRead); - // Detect the charset using only the chunk - let detectedCharset = chardet.detect(chunkArray); - // Fallback to utf8 if detection fails or returns null/undefined - const charset = detectedCharset || "utf8"; - this.charset = charset; - return { charset: charset }; - } - catch (error) { - console.error("Error detecting charset:", error); - // Fallback to utf8 in case of error during detection - this.charset = "utf8"; - return { charset: "utf8" }; - } - }); - } -} - -const makeComicBook = ({ entries, loadBlob, getSize }, file, readerMode) => { - const cache = new Map(); - const urls = new Map(); - const load = async (name, nameExtra) => { - if (cache.has(name)) return cache.get(name); - if (nameExtra) { - const src = URL.createObjectURL(await loadBlob(name)); - const srcExtra = URL.createObjectURL(await loadBlob(nameExtra)); - const page = URL.createObjectURL( - new Blob([`
`], { type: "text/html" }) - ); - urls.set(name, [src, page]); - cache.set(name, page); - return page; - } else { - const src = URL.createObjectURL(await loadBlob(name)); - const page = URL.createObjectURL( - new Blob([`
`], { type: "text/html" }) - ); - urls.set(name, [src, page]); - cache.set(name, page); - return page; - } - - - }; - const unload = (name) => { - urls.get(name)?.forEach?.((url) => URL.revokeObjectURL(url)); - urls.delete(name); - cache.delete(name); - }; - - const exts = [".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".svg"]; - const files = entries - .map((entry) => entry.filename) - .filter((name) => exts.some((ext) => name.endsWith(ext))).sort((a, b) => { - const numA = parseInt(a.replace(/\D/g, "")); - const numB = parseInt(b.replace(/\D/g, "")); - - // Check if both are numbers - if (!isNaN(numA) && !isNaN(numB)) { - return numA - numB; - } - // Check if only one is a number (place numbers first) - if (!isNaN(numA)) { - return -1; // a comes first - } - if (!isNaN(numB)) { - return 1; // b comes first - } - // If neither are numbers, sort alphabetically - return a.localeCompare(b); - }); - - const book = {}; - book.getCover = () => loadBlob(files[0]); - book.metadata = { title: file.name }; - book.sections = files.map((name, index) => ({ - id: name, - load: () => { - if (readerMode === "double") { - const nameExtra = files[index + 1]; - return load(name, nameExtra) - } else { - return load(name) - } - - }, - unload: () => unload(name), - size: getSize(name), - })).filter((_, i) => { - if (readerMode === "double") { - return i % 2 === 0 - } else { - return true - } - }); - book.toc = files.map((name) => ({ label: name, href: name })).filter((_, i) => { - if (readerMode === "double") { - return i % 2 === 0 - } else { - return true - } - }); - book.rendition = { layout: "pre-paginated" }; - book.resolveHref = (href) => ({ - index: book.sections.findIndex((s) => s.id === href), - }); - book.splitTOCHref = (href) => [href, null]; - book.getTOCFragment = (doc) => doc.documentElement; - return book; -}; - -class ComicRender extends GeneralRender { - constructor(comicBuffer, config) { - super(config); - this.comicBuffer = comicBuffer; - this.readerMode = config.readerMode; - this.format = config.format; - this.chapterList = []; - this.chapterDocList = []; - this.book = ""; - this.element = ""; - this.rpc; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - createIframe(element); - if (!this.book) { - try { - yield this.parse(); - } - catch (error) { - console.error(error); - reject(error); - } - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - try { - let blob = new Blob([this.comicBuffer]); - let file = new File([blob], "book." + this.format.toLocaleLowerCase(), { - lastModified: new Date().getTime(), - type: blob.type, - }); - if (this.format === "CBZ") { - const loader = yield this.makeZipLoader(file); - this.book = makeComicBook(loader, file, this.readerMode); - } - else if (this.format === "CBT") { - const loader = yield this.makeTarLoader(); - this.book = makeComicBook(loader, file, this.readerMode); - } - else if (this.format === "CBR") { - this.rpc = yield window.RPC.new("./lib/libunrar/worker.js", { - loaded: function () { - console.info("loaded"); - }, - progressShow: function (fileName, fileSize, progress) { - console.info(progress); - }, - }); - yield new Promise((r) => setTimeout(r, 200)); - const loader = yield this.makeRarLoader(); - this.book = makeComicBook(loader, file, this.readerMode); - } - else if (this.format === "CB7") { - const loader = yield this.make7zLoader(); - this.book = makeComicBook(loader, file, this.readerMode); - } - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } - makeZipLoader(file) { - return __awaiter(this, void 0, void 0, function* () { - let zip = yield JSZip.loadAsync(file); - const entries = zip.files; - const loadText = (name) => __awaiter(this, void 0, void 0, function* () { - let entry = zip.file(name); - if (entry) { - return entry.async("string"); - } - return ""; - }); - const loadBlob = (name) => __awaiter(this, void 0, void 0, function* () { - let entry = zip.file(name); - if (entry) { - let buffer = yield entry.async("arraybuffer"); - return new Blob([buffer]); - } - return new Blob([new ArrayBuffer(0)]); - }); - const getSize = (name) => { - let entry = zip.file(name); - if (entry) { - return entry._data.uncompressedSize || 1; - } - }; - return { - entries: Object.values(entries).map((item) => { - return { filename: item.name }; - }), - loadText, - loadBlob, - getSize, - }; - }); - } - makeTarLoader() { - return __awaiter(this, void 0, void 0, function* () { - const entries = yield untar(this.comicBuffer); - const map = new Map(entries.map((entry) => [entry.name, entry])); - const load = (f) => (name, ...args) => map.has(name) ? f(map.get(name), ...args) : null; - const loadText = load((entry) => entry.readAsString()); - const loadBlob = load((entry, type) => entry.blob); - const getSize = (name) => { var _a, _b; return (_b = (_a = map.get(name)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 1; }; - return { - entries: entries.map((item) => { - return { filename: item.name }; - }), - loadText, - loadBlob, - getSize, - }; - }); - } - makeRarLoader() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - var buffers = [this.comicBuffer]; - var dataToPass = [{ name: "book.rar", content: this.comicBuffer }]; - var password = null; - this.rpc.transferables = buffers; - this.rpc - .unrar(dataToPass, password, 0) - .then((ret) => { - let entries = this.getRarEntries(ret.ls); - const map = new Map(Object.values(entries).map((entry) => [ - entry.fullFileName, - entry, - ])); - const load = (f) => (name, ...args) => map.has(name) ? f(map.get(name), ...args) : null; - const loadText = load((entry) => entry.fullFileName); - const loadBlob = load((entry, type) => new Blob([entry.fileContent])); - const getSize = (name) => { var _a, _b; return (_b = (_a = map.get(name)) === null || _a === void 0 ? void 0 : _a.fileSize) !== null && _b !== void 0 ? _b : 1; }; - resolve({ - entries: Object.values(entries).map((item) => { - return { filename: item.fullFileName }; - }), - loadText, - loadBlob, - getSize, - }); - }) - .catch((err) => { - console.error(err); - reject(err); - }); - }); - }); - } - make7zLoader() { - return __awaiter(this, void 0, void 0, function* () { - const wasmBinaryFile = "./lib/7z-wasm/7zz.wasm"; - if (!window.wasmBinary) { - const response = yield fetch(wasmBinaryFile, { - credentials: "same-origin", - }); - if (!response["ok"]) { - throw "failed to load wasm binary file at '" + wasmBinaryFile + "'"; - } - window.wasmBinary = yield response["arrayBuffer"](); - } - const sevenZip = yield window.SevenZip({ - wasmBinary: window.wasmBinary, - }); - const archiveData = new Uint8Array(this.comicBuffer); - const archiveName = "archive.cb7"; - const stream = sevenZip.FS.open(archiveName, "w+"); - sevenZip.FS.write(stream, archiveData, 0, archiveData.length); - sevenZip.FS.close(stream); - sevenZip.callMain(["x", archiveName]); - const loader = sevenZip.FS; - const entries = this.get7zEntries(loader.lookupPath("/").node); - const map = new Map(entries.map((entry) => [entry.name, entry])); - const load = (f) => (name, ...args) => map.has(name) ? f(map.get(name), ...args) : null; - const loadText = load((entry) => entry.name); - const loadBlob = load((entry, type) => new Blob([entry.buffer])); - const getSize = (name) => { var _a, _b; return (_b = (_a = map.get(name)) === null || _a === void 0 ? void 0 : _a.size) !== null && _b !== void 0 ? _b : 1; }; - return { - entries: entries.map((item) => { - return { filename: item.name }; - }), - loadText, - loadBlob, - getSize, - }; - }); - } - getRarEntries(Node) { - const list = Object.keys(Node); - let entries = []; - for (let index = 0; index < list.length; index++) { - const item = list[index]; - if (Node[item].type === "dir") { - entries = entries.concat(this.getRarEntries(Node[item].ls)); - } - else { - entries.push({ - fullFileName: item, - fileContent: Node[item].fileContent, - fileSize: Node[item].fileSize, - }); - } - } - return entries; - } - get7zEntries(FSNode) { - const contents = FSNode.contents; - const list = Object.keys(contents).filter((item) => { - return (item != "archive.cb7" && - item != "dev" && - item != "home" && - item != "proc" && - item != "tmp"); - }); - let entries = []; - for (let index = 0; index < list.length; index++) { - const item = list[index]; - if (contents[item].isFolder) { - entries = entries.concat(this.get7zEntries(contents[item])); - } - else { - entries.push({ - name: item, - buffer: contents[item].contents, - size: contents[item].usedBytes, - }); - } - } - return entries; - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - const coverBlob = yield this.book.getCover(); - var reader = new FileReader(); - reader.readAsDataURL(coverBlob); - reader.onloadend = () => { - resolve({ - cover: reader.result, - }); - }; - } - catch (error) { - console.error(error); - reject(error); - } - })); - }); - } -} - -const trim = (str) => str?.trim()?.replace(/\s{2,}/g, " "); -const getElementText = (el) => trim(el?.textContent); - -const NS = { - XLINK: "http://www.w3.org/1999/xlink", - EPUB: "http://www.idpf.org/2007/ops", -}; - -const MIME = { - XML: "application/xml", - XHTML: "application/xhtml+xml", -}; - -const STYLE = { - strong: ["strong", "self"], - emphasis: ["em", "self"], - style: ["span", "self"], - a: "anchor", - strikethrough: ["s", "self"], - sub: ["sub", "self"], - sup: ["sup", "self"], - code: ["code", "self"], - image: "image", -}; - -const TABLE = { - tr: ["tr", ["align"]], - th: ["th", ["colspan", "rowspan", "align", "valign"]], - td: ["td", ["colspan", "rowspan", "align", "valign"]], -}; - -const POEM = { - epigraph: ["blockquote"], - subtitle: ["h2", STYLE], - "text-author": ["p", STYLE], - date: ["p", STYLE], - stanza: "stanza", -}; - -const SECTION = { - title: [ - "header", - { - p: ["h1", STYLE], - "empty-line": ["br"], - }, - ], - epigraph: ["blockquote", "self"], - image: "image", - annotation: ["aside"], - section: ["section", "self"], - p: ["p", STYLE], - poem: ["blockquote", POEM], - subtitle: ["h2", STYLE], - cite: ["blockquote", "self"], - "empty-line": ["br"], - table: ["table", TABLE], - "text-author": ["p", STYLE], -}; -POEM["epigraph"].push(SECTION); - -const BODY = { - image: "image", - title: [ - "section", - { - p: ["h1", STYLE], - "empty-line": ["br"], - }, - ], - epigraph: ["section", SECTION], - section: ["section", SECTION], -}; - -const getImageSrc = (el) => { - const href = el.getAttributeNS(NS.XLINK, "href"); - const [, id] = href.split("#"); - const bin = el.getRootNode().getElementById(id); - return bin - ? `data:${bin.getAttribute("content-type")};base64,${bin.textContent}` - : href; -}; - -class FB2Converter { - constructor(fb2) { - this.fb2 = fb2; - this.doc = document.implementation.createDocument(NS.XHTML, "html"); - } - image(node) { - const el = this.doc.createElement("img"); - el.alt = node.getAttribute("alt"); - el.title = node.getAttribute("title"); - el.setAttribute("src", getImageSrc(node)); - return el; - } - anchor(node) { - const el = this.convert(node, { a: ["a", STYLE] }); - el.setAttribute("href", node.getAttributeNS(NS.XLINK, "href")); - if (node.getAttribute("type") === "note") - el.setAttributeNS(NS.EPUB, "epub:type", "noteref"); - return el; - } - stanza(node) { - const el = this.convert(node, { - stanza: [ - "p", - { - title: [ - "header", - { - p: ["strong", STYLE], - "empty-line": ["br"], - }, - ], - subtitle: ["p", STYLE], - }, - ], - }); - for (const child of node.children) - if (child.nodeName === "v") { - el.append(this.doc.createTextNode(child.textContent)); - el.append(this.doc.createElement("br")); - } - return el; - } - convert(node, def) { - // not an element; return text content - if (node.nodeType === 3) return this.doc.createTextNode(node.textContent); - if (node.nodeType === 4) - return this.doc.createCDATASection(node.textContent); - if (node.nodeType === 8) return this.doc.createComment(node.textContent); - - const d = def?.[node.nodeName]; - if (!d) return null; - if (typeof d === "string") return this[d](node); - - const [name, opts] = d; - const el = this.doc.createElement(name); - - // copy the ID, and set class name from original element name - if (node.id) el.id = node.id; - el.classList.add(node.nodeName); - - // copy attributes - if (Array.isArray(opts)) - for (const attr of opts) el.setAttribute(attr, node.getAttribute(attr)); - - // process child elements recursively - const childDef = opts === "self" ? def : Array.isArray(opts) ? null : opts; - let child = node.firstChild; - while (child) { - const childEl = this.convert(child, childDef); - if (childEl) el.append(childEl); - child = child.nextSibling; - } - return el; - } -} - -const parseXML = async (blob) => { - const buffer = await blob.arrayBuffer(); - const str = new TextDecoder("utf-8").decode(buffer); - const parser = new DOMParser(); - const doc = parser.parseFromString(str, MIME.XML); - const encoding = - doc.xmlEncoding || - // `Document.xmlEncoding` is deprecated, and already removed in Firefox - // so parse the XML declaration manually - str.match( - /^<\?xml\s+version\s*=\s*["']1.\d+"\s+encoding\s*=\s*["']([A-Za-z0-9._-]*)["']/ - )?.[1]; - if (encoding && encoding.toLowerCase() !== "utf-8") { - const str = new TextDecoder(encoding).decode(buffer); - return parser.parseFromString(str, MIME.XML); - } - return doc; -}; - -const style = URL.createObjectURL( - new Blob( - [ - ` -@namespace epub "http://www.idpf.org/2007/ops"; -body > img, section > img { - display: block; - margin: auto; -} -.title { - text-align: center; -} -body > section > .title, body.notesBodyType > .title { - margin: 3em 0; -} -body.notesBodyType > section .title { - text-align: left; - margin: 1em 0; -} -p { - text-indent: 1em; - margin: 0; -} -:not(p) + p, p:first-child { - text-indent: 0; -} -.poem p { - text-indent: 0; - margin: 1em 0; -} -.text-author, .date { - text-align: end; -} -.text-author:before { - content: "—"; -} -table { - border-collapse: collapse; -} -td, th { - padding: .25em; -} -a[epub|type~="noteref"] { - font-size: .75em; - vertical-align: super; -} -body:not(.notesBodyType) > .title, body:not(.notesBodyType) > .epigraph { - margin: 3em 0; -} -`, - ], - { type: "text/css" } - ) -); - -const template = (html) => ` - - - ${html} -`; - -// name of custom ID attribute for TOC items -const dataID = "data-foliate-id"; - -const makeFB2 = async (blob) => { - const book = {}; - const doc = await parseXML(blob); - const converter = new FB2Converter(doc); - - const $ = (x) => doc.querySelector(x); - const $$ = (x) => [...doc.querySelectorAll(x)]; - const getPerson = (el) => { - const nick = getElementText(el.querySelector("nickname")); - if (nick) return nick; - const first = getElementText(el.querySelector("first-name")); - const middle = getElementText(el.querySelector("middle-name")); - const last = getElementText(el.querySelector("last-name")); - const name = [first, middle, last].filter((x) => x).join(" "); - const sortAs = last - ? [last, [first, middle].filter((x) => x).join(" ")].join(", ") - : null; - return { name, sortAs }; - }; - const getDate = (el) => el?.getAttribute("value") ?? getElementText(el); - const annotation = $("title-info annotation"); - book.metadata = { - title: getElementText($("title-info book-title")), - identifier: getElementText($("document-info id")), - language: getElementText($("title-info lang")), - author: $$("title-info author").map(getPerson), - translator: $$("title-info translator").map(getPerson), - producer: $$("document-info author") - .map(getPerson) - .concat($$("document-info program-used").map(getElementText)), - publisher: getElementText($("publish-info publisher")), - published: getDate($("title-info date")), - modified: getDate($("document-info date")), - description: annotation - ? converter.convert(annotation, { annotation: ["div", SECTION] }) - .innerHTML - : null, - subject: $$("title-info genre").map(getElementText), - }; - book.getCover = () => - fetch(getImageSrc($("coverpage image"))).then((res) => res.blob()); - - // get convert each body - const bodyData = Array.from(doc.querySelectorAll("body"), (body) => { - const converted = converter.convert(body, { body: ["body", BODY] }); - return [ - Array.from(converted.children, (el) => { - // get list of IDs in the section - const ids = [el, ...el.querySelectorAll("[id]")].map((el) => el.id); - return { el, ids }; - }), - converted, - ]; - }); - - const sectionData = bodyData[0][0] - // make a separate section for each section in the first body - .map(({ el, ids }) => { - // set up titles for TOC - const titles = Array.from( - el.querySelectorAll(":scope > section > .title"), - (el, index) => { - el.setAttribute(dataID, index); - return { title: getElementText(el), index }; - } - ); - return { ids, titles, el }; - }) - // for additional bodies, only make one section for each body - .concat( - bodyData.slice(1).map(([sections, body]) => { - const ids = sections.map((s) => s.ids).flat(); - body.classList.add("notesBodyType"); - return { ids, el: body, linear: "no" }; - }) - ) - .map(({ ids, titles, el, linear }) => { - const str = template(el.outerHTML); - const blob = new Blob([str], { type: MIME.XHTML }); - const url = URL.createObjectURL(blob); - const title = trim( - el.querySelector(".title, .subtitle, p")?.textContent ?? - (el.classList.contains("title") ? el.textContent : "") - ); - return { - ids, - title, - titles, - load: () => url, - createDocument: () => new DOMParser().parseFromString(str, MIME.XHTML), - // doo't count image data as it'd skew the size too much - size: - blob.size - - Array.from( - el.querySelectorAll("[src]"), - (el) => el.getAttribute("src")?.length ?? 0 - ).reduce((a, b) => a + b, 0), - linear, - }; - }); - - const idMap = new Map(); - book.sections = sectionData.map((section, index) => { - const { ids, load, createDocument, size, linear } = section; - for (const id of ids) if (id) idMap.set(id, index); - return { id: index, load, createDocument, size, linear }; - }); - - book.toc = sectionData - .map(({ title, titles }, index) => { - const id = index.toString(); - return { - label: title, - href: id, - subitems: titles?.length - ? titles.map(({ title, index }) => ({ - label: title, - href: `${id}#${index}`, - })) - : null, - }; - }) - .filter((item) => item); - - book.resolveHref = (href) => { - const [a, b] = href.split("#"); - return a - ? // the link is from the TOC - { - index: Number(a), - anchor: (doc) => doc.querySelector(`[${dataID}="${b}"]`), - } - : // link from within the page - { index: idMap.get(b), anchor: (doc) => doc.getElementById(b) }; - }; - book.splitTOCHref = (href) => href?.split("#")?.map((x) => Number(x)) ?? []; - book.getTOCFragment = (doc, id) => doc.querySelector(`[${dataID}="${id}"]`); - - return book; -}; - -class Fb2Render extends GeneralRender { - constructor(fb2Buffer, config) { - super(Object.assign(Object.assign({}, config), { format: "FB2" })); - this.fb2Buffer = fb2Buffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - try { - let blob = new Blob([this.fb2Buffer]); - this.book = yield makeFB2(blob); - } - catch (error) { - console.error(error); - throw error; - } - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } - getMetadata() { - return __awaiter(this, void 0, void 0, function* () { - try { - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - return yield parser.getMetadata(); - } - catch (error) { - console.error(error); - throw error; - } - }); - } -} - -class CacheRender extends GeneralRender { - constructor(cacheBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "CACHE" })); - this.cacheBuffer = cacheBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - this.book = yield makeCacheBook(this.cacheBuffer); - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } -} - -class DocxRender extends GeneralRender { - constructor(docxBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "DOCX" })); - this.docxBuffer = docxBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - try { - mammoth - .convertToHtml({ arrayBuffer: this.docxBuffer }) - .then((res) => __awaiter(this, void 0, void 0, function* () { - this.book = makeHtmlBook(res.value, false); - resolve(); - })); - } - catch (error) { - console.error(error); - reject(error); - } - }); - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } -} - -class MdRender extends GeneralRender { - constructor(mdBuffer, config) { - super(Object.assign(Object.assign({}, config), { format: "MD" })); - this.mdBuffer = mdBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - try { - var blob = new Blob([this.mdBuffer], { type: "text/plain" }); - var reader = new FileReader(); - reader.onload = (evt) => __awaiter(this, void 0, void 0, function* () { - var _a; - let docStr = yield marked((_a = evt.target) === null || _a === void 0 ? void 0 : _a.result); - this.book = makeHtmlBook(docStr, false); - resolve(); - }); - reader.readAsText(blob, "UTF-8"); - } - catch (error) { - console.error(error); - reject(error); - } - }); - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } -} - -class HtmlRender extends GeneralRender { - constructor(htmlBuffer, config) { - super(config); - this.htmlBuffer = htmlBuffer; - } - renderTo(element) { - return new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () { - this.element = element; - if (!this.book) { - yield this.parse(); - } - let parser = new GeneralParser(this.book); - this.chapterList = yield parser.getChapter(this.book.toc); - this.chapterDocList = yield parser.getChapterDoc(); - createIframe(element); - let doc = this.getDocument(); - if (!doc) - return; - handleLayout(element, this.readerMode, doc); - resolve(); - })); - } - parse() { - return __awaiter(this, void 0, void 0, function* () { - return new Promise((resolve, reject) => { - try { - var blob = new Blob([this.htmlBuffer], { - type: mimetype[this.format.toLocaleLowerCase()], - }); - var reader = new FileReader(); - reader.onload = (evt) => __awaiter(this, void 0, void 0, function* () { - var _a; - let html = (_a = evt.target) === null || _a === void 0 ? void 0 : _a.result; - if (this.format === "MHTML") { - html = - mhtml2html.convert(html).window.document.documentElement - .innerHTML; - } - this.book = makeHtmlBook(html, false); - resolve(); - }); - reader.readAsText(blob, "UTF-8"); - } - catch (error) { - console.error(error); - reject(error); - } - }); - }); - } - preCache() { - return __awaiter(this, void 0, void 0, function* () { - if (!this.book) { - yield this.parse(); - } - return yield getCache(this.book); - }); - } -} - -export { CacheRender, ComicRender, DocxRender, EpubRender, Fb2Render, HtmlRender, MdRender, MobiRender, PdfRender, PdfTextRender, TxtRender }; +import e from"underscore";import t from"rangy/lib/rangy-core.js";import"rangy/lib/rangy-textrange";import i from"jszip";import{unzlibSync as r}from"fflate";import n from"chardet";import o from"js-untar";import s from"mammoth";import{marked as a}from"marked";import l from"mhtml2html";function c(e,t,i,r){return new(i||(i=Promise))((function(n,o){function s(e){try{l(r.next(e))}catch(e){o(e)}}function a(e){try{l(r.throw(e))}catch(e){o(e)}}function l(e){var t;e.done?n(e.value):(t=e.value,t instanceof i?t:new i((function(e){e(t)}))).then(s,a)}l((r=r.apply(e,t||[])).next())}))}const h=e=>e?parseFloat(e+""):0,d=(e,t)=>c(void 0,void 0,void 0,(function*(){let i="";if(e&&e.load){let t=yield fetch(yield e.load()).then((e=>e.blob()));i=yield t.text()}return t||(e&&e.loadAsset&&(i=yield f(i,e.loadAsset)),i=p(i)),i})),u=e=>Array.from(e.querySelectorAll("img, image")),f=(e,t)=>c(void 0,void 0,void 0,(function*(){let i=(new DOMParser).parseFromString(e,"text/html"),r=u(i);for(let e=0;e{let t=(new DOMParser).parseFromString(e,"text/html"),i=u(t);if(0===i.length)return e;for(let e=0;e{var i=document.createElement("iframe");i.style.width=t?100*(t-.4)+"%":"100%",i.style.margin="0",i.style.border="0",i.style.padding="0",i.style.minHeight="calc(100% - 2px)",i.style.fontSize="100%",i.style.font="inherit",i.scrolling="no",i.tabIndex=0,i.id="kookit-iframe",i.style.verticalAlign="baseline",e.innerHTML="",e.appendChild(i),t&&(e.scrollLeft=e.scrollWidth/2-e.clientWidth/2)},m=(e,t,i)=>{let r=Math.floor(i.clientWidth/12),n=r%2==0?r:r-1;return{totalPage:"scroll"===e?Math.floor(i.scrollHeight/(i.clientHeight-50)):"single"===e?Math.round(parseFloat(t.body.scrollWidth/(t.body.clientWidth+n)+"")):2*Math.round(parseFloat(t.body.scrollWidth/(t.body.clientWidth+n)+"")),currentPage:"scroll"===e?Math.floor(i.scrollTop/(i.clientHeight-50))+1:Math.round(parseFloat(h(t.body.scrollLeft)/(t.body.clientWidth+n)+""))+1}},y=e=>{var t;let i=e.querySelectorAll("a, article, cite, div, li, p, span, pre, dt, dd, table, bold, font");for(let e=0;ec(void 0,void 0,void 0,(function*(){const t=new Image;t.src=e;try{yield t.decode()}catch(e){console.error(e)}return t})),v=(e,t,i,r)=>c(void 0,void 0,void 0,(function*(){var n,o;let s=Math.floor(e.clientWidth/12),a=s%2==0?s:s-1;e.clientWidth;let l=r.querySelectorAll("img, image");for(let s of l){let l=s.parentElement,c=0,h=0,d=s.naturalWidth,u=s.naturalHeight;if("image"===s.tagName){let e=yield b(s.getAttribute("xlink:href"));d=e.naturalWidth,u=e.naturalHeight}if(i.startsWith("CB")&&"scroll"===t)h=l.offsetWidth;else if(i.startsWith("CB")&&"single"===t)c=e.clientHeight,h=e.clientWidth;else if(l&&d&&u&&l.clientHeight&&l.clientWidth){u/d>l.clientHeight/l.clientWidth?(c=l.clientHeight,h=parseInt(c*d/u+"")):(h=l.clientWidth,c=parseInt(h*u/d+"")),c>r.body.clientHeight&&"scroll"!==t&&(h=parseInt(h*(r.body.clientHeight/c)+""),c=r.body.clientHeight),l.style.textIndent="0px"}else l&&l.clientWidth&&l.clientWidth>0?(h=l.clientWidth,c=l.clientHeight,l.style.textIndent="0px"):(h=e.clientWidth,c=e.clientHeight);h=h?Math.min("scroll"===t||"single"===t?e.clientWidth:(e.clientWidth-a)/2,h):"scroll"===t||"single"===t?e.clientWidth:(e.clientWidth-a)/2,d&&u&&(d>u||c/h>u/d?c=h*(u/d):h=c*(d/u)),(h||c)&&s.setAttribute("style",(s.getAttribute("style")?s.getAttribute("style"):"")+";"+`max-width: ${h>0?h+"px":""};max-height:${c>0?c+"px":""}; margin: 0 auto; min-width: 0px; min-height: 0px; ${i.startsWith("CB")?`margin-left: calc(100% - ${s.clientWidth}px);`:""}`),"image"===s.tagName&&(null===(n=s.parentElement)||void 0===n||n.setAttribute("width",h),null===(o=s.parentElement)||void 0===o||o.setAttribute("height",c)),i.startsWith("CB")&&"scroll"===t&&s.setAttribute("style",(s.getAttribute("style")?s.getAttribute("style"):"")+";margin-left: 0px; width: 100%;"),i.startsWith("CB")&&"scroll"!==t&&s.setAttribute("style",(s.getAttribute("style")?s.getAttribute("style"):"")+`;margin-left: calc(50% - ${s.getBoundingClientRect().width/2}px);`)}})),w=(e,t,i)=>{let r=i.createElement("style");if(r.id="default-style",r.textContent="p,empty-line{display: inherit;margin-block-start: inherit;margin-block-end: inherit;margin-inline-start: inherit;margin-inline-end: inherit;}body{margin: 0px}",i.head.appendChild(r),"scroll"===t)return;let n="double"===t?2:1,o=Math.floor(e.clientWidth/12),s=o%2==0?o:o-1;i.body.setAttribute("style",`width: ${e.clientWidth+"px"};height: 100%;overflow-y: hidden;overflow-X: hidden;padding-left: 0px;padding-right: 0px;margin: 0px;box-sizing: border-box;touch-action:none; overscroll-behavior: none;max-width: inherit;column-fill: auto;column-gap: ${s}px; column-width: ${(e.clientWidth-s)/n}px;`)};function x(e){const t=e.getSelection();if(!t)return null;if(t.rangeCount>0){return t.getRangeAt(0).startContainer.parentElement}return null}const C=e=>"string"==typeof e||e instanceof String;class T{constructor(e){this.book=e,this.chapterList=[],this.flattenChapters=[],this.chapterDocList=[]}unescapeHtml(e){if(!e)return"";return(new DOMParser).parseFromString(e,"text/html").documentElement.textContent||""}getChapter(e){return c(this,void 0,void 0,(function*(){return this.chapterList=e?yield Promise.all(e.map(((e,t)=>c(this,void 0,void 0,(function*(){let i=t;try{i=e.href&&(yield this.book.resolveHref(e.href))?(yield this.book.resolveHref(e.href)).index:i}catch(e){console.error(e)}return{label:this.unescapeHtml(e.label)?this.unescapeHtml(e.label):i+"",href:e.href?e.href:"title"+i,index:i,subitems:e.subitems?yield this.getChapter(e.subitems):[]}}))))):yield Promise.all(this.book.sections.map(((e,t)=>c(this,void 0,void 0,(function*(){return{label:this.unescapeHtml(e.label)?this.unescapeHtml(e.label):t+"",href:e.href?e.href:"title"+t,index:t,subitems:e.subitems?yield this.getChapter(e.subitems):[]}}))))),this.flattenChapters=this.flatChapter(this.chapterList),this.chapterList}))}getChapterDoc(){return c(this,void 0,void 0,(function*(){const e=this.flattenChapters.map((e=>e.index));return this.book.sections.map(((t,i)=>e.indexOf(i)>-1?{label:this.unescapeHtml(this.flattenChapters[e.indexOf(i)].label),href:this.flattenChapters[e.indexOf(i)].href,text:t}:{label:"",href:"",text:t}))}))}flatChapter(e){let t=[];for(let i=0;i0?(t.push(e[i]),t=t.concat(this.flatChapter(e[i].subitems))):t.push(e[i]);return t}getMetadata(){return new Promise(((e,t)=>c(this,void 0,void 0,(function*(){const i=this.book.metadata;let r=i.author&&i.author[0]&&i.author[0].name&&C(i.author[0].name)?i.author[0].name:i.author&&i.author[0]&&C(i.author[0])?i.author[0]:i.author&&C(i.author)?i.author:"";try{const t=yield this.book.getCover();var n=new FileReader;n.readAsDataURL(t),n.onloadend=()=>{e(Object.assign(Object.assign({},i),{name:i.title,author:r,description:i.description,publisher:i.publisher,cover:n.result}))}}catch(n){console.error(n);try{e(Object.assign(Object.assign({},i),{name:i.title,author:r,description:i.description,publisher:i.publisher,cover:""}))}catch(e){console.error(e),t(e)}}}))))}}const L=(e,t)=>[-1,...t,e.length].reduce((({xs:t,a:i},r)=>({xs:t?.concat([e.slice(i+1,r)])??[],a:r})),{}).xs,M=/\d/,S=/^epubcfi\((.*)\)$/,D=e=>e.replace(/[\^[\](),;=]/g,"^$&"),k=(e,t)=>{return i=([e])=>e===t,e.map(((e,t,r)=>i(e,t,r)?t:null)).filter((e=>null!=e));var i},A=e=>{const t=[];let i;for(const[r,n]of e){if("/"===r)t.push({index:n});else{const e=t[t.length-1];if(":"===r)e.offset=n;else if("~"===r)e.temporal=n;else if("@"===r)e.spatial=(e.spatial??[]).concat(n);else if(";s"===r)e.side=n;else if("["===r){if("/"!==i||!n){e.text=(e.text??[]).concat(n);continue}e.id=n}}i=r}return t},E=e=>L(e,k(e,"!")).map(A),I=e=>{const t=(e=>{const t=[];let i,r,n="";const o=e=>(t.push(e),i=null,n=""),s=e=>(n+=e,r=!1);for(const t of Array.from(e.trim()).concat(""))if("^"!==t||r){if("!"===i)o(["!"]);else if(","===i)o([","]);else if("/"===i||":"===i){if(M.test(t)){s(t);continue}o([i,parseInt(n)])}else if("~"===i){if(M.test(t)||"."===t){s(t);continue}o(["~",parseFloat(n)])}else if("@"===i){if(":"===t){o(["@",parseFloat(n)]),i="@";continue}if(M.test(t)||"."===t){s(t);continue}o(["@",parseFloat(n)])}else{if("["===i){";"!==t||r?","!==t||r?"]"!==t||r?s(t):o(["[",n]):(o(["[",n]),i="["):(o(["[",n]),i=";");continue}if(i?.startsWith(";")){"="!==t||r?";"!==t||r?"]"!==t||r?s(t):o([i,n]):(o([i,n]),i=";"):(i=`;${n}`,n="");continue}}"/"!==t&&":"!==t&&"~"!==t&&"@"!==t&&"["!==t&&"!"!==t&&","!==t||(i=t)}else r=!0;return t})((i=e,i.match(S)?.[1]??i));var i;const r=k(t,",");if(!r.length)return E(t);const[n,o,s]=L(t,r).map(E);return{parent:n,start:o,end:s}},N=({index:e,id:t,offset:i,temporal:r,spatial:n,text:o,side:s})=>{const a=s?`;s=${s}`:"";return`/${e}`+(t?`[${D(t)}${a}]`:"")+(null!=i&&e%2?`:${i}`:"")+(r?`~${r}`:"")+(n?`@${n.join(":")}`:"")+(o||!t&&s?"["+(o?.map(D)?.join(",")??"")+a+"]":"")},R=e=>e.parent?[e.parent,e.start,e.end].map(R).join(","):e.map((e=>e.map(N).join(""))).join("!"),P=e=>{return t=R(e),S.test(t)?t:`epubcfi(${t})`;var t},O=(e,t)=>{return"string"==typeof e?P(O(I(e),t)):e.parent?(i=e.parent,r=e[t?"end":"start"],i.slice(0,-1).concat([i[i.length-1].concat(r[0])]).concat(r.slice(1))):e;var i,r},B=({nodeType:e})=>3===e||4===e,F=({nodeType:e})=>1===e,H=e=>{const t=Array.from(e.childNodes).filter((e=>B(e)||F(e))).reduce(((e,t)=>{let i=e[e.length-1];return i?B(t)?Array.isArray(i)?i.push(t):B(i)?e[e.length-1]=[i,t]:e.push(t):F(i)?e.push(null,t):e.push(t):e.push(t),e}),[]);return F(t[0])&&t.unshift("first"),F(t[t.length-1])&&t.push("last"),t.unshift("before"),t.push("after"),t},j=(e,t)=>e?H(e)[t]:null,W=(e,t)=>{const{id:i}=t[t.length-1];if(i){const t=e.ownerDocument.getElementById(i);if(t)return{node:t,offset:0}}for(const{index:i}of t){const t=j(e,i);if("first"===t)return{node:e.firstChild??e};if("last"===t)return{node:e.lastChild??e};if("before"===t)return{node:e,before:!0};if("after"===t)return{node:e,after:!0};e=t}const{offset:r}=t[t.length-1];if(!Array.isArray(e))return{node:e,offset:r};let n=0;for(const t of e){const{length:e}=t.nodeValue;if(n+e>=r)return{node:t,offset:r-n};n+=e}},$=(e,t)=>{const{parentNode:i,id:r}=e,n=H(i),o=n.findIndex((t=>Array.isArray(t)?t.some((t=>t===e)):t===e)),s=n[o];if(Array.isArray(s)){let i=0;for(const r of s){if(r===e){i+=t;break}i+=r.nodeValue.length}t=i}const a={id:r,index:o,offset:t};return i!==e.ownerDocument.documentElement?$(i).concat(a):[a]},U=(e,t)=>W(e.documentElement,O(t)).node,q="urn:oasis:names:tc:opendocument:xmlns:container",z="http://www.w3.org/1999/xhtml",V="http://www.idpf.org/2007/opf",X="http://www.idpf.org/2007/ops",J="http://purl.org/dc/elements/1.1/",G="http://www.w3.org/2001/04/xmlenc#",_="http://www.daisy.org/z3986/2005/ncx/",Y="http://www.w3.org/1999/xlink",Z="http://www.w3.org/ns/SMIL",K={XML:"application/xml",NCX:"application/x-dtbncx+xml",XHTML:"application/xhtml+xml",HTML:"text/html",CSS:"text/css",SVG:"image/svg+xml",JS:/\/(x-)?(javascript|ecmascript)/},Q=e=>e.toLowerCase().replace(/[-:](.)/g,((e,t)=>t.toUpperCase())),ee=(e,t,i)=>i?i=>i.getAttribute(e)?.split(/\s/)?.includes(t):"function"==typeof t?i=>t(i.getAttribute(e)):i=>i.getAttribute(e)===t,te=(...e)=>t=>t?Object.fromEntries(e.map((e=>[Q(e),t.getAttribute(e)]))):null,ie=e=>{return t=e?.textContent,t?t.trim().replace(/\s{2,}/g," "):"";var t},re=(e,t)=>{const i=e.lookupNamespaceURI(null)===t||e.lookupPrefix(t),r=i?(e,i)=>e=>e.namespaceURI===t&&e.localName===i:(e,t)=>e=>e.localName===t;return{$:(e,t)=>[...e.children].find(r(e,t)),$$:(e,t)=>[...e.children].filter(r(e,t)),$$$:i?(e,i)=>[...e.getElementsByTagNameNS(t,i)]:(e,i)=>[...e.getElementsByTagName(t,i)]}},ne=(e,t)=>{try{const i="whatever://whatever/";return decodeURI(new URL(e,i+t).href.replace(i,""))}catch(t){return console.warn(t),e}},oe=e=>/^(?!blob)\w+:/i.test(e),se=async(e,t,i)=>{const r=[];e.replace(t,((...e)=>(r.push(e),null)));const n=[];for(const e of r)n.push(await i(...e));return e.replace(t,(()=>n.shift()))},ae=e=>e.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&"),le={attrs:["dir","xml:lang"]},ce={name:"alternate-script",many:!0,...le,props:["file-as"]},he={many:!0,...le,props:[{name:"role",many:!0,attrs:["scheme"]},"file-as",ce]},de=[{name:"title",many:!0,...le,props:["title-type","display-seq","file-as",ce]},{name:"identifier",many:!0,props:[{name:"identifier-type",attrs:["scheme"]}]},{name:"language",many:!0},{name:"creator",...he},{name:"contributor",...he},{name:"publisher",...le,props:["file-as",ce]},{name:"description",...le,props:[ce]},{name:"rights",...le,props:[ce]},{name:"date"},{name:"dcterms:modified",type:"meta"},{name:"subject",many:!0,...le,props:["term","authority",ce]},{name:"belongs-to-collection",type:"meta",many:!0,...le,props:["collection-type","group-position","dcterms:identifier","file-as",ce,{name:"belongs-to-collection",recursive:!0}]}],ue=(e,t=e=>e)=>{const{$:i,$$:r,$$$:n}=re(e,z),o=e=>r=>{const n=i(r,"a")??i(r,"span"),o=i(r,"ol"),a=(e=>e?decodeURI(t(e)):null)(n?.getAttribute("href")),l={label:ie(n)||n?.getAttribute("title"),href:a,subitems:s(o)};return e&&(l.type=n?.getAttributeNS(X,"type")?.split(/\s/)),l},s=(e,t)=>e?r(e,"li").map(o(t)):null,a=(e,t)=>s(i(e,"ol"),t),l=n(e,"nav");let c=null,h=null,d=null,u=[];for(const e of l){const t=e.getAttributeNS(X,"type")?.split(/\s/)??[];t.includes("toc")?c??=a(e):t.includes("page-list")?h??=a(e):t.includes("landmarks")?d??=a(e,!0):u.push({label:ie(e.firstElementChild),type:t,list:a(e)})}return{toc:c,pageList:h,landmarks:d,others:u}},fe=(e,t=e=>e)=>{const{$:i,$$:r}=re(e,_),n=e=>{const o=i(e,"navLabel"),s=i(e,"content"),a=ie(o),l=(e=>e?decodeURI(t(e)):null)(s.getAttribute("src"));if("navPoint"===e.localName){const t=r(e,"navPoint");return{label:a,href:l,subitems:t.length?t.map(n):null}}return{label:a,href:l}},o=(e,t)=>r(e,t).map(n),s=(t,r)=>{const n=i(e.documentElement,t);return n?o(n,r):null};return{toc:s("navMap","navPoint"),pageList:s("pageList","pageTarget"),others:r(e.documentElement,"navList").map((e=>({label:ie(i(e,"navLabel")),list:o(e,"navTarget")})))}},pe=e=>{if(!e)return;const t=e.split(":").map((e=>parseFloat(e)));if(3===t.length){const[e,i,r]=t;return 60*e*60+60*i+r}if(2===t.length){const[e,i]=t;return 60*e+i}const[i,r]=e.split(/(?=[^\d.])/);return parseFloat(i)*("h"===r?3600:"min"===r?60:"ms"===r?.001:1)},ge=/([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})/,me=e=>ie(e.getElementById(e.documentElement.getAttribute("unique-identifier"))??e.getElementsByTagNameNS(J,"identifier")[0]),ye=async(e,t,i)=>{const r=new Uint8Array(await i.slice(0,t).arrayBuffer());t=Math.min(t,r.length);for(var n=0;n{const t=(new TextEncoder).encode(e),i=await globalThis.crypto.subtle.digest("SHA-1",t);return new Uint8Array(i)},ve=(e=be)=>({"http://www.idpf.org/2008/embedding":{key:t=>e(me(t).replaceAll(/[\u0020\u0009\u000d\u000a]/g,"")),decode:(e,t)=>ye(e,1040,t)},"http://ns.adobe.com/pdf/enc#RC":{key:e=>{const t=(e=>{for(const t of e.getElementsByTagNameNS(J,"identifier")){const[e]=ie(t).split(":").slice(-1);if(ge.test(e))return e}return""})(e).replaceAll("-","");return Uint8Array.from({length:16},((e,i)=>parseInt(t.slice(2*i,2*i+2),16)))},decode:(e,t)=>ye(e,1024,t)}});class we{#e=new Map;#t=new Map;#i;constructor(e){this.#i=e}async init(e,t){if(!e)return;const i=Array.from(e.getElementsByTagNameNS(G,"EncryptedData"),(e=>({algorithm:e.getElementsByTagNameNS(G,"EncryptionMethod")[0]?.getAttribute("Algorithm"),uri:e.getElementsByTagNameNS(G,"CipherReference")[0]?.getAttribute("URI")})));for(const{algorithm:e,uri:r}of i){if(!this.#t.has(e)){const i=this.#i[e];if(!i){console.warn("Unknown encryption algorithm");continue}const r=await i.key(t);this.#t.set(e,(e=>i.decode(r,e)))}this.#e.set(r,e)}}getDecoder(e){return this.#t.get(this.#e.get(e))??(e=>e)}}class xe{constructor({opf:e,resolveHref:t}){this.opf=e;const{$:i,$$:r,$$$:n}=re(e,V),o=i(e.documentElement,"manifest"),s=i(e.documentElement,"spine"),a=r(s,"itemref");this.manifest=r(o,"item").map(te("href","id","media-type","properties","media-overlay")).map((e=>(e.href=t(e.href),e.properties=e.properties?.split(/\s/),e))),0===this.manifest.length&&(this.manifest=Array.from(o.children).map((e=>{const i=te("href","id","media-type","properties","media-overlay")(e);return i.href=t(i.href),i.properties=i.properties?.split(/\s/),i}))),this.spine=a.map(te("idref","id","linear","properties")).map((e=>(e.properties=e.properties?.split(/\s/),e))),this.pageProgressionDirection=s.getAttribute("page-progression-direction"),this.navPath=this.getItemByProperty("nav")?.href,this.ncxPath=(this.getItemByID(s.getAttribute("toc"))??this.manifest.find((e=>e.mediaType===K.NCX)))?.href;const l=i(e.documentElement,"guide");l&&(this.guide=r(l,"reference").map(te("type","title","href")).map((({type:e,title:i,href:r})=>({label:i,type:e.split(/\s/),href:t(r)})))),this.cover=this.getItemByProperty("cover-image")??this.getItemByID("cover-image")??this.getItemByID(n(e,"meta").find(ee("name","cover"))?.getAttribute("content"))??this.getItemByID("cover")??this.getItemByID("cover.jpg")??this.getItemByID("cover.png")??this.getItemByID("cover.jpeg")??this.getItemByHref(this.guide?.find((e=>e.type.includes("cover")&&!e.href.includes("html")&&!e.href.includes("xml")))?.href),this.cfis=(e=>{const t=[],{parentNode:i}=e[0],r=$(i);for(const[n,o]of H(i).entries()){const i=e[t.length];o===i&&t.push(P([r.concat({id:i.id,index:n})]))}return t})(a)}getItemByID(e){return this.manifest.find((t=>t.id===e))}getItemByHref(e){return this.manifest.find((t=>t.href===e))}getItemByProperty(e){return this.manifest.find((t=>t.properties?.includes(e)))}resolveCFI(e){const t=I(e),i=(t.parent??t).shift();let r=U(this.opf,i);r&&"idref"!==r.nodeName&&(i.at(-1).id=null,r=U(this.opf,i));const n=r?.getAttribute("idref");return{index:this.spine.findIndex((e=>e.idref===n)),anchor:e=>((e,t)=>{const i=O(t),r=O(t,!0),n=e.documentElement,o=W(n,i[0]),s=W(n,r[0]),a=e.createRange();return o.before?a.setStartBefore(o.node):o.after?a.setStartAfter(o.node):a.setStart(o.node,o.offset),s.before?a.setEndBefore(s.node):s.after?a.setEndAfter(s.node):a.setEnd(s.node,s.offset),a})(e,t)}}}class Ce{#r=new Map;#n=new Map;#o=new Map;allowScript=!1;constructor({loadText:e,loadBlob:t,resources:i}){this.loadText=e,this.loadBlob=t,this.manifest=i.manifest,this.assets=i.manifest}createURL(e,t,i,r){if(!t)return"";const n=URL.createObjectURL(new Blob([t],{type:i}));if(this.#r.set(e,n),this.#o.set(e,1),r){const t=this.#n.get(r);t?t.push(e):this.#n.set(r,[e])}return n}ref(e,t){const i=this.#n.get(t);return i?.includes(e)||(this.#o.set(e,this.#o.get(e)+1),i?i.push(e):this.#n.set(t,[e])),this.#r.get(e)}unref(e){if(!this.#o.has(e))return;const t=this.#o.get(e)-1;if(t<1){URL.revokeObjectURL(this.#r.get(e)),this.#r.delete(e),this.#o.delete(e);const t=this.#n.get(e);if(t)for(;t.length;)this.unref(t.pop());this.#n.delete(e)}else this.#o.set(e,t)}async loadItem(e,t=[]){if(!e)return null;const{href:i,mediaType:r}=e,n=K.JS.test(e.mediaType);if(n&&!this.allowScript)return null;const o=t.at(-1);if(this.#r.has(i))return this.ref(i,o);return(n||[K.XHTML,K.HTML,K.CSS,K.SVG].includes(r))&&t.every((e=>e!==i))?this.loadReplaced(e,t):this.createURL(i,await this.loadBlob(i),r,o)}async loadHref(e,t,i=[]){if(oe(e))return e;const r=ne(e,t);let n=this.manifest.find((e=>e.href===r));return n||(n={href:r,mediaType:""}),this.loadItem(n,i.concat(t))}async loadReplaced(e,t=[]){const{href:i,mediaType:r}=e,n=t.at(-1),o=await this.loadText(i);if(!o)return null;if([K.XHTML,K.HTML,K.SVG].includes(r)){let s=(new DOMParser).parseFromString(o.trim(),r);if(r===K.XHTML&&s.querySelector("parsererror")&&(console.warn(s.querySelector("parsererror").innerText),e.mediaType=K.HTML,s=(new DOMParser).parseFromString(o.trim(),e.mediaType)),[K.XHTML,K.SVG].includes(e.mediaType)){let e=s.firstChild;for(;e instanceof ProcessingInstruction;){if(e.data){const r=await se(e.data,/(?:^|\s*)(href\s*=\s*['"])([^'"]*)(['"])/i,((e,r,n,o)=>this.loadHref(n,i,t).then((e=>`${r}${e}${o}`))));e.replaceWith(s.createProcessingInstruction(e.target,r))}e=e.nextSibling}}const a=async(e,r)=>e.setAttribute(r,await this.loadHref(e.getAttribute(r),i,t));for(const e of s.querySelectorAll("link[href]"))await a(e,"href");for(const e of s.querySelectorAll("[src]"))await a(e,"src");for(const e of s.querySelectorAll("[poster]"))await a(e,"poster");for(const e of s.querySelectorAll("object[data]"))await a(e,"data");for(const e of s.querySelectorAll("[*|href]:not([href]"))e.setAttributeNS(Y,"href",await this.loadHref(e.getAttributeNS(Y,"href"),i,t));for(const e of s.querySelectorAll("style"))e.textContent&&(e.textContent=await this.replaceCSS(e.textContent,i,t));for(const e of s.querySelectorAll("[style]"))e.setAttribute("style",await this.replaceCSS(e.getAttribute("style"),i,t));const l=(new XMLSerializer).serializeToString(s);return this.createURL(i,l,e.mediaType,n)}const s=r===K.CSS?await this.replaceCSS(o,i,t):await this.replaceString(o,i,t);return this.createURL(i,s,r,n)}async replaceCSS(e,t,i=[]){const r=await se(e,/url\(\s*["']?([^'"\n]*?)\s*["']?\s*\)/gi,((e,r)=>this.loadHref(r,t,i).then((e=>`url("${e}")`)))),n=await se(r,/@import\s*["']([^"'\n]*?)["']/gi,((e,r)=>this.loadHref(r,t,i).then((e=>`@import "${e}"`)))),o=window?.innerWidth??800,s=window?.innerHeight??600;return n.replace(/-epub-/gi,"").replace(/(\d*\.?\d+)vw/gi,((e,t)=>parseFloat(t)*o/100+"px")).replace(/(\d*\.?\d+)vh/gi,((e,t)=>parseFloat(t)*s/100+"px")).replace(/page-break-(after|before|inside)/gi,((e,t)=>`-webkit-column-break-${t}`))}replaceString(e,t,i=[]){const r=new Map,n=this.assets.map((e=>{if(e.href===t)return;const i=((e,t)=>{if(!e)return t;const i=e.replace(/\/$/,"").split("/"),r=t.replace(/\/$/,"").split("/"),n=(i.length>r.length?i:r).findIndex(((e,t)=>i[t]!==r[t]));return n<0?"":Array(i.length-n).fill("..").concat(r.slice(n)).join("/")})((e=>e.slice(0,e.lastIndexOf("/")+1))(t),e.href),n=encodeURI(i),o="/"+e.href,s=encodeURI(o),a=new Set([i,n,o,s]);for(const t of a)r.set(t,e);return Array.from(a)})).flat().filter((e=>e));if(!n.length)return e;const o=new RegExp(n.map(ae).join("|"),"g");return se(e,o,(async e=>this.loadItem(r.get(e.replace(/^\//,"")),i.concat(t))))}unloadItem(e){this.unref(e?.href)}}const Te=e=>{for(const t of e){if("page-spread-left"===t||"rendition:page-spread-left"===t)return"left";if("page-spread-right"===t||"rendition:page-spread-right"===t)return"right";if("rendition:page-spread-center"===t)return"center"}};class Le{parser=new DOMParser;#s;constructor({loadText:e,loadBlob:t,getSize:i,sha1:r}){this.loadText=e,this.loadBlob=t,this.getSize=i,this.#s=new we(ve(r))}#a(e){return e&&e.includes("opf:scheme")&&(e=e.replaceAll("opf:scheme","scheme")),e&&(e=e.replace(/^\uFEFF/,"").replace(//g,((e,t)=>`\x3c!--${t.replace(/--/g,"- -")}--\x3e`)).replace(/&(?!(?:amp|lt|gt|quot|apos|#\d+|#x[\da-fA-F]+);)/g,"&").replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g,"")),e?this.parser.parseFromString(e.trim(),K.XML):null}async#l(e){return this.#a(await this.loadText(e))}async init(){const e=await this.#l("META-INF/container.xml");if(!e)throw new Error("Failed to load container file");const t=Array.from(e.getElementsByTagNameNS(q,"rootfile"),te("full-path","media-type")).filter((e=>"application/oebps-package+xml"===e.mediaType));if(!t.length)throw new Error("No package document defined in container");const i=t[0].fullPath,r=await this.#l(i);if(!r)throw new Error("Failed to load package document");if(r.querySelector("parsererror"))throw new Error("Package document is not a valid XML");const n=await this.#l("META-INF/encryption.xml");await this.#s.init(n,r),this.resources=new xe({opf:r,resolveHref:e=>ne(e,i)});const o=new Ce({loadText:this.loadText,loadBlob:e=>Promise.resolve(this.loadBlob(e)).then(this.#s.getDecoder(e)),resources:this.resources});this.sections=this.resources.spine.map(((e,t)=>{const{idref:i,linear:r,properties:n=[]}=e,s=this.resources.getItemByID(i);return s?{id:this.resources.getItemByID(i)?.href,load:()=>o.loadItem(s),unload:()=>o.unloadItem(s),createDocument:()=>this.loadDocument(s),size:this.getSize(s.href),cfi:this.resources.cfis[t],linear:r,pageSpread:Te(n),resolveHref:e=>ne(e,s.href),loadMediaOverlay:()=>this.loadMediaOverlay(s)}:(console.warn(`Could not find item with ID "${i}" in manifest`),null)}));const{navPath:s,ncxPath:a}=this.resources;if(s)try{const e=e=>ne(e,s),t=ue(await this.#l(s),e);this.toc=t.toc,this.pageList=t.pageList,this.landmarks=t.landmarks}catch(e){console.warn(e)}if((!this.toc||0===this.toc.length)&&a)try{const e=e=>ne(e,a),t=fe(await this.#l(a),e);this.toc=t.toc,this.pageList=t.pageList}catch(e){console.warn(e)}this.landmarks??=this.resources.guide;const{metadata:l,rendition:c,media:h}=(e=>{const{$:t,$$:i}=re(e,V),r=t(e.documentElement,"metadata"),n=Array.from(r.children),o=(e,t)=>{if(!t)return null;const{props:i=[],attrs:r=[]}=e,s=ie(t);if(!i.length&&!r.length)return s;const a=t.getAttribute("id"),l=a?n.filter(ee("refines","#"+a)):[];return Object.fromEntries([["value",s]].concat(i.map((t=>{const{many:i,recursive:r}=t,n="string"==typeof t?t:t.name,s=ee("property",n),a=r?e:t;return[Q(n),i?l.filter(s).map((e=>o(a,e))):o(a,l.find(s))]}))).concat(r.map((e=>[Q(e),t.getAttribute(e)]))))},s=n.filter(ee("refines",null)),a=e=>Object.fromEntries(i(r,"meta").filter(ee("property",(t=>t?.startsWith(e)))).map((t=>[t.getAttribute("property").replace(e,""),ie(t)])));return{metadata:Object.fromEntries(de.map((e=>{const{type:t,name:i,many:r}=e,n="meta"===t?e=>e.namespaceURI===V&&e.getAttribute("property")===i:e=>e.namespaceURI===J&&e.localName===i;return[Q(i),r?s.filter(n).map((t=>o(e,t))):o(e,s.find(n))]}))),rendition:a("rendition:"),media:a("media:")}})(r);this.rendition=c,this.media=h,h.duration=pe(h.duration),this.dir=this.resources.pageProgressionDirection,this.rawMetadata=l;const d=l?.title?.[0];this.metadata={title:d?.value,sortAs:d?.fileAs,language:l?.language,identifier:me(r),description:l?.description?.value,publisher:l?.publisher?.value,published:l?.date,modified:l?.dctermsModified,subject:l?.subject?.filter((({value:e,code:t})=>e||t))?.map((({value:e,code:t,scheme:i})=>({name:e,code:t,scheme:i}))),rights:l?.rights?.value};const u={art:"artist",aut:"author",bkp:"producer",clr:"colorist",edt:"editor",ill:"illustrator",trl:"translator",pbl:"publisher"},f=e=>t=>{const i=[...new Set(t.role?.map((({value:t,scheme:i})=>(i&&"marc:relators"!==i?null:u[t])??e)))],r={name:t.value,sortAs:t.fileAs};return[i?.length?i:[e],r]};return l?.creator?.map(f("author"))?.concat(l?.contributor?.map?.(f("contributor")))?.forEach((([e,t])=>e.forEach((e=>{this.metadata[e]?this.metadata[e].push(t):this.metadata[e]=[t]})))),this}async loadDocument(e){const t=await this.loadText(e.href);return this.parser.parseFromString(t.trim(),e.mediaType)}async loadMediaOverlay(e){const t=e.mediaOverlay;if(!t)return null;const i=this.resources.getItemByID(t),r=((e,t=e=>e)=>{const{$:i,$$$:r}=re(e,Z);return r(e,"par").map((e=>{const r=i(e,"text")?.getAttribute("src")?.split("#")?.[1],n=i(e,"audio");return n?{id:r,audio:{src:(o=n.getAttribute("src"),o?decodeURI(t(o)):null),clipBegin:pe(n.getAttribute("clipBegin")),clipEnd:pe(n.getAttribute("clipEnd"))}}:{id:r};var o}))})(await this.#l(i.href),(e=>ne(e,i.href)));return r}resolveCFI(e){return this.resources.resolveCFI(e)}resolveHref(e){const[t,i]=e.split("#"),r=this.resources.getItemByHref(decodeURI(t));if(!r)return null;return{index:this.resources.spine.findIndex((({idref:e})=>e===r.id)),anchor:i?e=>((e,t)=>e.getElementById(t)??e.querySelector(`[name="${CSS.escape(t)}"]`))(e,i):()=>0}}splitTOCHref(e){return e?.split("#")??[]}getTOCFragment(e,t){return e.getElementById(t)??e.querySelector(`[name="${CSS.escape(t)}"]`)}isExternal(e){return oe(e)}async getCover(){const e=this.resources?.cover;return e?.href?new Blob([await this.loadBlob(e.href)],{type:e.mediaType}):null}async getCalibreBookmarks(){const e=await this.loadText("META-INF/calibre_bookmarks.txt"),t="encoding=json+base64:";if(e?.startsWith(t)){const t=atob(e.slice(21));return JSON.parse(t)}}}const Me={"㐷":"傌","㐹":"㑶","㐽":"偑","㑇":"㑳","㑈":"倲","㑔":"㑯","㑩":"儸","㓆":"𠗣","㓥":"劏","㓰":"劃","㔉":"劚","㖊":"噚","㖞":"喎","㘎":"㘚","㚯":"㜄","㛀":"媰","㛟":"𡞵","㛠":"𡢃","㛣":"㜏","㛤":"孋","㛿":"𡠹","㟆":"㠏","㟜":"𡾱","㟥":"嵾","㡎":"幓","㤘":"㥮","㤽":"懤","㥪":"慺","㧏":"掆","㧐":"㩳","㧑":"撝","㧟":"擓","㧰":"擽","㨫":"㩜","㭎":"棡","㭏":"椲","㭣":"𣙎","㭤":"樢","㭴":"樫","㱩":"殰","㱮":"殨","㲿":"瀇","㳔":"濧","㳕":"灡","㳠":"澾","㳡":"濄","㳢":"𣾷","㳽":"瀰","㴋":"潚","㶉":"鸂","㶶":"燶","㶽":"煱","㺍":"獱","㻅":"璯","㻏":"𤫩","㻘":"𤪺","䀥":"䁻","䁖":"瞜","䂵":"碽","䃅":"磾","䅉":"稏","䅟":"穇","䅪":"𥢢","䇲":"筴","䉤":"籔","䌶":"䊷","䌷":"紬","䌸":"縳","䌹":"絅","䌺":"䋙","䌻":"䋚","䌼":"綐","䌽":"綵","䌾":"䋻","䌿":"䋹","䍀":"繿","䍁":"繸","䍠":"䍦","䎬":"䎱","䏝":"膞","䑽":"𦪙","䓓":"薵","䓕":"薳","䓖":"藭","䓨":"罃","䗖":"螮","䘛":"𧝞","䘞":"𧜗","䙊":"𧜵","䙌":"䙡","䙓":"襬","䜣":"訢","䜤":"鿁","䜥":"𧩙","䜧":"䜀","䜩":"讌","䝙":"貙","䞌":"𧵳","䞍":"䝼","䞎":"𧶧","䞐":"賰","䟢":"躎","䢀":"𨊰","䢁":"𨊸","䢂":"𨋢","䥺":"釾","䥽":"鏺","䥾":"䥱","䥿":"𨯅","䦀":"𨦫","䦁":"𨧜","䦂":"䥇","䦃":"鐯","䦅":"鐥","䦆":"钁","䦶":"䦛","䦷":"䦟","䩄":"靦","䭪":"𩞯","䯃":"𩣑","䯄":"騧","䯅":"䯀","䲝":"䱽","䲞":"𩶘","䲟":"鮣","䲠":"鰆","䲡":"鰌","䲢":"鰧","䲣":"䱷","䴓":"鳾","䴔":"鵁","䴕":"鴷","䴖":"鶄","䴗":"鶪","䴘":"鷉","䴙":"鸊","䶮":"龑","万":"萬","与":"與","丑":"醜","专":"專","业":"業","丛":"叢","东":"東","丝":"絲","丢":"丟","两":"兩","严":"嚴","丧":"喪","个":"個","丰":"豐","临":"臨","为":"爲","丽":"麗","举":"舉","么":"麼","义":"義","乌":"烏","乐":"樂","乔":"喬","习":"習","乡":"鄉","书":"書","买":"買","乱":"亂","了":"了","争":"爭","于":"於","亏":"虧","云":"雲","亘":"亙","亚":"亞","产":"產","亩":"畝","亲":"親","亵":"褻","亸":"嚲","亿":"億","仅":"僅","仆":"僕","仇":"仇","从":"從","仑":"侖","仓":"倉","仪":"儀","们":"們","价":"價","仿":"仿","众":"衆","优":"優","伙":"夥","会":"會","伛":"傴","伞":"傘","伟":"偉","传":"傳","伡":"俥","伣":"俔","伤":"傷","伥":"倀","伦":"倫","伧":"傖","伪":"僞","伫":"佇","体":"體","余":"餘","佛":"佛","佣":"傭","佥":"僉","侠":"俠","侣":"侶","侥":"僥","侦":"偵","侧":"側","侨":"僑","侩":"儈","侪":"儕","侬":"儂","侭":"儘","俊":"俊","俣":"俁","俦":"儔","俨":"儼","俩":"倆","俪":"儷","俫":"倈","俭":"儉","修":"修","借":"借","债":"債","倾":"傾","偬":"傯","偻":"僂","偾":"僨","偿":"償","傤":"儎","傥":"儻","傧":"儐","储":"儲","傩":"儺","僵":"僵","儿":"兒","克":"克","兑":"兌","兖":"兗","党":"黨","兰":"蘭","关":"關","兴":"興","兹":"茲","养":"養","兽":"獸","冁":"囅","内":"內","冈":"岡","册":"冊","写":"寫","军":"軍","农":"農","冬":"冬","冯":"馮","冲":"衝","决":"決","况":"況","冻":"凍","净":"淨","凄":"悽","准":"準","凉":"涼","凌":"凌","减":"減","凑":"湊","凛":"凜","几":"幾","凤":"鳳","凫":"鳧","凭":"憑","凯":"凱","凶":"兇","出":"出","击":"擊","凿":"鑿","刍":"芻","划":"劃","刘":"劉","则":"則","刚":"剛","创":"創","删":"刪","别":"別","刬":"剗","刭":"剄","刮":"刮","制":"制","刹":"剎","刽":"劊","刾":"㓨","刿":"劌","剀":"剴","剂":"劑","剐":"剮","剑":"劍","剥":"剝","剧":"劇","劝":"勸","办":"辦","务":"務","劢":"勱","动":"動","励":"勵","劲":"勁","劳":"勞","势":"勢","勋":"勳","勚":"勩","匀":"勻","匦":"匭","匮":"匱","区":"區","医":"醫","千":"千","升":"升","华":"華","协":"協","单":"單","卖":"賣","卜":"卜","占":"佔","卢":"盧","卤":"滷","卧":"臥","卫":"衛","却":"卻","卷":"卷","卺":"巹","厂":"廠","厅":"廳","历":"歷","厉":"厲","压":"壓","厌":"厭","厍":"厙","厐":"龎","厕":"廁","厘":"釐","厢":"廂","厣":"厴","厦":"廈","厨":"廚","厩":"廄","厮":"廝","县":"縣","叁":"叄","参":"參","叆":"靉","叇":"靆","双":"雙","发":"發","变":"變","叙":"敘","叠":"疊","只":"只","台":"臺","叶":"葉","号":"號","叹":"嘆","叽":"嘰","吁":"籲","吃":"喫","合":"合","吊":"吊","同":"同","后":"後","向":"向","吓":"嚇","吕":"呂","吗":"嗎","吨":"噸","听":"聽","启":"啓","吴":"吳","呐":"吶","呒":"嘸","呓":"囈","呕":"嘔","呖":"嚦","呗":"唄","员":"員","呙":"咼","呛":"嗆","呜":"嗚","周":"周","咏":"詠","咙":"嚨","咛":"嚀","咝":"噝","咤":"吒","咨":"諮","咸":"鹹","咽":"咽","哄":"哄","响":"響","哑":"啞","哒":"噠","哓":"嘵","哔":"嗶","哕":"噦","哗":"譁","哙":"噲","哜":"嚌","哝":"噥","哟":"喲","唇":"脣","唛":"嘜","唝":"嗊","唠":"嘮","唡":"啢","唢":"嗩","唤":"喚","啧":"嘖","啬":"嗇","啭":"囀","啮":"齧","啯":"嘓","啰":"囉","啴":"嘽","啸":"嘯","喂":"喂","喷":"噴","喽":"嘍","喾":"嚳","嗫":"囁","嗳":"噯","嘘":"噓","嘤":"嚶","嘱":"囑","噜":"嚕","噪":"噪","嚣":"囂","回":"回","团":"團","园":"園","困":"困","囱":"囪","围":"圍","囵":"圇","国":"國","图":"圖","圆":"圓","圣":"聖","圹":"壙","场":"場","坏":"壞","块":"塊","坚":"堅","坛":"壇","坜":"壢","坝":"壩","坞":"塢","坟":"墳","坠":"墜","垄":"壟","垅":"壠","垆":"壚","垒":"壘","垦":"墾","垩":"堊","垫":"墊","垭":"埡","垯":"墶","垱":"壋","垲":"塏","垴":"堖","埘":"塒","埙":"壎","埚":"堝","堑":"塹","堕":"墮","塆":"壪","墙":"牆","壮":"壯","声":"聲","壳":"殼","壶":"壺","壸":"壼","处":"處","备":"備","复":"復","够":"夠","夫":"夫","头":"頭","夸":"誇","夹":"夾","夺":"奪","奁":"奩","奂":"奐","奋":"奮","奖":"獎","奥":"奧","奸":"奸","妆":"妝","妇":"婦","妈":"媽","妩":"嫵","妪":"嫗","妫":"嬀","姗":"姍","姜":"姜","姹":"奼","娄":"婁","娅":"婭","娆":"嬈","娇":"嬌","娈":"孌","娘":"娘","娱":"娛","娲":"媧","娴":"嫺","婳":"嫿","婴":"嬰","婵":"嬋","婶":"嬸","媪":"媼","媭":"嬃","嫒":"嬡","嫔":"嬪","嫱":"嬙","嬷":"嬤","孙":"孫","学":"學","孪":"孿","宁":"寧","它":"它","宝":"寶","实":"實","宠":"寵","审":"審","宪":"憲","宫":"宮","家":"家","宽":"寬","宾":"賓","寝":"寢","对":"對","寻":"尋","导":"導","寿":"壽","将":"將","尔":"爾","尘":"塵","尝":"嘗","尧":"堯","尴":"尷","尸":"屍","尽":"盡","局":"局","层":"層","屃":"屓","屉":"屜","届":"屆","属":"屬","屡":"屢","屦":"屨","屿":"嶼","岁":"歲","岂":"豈","岖":"嶇","岗":"崗","岘":"峴","岚":"嵐","岛":"島","岩":"巖","岭":"嶺","岳":"嶽","岽":"崬","岿":"巋","峃":"嶨","峄":"嶧","峡":"峽","峣":"嶢","峤":"嶠","峥":"崢","峦":"巒","峰":"峯","崂":"嶗","崃":"崍","崄":"嶮","崭":"嶄","嵘":"嶸","嵚":"嶔","嵝":"嶁","巅":"巔","巨":"巨","巩":"鞏","巯":"巰","币":"幣","布":"布","帅":"帥","师":"師","帏":"幃","帐":"帳","帘":"簾","帜":"幟","带":"帶","帧":"幀","席":"席","帮":"幫","帱":"幬","帻":"幘","帼":"幗","幂":"冪","干":"幹","并":"並","幸":"幸","广":"廣","庄":"莊","庆":"慶","床":"牀","庐":"廬","庑":"廡","库":"庫","应":"應","庙":"廟","庞":"龐","废":"廢","庵":"庵","庼":"廎","廪":"廩","开":"開","异":"異","弃":"棄","弑":"弒","张":"張","弥":"彌","弦":"弦","弪":"弳","弯":"彎","弹":"彈","强":"強","归":"歸","当":"當","录":"錄","彟":"彠","彦":"彥","彨":"彲","彩":"彩","彻":"徹","征":"徵","径":"徑","徕":"徠","御":"御","忆":"憶","忏":"懺","志":"志","忧":"憂","念":"念","忾":"愾","怀":"懷","态":"態","怂":"慫","怃":"憮","怄":"慪","怅":"悵","怆":"愴","怜":"憐","总":"總","怼":"懟","怿":"懌","恋":"戀","恒":"恆","恤":"恤","恳":"懇","恶":"惡","恸":"慟","恹":"懨","恺":"愷","恻":"惻","恼":"惱","恽":"惲","悦":"悅","悫":"愨","悬":"懸","悭":"慳","悮":"悞","悯":"憫","惊":"驚","惧":"懼","惨":"慘","惩":"懲","惫":"憊","惬":"愜","惭":"慚","惮":"憚","惯":"慣","愈":"愈","愠":"慍","愤":"憤","愦":"憒","愿":"願","慑":"懾","慭":"憖","懑":"懣","懒":"懶","懔":"懍","戆":"戇","戋":"戔","戏":"戲","戗":"戧","战":"戰","戚":"戚","戬":"戩","戯":"戱","户":"戶","才":"才","扎":"扎","扑":"撲","托":"託","扣":"扣","执":"執","扩":"擴","扪":"捫","扫":"掃","扬":"揚","扰":"擾","折":"折","抚":"撫","抛":"拋","抟":"摶","抠":"摳","抡":"掄","抢":"搶","护":"護","报":"報","抵":"抵","担":"擔","拐":"拐","拟":"擬","拢":"攏","拣":"揀","拥":"擁","拦":"攔","拧":"擰","拨":"撥","择":"擇","挂":"掛","挚":"摯","挛":"攣","挜":"掗","挝":"撾","挞":"撻","挟":"挾","挠":"撓","挡":"擋","挢":"撟","挣":"掙","挤":"擠","挥":"揮","挦":"撏","挨":"挨","挽":"挽","捝":"挩","捞":"撈","损":"損","捡":"撿","换":"換","捣":"搗","据":"據","掳":"擄","掴":"摑","掷":"擲","掸":"撣","掺":"摻","掼":"摜","揽":"攬","揾":"搵","揿":"撳","搀":"攙","搁":"擱","搂":"摟","搄":"揯","搅":"攪","搜":"搜","携":"攜","摄":"攝","摅":"攄","摆":"擺","摇":"搖","摈":"擯","摊":"攤","撄":"攖","撑":"撐","撵":"攆","撷":"擷","撸":"擼","撺":"攛","擜":"㩵","擞":"擻","攒":"攢","敌":"敵","敚":"敓","敛":"斂","敩":"斆","数":"數","斋":"齋","斓":"斕","斗":"鬥","斩":"斬","断":"斷","旋":"旋","无":"無","旧":"舊","时":"時","旷":"曠","旸":"暘","昆":"昆","昙":"曇","昵":"暱","昼":"晝","昽":"曨","显":"顯","晋":"晉","晒":"曬","晓":"曉","晔":"曄","晕":"暈","晖":"暉","暂":"暫","暅":"𣈶","暗":"暗","暧":"曖","曲":"曲","术":"術","朱":"朱","朴":"樸","机":"機","杀":"殺","杂":"雜","权":"權","杆":"杆","杠":"槓","条":"條","来":"來","杨":"楊","杩":"榪","杯":"杯","杰":"傑","松":"松","板":"板","极":"極","构":"構","枞":"樅","枢":"樞","枣":"棗","枥":"櫪","枧":"梘","枨":"棖","枪":"槍","枫":"楓","枭":"梟","柜":"櫃","柠":"檸","柽":"檉","栀":"梔","栅":"柵","标":"標","栈":"棧","栉":"櫛","栊":"櫳","栋":"棟","栌":"櫨","栎":"櫟","栏":"欄","树":"樹","栖":"棲","栗":"栗","样":"樣","核":"核","栾":"欒","桠":"椏","桡":"橈","桢":"楨","档":"檔","桤":"榿","桥":"橋","桦":"樺","桧":"檜","桨":"槳","桩":"樁","桪":"樳","梁":"梁","梦":"夢","梼":"檮","梾":"棶","梿":"槤","检":"檢","棁":"梲","棂":"欞","椁":"槨","椝":"槼","椟":"櫝","椠":"槧","椢":"槶","椤":"欏","椫":"樿","椭":"橢","椮":"槮","楼":"樓","榄":"欖","榅":"榲","榇":"櫬","榈":"櫚","榉":"櫸","榝":"樧","槚":"檟","槛":"檻","槟":"檳","槠":"櫧","横":"橫","樯":"檣","樱":"櫻","橥":"櫫","橱":"櫥","橹":"櫓","橼":"櫞","檩":"檁","欢":"歡","欤":"歟","欧":"歐","欲":"欲","歼":"殲","殁":"歿","殇":"殤","残":"殘","殒":"殞","殓":"殮","殚":"殫","殡":"殯","殴":"毆","毁":"毀","毂":"轂","毕":"畢","毙":"斃","毡":"氈","毵":"毿","毶":"𣯶","氇":"氌","气":"氣","氢":"氫","氩":"氬","氲":"氳","汇":"匯","汉":"漢","汤":"湯","汹":"洶","沄":"澐","沈":"沈","沟":"溝","没":"沒","沣":"灃","沤":"漚","沥":"瀝","沦":"淪","沧":"滄","沨":"渢","沩":"潙","沪":"滬","沾":"沾","泛":"泛","泞":"濘","注":"注","泪":"淚","泶":"澩","泷":"瀧","泸":"瀘","泺":"濼","泻":"瀉","泼":"潑","泽":"澤","泾":"涇","洁":"潔","洒":"灑","洼":"窪","浃":"浹","浅":"淺","浆":"漿","浇":"澆","浈":"湞","浉":"溮","浊":"濁","测":"測","浍":"澮","济":"濟","浏":"瀏","浐":"滻","浑":"渾","浒":"滸","浓":"濃","浔":"潯","浕":"濜","涂":"塗","涌":"湧","涚":"涗","涛":"濤","涝":"澇","涞":"淶","涟":"漣","涠":"潿","涡":"渦","涢":"溳","涣":"渙","涤":"滌","润":"潤","涧":"澗","涨":"漲","涩":"澀","淀":"澱","渊":"淵","渌":"淥","渍":"漬","渎":"瀆","渐":"漸","渑":"澠","渔":"漁","渖":"瀋","渗":"滲","温":"溫","游":"遊","湾":"灣","湿":"溼","溁":"濚","溃":"潰","溅":"濺","溆":"漵","溇":"漊","滗":"潷","滚":"滾","滞":"滯","滟":"灩","滠":"灄","满":"滿","滢":"瀅","滤":"濾","滥":"濫","滦":"灤","滨":"濱","滩":"灘","滪":"澦","漓":"漓","潆":"瀠","潇":"瀟","潋":"瀲","潍":"濰","潜":"潛","潴":"瀦","澛":"瀂","澜":"瀾","濑":"瀨","濒":"瀕","灏":"灝","灭":"滅","灯":"燈","灵":"靈","灶":"竈","灾":"災","灿":"燦","炀":"煬","炉":"爐","炖":"燉","炜":"煒","炝":"熗","点":"點","炼":"煉","炽":"熾","烁":"爍","烂":"爛","烃":"烴","烛":"燭","烟":"煙","烦":"煩","烧":"燒","烨":"燁","烩":"燴","烫":"燙","烬":"燼","热":"熱","焕":"煥","焖":"燜","焘":"燾","煴":"熅","熏":"燻","爱":"愛","爷":"爺","牍":"牘","牦":"犛","牵":"牽","牺":"犧","犊":"犢","状":"狀","犷":"獷","犸":"獁","犹":"猶","狈":"狽","狝":"獮","狞":"獰","独":"獨","狭":"狹","狮":"獅","狯":"獪","狰":"猙","狱":"獄","狲":"猻","猃":"獫","猎":"獵","猕":"獼","猡":"玀","猪":"豬","猫":"貓","猬":"蝟","献":"獻","獭":"獺","玑":"璣","玙":"璵","玚":"瑒","玛":"瑪","玩":"玩","玮":"瑋","环":"環","现":"現","玱":"瑲","玺":"璽","珐":"琺","珑":"瓏","珰":"璫","珲":"琿","琎":"璡","琏":"璉","琐":"瑣","琼":"瓊","瑶":"瑤","瑷":"璦","瑸":"璸","璇":"璇","璎":"瓔","瓒":"瓚","瓮":"甕","瓯":"甌","电":"電","画":"畫","畅":"暢","畴":"疇","疖":"癤","疗":"療","疟":"瘧","疠":"癘","疡":"瘍","疬":"癧","疭":"瘲","疮":"瘡","疯":"瘋","疱":"皰","疴":"痾","症":"症","痈":"癰","痉":"痙","痒":"癢","痖":"瘂","痨":"癆","痪":"瘓","痫":"癇","痴":"癡","瘅":"癉","瘆":"瘮","瘗":"瘞","瘘":"瘻","瘪":"癟","瘫":"癱","瘾":"癮","瘿":"癭","癞":"癩","癣":"癬","癫":"癲","皂":"皁","皑":"皚","皱":"皺","皲":"皸","盏":"盞","盐":"鹽","监":"監","盖":"蓋","盗":"盜","盘":"盤","眍":"瞘","眦":"眥","眬":"矓","睁":"睜","睐":"睞","睑":"瞼","瞆":"瞶","瞒":"瞞","瞩":"矚","矩":"矩","矫":"矯","矶":"磯","矾":"礬","矿":"礦","砀":"碭","码":"碼","砖":"磚","砗":"硨","砚":"硯","砜":"碸","砺":"礪","砻":"礱","砾":"礫","础":"礎","硁":"硜","硕":"碩","硖":"硤","硗":"磽","硙":"磑","硚":"礄","确":"確","硵":"磠","硷":"礆","碍":"礙","碛":"磧","碜":"磣","碱":"鹼","礼":"禮","祃":"禡","祎":"禕","祢":"禰","祯":"禎","祷":"禱","祸":"禍","禀":"稟","禄":"祿","禅":"禪","离":"離","私":"私","秃":"禿","秆":"稈","秋":"秋","种":"種","秘":"祕","积":"積","称":"稱","秽":"穢","秾":"穠","稆":"穭","税":"稅","稣":"穌","稳":"穩","穑":"穡","穞":"穭","穷":"窮","窃":"竊","窍":"竅","窎":"窵","窑":"窯","窜":"竄","窝":"窩","窥":"窺","窦":"竇","窭":"窶","竖":"豎","竞":"競","笃":"篤","笋":"筍","笔":"筆","笕":"筧","笺":"箋","笼":"籠","笾":"籩","筑":"築","筚":"篳","筛":"篩","筜":"簹","筝":"箏","筹":"籌","筼":"篔","签":"籤","筿":"篠","简":"簡","箓":"籙","箦":"簀","箧":"篋","箨":"籜","箩":"籮","箪":"簞","箫":"簫","篑":"簣","篓":"簍","篮":"籃","篯":"籛","篱":"籬","簖":"籪","籁":"籟","籴":"糴","类":"類","籼":"秈","粜":"糶","粝":"糲","粤":"粵","粪":"糞","粮":"糧","粽":"糉","糁":"糝","糇":"餱","糍":"餈","系":"系","紧":"緊","絷":"縶","緼":"縕","縆":"緪","纟":"糹","纠":"糾","纡":"紆","红":"紅","纣":"紂","纤":"纖","纥":"紇","约":"約","级":"級","纨":"紈","纩":"纊","纪":"紀","纫":"紉","纬":"緯","纭":"紜","纮":"紘","纯":"純","纰":"紕","纱":"紗","纲":"綱","纳":"納","纴":"紝","纵":"縱","纶":"綸","纷":"紛","纸":"紙","纹":"紋","纺":"紡","纻":"紵","纼":"紖","纽":"紐","纾":"紓","线":"線","绀":"紺","绁":"紲","绂":"紱","练":"練","组":"組","绅":"紳","细":"細","织":"織","终":"終","绉":"縐","绊":"絆","绋":"紼","绌":"絀","绍":"紹","绎":"繹","经":"經","绐":"紿","绑":"綁","绒":"絨","结":"結","绔":"絝","绕":"繞","绖":"絰","绗":"絎","绘":"繪","给":"給","绚":"絢","绛":"絳","络":"絡","绝":"絕","绞":"絞","统":"統","绠":"綆","绡":"綃","绢":"絹","绣":"繡","绤":"綌","绥":"綏","绦":"絛","继":"繼","绨":"綈","绩":"績","绪":"緒","绫":"綾","绬":"緓","续":"續","绮":"綺","绯":"緋","绰":"綽","绱":"鞝","绲":"緄","绳":"繩","维":"維","绵":"綿","绶":"綬","绷":"繃","绸":"綢","绹":"綯","绺":"綹","绻":"綣","综":"綜","绽":"綻","绾":"綰","绿":"綠","缀":"綴","缁":"緇","缂":"緙","缃":"緗","缄":"緘","缅":"緬","缆":"纜","缇":"緹","缈":"緲","缉":"緝","缊":"縕","缋":"繢","缌":"緦","缍":"綞","缎":"緞","缏":"緶","缐":"線","缑":"緱","缒":"縋","缓":"緩","缔":"締","缕":"縷","编":"編","缗":"緡","缘":"緣","缙":"縉","缚":"縛","缛":"縟","缜":"縝","缝":"縫","缞":"縗","缟":"縞","缠":"纏","缡":"縭","缢":"縊","缣":"縑","缤":"繽","缥":"縹","缦":"縵","缧":"縲","缨":"纓","缩":"縮","缪":"繆","缫":"繅","缬":"纈","缭":"繚","缮":"繕","缯":"繒","缰":"繮","缱":"繾","缲":"繰","缳":"繯","缴":"繳","缵":"纘","罂":"罌","网":"網","罗":"羅","罚":"罰","罢":"罷","罴":"羆","羁":"羈","羟":"羥","羡":"羨","群":"羣","翘":"翹","翙":"翽","翚":"翬","耢":"耮","耧":"耬","耸":"聳","耻":"恥","聂":"聶","聋":"聾","职":"職","聍":"聹","联":"聯","聩":"聵","聪":"聰","肃":"肅","肠":"腸","肤":"膚","肮":"骯","肴":"餚","肾":"腎","肿":"腫","胀":"脹","胁":"脅","胄":"胄","胆":"膽","背":"背","胜":"勝","胡":"胡","胧":"朧","胨":"腖","胪":"臚","胫":"脛","胶":"膠","脉":"脈","脍":"膾","脏":"髒","脐":"臍","脑":"腦","脓":"膿","脔":"臠","脚":"腳","脱":"脫","脶":"腡","脸":"臉","腊":"臘","腌":"醃","腘":"膕","腭":"齶","腻":"膩","腼":"靦","腽":"膃","腾":"騰","膑":"臏","膻":"羶","臜":"臢","致":"致","舆":"輿","舍":"舍","舣":"艤","舰":"艦","舱":"艙","舻":"艫","艰":"艱","艳":"豔","艺":"藝","节":"節","芈":"羋","芗":"薌","芜":"蕪","芦":"蘆","芸":"芸","苁":"蓯","苇":"葦","苈":"藶","苋":"莧","苌":"萇","苍":"蒼","苎":"苧","苏":"蘇","苔":"苔","苧":"薴","苹":"蘋","范":"範","茎":"莖","茏":"蘢","茑":"蔦","茔":"塋","茕":"煢","茧":"繭","荆":"荊","荐":"薦","荙":"薘","荚":"莢","荛":"蕘","荜":"蓽","荝":"萴","荞":"蕎","荟":"薈","荠":"薺","荡":"蕩","荣":"榮","荤":"葷","荥":"滎","荦":"犖","荧":"熒","荨":"蕁","荩":"藎","荪":"蓀","荫":"蔭","荬":"蕒","荭":"葒","荮":"葤","药":"藥","莅":"蒞","莱":"萊","莲":"蓮","莳":"蒔","莴":"萵","莶":"薟","获":"獲","莸":"蕕","莹":"瑩","莺":"鶯","莼":"蓴","萚":"蘀","萝":"蘿","萤":"螢","营":"營","萦":"縈","萧":"蕭","萨":"薩","葱":"蔥","蒀":"蒕","蒇":"蕆","蒉":"蕢","蒋":"蔣","蒌":"蔞","蒏":"醟","蒙":"蒙","蓝":"藍","蓟":"薊","蓠":"蘺","蓣":"蕷","蓥":"鎣","蓦":"驀","蔂":"虆","蔑":"蔑","蔷":"薔","蔹":"蘞","蔺":"藺","蔼":"藹","蕰":"薀","蕲":"蘄","蕴":"蘊","薮":"藪","藓":"蘚","藴":"蘊","蘖":"櫱","虏":"虜","虑":"慮","虚":"虛","虫":"蟲","虬":"虯","虮":"蟣","虱":"蝨","虽":"雖","虾":"蝦","虿":"蠆","蚀":"蝕","蚁":"蟻","蚂":"螞","蚃":"蠁","蚕":"蠶","蚝":"蠔","蚬":"蜆","蛊":"蠱","蛎":"蠣","蛏":"蟶","蛮":"蠻","蛰":"蟄","蛱":"蛺","蛲":"蟯","蛳":"螄","蛴":"蠐","蜕":"蛻","蜗":"蝸","蜡":"蠟","蝇":"蠅","蝈":"蟈","蝉":"蟬","蝎":"蠍","蝼":"螻","蝾":"蠑","螀":"螿","螨":"蟎","蟏":"蠨","衅":"釁","衔":"銜","补":"補","表":"表","衬":"襯","衮":"袞","袄":"襖","袅":"嫋","袆":"褘","袜":"襪","袭":"襲","袯":"襏","装":"裝","裆":"襠","裈":"褌","裢":"褳","裣":"襝","裤":"褲","裥":"襉","褛":"褸","褴":"襤","襕":"襴","见":"見","观":"觀","觃":"覎","规":"規","觅":"覓","视":"視","觇":"覘","览":"覽","觉":"覺","觊":"覬","觋":"覡","觌":"觿","觍":"覥","觎":"覦","觏":"覯","觐":"覲","觑":"覷","觞":"觴","触":"觸","觯":"觶","訚":"誾","詟":"讋","誉":"譽","誊":"謄","讠":"訁","计":"計","订":"訂","讣":"訃","认":"認","讥":"譏","讦":"訐","讧":"訌","讨":"討","让":"讓","讪":"訕","讫":"訖","讬":"託","训":"訓","议":"議","讯":"訊","记":"記","讱":"訒","讲":"講","讳":"諱","讴":"謳","讵":"詎","讶":"訝","讷":"訥","许":"許","讹":"訛","论":"論","讻":"訩","讼":"訟","讽":"諷","设":"設","访":"訪","诀":"訣","证":"證","诂":"詁","诃":"訶","评":"評","诅":"詛","识":"識","诇":"詗","诈":"詐","诉":"訴","诊":"診","诋":"詆","诌":"謅","词":"詞","诎":"詘","诏":"詔","诐":"詖","译":"譯","诒":"詒","诓":"誆","诔":"誄","试":"試","诖":"詿","诗":"詩","诘":"詰","诙":"詼","诚":"誠","诛":"誅","诜":"詵","话":"話","诞":"誕","诟":"詬","诠":"詮","诡":"詭","询":"詢","诣":"詣","诤":"諍","该":"該","详":"詳","诧":"詫","诨":"諢","诩":"詡","诪":"譸","诫":"誡","诬":"誣","语":"語","诮":"誚","误":"誤","诰":"誥","诱":"誘","诲":"誨","诳":"誑","说":"說","诵":"誦","诶":"誒","请":"請","诸":"諸","诹":"諏","诺":"諾","读":"讀","诼":"諑","诽":"誹","课":"課","诿":"諉","谀":"諛","谁":"誰","谂":"諗","调":"調","谄":"諂","谅":"諒","谆":"諄","谇":"誶","谈":"談","谉":"讅","谊":"誼","谋":"謀","谌":"諶","谍":"諜","谎":"謊","谏":"諫","谐":"諧","谑":"謔","谒":"謁","谓":"謂","谔":"諤","谕":"諭","谖":"諼","谗":"讒","谘":"諮","谙":"諳","谚":"諺","谛":"諦","谜":"謎","谝":"諞","谞":"諝","谟":"謨","谠":"讜","谡":"謖","谢":"謝","谣":"謠","谤":"謗","谥":"諡","谦":"謙","谧":"謐","谨":"謹","谩":"謾","谪":"謫","谫":"譾","谬":"謬","谭":"譚","谮":"譖","谯":"譙","谰":"讕","谱":"譜","谲":"譎","谳":"讞","谴":"譴","谵":"譫","谶":"讖","谷":"谷","豮":"豶","贝":"貝","贞":"貞","负":"負","贠":"貟","贡":"貢","财":"財","责":"責","贤":"賢","败":"敗","账":"賬","货":"貨","质":"質","贩":"販","贪":"貪","贫":"貧","贬":"貶","购":"購","贮":"貯","贯":"貫","贰":"貳","贱":"賤","贲":"賁","贳":"貰","贴":"貼","贵":"貴","贶":"貺","贷":"貸","贸":"貿","费":"費","贺":"賀","贻":"貽","贼":"賊","贽":"贄","贾":"賈","贿":"賄","赀":"貲","赁":"賃","赂":"賂","赃":"贓","资":"資","赅":"賅","赆":"贐","赇":"賕","赈":"賑","赉":"賚","赊":"賒","赋":"賦","赌":"賭","赍":"齎","赎":"贖","赏":"賞","赐":"賜","赑":"贔","赒":"賙","赓":"賡","赔":"賠","赕":"賧","赖":"賴","赗":"賵","赘":"贅","赙":"賻","赚":"賺","赛":"賽","赜":"賾","赝":"贗","赞":"贊","赟":"贇","赠":"贈","赡":"贍","赢":"贏","赣":"贛","赪":"赬","赵":"趙","赶":"趕","趋":"趨","趱":"趲","趸":"躉","跃":"躍","跄":"蹌","跖":"蹠","跞":"躒","践":"踐","跶":"躂","跷":"蹺","跸":"蹕","跹":"躚","跻":"躋","踌":"躊","踪":"蹤","踬":"躓","踯":"躑","蹑":"躡","蹒":"蹣","蹰":"躕","蹿":"躥","躏":"躪","躜":"躦","躯":"軀","輼":"轀","车":"車","轧":"軋","轨":"軌","轩":"軒","轪":"軑","轫":"軔","转":"轉","轭":"軛","轮":"輪","软":"軟","轰":"轟","轱":"軲","轲":"軻","轳":"轤","轴":"軸","轵":"軹","轶":"軼","轷":"軤","轸":"軫","轹":"轢","轺":"軺","轻":"輕","轼":"軾","载":"載","轾":"輊","轿":"轎","辀":"輈","辁":"輇","辂":"輅","较":"較","辄":"輒","辅":"輔","辆":"輛","辇":"輦","辈":"輩","辉":"輝","辊":"輥","辋":"輞","辌":"輬","辍":"輟","辎":"輜","辏":"輳","辐":"輻","辑":"輯","辒":"轀","输":"輸","辔":"轡","辕":"轅","辖":"轄","辗":"輾","辘":"轆","辙":"轍","辚":"轔","辞":"辭","辟":"闢","辩":"辯","辫":"辮","边":"邊","辽":"遼","达":"達","迁":"遷","过":"過","迈":"邁","运":"運","还":"還","这":"這","进":"進","远":"遠","违":"違","连":"連","迟":"遲","迩":"邇","迳":"逕","迹":"跡","适":"適","选":"選","逊":"遜","递":"遞","逦":"邐","逻":"邏","遗":"遺","遥":"遙","邓":"鄧","邝":"鄺","邬":"鄔","邮":"郵","邹":"鄒","邺":"鄴","邻":"鄰","郁":"鬱","郏":"郟","郐":"鄶","郑":"鄭","郓":"鄆","郦":"酈","郧":"鄖","郸":"鄲","酂":"酇","酝":"醞","酦":"醱","酱":"醬","酸":"酸","酽":"釅","酾":"釃","酿":"釀","醖":"醞","采":"採","释":"釋","里":"裏","鉴":"鑑","銮":"鑾","錾":"鏨","钅":"釒","钆":"釓","钇":"釔","针":"針","钉":"釘","钊":"釗","钋":"釙","钌":"釕","钍":"釷","钎":"釺","钏":"釧","钐":"釤","钑":"鈒","钒":"釩","钓":"釣","钔":"鍆","钕":"釹","钖":"鍚","钗":"釵","钘":"鈃","钙":"鈣","钚":"鈈","钛":"鈦","钜":"鉅","钝":"鈍","钞":"鈔","钟":"鍾","钠":"鈉","钡":"鋇","钢":"鋼","钣":"鈑","钤":"鈐","钥":"鑰","钦":"欽","钧":"鈞","钨":"鎢","钩":"鉤","钪":"鈧","钫":"鈁","钬":"鈥","钭":"鈄","钮":"鈕","钯":"鈀","钰":"鈺","钱":"錢","钲":"鉦","钳":"鉗","钴":"鈷","钵":"鉢","钶":"鈳","钷":"鉕","钸":"鈽","钹":"鈸","钺":"鉞","钻":"鑽","钼":"鉬","钽":"鉭","钾":"鉀","钿":"鈿","铀":"鈾","铁":"鐵","铂":"鉑","铃":"鈴","铄":"鑠","铅":"鉛","铆":"鉚","铇":"鉋","铈":"鈰","铉":"鉉","铊":"鉈","铋":"鉍","铌":"鈮","铍":"鈹","铎":"鐸","铏":"鉶","铐":"銬","铑":"銠","铒":"鉺","铓":"鋩","铔":"錏","铕":"銪","铖":"鋮","铗":"鋏","铘":"鋣","铙":"鐃","铚":"銍","铛":"鐺","铜":"銅","铝":"鋁","铞":"銱","铟":"銦","铠":"鎧","铡":"鍘","铢":"銖","铣":"銑","铤":"鋌","铥":"銩","铦":"銛","铧":"鏵","铨":"銓","铩":"鎩","铪":"鉿","铫":"銚","铬":"鉻","铭":"銘","铮":"錚","铯":"銫","铰":"鉸","铱":"銥","铲":"鏟","铳":"銃","铴":"鐋","铵":"銨","银":"銀","铷":"銣","铸":"鑄","铹":"鐒","铺":"鋪","铻":"鋙","铼":"錸","铽":"鋱","链":"鏈","铿":"鏗","销":"銷","锁":"鎖","锂":"鋰","锃":"鋥","锄":"鋤","锅":"鍋","锆":"鋯","锇":"鋨","锈":"鏽","锉":"銼","锊":"鋝","锋":"鋒","锌":"鋅","锍":"鋶","锎":"鐦","锏":"鐧","锐":"銳","锑":"銻","锒":"鋃","锓":"鋟","锔":"鋦","锕":"錒","锖":"錆","锗":"鍺","锘":"鍩","错":"錯","锚":"錨","锛":"錛","锜":"錡","锝":"鍀","锞":"錁","锟":"錕","锠":"錩","锡":"錫","锢":"錮","锣":"鑼","锤":"錘","锥":"錐","锦":"錦","锧":"鑕","锨":"鍁","锩":"錈","锪":"鍃","锫":"錇","锬":"錟","锭":"錠","键":"鍵","锯":"鋸","锰":"錳","锱":"錙","锲":"鍥","锳":"鍈","锴":"鍇","锵":"鏘","锶":"鍶","锷":"鍔","锸":"鍤","锹":"鍬","锺":"鍾","锻":"鍛","锼":"鎪","锽":"鍠","锾":"鍰","锿":"鎄","镀":"鍍","镁":"鎂","镂":"鏤","镃":"鎡","镄":"鐨","镅":"鎇","镆":"鏌","镇":"鎮","镈":"鎛","镉":"鎘","镊":"鑷","镋":"钂","镌":"鐫","镍":"鎳","镎":"鎿","镏":"鎦","镐":"鎬","镑":"鎊","镒":"鎰","镓":"鎵","镔":"鑌","镕":"鎔","镖":"鏢","镗":"鏜","镘":"鏝","镙":"鏍","镚":"鏰","镛":"鏞","镜":"鏡","镝":"鏑","镞":"鏃","镟":"鏇","镠":"鏐","镡":"鐔","镢":"钁","镣":"鐐","镤":"鏷","镥":"鑥","镦":"鐓","镧":"鑭","镨":"鐠","镩":"鑹","镪":"鏹","镫":"鐙","镬":"鑊","镭":"鐳","镮":"鐶","镯":"鐲","镰":"鐮","镱":"鐿","镲":"鑔","镳":"鑣","镴":"鑞","镵":"鑱","镶":"鑲","长":"長","门":"門","闩":"閂","闪":"閃","闫":"閆","闬":"閈","闭":"閉","问":"問","闯":"闖","闰":"閏","闱":"闈","闲":"閒","闳":"閎","间":"間","闵":"閔","闶":"閌","闷":"悶","闸":"閘","闹":"鬧","闺":"閨","闻":"聞","闼":"闥","闽":"閩","闾":"閭","闿":"闓","阀":"閥","阁":"閣","阂":"閡","阃":"閫","阄":"鬮","阅":"閱","阆":"閬","阇":"闍","阈":"閾","阉":"閹","阊":"閶","阋":"鬩","阌":"閿","阍":"閽","阎":"閻","阏":"閼","阐":"闡","阑":"闌","阒":"闃","阓":"闠","阔":"闊","阕":"闋","阖":"闔","阗":"闐","阘":"闒","阙":"闕","阚":"闞","阛":"闤","队":"隊","阳":"陽","阴":"陰","阵":"陣","阶":"階","际":"際","陆":"陸","陇":"隴","陈":"陳","陉":"陘","陕":"陝","陦":"隯","陧":"隉","陨":"隕","险":"險","随":"隨","隐":"隱","隶":"隸","隽":"雋","难":"難","雇":"僱","雏":"雛","雕":"雕","雠":"讎","雳":"靂","雾":"霧","霁":"霽","霉":"黴","霡":"霢","霭":"靄","靓":"靚","靔":"靝","静":"靜","面":"面","靥":"靨","鞑":"韃","鞒":"鞽","鞯":"韉","鞲":"韝","韦":"韋","韧":"韌","韨":"韍","韩":"韓","韪":"韙","韫":"韞","韬":"韜","韵":"韻","页":"頁","顶":"頂","顷":"頃","顸":"頇","项":"項","顺":"順","须":"須","顼":"頊","顽":"頑","顾":"顧","顿":"頓","颀":"頎","颁":"頒","颂":"頌","颃":"頏","预":"預","颅":"顱","领":"領","颇":"頗","颈":"頸","颉":"頡","颊":"頰","颋":"頲","颌":"頜","颍":"潁","颎":"熲","颏":"頦","颐":"頤","频":"頻","颒":"頮","颓":"頹","颔":"頷","颕":"頴","颖":"穎","颗":"顆","题":"題","颙":"顒","颚":"顎","颛":"顓","颜":"顏","额":"額","颞":"顳","颟":"顢","颠":"顛","颡":"顙","颢":"顥","颣":"纇","颤":"顫","颥":"顬","颦":"顰","颧":"顴","风":"風","飏":"颺","飐":"颭","飑":"颮","飒":"颯","飓":"颶","飔":"颸","飕":"颼","飖":"颻","飗":"飀","飘":"飄","飙":"飆","飚":"飈","飞":"飛","飨":"饗","餍":"饜","饣":"飠","饤":"飣","饥":"飢","饦":"飥","饧":"餳","饨":"飩","饩":"餼","饪":"飪","饫":"飫","饬":"飭","饭":"飯","饮":"飲","饯":"餞","饰":"飾","饱":"飽","饲":"飼","饳":"飿","饴":"飴","饵":"餌","饶":"饒","饷":"餉","饸":"餄","饹":"餎","饺":"餃","饻":"餏","饼":"餅","饽":"餑","饾":"餖","饿":"餓","馀":"餘","馁":"餒","馂":"餕","馃":"餜","馄":"餛","馅":"餡","馆":"館","馇":"餷","馈":"饋","馉":"餶","馊":"餿","馋":"饞","馌":"饁","馍":"饃","馎":"餺","馏":"餾","馐":"饈","馑":"饉","馒":"饅","馓":"饊","馔":"饌","馕":"饢","马":"馬","驭":"馭","驮":"馱","驯":"馴","驰":"馳","驱":"驅","驲":"馹","驳":"駁","驴":"驢","驵":"駔","驶":"駛","驷":"駟","驸":"駙","驹":"駒","驺":"騶","驻":"駐","驼":"駝","驽":"駑","驾":"駕","驿":"驛","骀":"駘","骁":"驍","骂":"罵","骃":"駰","骄":"驕","骅":"驊","骆":"駱","骇":"駭","骈":"駢","骉":"驫","骊":"驪","骋":"騁","验":"驗","骍":"騂","骎":"駸","骏":"駿","骐":"騏","骑":"騎","骒":"騍","骓":"騅","骔":"騌","骕":"驌","骖":"驂","骗":"騙","骘":"騭","骙":"騤","骚":"騷","骛":"騖","骜":"驁","骝":"騮","骞":"騫","骟":"騸","骠":"驃","骡":"騾","骢":"驄","骣":"驏","骤":"驟","骥":"驥","骦":"驦","骧":"驤","髅":"髏","髋":"髖","髌":"髕","鬓":"鬢","鬶":"鬹","魇":"魘","魉":"魎","鱼":"魚","鱽":"魛","鱾":"魢","鱿":"魷","鲀":"魨","鲁":"魯","鲂":"魴","鲃":"䰾","鲄":"魺","鲅":"鮁","鲆":"鮃","鲇":"鮎","鲈":"鱸","鲉":"鮋","鲊":"鮓","鲋":"鮒","鲌":"鮊","鲍":"鮑","鲎":"鱟","鲏":"鮍","鲐":"鮐","鲑":"鮭","鲒":"鮚","鲓":"鮳","鲔":"鮪","鲕":"鮞","鲖":"鮦","鲗":"鰂","鲘":"鮜","鲙":"鱠","鲚":"鱭","鲛":"鮫","鲜":"鮮","鲝":"鮺","鲞":"鯗","鲟":"鱘","鲠":"鯁","鲡":"鱺","鲢":"鰱","鲣":"鰹","鲤":"鯉","鲥":"鰣","鲦":"鰷","鲧":"鯀","鲨":"鯊","鲩":"鯇","鲪":"鮶","鲫":"鯽","鲬":"鯒","鲭":"鯖","鲮":"鯪","鲯":"鯕","鲰":"鯫","鲱":"鯡","鲲":"鯤","鲳":"鯧","鲴":"鯝","鲵":"鯢","鲶":"鯰","鲷":"鯛","鲸":"鯨","鲹":"鰺","鲺":"鯴","鲻":"鯔","鲼":"鱝","鲽":"鰈","鲾":"鰏","鲿":"鱨","鳀":"鯷","鳁":"鰮","鳂":"鰃","鳃":"鰓","鳄":"鱷","鳅":"鰍","鳆":"鰒","鳇":"鰉","鳈":"鰁","鳉":"鱂","鳊":"鯿","鳋":"鰠","鳌":"鰲","鳍":"鰭","鳎":"鰨","鳏":"鰥","鳐":"鰩","鳑":"鰟","鳒":"鰜","鳓":"鰳","鳔":"鰾","鳕":"鱈","鳖":"鱉","鳗":"鰻","鳘":"鰵","鳙":"鱅","鳚":"䲁","鳛":"鰼","鳜":"鱖","鳝":"鱔","鳞":"鱗","鳟":"鱒","鳠":"鱯","鳡":"鱤","鳢":"鱧","鳣":"鱣","鳤":"䲘","鸟":"鳥","鸠":"鳩","鸡":"雞","鸢":"鳶","鸣":"鳴","鸤":"鳲","鸥":"鷗","鸦":"鴉","鸧":"鶬","鸨":"鴇","鸩":"鴆","鸪":"鴣","鸫":"鶇","鸬":"鸕","鸭":"鴨","鸮":"鴞","鸯":"鴦","鸰":"鴒","鸱":"鴟","鸲":"鴝","鸳":"鴛","鸴":"鷽","鸵":"鴕","鸶":"鷥","鸷":"鷙","鸸":"鴯","鸹":"鴰","鸺":"鵂","鸻":"鴴","鸼":"鵃","鸽":"鴿","鸾":"鸞","鸿":"鴻","鹀":"鵐","鹁":"鵓","鹂":"鸝","鹃":"鵑","鹄":"鵠","鹅":"鵝","鹆":"鵒","鹇":"鷳","鹈":"鵜","鹉":"鵡","鹊":"鵲","鹋":"鶓","鹌":"鵪","鹍":"鵾","鹎":"鵯","鹏":"鵬","鹐":"鵮","鹑":"鶉","鹒":"鶊","鹓":"鵷","鹔":"鷫","鹕":"鶘","鹖":"鶡","鹗":"鶚","鹘":"鶻","鹙":"鶖","鹚":"鷀","鹛":"鶥","鹜":"鶩","鹝":"鷊","鹞":"鷂","鹟":"鶲","鹠":"鶹","鹡":"鶺","鹢":"鷁","鹣":"鶼","鹤":"鶴","鹥":"鷖","鹦":"鸚","鹧":"鷓","鹨":"鷚","鹩":"鷯","鹪":"鷦","鹫":"鷲","鹬":"鷸","鹭":"鷺","鹮":"䴉","鹯":"鸇","鹰":"鷹","鹱":"鸌","鹲":"鸏","鹳":"鸛","鹴":"鸘","鹾":"鹺","麦":"麥","麸":"麩","麹":"麴","麺":"麪","麽":"麼","黄":"黃","黉":"黌","黡":"黶","黩":"黷","黪":"黲","黾":"黽","鼋":"黿","鼌":"鼂","鼍":"鼉","鼹":"鼴","齐":"齊","齑":"齏","齿":"齒","龀":"齔","龁":"齕","龂":"齗","龃":"齟","龄":"齡","龅":"齙","龆":"齠","龇":"齜","龈":"齦","龉":"齬","龊":"齪","龋":"齲","龌":"齷","龙":"龍","龚":"龔","龛":"龕","龟":"龜","鿎":"䃮","鿏":"䥑","鿒":"鿓","鿔":"鎶","𠀾":"𠁞","𠆲":"儣","𠆿":"𠌥","𠇹":"俓","𠉂":"㒓","𠉗":"𠏢","𠋆":"儭","𠚳":"𠠎","𠛅":"剾","𠛆":"𠞆","𠛾":"𪟖","𠡠":"勑","𠮶":"嗰","𠯟":"哯","𠯠":"噅","𠰱":"㘉","𠰷":"嚧","𠱞":"囃","𠲥":"𡅏","𠴛":"𡃕","𠴢":"𡄔","𠵸":"𡄣","𠵾":"㗲","𡋀":"𡓾","𡋗":"𡑭","𡋤":"壗","𡍣":"𡔖","𡒄":"壈","𡝠":"㜷","𡞋":"㜗","𡞱":"㜢","𡠟":"孎","𡥧":"孻","𡭜":"𡮉","𡭬":"𡮣","𡳃":"𡳳","𡳒":"𦘧","𡶴":"嵼","𡸃":"𡽗","𡺃":"嶈","𡺄":"嶘","𢋈":"㢝","𢗓":"㦛","𢘙":"𢤱","𢘝":"𢣚","𢘞":"𢣭","𢙏":"愻","𢙐":"憹","𢙑":"𢠼","𢙒":"憢","𢙓":"懀","𢛯":"㦎","𢠁":"懎","𢢐":"𤢻","𢧐":"戰","𢫊":"𢷮","𢫞":"𢶫","𢫬":"摋","𢬍":"擫","𢬦":"𢹿","𢭏":"擣","𢽾":"斅","𣃁":"斸","𣆐":"曥","𣈣":"𣋋","𣍨":"𦢈","𣍯":"腪","𣍰":"脥","𣎑":"臗","𣏢":"槫","𣐕":"桱","𣐤":"欍","𣑶":"𣠲","𣒌":"楇","𣓿":"橯","𣔌":"樤","𣗊":"樠","𣗋":"欓","𣗙":"㰙","𣘐":"㯤","𣘓":"𣞻","𣘴":"檭","𣘷":"𣝕","𣚚":"欘","𣞎":"𣠩","𣨼":"殢","𣭤":"𣯴","𣯣":"𣯩","𣱝":"氭","𣲗":"湋","𣲘":"潕","𣳆":"㵗","𣶩":"澅","𣶫":"𣿉","𣶭":"𪷓","𣷷":"𤅶","𣸣":"濆","𣺼":"灙","𣺽":"𤁣","𣽷":"瀃","𤆡":"熓","𤆢":"㷍","𤇃":"爄","𤇄":"熌","𤇭":"爖","𤇹":"熚","𤈶":"熉","𤈷":"㷿","𤊀":"𤒎","𤊰":"𤓩","𤋏":"熡","𤎺":"𤓎","𤎻":"𤑳","𤙯":"𤛮","𤝢":"𤢟","𤞃":"獩","𤞤":"玁","𤠋":"㺏","𤦀":"瓕","𤩽":"瓛","𤳄":"𤳸","𤶊":"癐","𤶧":"𤸫","𤻊":"㿗","𤽯":"㿧","𤾀":"皟","𤿲":"麬","𥁢":"䀉","𥅘":"𥌃","𥅴":"䀹","𥅿":"𥊝","𥆧":"瞤","𥇢":"䁪","𥎝":"䂎","𥐟":"礒","𥐯":"𥖅","𥐰":"𥕥","𥐻":"碙","𥞦":"𥞵","𥧂":"𥨐","𥩟":"竚","𥩺":"𥪂","𥫣":"籅","𥬀":"䉙","𥬞":"籋","𥬠":"篘","𥭉":"𥵊","𥮋":"𥸠","𥮜":"䉲","𥮾":"篸","𥱔":"𥵃","𥹥":"𥼽","𥺅":"䊭","𥺇":"𥽖","𦈈":"𥿊","𦈉":"緷","𦈋":"綇","𦈌":"綀","𦈎":"繟","𦈏":"緍","𦈐":"縺","𦈑":"緸","𦈒":"𦂅","𦈓":"䋿","𦈔":"縎","𦈕":"緰","𦈖":"䌈","𦈗":"𦃄","𦈘":"䌋","𦈙":"䌰","𦈚":"縬","𦈛":"繓","𦈜":"䌖","𦈝":"繏","𦈞":"䌟","𦈟":"䌝","𦈠":"䌥","𦈡":"繻","𦍠":"䍽","𦛨":"朥","𦝼":"膢","𦟗":"𦣎","𦨩":"𦪽","𦰏":"蓧","𦰴":"䕳","𦶟":"爇","𦶻":"𦾟","𦻕":"蘟","𧉐":"𧕟","𧉞":"䗿","𧌥":"𧎈","𧏖":"蠙","𧏗":"蠀","𧑏":"蠾","𧒭":"𧔥","𧜭":"䙱","𧝝":"襰","𧝧":"𧟀","𧮪":"詀","𧳕":"𧳟","𧹑":"䞈","𧹒":"買","𧹓":"𧶔","𧹔":"賬","𧹕":"𝻻","𧹖":"賟","𧹗":"贃","𧿈":"𨇁","𨀁":"躘","𨀱":"𨄣","𨁴":"𨅍","𨂺":"𨈊","𨄄":"𨈌","𨅛":"䠱","𨅫":"𨇞","𨅬":"躝","𨉗":"軉","𨐅":"軗","𨐆":"𨊻","𨐇":"𨏠","𨐈":"輄","𨐉":"𨎮","𨐊":"𨏥","𨑹":"䢨","𨟳":"𨣞","𨠨":"𨣧","𨡙":"𨢿","𨡺":"𨣈","𨤰":"𨤻","𨰾":"鎷","𨰿":"釳","𨱀":"𨥛","𨱁":"鈠","𨱂":"鈋","𨱃":"鈲","𨱄":"鈯","𨱅":"鉁","𨱆":"龯","𨱇":"銶","𨱈":"鋉","𨱉":"鍄","𨱊":"𨧱","𨱋":"錂","𨱌":"鏆","𨱍":"鎯","𨱎":"鍮","𨱏":"鎝","𨱐":"𨫒","𨱑":"鐄","𨱒":"鏉","𨱓":"鐎","𨱔":"鐏","𨱕":"𨮂","𨱖":"䥩","𨷿":"䦳","𨸀":"𨳕","𨸁":"𨳑","𨸂":"閍","𨸃":"閐","𨸄":"䦘","𨸅":"𨴗","𨸆":"𨵩","𨸇":"𨵸","𨸉":"𨶀","𨸊":"𨶏","𨸋":"𨶲","𨸌":"𨶮","𨸎":"𨷲","𨸘":"𨽏","𨸟":"䧢","𩏼":"䪏","𩏽":"𩏪","𩏾":"𩎢","𩏿":"䪘","𩐀":"䪗","𩓋":"顂","𩖕":"𩓣","𩖖":"顃","𩖗":"䫴","𩙥":"颰","𩙦":"𩗀","𩙧":"䬞","𩙨":"𩘹","𩙩":"𩘀","𩙪":"颷","𩙫":"颾","𩙬":"𩘺","𩙭":"𩘝","𩙮":"䬘","𩙯":"䬝","𩙰":"𩙈","𩟿":"𩚛","𩠀":"𩚥","𩠁":"𩚵","𩠂":"𩛆","𩠃":"𩛩","𩠅":"𩟐","𩠆":"𩜦","𩠇":"䭀","𩠈":"䭃","𩠉":"𩜇","𩠊":"𩜵","𩠋":"𩝔","𩠌":"餸","𩠎":"𩞄","𩠏":"𩞦","𩠠":"𩠴","𩡖":"𩡣","𩧦":"𩡺","𩧨":"駎","𩧩":"𩤊","𩧪":"䮾","𩧫":"駚","𩧬":"𩢡","𩧭":"䭿","𩧮":"𩢾","𩧯":"驋","𩧰":"䮝","𩧱":"𩥉","𩧲":"駧","𩧳":"𩢸","𩧴":"駩","𩧵":"𩢴","𩧶":"𩣏","𩧸":"𩣫","𩧺":"駶","𩧻":"𩣵","𩧼":"𩣺","𩧿":"䮠","𩨀":"騔","𩨁":"䮞","𩨂":"驄","𩨃":"騝","𩨄":"騪","𩨅":"𩤸","𩨆":"𩤙","𩨇":"䮫","𩨈":"騟","𩨉":"𩤲","𩨊":"騚","𩨋":"𩥄","𩨌":"𩥑","𩨍":"𩥇","𩨎":"龭","𩨏":"䮳","𩨐":"𩧆","𩩈":"䯤","𩬣":"𩭙","𩬤":"𩰀","𩭹":"鬖","𩯒":"𩯳","𩰰":"𩰹","𩲒":"𩳤","𩴌":"𩴵","𩽹":"魥","𩽺":"𩵩","𩽻":"𩵹","𩽼":"鯶","𩽽":"𩶱","𩽾":"鮟","𩽿":"𩶰","𩾁":"鯄","𩾂":"䲖","𩾃":"鮸","𩾄":"𩷰","𩾅":"𩸃","𩾆":"𩸦","𩾇":"鯱","𩾈":"䱙","𩾊":"䱬","𩾋":"䱰","𩾌":"鱇","𩾎":"𩽇","𪉂":"䲰","𪉃":"鳼","𪉄":"𩿪","𪉅":"𪀦","𪉆":"鴲","𪉈":"鴜","𪉉":"𪁈","𪉊":"鷨","𪉋":"𪀾","𪉌":"𪁖","𪉍":"鵚","𪉎":"𪂆","𪉏":"𪃏","𪉐":"𪃍","𪉑":"鷔","𪉒":"𪄕","𪉔":"𪄆","𪉕":"𪇳","𪎈":"䴬","𪎉":"麲","𪎊":"麨","𪎋":"䴴","𪎌":"麳","𪑅":"䵳","𪔭":"𪔵","𪚏":"𪘀","𪚐":"𪘯","𪜎":"𠿕","𪞝":"凙","𪟎":"㔋","𪟝":"勣","𪠀":"𧷎","𪠟":"㓄","𪠡":"𠬙","𪠳":"唓","𪠵":"㖮","𪠸":"嚛","𪠺":"𠽃","𪠽":"噹","𪡀":"嘺","𪡃":"嘪","𪡋":"噞","𪡏":"嗹","𪡛":"㗿","𪡞":"嘳","𪡺":"𡃄","𪢌":"㘓","𪢐":"𡃤","𪢒":"𡂡","𪢕":"嚽","𪢖":"𡅯","𪢠":"囒","𪢮":"圞","𪢸":"墲","𪣆":"埬","𪣒":"堚","𪣻":"塿","𪤄":"𡓁","𪤚":"壣","𪥠":"𧹈","𪥫":"孇","𪥰":"嬣","𪥿":"嬻","𪧀":"孾","𪧘":"寠","𪨊":"㞞","𪨗":"屩","𪨧":"崙","𪨩":"𡸗","𪨶":"輋","𪨷":"巗","𪨹":"𡹬","𪩇":"㟺","𪩎":"巊","𪩘":"巘","𪩛":"𡿖","𪩷":"幝","𪩸":"幩","𪪏":"廬","𪪑":"㢗","𪪞":"廧","𪪴":"𢍰","𪪼":"彃","𪫌":"徿","𪫡":"𢤩","𪫷":"㦞","𪫺":"憸","𪬚":"𢣐","𪬯":"𢤿","𪭝":"𢯷","𪭢":"摐","𪭧":"擟","𪭯":"𢶒","𪭵":"掚","𪭾":"撊","𪮃":"㨻","𪮋":"㩋","𪮖":"撧","𪮳":"𢺳","𪮶":"攋","𪯋":"㪎","𪰶":"曊","𪱥":"膹","𪱷":"梖","𪲎":"櫅","𪲔":"欐","𪲛":"檵","𪲮":"櫠","𪳍":"欇","𪳗":"𣜬","𪴙":"欑","𪵑":"毊","𪵣":"霼","𪵱":"濿","𪶄":"溡","𪶒":"𤄷","𪶮":"𣽏","𪷍":"㵾","𪷽":"灒","𪸕":"熂","𪸩":"煇","𪹀":"𤑹","𪹠":"𤓌","𪹳":"爥","𪹹":"𤒻","𪺣":"𤘀","𪺪":"𤜆","𪺭":"犞","𪺷":"獊","𪺸":"𤠮","𪺻":"㺜","𪺽":"猌","𪻐":"瑽","𪻨":"瓄","𪻲":"瑻","𪻺":"璝","𪼋":"㻶","𪼴":"𤬅","𪽈":"畼","𪽝":"𤳷","𪽪":"痮","𪽭":"𤷃","𪽮":"㿖","𪽴":"𤺔","𪽷":"瘱","𪾔":"盨","𪾢":"睍","𪾣":"眝","𪾦":"矑","𪾸":"矉","𪿊":"𥏝","𪿞":"𥖲","𪿫":"礮","𪿵":"𥗇","𫀌":"𥜰","𫀓":"𥜐","𫀨":"䅐","𫀬":"䅳","𫀮":"𥢷","𫁂":"䆉","𫁟":"竱","𫁡":"鴗","𫁱":"𥶽","𫁲":"䉑","𫁳":"𥯤","𫁷":"䉶","𫁺":"𥴼","𫂃":"簢","𫂆":"簂","𫂈":"䉬","𫂖":"𥴨","𫂿":"𥻦","𫃗":"𩏷","𫄙":"糺","𫄚":"䊺","𫄛":"紟","𫄜":"䋃","𫄝":"𥾯","𫄞":"䋔","𫄟":"絁","𫄠":"絙","𫄡":"絧","𫄢":"絥","𫄣":"繷","𫄤":"繨","𫄥":"纚","𫄦":"𦀖","𫄧":"綖","𫄨":"絺","𫄩":"䋦","𫄪":"𦅇","𫄫":"綟","𫄬":"緤","𫄭":"緮","𫄮":"䋼","𫄯":"𦃩","𫄰":"縍","𫄱":"繬","𫄲":"縸","𫄳":"縰","𫄴":"繂","𫄵":"𦅈","𫄶":"繈","𫄷":"繶","𫄸":"纁","𫄹":"纗","𫅅":"䍤","𫅗":"羵","𫅥":"𦒀","𫅭":"䎙","𫅼":"𦔖","𫆏":"聻","𫆝":"𦟼","𫆫":"𦡝","𫇘":"𦧺","𫇛":"艣","𫇪":"𦱌","𫇭":"蔿","𫇴":"蒭","𫇽":"蕽","𫈉":"蕳","𫈎":"葝","𫈟":"蔯","𫈵":"蕝","𫉁":"薆","𫉄":"藷","𫊪":"䗅","𫊮":"蠦","𫊸":"蟜","𫊹":"𧒯","𫊻":"蟳","𫋇":"蟂","𫋌":"蟘","𫋲":"䙔","𫋷":"襗","𫋹":"襓","𫋻":"襘","𫌀":"襀","𫌇":"襵","𫌋":"𧞫","𫌨":"覼","𫌪":"覛","𫌫":"𧡴","𫌬":"𧢄","𫌭":"覹","𫌯":"䚩","𫍐":"𧭹","𫍙":"訑","𫍚":"訞","𫍛":"訜","𫍜":"詓","𫍝":"諫","𫍞":"𧦝","𫍟":"𧦧","𫍠":"䛄","𫍡":"詑","𫍢":"譊","𫍣":"詷","𫍤":"譑","𫍥":"誂","𫍦":"譨","𫍧":"誺","𫍨":"誫","𫍩":"諣","𫍪":"誋","𫍫":"䛳","𫍬":"誷","𫍭":"𧩕","𫍮":"誳","𫍯":"諴","𫍰":"諰","𫍱":"諯","𫍲":"謏","𫍳":"諥","𫍴":"謱","𫍵":"謸","𫍶":"𧩼","𫍷":"謉","𫍸":"謆","𫍹":"謯","𫍺":"𧫝","𫍻":"譆","𫍼":"𧬤","𫍽":"譞","𫍾":"𧭈","𫍿":"譾","𫎆":"豵","𫎌":"貗","𫎦":"贚","𫎧":"䝭","𫎨":"𧸘","𫎩":"賝","𫎪":"䞋","𫎫":"贉","𫎬":"贑","𫎭":"䞓","𫎱":"䟐","𫎳":"䟆","𫎸":"𧽯","𫎺":"䟃","𫏃":"䠆","𫏆":"蹳","𫏋":"蹻","𫏌":"𨂐","𫏐":"蹔","𫏑":"𨇽","𫏕":"𨆪","𫏞":"𨇰","𫏨":"𨇤","𫐄":"軏","𫐅":"軕","𫐆":"轣","𫐇":"軜","𫐈":"軷","𫐉":"軨","𫐊":"軬","𫐋":"𨎌","𫐌":"軿","𫐍":"𨌈","𫐎":"輢","𫐏":"輖","𫐐":"輗","𫐑":"輨","𫐒":"輷","𫐓":"輮","𫐔":"𨍰","𫐕":"轊","𫐖":"轇","𫐗":"轐","𫐘":"轗","𫐙":"轠","𫐷":"遱","𫑘":"鄟","𫑡":"鄳","𫑷":"醶","𫓥":"釟","𫓦":"釨","𫓧":"鈇","𫓨":"鈛","𫓩":"鏦","𫓪":"鈆","𫓫":"𨥟","𫓬":"鉔","𫓭":"鉠","𫓮":"𨪕","𫓯":"銈","𫓰":"銊","𫓱":"鐈","𫓲":"銁","𫓳":"𨰋","𫓴":"鉾","𫓵":"鋠","𫓶":"鋗","𫓷":"𫒡","𫓸":"錽","𫓹":"錤","𫓺":"鐪","𫓻":"錜","𫓼":"𨨛","𫓽":"錝","𫓾":"錥","𫓿":"𨨢","𫔀":"鍊","𫔁":"鐼","𫔂":"鍉","𫔃":"𨰲","𫔄":"鍒","𫔅":"鎍","𫔆":"䥯","𫔇":"鎞","𫔈":"鎙","𫔉":"𨰃","𫔊":"鏥","𫔋":"䥗","𫔌":"鏾","𫔍":"鐇","𫔎":"鐍","𫔏":"𨬖","𫔐":"𨭸","𫔑":"𨭖","𫔒":"𨮳","𫔓":"𨯟","𫔔":"鑴","𫔕":"𨰥","𫔖":"𨲳","𫔭":"開","𫔮":"閒","𫔯":"閗","𫔰":"閞","𫔲":"𨴹","𫔴":"閵","𫔵":"䦯","𫔶":"闑","𫔽":"𨼳","𫕚":"𩀨","𫕥":"霣","𫕨":"𩅙","𫖃":"靧","𫖅":"䪊","𫖇":"鞾","𫖑":"𩎖","𫖒":"韠","𫖓":"𩏂","𫖔":"韛","𫖕":"韝","𫖖":"𩏠","𫖪":"𩑔","𫖫":"䪴","𫖬":"䪾","𫖭":"𩒎","𫖮":"顗","𫖯":"頫","𫖰":"䫂","𫖱":"䫀","𫖲":"䫟","𫖳":"頵","𫖴":"𩔳","𫖵":"𩓥","𫖶":"顅","𫖷":"𩔑","𫖸":"願","𫖹":"顣","𫖺":"䫶","𫗇":"䫻","𫗈":"𩗓","𫗉":"𩗴","𫗊":"䬓","𫗋":"飋","𫗚":"𩟗","𫗞":"飦","𫗟":"䬧","𫗠":"餦","𫗡":"𩚩","𫗢":"飵","𫗣":"飶","𫗤":"𩛌","𫗥":"餫","𫗦":"餔","𫗧":"餗","𫗨":"𩛡","𫗩":"饠","𫗪":"餧","𫗫":"餬","𫗬":"餪","𫗭":"餵","𫗮":"餭","𫗯":"餱","𫗰":"䭔","𫗱":"䭑","𫗳":"𩝽","𫗴":"饘","𫗵":"饟","𫘛":"馯","𫘜":"馼","𫘝":"駃","𫘞":"駞","𫘟":"駊","𫘠":"駤","𫘡":"駫","𫘣":"駻","𫘤":"騃","𫘥":"騉","𫘦":"騊","𫘧":"騄","𫘨":"騠","𫘩":"騜","𫘪":"騵","𫘫":"騴","𫘬":"騱","𫘭":"騻","𫘮":"䮰","𫘯":"驓","𫘰":"驙","𫘱":"驨","𫘽":"鬠","𫙂":"𩯁","𫚈":"鱮","𫚉":"魟","𫚊":"鰑","𫚋":"鱄","𫚌":"魦","𫚍":"魵","𫚎":"𩶁","𫚏":"䱁","𫚐":"䱀","𫚑":"鮅","𫚒":"鮄","𫚓":"鮤","𫚔":"鮰","𫚕":"鰤","𫚖":"鮆","𫚗":"鮯","𫚘":"𩻮","𫚙":"鯆","𫚚":"鮿","𫚛":"鮵","𫚜":"䲅","𫚝":"𩸄","𫚞":"鯬","𫚟":"𩸡","𫚠":"䱧","𫚡":"鯞","𫚢":"鰋","𫚣":"鯾","𫚤":"鰦","𫚥":"鰕","𫚦":"鰫","𫚧":"鰽","𫚨":"𩻗","𫚩":"𩻬","𫚪":"鱊","𫚫":"鱢","𫚬":"𩼶","𫚭":"鱲","𫛚":"鳽","𫛛":"鳷","𫛜":"鴀","𫛝":"鴅","𫛞":"鴃","𫛟":"鸗","𫛠":"𩿤","𫛡":"鴔","𫛢":"鸋","𫛣":"鴥","𫛤":"鴐","𫛥":"鵊","𫛦":"鴮","𫛧":"𪀖","𫛨":"鵧","𫛩":"鴳","𫛪":"鴽","𫛫":"鶰","𫛬":"䳜","𫛭":"鵟","𫛮":"䳤","𫛯":"鶭","𫛰":"䳢","𫛱":"鵫","𫛲":"鵰","𫛳":"鵩","𫛴":"鷤","𫛵":"鶌","𫛶":"鶒","𫛷":"鶦","𫛸":"鶗","𫛹":"𪃧","𫛺":"䳧","𫛻":"𪃒","𫛼":"䳫","𫛽":"鷅","𫛾":"𪆷","𫜀":"鷐","𫜁":"鷩","𫜂":"𪅂","𫜃":"鷣","𫜄":"鷷","𫜅":"䴋","𫜊":"𪉸","𫜑":"麷","𫜒":"䴱","𫜓":"𪌭","𫜔":"䴽","𫜕":"𪍠","𫜙":"䵴","𫜟":"𪓰","𫜨":"𪶕","𫜩":"齧","𫜪":"齩","𫜫":"𫜦","𫜬":"齰","𫜭":"齭","𫜮":"齴","𫜯":"𪙏","𫜰":"齾","𫜲":"龓","𫜳":"䶲","𫝈":"㑮","𫝋":"𠐊","𫝦":"㛝","𫝧":"㜐","𫝨":"媈","𫝩":"嬦","𫝪":"𡟫","𫝫":"婡","𫝬":"嬇","𫝭":"孆","𫝮":"孄","𫝵":"嶹","𫞅":"𦠅","𫞗":"潣","𫞚":"澬","𫞛":"㶆","𫞝":"灍","𫞠":"爧","𫞡":"爃","𫞢":"𤛱","𫞣":"㹽","𫞥":"珼","𫞦":"璾","𫞧":"𤩂","𫞨":"璼","𫞩":"璊","𫞷":"𥢶","𫟃":"絍","𫟄":"綋","𫟅":"綡","𫟆":"緟","𫟇":"𦆲","𫟑":"䖅","𫟕":"䕤","𫟞":"訨","𫟟":"詊","𫟠":"譂","𫟡":"誴","𫟢":"䜖","𫟤":"䡐","𫟥":"䡩","𫟦":"䡵","𫟫":"𨞺","𫟬":"𨟊","𫟲":"釚","𫟳":"釲","𫟴":"鈖","𫟵":"鈗","𫟶":"銏","𫟷":"鉝","𫟸":"鉽","𫟹":"鉷","𫟺":"䤤","𫟻":"銂","𫟼":"鐽","𫟽":"𨧰","𫟾":"𨩰","𫟿":"鎈","𫠀":"䥄","𫠁":"鑉","𫠂":"閝","𫠅":"韚","𫠆":"頍","𫠇":"𩖰","𫠈":"䫾","𫠊":"䮄","𫠋":"騼","𫠌":"𩦠","𫠏":"𩵦","𫠐":"魽","𫠑":"䱸","𫠒":"鱆","𫠖":"𩿅","𫠜":"齯","𫢸":"僤","𫧃":"𣍐","𫧮":"𪋿","𫫇":"噁","𫬐":"㘔","𫭟":"塸","𫭢":"埨","𫭼":"𡑍","𫮃":"墠","𫰛":"娙","𫵷":"㠣","𫶇":"嵽","𫷷":"廞","𫸩":"彄","𬀩":"暐","𬀪":"晛","𬂩":"梜","𬃊":"櫍","𬇕":"澫","𬇙":"浿","𬇹":"漍","𬉼":"熰","𬊈":"燖","𬊤":"燀","𬍛":"瓅","𬍡":"璗","𬍤":"璕","𬒈":"礐","𬒗":"𥗽","𬕂":"篢","𬘓":"紃","𬘘":"紞","𬘡":"絪","𬘩":"綎","𬘫":"綄","𬘬":"綪","𬘭":"綝","𬘯":"綧","𬙂":"縯","𬙊":"纆","𬙋":"纕","𬜬":"蔄","𬜯":"䓣","𬞟":"蘋","𬟁":"虉","𬟽":"蝀","𬣙":"訏","𬣞":"詝","𬣡":"諓","𬣳":"詪","𬤇":"諲","𬤊":"諟","𬤝":"譓","𬨂":"軝","𬨎":"輶","𬩽":"鄩","𬪩":"醲","𬬩":"釴","𬬭":"錀","𬬮":"鋹","𬬱":"釿","𬬸":"鉥","𬬹":"鉮","𬬻":"鑪","𬬿":"鉊","𬭁":"鉧","𬭊":"𨧀","𬭎":"鋐","𬭚":"錞","𬭛":"𨨏","𬭤":"鍭","𬭩":"鎓","𬭬":"鏏","𬭭":"鏚","𬭯":"䥕","𬭳":"𨭎","𬭶":"𨭆","𬭸":"鏻","𬭼":"鐩","𬮱":"闉","𬮿":"隑","𬯀":"隮","𬯎":"隤","𬱖":"頔","𬱟":"頠","𬳵":"駓","𬳶":"駉","𬳽":"駪","𬳿":"駼","𬴂":"騑","𬴃":"騞","𬴊":"驎","𬶋":"鮈","𬶍":"鮀","𬶏":"鮠","𬶐":"鮡","𬶟":"鯻","𬶠":"鰊","𬶨":"鱀","𬶭":"鰶","𬶮":"鱚","𬷕":"鵏","𬸘":"鶠","𬸚":"鸑","𬸣":"鶱","𬸦":"鷟","𬸪":"鷭","𬸯":"鷿","𬹼":"齘","𬺈":"齮","𬺓":"齼","𰬸":"繐","𰰨":"菕","𰶎":"譅","𰾄":"鋂","𰾭":"鑀","𱊜":"𪈼"},Se={"㑮":"𫝈","㑯":"㑔","㑳":"㑇","㑶":"㐹","㒓":"𠉂","㓄":"𪠟","㓨":"刾","㔋":"𪟎","㖮":"𪠵","㗲":"𠵾","㗿":"𪡛","㘉":"𠰱","㘓":"𪢌","㘔":"𫬐","㘚":"㘎","㛝":"𫝦","㜄":"㚯","㜏":"㛣","㜐":"𫝧","㜗":"𡞋","㜢":"𡞱","㜷":"𡝠","㞞":"𪨊","㟺":"𪩇","㠏":"㟆","㠣":"𫵷","㢗":"𪪑","㢝":"𢋈","㥮":"㤘","㦎":"𢛯","㦛":"𢗓","㦞":"𪫷","㨻":"𪮃","㩋":"𪮋","㩜":"㨫","㩳":"㧐","㩵":"擜","㪎":"𪯋","㯤":"𣘐","㰙":"𣗙","㵗":"𣳆","㵾":"𪷍","㶆":"𫞛","㷍":"𤆢","㷿":"𤈷","㸇":"𤎺","㹽":"𫞣","㺏":"𤠋","㺜":"𪺻","㻶":"𪼋","㿖":"𪽮","㿗":"𤻊","㿧":"𤽯","䀉":"𥁢","䀹":"𥅴","䁪":"𥇢","䁻":"䀥","䂎":"𥎝","䃮":"鿎","䅐":"𫀨","䅳":"𫀬","䆉":"𫁂","䉑":"𫁲","䉙":"𥬀","䉬":"𫂈","䉲":"𥮜","䉶":"𫁷","䊭":"𥺅","䊷":"䌶","䊺":"𫄚","䋃":"𫄜","䋔":"𫄞","䋙":"䌺","䋚":"䌻","䋦":"𫄩","䋹":"䌿","䋻":"䌾","䋼":"𫄮","䋿":"𦈓","䌈":"𦈖","䌋":"𦈘","䌖":"𦈜","䌝":"𦈟","䌟":"𦈞","䌥":"𦈠","䌰":"𦈙","䍤":"𫅅","䍦":"䍠","䍽":"𦍠","䎙":"𫅭","䎱":"䎬","䓣":"𬜯","䕤":"𫟕","䕳":"𦰴","䖅":"𫟑","䗅":"𫊪","䗿":"𧉞","䙔":"𫋲","䙡":"䙌","䙱":"𧜭","䚩":"𫌯","䛄":"𫍠","䛳":"𫍫","䜀":"䜧","䜖":"𫟢","䝭":"𫎧","䝻":"𧹕","䝼":"䞍","䞈":"𧹑","䞋":"𫎪","䞓":"𫎭","䟃":"𫎺","䟆":"𫎳","䟐":"𫎱","䠆":"𫏃","䠱":"𨅛","䡐":"𫟤","䡩":"𫟥","䡵":"𫟦","䢨":"𨑹","䤤":"𫟺","䥄":"𫠀","䥇":"䦂","䥑":"鿏","䥕":"𬭯","䥗":"𫔋","䥩":"𨱖","䥯":"𫔆","䥱":"䥾","䦘":"𨸄","䦛":"䦶","䦟":"䦷","䦯":"𫔵","䦳":"𨷿","䧢":"𨸟","䪊":"𫖅","䪏":"𩏼","䪗":"𩐀","䪘":"𩏿","䪴":"𫖫","䪾":"𫖬","䫀":"𫖱","䫂":"𫖰","䫟":"𫖲","䫴":"𩖗","䫶":"𫖺","䫻":"𫗇","䫾":"𫠈","䬓":"𫗊","䬘":"𩙮","䬝":"𩙯","䬞":"𩙧","䬧":"𫗟","䭀":"𩠇","䭃":"𩠈","䭑":"𫗱","䭔":"𫗰","䭿":"𩧭","䮄":"𫠊","䮝":"𩧰","䮞":"𩨁","䮠":"𩧿","䮫":"𩨇","䮰":"𫘮","䮳":"𩨏","䮾":"𩧪","䯀":"䯅","䯤":"𩩈","䰾":"鲃","䱀":"𫚐","䱁":"𫚏","䱙":"𩾈","䱧":"𫚠","䱬":"𩾊","䱰":"𩾋","䱷":"䲣","䱸":"𫠑","䱽":"䲝","䲁":"鳚","䲅":"𫚜","䲖":"𩾂","䲘":"鳤","䲰":"𪉂","䳜":"𫛬","䳢":"𫛰","䳤":"𫛮","䳧":"𫛺","䳫":"𫛼","䴉":"鹮","䴋":"𫜅","䴬":"𪎈","䴱":"𫜒","䴴":"𪎋","䴽":"𫜔","䵳":"𪑅","䵴":"𫜙","䶕":"𫜨","䶲":"𫜳","丟":"丢","並":"并","乾":"干","亂":"乱","亙":"亘","亞":"亚","佇":"伫","佈":"布","佔":"占","併":"并","來":"来","侖":"仑","侶":"侣","侷":"局","俁":"俣","係":"系","俓":"𠇹","俔":"伣","俠":"侠","俥":"伡","俬":"私","倀":"伥","倆":"俩","倈":"俫","倉":"仓","個":"个","們":"们","倖":"幸","倫":"伦","倲":"㑈","偉":"伟","偑":"㐽","側":"侧","偵":"侦","偽":"伪","傌":"㐷","傑":"杰","傖":"伧","傘":"伞","備":"备","傢":"家","傭":"佣","傯":"偬","傳":"传","傴":"伛","債":"债","傷":"伤","傾":"倾","僂":"偻","僅":"仅","僉":"佥","僑":"侨","僕":"仆","僞":"伪","僤":"𫢸","僥":"侥","僨":"偾","僱":"雇","價":"价","儀":"仪","儁":"俊","儂":"侬","億":"亿","儈":"侩","儉":"俭","儎":"","儐":"","儔":"俦","儕":"侪","儘":"尽","償":"偿","儣":"𠆲","優":"优","儭":"𠋆","儲":"储","儷":"俪","儸":"㑩","儺":"傩","儻":"傥","儼":"俨","兇":"凶","兌":"兑","兒":"儿","兗":"兖","內":"内","兩":"两","冊":"册","冑":"胄","冪":"幂","凈":"净","凍":"冻","凙":"𪞝","凜":"凛","凱":"凯","別":"别","刪":"删","剄":"刭","則":"则","剋":"克","剎":"刹","剗":"刬","剛":"刚","剝":"剥","剮":"剐","剴":"剀","創":"创","剷":"铲","剾":"𠛅","劃":"划","劇":"剧","劉":"刘","劊":"刽","劌":"刿","劍":"剑","劏":"㓥","劑":"剂","劚":"㔉","勁":"劲","勑":"𠡠","動":"动","務":"务","勛":"勋","勝":"胜","勞":"劳","勢":"势","勣":"𪟝","勩":"勚","勱":"劢","勳":"勋","勵":"励","勸":"劝","勻":"匀","匭":"匦","匯":"汇","匱":"匮","區":"区","協":"协","卹":"恤","卻":"却","卽":"即","厙":"厍","厠":"厕","厤":"历","厭":"厌","厲":"厉","厴":"厣","參":"参","叄":"叁","叢":"丛","吒":"咤","吳":"吴","吶":"呐","呂":"吕","咼":"呙","員":"员","哯":"𠯟","唄":"呗","唓":"𪠳","唸":"念","問":"问","啓":"启","啞":"哑","啟":"启","啢":"唡","喎":"㖞","喚":"唤","喪":"丧","喫":"吃","喬":"乔","單":"单","喲":"哟","嗆":"呛","嗇":"啬","嗊":"唝","嗎":"吗","嗚":"呜","嗩":"唢","嗰":"𠮶","嗶":"哔","嗹":"𪡏","嘆":"叹","嘍":"喽","嘓":"啯","嘔":"呕","嘖":"啧","嘗":"尝","嘜":"唛","嘩":"哗","嘪":"𪡃","嘮":"唠","嘯":"啸","嘰":"叽","嘳":"𪡞","嘵":"哓","嘸":"呒","嘺":"𪡀","嘽":"啴","噁":"恶","噅":"𠯠","噓":"嘘","噚":"㖊","噝":"咝","噞":"𪡋","噠":"哒","噥":"哝","噦":"哕","噯":"嗳","噲":"哙","噴":"喷","噸":"吨","噹":"当","嚀":"咛","嚇":"吓","嚌":"哜","嚐":"尝","嚕":"噜","嚙":"啮","嚛":"𪠸","嚥":"咽","嚦":"呖","嚧":"𠰷","嚨":"咙","嚮":"向","嚲":"亸","嚳":"喾","嚴":"严","嚶":"嘤","嚽":"𪢕","囀":"啭","囁":"嗫","囂":"嚣","囃":"𠱞","囅":"冁","囈":"呓","囉":"啰","囌":"苏","囑":"嘱","囒":"𪢠","囪":"囱","圇":"囵","國":"国","圍":"围","園":"园","圓":"圆","圖":"图","團":"团","圞":"𪢮","垻":"坝","埡":"垭","埨":"𫭢","埬":"𪣆","埰":"采","執":"执","堅":"坚","堊":"垩","堖":"垴","堚":"𪣒","堝":"埚","堯":"尧","報":"报","場":"场","塊":"块","塋":"茔","塏":"垲","塒":"埘","塗":"涂","塚":"冢","塢":"坞","塤":"埙","塵":"尘","塸":"𫭟","塹":"堑","塿":"𪣻","墊":"垫","墜":"坠","墠":"𫮃","墮":"堕","墰":"坛","墲":"𪢸","墳":"坟","墶":"垯","墻":"墙","墾":"垦","壇":"坛","壈":"𡒄","壋":"垱","壎":"埙","壓":"压","壗":"𡋤","壘":"垒","壙":"圹","壚":"垆","壜":"坛","壞":"坏","壟":"垄","壠":"垅","壢":"坜","壣":"𪤚","壩":"坝","壪":"塆","壯":"壮","壺":"壶","壼":"壸","壽":"寿","夠":"够","夢":"梦","夥":"伙","夾":"夹","奐":"奂","奧":"奥","奩":"奁","奪":"夺","奬":"奖","奮":"奋","奼":"姹","妝":"妆","姍":"姗","姦":"奸","娙":"𫰛","娛":"娱","婁":"娄","婡":"𫝫","婦":"妇","婭":"娅","媈":"𫝨","媧":"娲","媯":"妫","媰":"㛀","媼":"媪","媽":"妈","嫋":"袅","嫗":"妪","嫵":"妩","嫺":"娴","嫻":"娴","嫿":"婳","嬀":"妫","嬃":"媭","嬇":"𫝬","嬈":"娆","嬋":"婵","嬌":"娇","嬙":"嫱","嬡":"嫒","嬣":"𪥰","嬤":"嬷","嬦":"𫝩","嬪":"嫔","嬰":"婴","嬸":"婶","嬻":"𪥿","孃":"娘","孄":"𫝮","孆":"𫝭","孇":"𪥫","孋":"㛤","孌":"娈","孎":"𡠟","孫":"孙","學":"学","孻":"𡥧","孾":"𪧀","孿":"孪","宮":"宫","寀":"采","寠":"𪧘","寢":"寝","實":"实","寧":"宁","審":"审","寫":"写","寬":"宽","寵":"宠","寶":"宝","將":"将","專":"专","尋":"寻","對":"对","導":"导","尷":"尴","屆":"届","屍":"尸","屓":"屃","屜":"屉","屢":"屡","層":"层","屨":"屦","屩":"𪨗","屬":"属","岡":"冈","峯":"峰","峴":"岘","島":"岛","峽":"峡","崍":"崃","崑":"昆","崗":"岗","崙":"仑","崢":"峥","崬":"岽","嵐":"岚","嵗":"岁","嵼":"𡶴","嵽":"𫶇","嵾":"㟥","嶁":"嵝","嶄":"崭","嶇":"岖","嶈":"𡺃","嶔":"嵚","嶗":"崂","嶘":"𡺄","嶠":"峤","嶢":"峣","嶧":"峄","嶨":"峃","嶮":"崄","嶸":"嵘","嶹":"𫝵","嶺":"岭","嶼":"屿","嶽":"岳","巊":"𪩎","巋":"岿","巒":"峦","巔":"巅","巖":"岩","巗":"𪨷","巘":"𪩘","巰":"巯","巹":"卺","帥":"帅","師":"师","帳":"帐","帶":"带","幀":"帧","幃":"帏","幓":"㡎","幗":"帼","幘":"帻","幝":"𪩷","幟":"帜","幣":"币","幩":"𪩸","幫":"帮","幬":"帱","幹":"干","幾":"几","庫":"库","廁":"厕","廂":"厢","廄":"厩","廈":"厦","廎":"庼","廕":"荫","廚":"厨","廝":"厮","廞":"𫷷","廟":"庙","廠":"厂","廡":"庑","廢":"废","廣":"广","廧":"𪪞","廩":"廪","廬":"庐","廳":"厅","弒":"弑","弔":"吊","弳":"弪","張":"张","強":"强","彃":"𪪼","彄":"𫸩","彆":"别","彈":"弹","彌":"弥","彎":"弯","彔":"录","彙":"汇","彠":"彟","彥":"彦","彫":"雕","彲":"彨","彷":"彷","彿":"佛","後":"后","徑":"径","從":"从","徠":"徕","復":"复","徵":"征","徹":"彻","徿":"𪫌","恆":"恒","恥":"耻","悅":"悦","悞":"悮","悵":"怅","悶":"闷","悽":"凄","惡":"恶","惱":"恼","惲":"恽","惻":"恻","愛":"爱","愜":"惬","愨":"悫","愴":"怆","愷":"恺","愻":"𢙏","愾":"忾","慄":"栗","態":"态","慍":"愠","慘":"惨","慚":"惭","慟":"恸","慣":"惯","慤":"悫","慪":"怄","慫":"怂","慮":"虑","慳":"悭","慶":"庆","慺":"㥪","慼":"戚","慾":"欲","憂":"忧","憊":"惫","憐":"怜","憑":"凭","憒":"愦","憖":"慭","憚":"惮","憢":"𢙒","憤":"愤","憫":"悯","憮":"怃","憲":"宪","憶":"忆","憸":"𪫺","憹":"𢙐","懀":"𢙓","懇":"恳","應":"应","懌":"怿","懍":"懔","懎":"𢠁","懞":"蒙","懟":"怼","懣":"懑","懤":"㤽","懨":"恹","懲":"惩","懶":"懒","懷":"怀","懸":"悬","懺":"忏","懼":"惧","懾":"慑","戀":"恋","戇":"戆","戔":"戋","戧":"戗","戩":"戬","戰":"战","戱":"戯","戲":"戏","戶":"户","拋":"抛","挩":"捝","挱":"挲","挾":"挟","捨":"舍","捫":"扪","捱":"挨","捲":"卷","掃":"扫","掄":"抡","掆":"㧏","掗":"挜","掙":"挣","掚":"𪭵","掛":"挂","採":"采","揀":"拣","揚":"扬","換":"换","揮":"挥","揯":"搄","損":"损","搖":"摇","搗":"捣","搵":"揾","搶":"抢","摋":"𢫬","摐":"𪭢","摑":"掴","摜":"掼","摟":"搂","摯":"挚","摳":"抠","摶":"抟","摺":"折","摻":"掺","撈":"捞","撊":"𪭾","撏":"挦","撐":"撑","撓":"挠","撝":"㧑","撟":"挢","撣":"掸","撥":"拨","撧":"𪮖","撫":"抚","撲":"扑","撳":"揿","撻":"挞","撾":"挝","撿":"捡","擁":"拥","擄":"掳","擇":"择","擊":"击","擋":"挡","擓":"㧟","擔":"担","據":"据","擟":"𪭧","擠":"挤","擣":"捣","擫":"𢬍","擬":"拟","擯":"摈","擰":"拧","擱":"搁","擲":"掷","擴":"扩","擷":"撷","擺":"摆","擻":"擞","擼":"撸","擽":"㧰","擾":"扰","攄":"摅","攆":"撵","攋":"𪮶","攏":"拢","攔":"拦","攖":"撄","攙":"搀","攛":"撺","攜":"携","攝":"摄","攢":"攒","攣":"挛","攤":"摊","攪":"搅","攬":"揽","敎":"教","敓":"敚","敗":"败","敘":"叙","敵":"敌","數":"数","斂":"敛","斃":"毙","斅":"𢽾","斆":"敩","斕":"斓","斬":"斩","斷":"断","斸":"𣃁","於":"于","旂":"旗","旣":"既","昇":"升","時":"时","晉":"晋","晛":"𬀪","晝":"昼","暈":"晕","暉":"晖","暐":"𬀩","暘":"旸","暢":"畅","暫":"暂","曄":"晔","曆":"历","曇":"昙","曉":"晓","曊":"𪰶","曏":"向","曖":"暧","曠":"旷","曥":"𣆐","曨":"昽","曬":"晒","書":"书","會":"会","朥":"𦛨","朧":"胧","朮":"术","東":"东","枴":"拐","柵":"栅","柺":"拐","査":"查","桱":"𣐕","桿":"杆","梔":"栀","梖":"𪱷","梘":"枧","梜":"𬂩","條":"条","梟":"枭","梲":"棁","棄":"弃","棊":"棋","棖":"枨","棗":"枣","棟":"栋","棡":"㭎","棧":"栈","棲":"栖","棶":"梾","椏":"桠","椲":"㭏","楇":"𣒌","楊":"杨","楓":"枫","楨":"桢","業":"业","極":"极","榘":"矩","榦":"干","榪":"杩","榮":"荣","榲":"榅","榿":"桤","構":"构","槍":"枪","槓":"杠","槤":"梿","槧":"椠","槨":"椁","槫":"𣏢","槮":"椮","槳":"桨","槶":"椢","槼":"椝","樁":"桩","樂":"乐","樅":"枞","樑":"梁","樓":"楼","標":"标","樞":"枢","樠":"𣗊","樢":"㭤","樣":"样","樤":"𣔌","樧":"榝","樫":"㭴","樳":"桪","樸":"朴","樹":"树","樺":"桦","樿":"椫","橈":"桡","橋":"桥","機":"机","橢":"椭","橫":"横","橯":"𣓿","檁":"檩","檉":"柽","檔":"档","檜":"桧","檟":"槚","檢":"检","檣":"樯","檭":"𣘴","檮":"梼","檯":"台","檳":"槟","檵":"𪲛","檸":"柠","檻":"槛","櫃":"柜","櫅":"𪲎","櫍":"𬃊","櫓":"橹","櫚":"榈","櫛":"栉","櫝":"椟","櫞":"橼","櫟":"栎","櫠":"𪲮","櫥":"橱","櫧":"槠","櫨":"栌","櫪":"枥","櫫":"橥","櫬":"榇","櫱":"蘖","櫳":"栊","櫸":"榉","櫻":"樱","欄":"栏","欅":"榉","欇":"𪳍","權":"权","欍":"𣐤","欏":"椤","欐":"𪲔","欑":"𪴙","欒":"栾","欓":"𣗋","欖":"榄","欘":"𣚚","欞":"棂","欽":"钦","歎":"叹","歐":"欧","歟":"欤","歡":"欢","歲":"岁","歷":"历","歸":"归","歿":"殁","殘":"残","殞":"殒","殢":"𣨼","殤":"殇","殨":"㱮","殫":"殚","殭":"僵","殮":"殓","殯":"殡","殰":"㱩","殲":"歼","殺":"杀","殻":"壳","殼":"壳","毀":"毁","毆":"殴","毊":"𪵑","毿":"毵","氂":"牦","氈":"毡","氌":"氇","氣":"气","氫":"氢","氬":"氩","氭":"𣱝","氳":"氲","氾":"泛","汎":"泛","汙":"污","決":"决","沒":"没","沖":"冲","況":"况","泝":"溯","洩":"泄","洶":"汹","浹":"浃","浿":"𬇙","涇":"泾","涗":"涚","涼":"凉","淒":"凄","淚":"泪","淥":"渌","淨":"净","淩":"凌","淪":"沦","淵":"渊","淶":"涞","淺":"浅","渙":"涣","減":"减","渢":"沨","渦":"涡","測":"测","渾":"浑","湊":"凑","湋":"𣲗","湞":"浈","湧":"涌","湯":"汤","溈":"沩","準":"准","溝":"沟","溡":"𪶄","溫":"温","溮":"浉","溳":"涢","溼":"湿","滄":"沧","滅":"灭","滌":"涤","滎":"荥","滙":"汇","滬":"沪","滯":"滞","滲":"渗","滷":"卤","滸":"浒","滻":"浐","滾":"滚","滿":"满","漁":"渔","漊":"溇","漍":"𬇹","漚":"沤","漢":"汉","漣":"涟","漬":"渍","漲":"涨","漵":"溆","漸":"渐","漿":"浆","潁":"颍","潑":"泼","潔":"洁","潕":"𣲘","潙":"沩","潚":"㴋","潛":"潜","潣":"𫞗","潤":"润","潯":"浔","潰":"溃","潷":"滗","潿":"涠","澀":"涩","澅":"𣶩","澆":"浇","澇":"涝","澐":"沄","澗":"涧","澠":"渑","澤":"泽","澦":"滪","澩":"泶","澫":"𬇕","澬":"𫞚","澮":"浍","澱":"淀","澾":"㳠","濁":"浊","濃":"浓","濄":"㳡","濆":"𣸣","濕":"湿","濘":"泞","濚":"溁","濛":"蒙","濜":"浕","濟":"济","濤":"涛","濧":"㳔","濫":"滥","濰":"潍","濱":"滨","濺":"溅","濼":"泺","濾":"滤","濿":"𪵱","瀂":"澛","瀃":"𣽷","瀅":"滢","瀆":"渎","瀇":"㲿","瀉":"泻","瀋":"沈","瀏":"浏","瀕":"濒","瀘":"泸","瀝":"沥","瀟":"潇","瀠":"潆","瀦":"潴","瀧":"泷","瀨":"濑","瀰":"弥","瀲":"潋","瀾":"澜","灃":"沣","灄":"滠","灍":"𫞝","灑":"洒","灒":"𪷽","灕":"漓","灘":"滩","灙":"𣺼","灝":"灏","灡":"㳕","灣":"湾","灤":"滦","灧":"滟","灩":"滟","災":"灾","為":"为","烏":"乌","烴":"烃","無":"无","煇":"𪸩","煉":"炼","煒":"炜","煙":"烟","煢":"茕","煥":"焕","煩":"烦","煬":"炀","煱":"㶽","熂":"𪸕","熅":"煴","熉":"𤈶","熌":"𤇄","熒":"荧","熓":"𤆡","熗":"炝","熚":"𤇹","熡":"𤋏","熰":"𬉼","熱":"热","熲":"颎","熾":"炽","燀":"𬊤","燁":"烨","燈":"灯","燉":"炖","燒":"烧","燖":"𬊈","燙":"烫","燜":"焖","營":"营","燦":"灿","燬":"毁","燭":"烛","燴":"烩","燶":"㶶","燻":"熏","燼":"烬","燾":"焘","爃":"𫞡","爄":"𤇃","爇":"𦶟","爍":"烁","爐":"炉","爖":"𤇭","爛":"烂","爥":"𪹳","爧":"𫞠","爭":"争","爲":"为","爺":"爷","爾":"尔","牀":"床","牆":"墙","牘":"牍","牴":"牴","牽":"牵","犖":"荦","犛":"牦","犞":"𪺭","犢":"犊","犧":"牺","狀":"状","狹":"狭","狽":"狈","猌":"𪺽","猙":"狰","猶":"犹","猻":"狲","獁":"犸","獃":"呆","獄":"狱","獅":"狮","獊":"𪺷","獎":"奖","獨":"独","獩":"𤞃","獪":"狯","獫":"猃","獮":"狝","獰":"狞","獱":"㺍","獲":"获","獵":"猎","獷":"犷","獸":"兽","獺":"獭","獻":"献","獼":"猕","玀":"猡","玁":"𤞤","珼":"𫞥","現":"现","琱":"雕","琺":"珐","琿":"珲","瑋":"玮","瑒":"玚","瑣":"琐","瑤":"瑶","瑩":"莹","瑪":"玛","瑲":"玱","瑻":"𪻲","瑽":"𪻐","璉":"琏","璊":"𫞩","璕":"𬍤","璗":"𬍡","璝":"𪻺","璡":"琎","璣":"玑","璦":"瑷","璫":"珰","璯":"㻅","環":"环","璵":"玙","璸":"瑸","璼":"𫞨","璽":"玺","璾":"𫞦","璿":"璇","瓄":"𪻨","瓅":"𬍛","瓊":"琼","瓏":"珑","瓔":"璎","瓕":"𤦀","瓚":"瓒","瓛":"𤩽","甌":"瓯","甕":"瓮","產":"产","産":"产","甦":"苏","甯":"宁","畝":"亩","畢":"毕","畫":"画","異":"异","畵":"画","當":"当","畼":"𪽈","疇":"畴","疊":"叠","痙":"痉","痠":"酸","痮":"𪽪","痾":"疴","瘂":"痖","瘋":"疯","瘍":"疡","瘓":"痪","瘞":"瘗","瘡":"疮","瘧":"疟","瘮":"瘆","瘱":"𪽷","瘲":"疭","瘺":"瘘","瘻":"瘘","療":"疗","癆":"痨","癇":"痫","癉":"瘅","癐":"𤶊","癒":"愈","癘":"疠","癟":"瘪","癡":"痴","癢":"痒","癤":"疖","癥":"症","癧":"疬","癩":"癞","癬":"癣","癭":"瘿","癮":"瘾","癰":"痈","癱":"瘫","癲":"癫","發":"发","皁":"皂","皚":"皑","皟":"𤾀","皰":"疱","皸":"皲","皺":"皱","盃":"杯","盜":"盗","盞":"盏","盡":"尽","監":"监","盤":"盘","盧":"卢","盨":"𪾔","盪":"荡","眝":"𪾣","眞":"真","眥":"眦","眾":"众","睍":"𪾢","睏":"困","睜":"睁","睞":"睐","瞘":"眍","瞜":"䁖","瞞":"瞒","瞤":"𥆧","瞭":"瞭","瞶":"瞆","瞼":"睑","矇":"蒙","矉":"𪾸","矑":"𪾦","矓":"眬","矚":"瞩","矯":"矫","硃":"朱","硜":"硁","硤":"硖","硨":"砗","硯":"砚","碕":"埼","碙":"𥐻","碩":"硕","碭":"砀","碸":"砜","確":"确","碼":"码","碽":"䂵","磑":"硙","磚":"砖","磠":"硵","磣":"碜","磧":"碛","磯":"矶","磽":"硗","磾":"䃅","礄":"硚","礆":"硷","礎":"础","礐":"𬒈","礒":"𥐟","礙":"碍","礦":"矿","礪":"砺","礫":"砾","礬":"矾","礮":"𪿫","礱":"砻","祇":"祇","祕":"秘","祿":"禄","禍":"祸","禎":"祯","禕":"祎","禡":"祃","禦":"御","禪":"禅","禮":"礼","禰":"祢","禱":"祷","禿":"秃","秈":"籼","稅":"税","稈":"秆","稏":"䅉","稜":"棱","稟":"禀","種":"种","稱":"称","穀":"谷","穇":"䅟","穌":"稣","積":"积","穎":"颖","穠":"秾","穡":"穑","穢":"秽","穩":"稳","穫":"获","穭":"穞","窩":"窝","窪":"洼","窮":"穷","窯":"窑","窵":"窎","窶":"窭","窺":"窥","竄":"窜","竅":"窍","竇":"窦","竈":"灶","竊":"窃","竚":"𥩟","竪":"竖","竱":"𫁟","競":"竞","筆":"笔","筍":"笋","筧":"笕","筴":"䇲","箇":"个","箋":"笺","箏":"筝","節":"节","範":"范","築":"筑","篋":"箧","篔":"筼","篘":"𥬠","篠":"筿","篢":"𬕂","篤":"笃","篩":"筛","篳":"筚","篸":"𥮾","簀":"箦","簂":"𫂆","簍":"篓","簑":"蓑","簞":"箪","簡":"简","簢":"𫂃","簣":"篑","簫":"箫","簹":"筜","簽":"签","簾":"帘","籃":"篮","籅":"𥫣","籋":"𥬞","籌":"筹","籔":"䉤","籙":"箓","籛":"篯","籜":"箨","籟":"籁","籠":"笼","籤":"签","籩":"笾","籪":"簖","籬":"篱","籮":"箩","籲":"吁","粵":"粤","糉":"粽","糝":"糁","糞":"粪","糧":"粮","糰":"团","糲":"粝","糴":"籴","糶":"粜","糹":"纟","糺":"𫄙","糾":"纠","紀":"纪","紂":"纣","紃":"𬘓","約":"约","紅":"红","紆":"纡","紇":"纥","紈":"纨","紉":"纫","紋":"纹","納":"纳","紐":"纽","紓":"纾","純":"纯","紕":"纰","紖":"纼","紗":"纱","紘":"纮","紙":"纸","級":"级","紛":"纷","紜":"纭","紝":"纴","紞":"𬘘","紟":"𫄛","紡":"纺","紬":"䌷","紮":"扎","細":"细","紱":"绂","紲":"绁","紳":"绅","紵":"纻","紹":"绍","紺":"绀","紼":"绋","紿":"绐","絀":"绌","絁":"𫄟","終":"终","絃":"弦","組":"组","絅":"䌹","絆":"绊","絍":"𫟃","絎":"绗","結":"结","絕":"绝","絙":"𫄠","絛":"绦","絝":"绔","絞":"绞","絡":"络","絢":"绚","絥":"𫄢","給":"给","絧":"𫄡","絨":"绒","絪":"𬘡","絰":"绖","統":"统","絲":"丝","絳":"绛","絶":"绝","絹":"绢","絺":"𫄨","綀":"𦈌","綁":"绑","綃":"绡","綄":"𬘫","綆":"绠","綇":"𦈋","綈":"绨","綉":"绣","綋":"𫟄","綌":"绤","綎":"𬘩","綏":"绥","綐":"䌼","綑":"捆","經":"经","綖":"𫄧","綜":"综","綝":"𬘭","綞":"缍","綟":"𫄫","綠":"绿","綡":"𫟅","綢":"绸","綣":"绻","綧":"𬘯","綪":"𬘬","綫":"线","綬":"绶","維":"维","綯":"绹","綰":"绾","綱":"纲","網":"网","綳":"绷","綴":"缀","綵":"彩","綸":"纶","綹":"绺","綺":"绮","綻":"绽","綽":"绰","綾":"绫","綿":"绵","緄":"绲","緇":"缁","緊":"紧","緋":"绯","緍":"𦈏","緑":"绿","緒":"绪","緓":"绬","緔":"绱","緗":"缃","緘":"缄","緙":"缂","線":"线","緝":"缉","緞":"缎","緟":"𫟆","締":"缔","緡":"缗","緣":"缘","緤":"𫄬","緦":"缌","編":"编","緩":"缓","緬":"缅","緮":"𫄭","緯":"纬","緰":"𦈕","緱":"缑","緲":"缈","練":"练","緶":"缏","緷":"𦈉","緸":"𦈑","緹":"缇","緻":"致","緼":"缊","縈":"萦","縉":"缙","縊":"缢","縋":"缒","縍":"𫄰","縎":"𦈔","縐":"绉","縑":"缣","縕":"缊","縗":"缞","縛":"缚","縝":"缜","縞":"缟","縟":"缛","縣":"县","縧":"绦","縫":"缝","縬":"𦈚","縭":"缡","縮":"缩","縯":"𬙂","縰":"𫄳","縱":"纵","縲":"缧","縳":"䌸","縴":"纤","縵":"缦","縶":"絷","縷":"缕","縸":"𫄲","縹":"缥","縺":"𦈐","總":"总","績":"绩","繂":"𫄴","繃":"绷","繅":"缫","繆":"缪","繈":"𫄶","繏":"𦈝","繐":"𰬸","繒":"缯","繓":"𦈛","織":"织","繕":"缮","繚":"缭","繞":"绕","繟":"𦈎","繡":"绣","繢":"缋","繨":"𫄤","繩":"绳","繪":"绘","繫":"系","繬":"𫄱","繭":"茧","繮":"缰","繯":"缳","繰":"缲","繳":"缴","繶":"𫄷","繷":"𫄣","繸":"䍁","繹":"绎","繻":"𦈡","繼":"继","繽":"缤","繾":"缱","繿":"䍀","纁":"𫄸","纆":"𬙊","纇":"颣","纈":"缬","纊":"纩","續":"续","纍":"累","纏":"缠","纓":"缨","纔":"才","纕":"𬙋","纖":"纤","纗":"𫄹","纘":"缵","纚":"𫄥","纜":"缆","缽":"钵","罃":"䓨","罈":"坛","罌":"罂","罎":"坛","罰":"罚","罵":"骂","罷":"罢","羅":"罗","羆":"罴","羈":"羁","羋":"芈","羣":"群","羥":"羟","羨":"羡","義":"义","羵":"𫅗","羶":"膻","習":"习","翫":"玩","翬":"翚","翹":"翘","翽":"翙","耬":"耧","耮":"耢","聖":"圣","聞":"闻","聯":"联","聰":"聪","聲":"声","聳":"耸","聵":"聩","聶":"聂","職":"职","聹":"聍","聻":"𫆏","聽":"听","聾":"聋","肅":"肃","脅":"胁","脈":"脉","脛":"胫","脣":"唇","脥":"𣍰","脩":"修","脫":"脱","脹":"胀","腎":"肾","腖":"胨","腡":"脶","腦":"脑","腪":"𣍯","腫":"肿","腳":"脚","腸":"肠","膃":"腽","膕":"腘","膚":"肤","膞":"䏝","膠":"胶","膢":"𦝼","膩":"腻","膹":"𪱥","膽":"胆","膾":"脍","膿":"脓","臉":"脸","臍":"脐","臏":"膑","臗":"𣎑","臘":"腊","臚":"胪","臟":"脏","臠":"脔","臢":"臜","臥":"卧","臨":"临","臺":"台","與":"与","興":"兴","舉":"举","舊":"旧","舘":"馆","艙":"舱","艣":"𫇛","艤":"舣","艦":"舰","艫":"舻","艱":"艰","艷":"艳","芻":"刍","苧":"苎","茲":"兹","荊":"荆","莊":"庄","莖":"茎","莢":"荚","莧":"苋","菕":"𰰨","華":"华","菴":"庵","菸":"烟","萇":"苌","萊":"莱","萬":"万","萴":"荝","萵":"莴","葉":"叶","葒":"荭","葝":"𫈎","葤":"荮","葦":"苇","葯":"药","葷":"荤","蒍":"𫇭","蒐":"搜","蒓":"莼","蒔":"莳","蒕":"蒀","蒞":"莅","蒭":"𫇴","蒼":"苍","蓀":"荪","蓆":"席","蓋":"盖","蓧":"𦰏","蓮":"莲","蓯":"苁","蓴":"莼","蓽":"荜","蔄":"𬜬","蔔":"卜","蔘":"参","蔞":"蒌","蔣":"蒋","蔥":"葱","蔦":"茑","蔭":"荫","蔯":"𫈟","蔿":"𫇭","蕁":"荨","蕆":"蒇","蕎":"荞","蕒":"荬","蕓":"芸","蕕":"莸","蕘":"荛","蕝":"𫈵","蕢":"蒉","蕩":"荡","蕪":"芜","蕭":"萧","蕳":"𫈉","蕷":"蓣","蕽":"𫇽","薀":"蕰","薆":"𫉁","薈":"荟","薊":"蓟","薌":"芗","薑":"姜","薔":"蔷","薘":"荙","薟":"莶","薦":"荐","薩":"萨","薳":"䓕","薴":"苧","薵":"䓓","薹":"苔","薺":"荠","藉":"藉","藍":"蓝","藎":"荩","藝":"艺","藥":"药","藪":"薮","藭":"䓖","藴":"蕴","藶":"苈","藷":"𫉄","藹":"蔼","藺":"蔺","蘀":"萚","蘄":"蕲","蘆":"芦","蘇":"苏","蘊":"蕴","蘋":"苹","蘚":"藓","蘞":"蔹","蘟":"𦻕","蘢":"茏","蘭":"兰","蘺":"蓠","蘿":"萝","虆":"蔂","虉":"𬟁","處":"处","虛":"虚","虜":"虏","號":"号","虧":"亏","虯":"虬","蛺":"蛱","蛻":"蜕","蜆":"蚬","蝀":"𬟽","蝕":"蚀","蝟":"猬","蝦":"虾","蝨":"虱","蝸":"蜗","螄":"蛳","螞":"蚂","螢":"萤","螮":"䗖","螻":"蝼","螿":"螀","蟂":"𫋇","蟄":"蛰","蟈":"蝈","蟎":"螨","蟘":"𫋌","蟜":"𫊸","蟣":"虮","蟬":"蝉","蟯":"蛲","蟲":"虫","蟳":"𫊻","蟶":"蛏","蟻":"蚁","蠀":"𧏗","蠁":"蚃","蠅":"蝇","蠆":"虿","蠍":"蝎","蠐":"蛴","蠑":"蝾","蠔":"蚝","蠙":"𧏖","蠟":"蜡","蠣":"蛎","蠦":"𫊮","蠨":"蟏","蠱":"蛊","蠶":"蚕","蠻":"蛮","蠾":"𧑏","衆":"众","衊":"蔑","術":"术","衕":"同","衚":"胡","衛":"卫","衝":"冲","衹":"衹","袞":"衮","裊":"袅","裏":"里","補":"补","裝":"装","裡":"里","製":"制","複":"复","褌":"裈","褘":"袆","褲":"裤","褳":"裢","褸":"褛","褻":"亵","襀":"𫌀","襇":"裥","襉":"裥","襏":"袯","襓":"𫋹","襖":"袄","襗":"𫋷","襘":"𫋻","襝":"裣","襠":"裆","襤":"褴","襪":"袜","襬":"摆","襯":"衬","襰":"𧝝","襲":"袭","襴":"襕","襵":"𫌇","覆":"覆","覈":"核","見":"见","覎":"觃","規":"规","覓":"觅","視":"视","覘":"觇","覛":"𫌪","覡":"觋","覥":"觍","覦":"觎","親":"亲","覬":"觊","覯":"觏","覲":"觐","覷":"觑","覹":"𫌭","覺":"觉","覼":"𫌨","覽":"览","覿":"觌","觀":"观","觴":"觞","觶":"觯","觸":"触","訁":"讠","訂":"订","訃":"讣","計":"计","訊":"讯","訌":"讧","討":"讨","訏":"𬣙","訐":"讦","訑":"𫍙","訒":"讱","訓":"训","訕":"讪","訖":"讫","託":"托","記":"记","訛":"讹","訜":"𫍛","訝":"讶","訞":"𫍚","訟":"讼","訢":"䜣","訣":"诀","訥":"讷","訨":"𫟞","訩":"讻","訪":"访","設":"设","許":"许","訴":"诉","訶":"诃","診":"诊","註":"注","証":"证","詀":"𧮪","詁":"诂","詆":"诋","詊":"𫟟","詎":"讵","詐":"诈","詑":"𫍡","詒":"诒","詓":"𫍜","詔":"诏","評":"评","詖":"诐","詗":"诇","詘":"诎","詛":"诅","詝":"𬣞","詞":"词","詠":"咏","詡":"诩","詢":"询","詣":"诣","試":"试","詩":"诗","詪":"𬣳","詫":"诧","詬":"诟","詭":"诡","詮":"诠","詰":"诘","話":"话","該":"该","詳":"详","詵":"诜","詷":"𫍣","詼":"诙","詿":"诖","誂":"𫍥","誄":"诔","誅":"诛","誆":"诓","誇":"夸","誋":"𫍪","誌":"志","認":"认","誑":"诳","誒":"诶","誕":"诞","誘":"诱","誚":"诮","語":"语","誠":"诚","誡":"诫","誣":"诬","誤":"误","誥":"诰","誦":"诵","誨":"诲","說":"说","誫":"𫍨","説":"说","誰":"谁","課":"课","誳":"𫍮","誴":"𫟡","誶":"谇","誷":"𫍬","誹":"诽","誺":"𫍧","誼":"谊","誾":"訚","調":"调","諂":"谄","諄":"谆","談":"谈","諉":"诿","請":"请","諍":"诤","諏":"诹","諑":"诼","諒":"谅","諓":"𬣡","論":"论","諗":"谂","諛":"谀","諜":"谍","諝":"谞","諞":"谝","諟":"𬤊","諡":"谥","諢":"诨","諣":"𫍩","諤":"谔","諥":"𫍳","諦":"谛","諧":"谐","諫":"谏","諭":"谕","諮":"咨","諯":"𫍱","諰":"𫍰","諱":"讳","諲":"𬤇","諳":"谙","諴":"𫍯","諶":"谌","諷":"讽","諸":"诸","諺":"谚","諼":"谖","諾":"诺","謀":"谋","謁":"谒","謂":"谓","謄":"誊","謅":"诌","謆":"𫍸","謉":"𫍷","謊":"谎","謎":"谜","謏":"𫍲","謐":"谧","謔":"谑","謖":"谡","謗":"谤","謙":"谦","謚":"谥","講":"讲","謝":"谢","謠":"谣","謡":"谣","謨":"谟","謫":"谪","謬":"谬","謭":"谫","謯":"𫍹","謱":"𫍴","謳":"讴","謸":"𫍵","謹":"谨","謾":"谩","譁":"哗","譂":"𫟠","譅":"𰶎","譆":"𫍻","證":"证","譊":"𫍢","譎":"谲","譏":"讥","譑":"𫍤","譓":"𬤝","譖":"谮","識":"识","譙":"谯","譚":"谭","譜":"谱","譞":"𫍽","譟":"噪","譨":"𫍦","譫":"谵","譭":"毁","譯":"译","議":"议","譴":"谴","護":"护","譸":"诪","譽":"誉","譾":"谫","讀":"读","讅":"谉","變":"变","讋":"詟","讌":"䜩","讎":"雠","讒":"谗","讓":"让","讕":"谰","讖":"谶","讚":"赞","讜":"谠","讞":"谳","豈":"岂","豎":"竖","豐":"丰","豔":"艳","豬":"猪","豵":"𫎆","豶":"豮","貓":"猫","貗":"𫎌","貙":"䝙","貝":"贝","貞":"贞","貟":"贠","負":"负","財":"财","貢":"贡","貧":"贫","貨":"货","販":"贩","貪":"贪","貫":"贯","責":"责","貯":"贮","貰":"贳","貲":"赀","貳":"贰","貴":"贵","貶":"贬","買":"买","貸":"贷","貺":"贶","費":"费","貼":"贴","貽":"贻","貿":"贸","賀":"贺","賁":"贲","賂":"赂","賃":"赁","賄":"贿","賅":"赅","資":"资","賈":"贾","賊":"贼","賑":"赈","賒":"赊","賓":"宾","賕":"赇","賙":"赒","賚":"赉","賜":"赐","賝":"𫎩","賞":"赏","賟":"𧹖","賠":"赔","賡":"赓","賢":"贤","賣":"卖","賤":"贱","賦":"赋","賧":"赕","質":"质","賫":"赍","賬":"账","賭":"赌","賰":"䞐","賴":"赖","賵":"赗","賺":"赚","賻":"赙","購":"购","賽":"赛","賾":"赜","贃":"𧹗","贄":"贽","贅":"赘","贇":"赟","贈":"赠","贉":"𫎫","贊":"赞","贋":"赝","贍":"赡","贏":"赢","贐":"赆","贑":"𫎬","贓":"赃","贔":"赑","贖":"赎","贗":"赝","贚":"𫎦","贛":"赣","贜":"赃","赬":"赪","趕":"赶","趙":"赵","趨":"趋","趲":"趱","跡":"迹","踐":"践","踰":"逾","踴":"踊","蹌":"跄","蹔":"𫏐","蹕":"跸","蹟":"迹","蹠":"跖","蹣":"蹒","蹤":"踪","蹳":"𫏆","蹺":"跷","蹻":"𫏋","躂":"跶","躉":"趸","躊":"踌","躋":"跻","躍":"跃","躎":"䟢","躑":"踯","躒":"跞","躓":"踬","躕":"蹰","躘":"𨀁","躚":"跹","躝":"𨅬","躡":"蹑","躥":"蹿","躦":"躜","躪":"躏","軀":"躯","軉":"𨉗","車":"车","軋":"轧","軌":"轨","軍":"军","軏":"𫐄","軑":"轪","軒":"轩","軔":"轫","軕":"𫐅","軗":"𨐅","軛":"轭","軜":"𫐇","軝":"𬨂","軟":"软","軤":"轷","軨":"𫐉","軫":"轸","軬":"𫐊","軲":"轱","軷":"𫐈","軸":"轴","軹":"轵","軺":"轺","軻":"轲","軼":"轶","軾":"轼","軿":"𫐌","較":"较","輄":"𨐈","輅":"辂","輇":"辁","輈":"辀","載":"载","輊":"轾","輋":"𪨶","輒":"辄","輓":"挽","輔":"辅","輕":"轻","輖":"𫐏","輗":"𫐐","輛":"辆","輜":"辎","輝":"辉","輞":"辋","輟":"辍","輢":"𫐎","輥":"辊","輦":"辇","輨":"𫐑","輩":"辈","輪":"轮","輬":"辌","輮":"𫐓","輯":"辑","輳":"辏","輶":"𬨎","輷":"𫐒","輸":"输","輻":"辐","輼":"辒","輾":"辗","輿":"舆","轀":"辒","轂":"毂","轄":"辖","轅":"辕","轆":"辘","轇":"𫐖","轉":"转","轊":"𫐕","轍":"辙","轎":"轿","轐":"𫐗","轔":"辚","轗":"𫐘","轟":"轰","轠":"𫐙","轡":"辔","轢":"轹","轣":"𫐆","轤":"轳","辦":"办","辭":"辞","辮":"辫","辯":"辩","農":"农","迴":"回","逕":"迳","這":"这","連":"连","週":"周","進":"进","遊":"游","運":"运","過":"过","達":"达","違":"违","遙":"遥","遜":"逊","遞":"递","遠":"远","遡":"溯","適":"适","遱":"𫐷","遲":"迟","遷":"迁","選":"选","遺":"遗","遼":"辽","邁":"迈","還":"还","邇":"迩","邊":"边","邏":"逻","邐":"逦","郟":"郏","郵":"邮","鄆":"郓","鄉":"乡","鄒":"邹","鄔":"邬","鄖":"郧","鄟":"𫑘","鄧":"邓","鄩":"𬩽","鄭":"郑","鄰":"邻","鄲":"郸","鄳":"𫑡","鄴":"邺","鄶":"郐","鄺":"邝","酇":"酂","酈":"郦","醃":"腌","醖":"酝","醜":"丑","醞":"酝","醟":"蒏","醣":"糖","醫":"医","醬":"酱","醱":"酦","醲":"𬪩","醶":"𫑷","釀":"酿","釁":"衅","釃":"酾","釅":"酽","釋":"释","釐":"厘","釒":"钅","釓":"钆","釔":"钇","釕":"钌","釗":"钊","釘":"钉","釙":"钋","釚":"𫟲","針":"针","釟":"𫓥","釣":"钓","釤":"钐","釦":"扣","釧":"钏","釨":"𫓦","釩":"钒","釲":"𫟳","釳":"𨰿","釴":"𬬩","釵":"钗","釷":"钍","釹":"钕","釺":"钎","釾":"䥺","釿":"𬬱","鈀":"钯","鈁":"钫","鈃":"钘","鈄":"钭","鈅":"钥","鈆":"𫓪","鈇":"𫓧","鈈":"钚","鈉":"钠","鈋":"𨱂","鈍":"钝","鈎":"钩","鈐":"钤","鈑":"钣","鈒":"钑","鈔":"钞","鈕":"钮","鈖":"𫟴","鈗":"𫟵","鈛":"𫓨","鈞":"钧","鈠":"𨱁","鈡":"钟","鈣":"钙","鈥":"钬","鈦":"钛","鈧":"钪","鈮":"铌","鈯":"𨱄","鈰":"铈","鈲":"𨱃","鈳":"钶","鈴":"铃","鈷":"钴","鈸":"钹","鈹":"铍","鈺":"钰","鈽":"钸","鈾":"铀","鈿":"钿","鉀":"钾","鉁":"𨱅","鉅":"巨","鉆":"钻","鉈":"铊","鉉":"铉","鉊":"𬬿","鉋":"铇","鉍":"铋","鉑":"铂","鉔":"𫓬","鉕":"钷","鉗":"钳","鉚":"铆","鉛":"铅","鉝":"𫟷","鉞":"钺","鉠":"𫓭","鉢":"钵","鉤":"钩","鉥":"𬬸","鉦":"钲","鉧":"𬭁","鉬":"钼","鉭":"钽","鉮":"𬬹","鉳":"锫","鉶":"铏","鉷":"𫟹","鉸":"铰","鉺":"铒","鉻":"铬","鉽":"𫟸","鉾":"𫓴","鉿":"铪","銀":"银","銁":"𫓲","銂":"𫟻","銃":"铳","銅":"铜","銈":"𫓯","銊":"𫓰","銍":"铚","銏":"𫟶","銑":"铣","銓":"铨","銖":"铢","銘":"铭","銚":"铫","銛":"铦","銜":"衔","銠":"铑","銣":"铷","銥":"铱","銦":"铟","銨":"铵","銩":"铥","銪":"铕","銫":"铯","銬":"铐","銱":"铞","銳":"锐","銶":"𨱇","銷":"销","銹":"锈","銻":"锑","銼":"锉","鋁":"铝","鋂":"𰾄","鋃":"锒","鋅":"锌","鋇":"钡","鋉":"𨱈","鋌":"铤","鋏":"铗","鋐":"𬭎","鋒":"锋","鋗":"𫓶","鋙":"铻","鋝":"锊","鋟":"锓","鋠":"𫓵","鋣":"铘","鋤":"锄","鋥":"锃","鋦":"锔","鋨":"锇","鋩":"铓","鋪":"铺","鋭":"锐","鋮":"铖","鋯":"锆","鋰":"锂","鋱":"铽","鋶":"锍","鋸":"锯","鋹":"𬬮","鋼":"钢","錀":"𬬭","錁":"锞","錂":"𨱋","錄":"录","錆":"锖","錇":"锫","錈":"锩","錏":"铔","錐":"锥","錒":"锕","錕":"锟","錘":"锤","錙":"锱","錚":"铮","錛":"锛","錜":"𫓻","錝":"𫓽","錞":"𬭚","錟":"锬","錠":"锭","錡":"锜","錢":"钱","錤":"𫓹","錥":"𫓾","錦":"锦","錨":"锚","錩":"锠","錫":"锡","錮":"锢","錯":"错","録":"录","錳":"锰","錶":"表","錸":"铼","錼":"镎","錽":"𫓸","鍀":"锝","鍁":"锨","鍃":"锪","鍄":"𨱉","鍅":"钫","鍆":"钔","鍇":"锴","鍈":"锳","鍉":"𫔂","鍊":"炼","鍋":"锅","鍍":"镀","鍒":"𫔄","鍔":"锷","鍘":"铡","鍚":"钖","鍛":"锻","鍠":"锽","鍤":"锸","鍥":"锲","鍩":"锘","鍬":"锹","鍭":"𬭤","鍮":"𨱎","鍰":"锾","鍵":"键","鍶":"锶","鍺":"锗","鍼":"针","鍾":"钟","鎂":"镁","鎄":"锿","鎇":"镅","鎈":"𫟿","鎊":"镑","鎌":"镰","鎍":"𫔅","鎓":"𬭩","鎔":"镕","鎖":"锁","鎘":"镉","鎙":"𫔈","鎚":"锤","鎛":"镈","鎝":"𨱏","鎞":"𫔇","鎡":"镃","鎢":"钨","鎣":"蓥","鎦":"镏","鎧":"铠","鎩":"铩","鎪":"锼","鎬":"镐","鎭":"镇","鎮":"镇","鎯":"𨱍","鎰":"镒","鎲":"镋","鎳":"镍","鎵":"镓","鎶":"鿔","鎷":"𨰾","鎸":"镌","鎿":"镎","鏃":"镞","鏆":"𨱌","鏇":"旋","鏈":"链","鏉":"𨱒","鏌":"镆","鏍":"镙","鏏":"𬭬","鏐":"镠","鏑":"镝","鏗":"铿","鏘":"锵","鏚":"𬭭","鏜":"镗","鏝":"镘","鏞":"镛","鏟":"铲","鏡":"镜","鏢":"镖","鏤":"镂","鏥":"𫔊","鏦":"𫓩","鏨":"錾","鏰":"镚","鏵":"铧","鏷":"镤","鏹":"镪","鏺":"䥽","鏻":"𬭸","鏽":"锈","鏾":"𫔌","鐃":"铙","鐄":"𨱑","鐇":"𫔍","鐈":"𫓱","鐋":"铴","鐍":"𫔎","鐎":"𨱓","鐏":"𨱔","鐐":"镣","鐒":"铹","鐓":"镦","鐔":"镡","鐘":"钟","鐙":"镫","鐝":"镢","鐠":"镨","鐥":"䦅","鐦":"锎","鐧":"锏","鐨":"镄","鐩":"𬭼","鐪":"𫓺","鐫":"镌","鐮":"镰","鐯":"䦃","鐲":"镯","鐳":"镭","鐵":"铁","鐶":"镮","鐸":"铎","鐺":"铛","鐼":"𫔁","鐽":"𫟼","鐿":"镱","鑀":"𰾭","鑄":"铸","鑉":"𫠁","鑊":"镬","鑌":"镔","鑑":"鉴","鑒":"鉴","鑔":"镲","鑕":"锧","鑞":"镴","鑠":"铄","鑣":"镳","鑥":"镥","鑪":"𬬻","鑭":"镧","鑰":"钥","鑱":"镵","鑲":"镶","鑴":"𫔔","鑷":"镊","鑹":"镩","鑼":"锣","鑽":"钻","鑾":"銮","鑿":"凿","钁":"镢","钂":"镋","長":"长","門":"门","閂":"闩","閃":"闪","閆":"闫","閈":"闬","閉":"闭","開":"开","閌":"闶","閍":"𨸂","閎":"闳","閏":"闰","閐":"𨸃","閑":"闲","閒":"闲","間":"间","閔":"闵","閗":"𫔯","閘":"闸","閝":"𫠂","閞":"𫔰","閡":"阂","閣":"阁","閤":"合","閥":"阀","閨":"闺","閩":"闽","閫":"阃","閬":"阆","閭":"闾","閱":"阅","閲":"阅","閵":"𫔴","閶":"阊","閹":"阉","閻":"阎","閼":"阏","閽":"阍","閾":"阈","閿":"阌","闃":"阒","闆":"板","闇":"暗","闈":"闱","闉":"𬮱","闊":"阔","闋":"阕","闌":"阑","闍":"阇","闐":"阗","闑":"𫔶","闒":"阘","闓":"闿","闔":"阖","闕":"阙","闖":"闯","關":"关","闞":"阚","闠":"阓","闡":"阐","闢":"辟","闤":"阛","闥":"闼","阪":"阪","陘":"陉","陝":"陕","陞":"升","陣":"阵","陰":"阴","陳":"陈","陸":"陆","陽":"阳","隉":"陧","隊":"队","階":"阶","隑":"𬮿","隕":"陨","際":"际","隤":"𬯎","隨":"随","險":"险","隮":"𬯀","隯":"陦","隱":"隐","隴":"陇","隸":"隶","隻":"只","雋":"隽","雖":"虽","雙":"双","雛":"雏","雜":"杂","雞":"鸡","離":"离","難":"难","雲":"云","電":"电","霑":"沾","霢":"霡","霣":"𫕥","霧":"雾","霼":"𪵣","霽":"霁","靂":"雳","靄":"霭","靆":"叇","靈":"灵","靉":"叆","靚":"靓","靜":"静","靝":"靔","靦":"腼","靧":"𫖃","靨":"靥","鞏":"巩","鞝":"绱","鞦":"秋","鞽":"鞒","鞾":"𫖇","韁":"缰","韃":"鞑","韆":"千","韉":"鞯","韋":"韦","韌":"韧","韍":"韨","韓":"韩","韙":"韪","韚":"𫠅","韛":"𫖔","韜":"韬","韝":"鞲","韞":"韫","韠":"𫖒","韻":"韵","響":"响","頁":"页","頂":"顶","頃":"顷","項":"项","順":"顺","頇":"顸","須":"须","頊":"顼","頌":"颂","頍":"𫠆","頎":"颀","頏":"颃","預":"预","頑":"顽","頒":"颁","頓":"顿","頔":"𬱖","頗":"颇","領":"领","頜":"颌","頠":"𬱟","頡":"颉","頤":"颐","頦":"颏","頫":"𫖯","頭":"头","頮":"颒","頰":"颊","頲":"颋","頴":"颕","頵":"𫖳","頷":"颔","頸":"颈","頹":"颓","頻":"频","頽":"颓","顂":"𩓋","顃":"𩖖","顅":"𫖶","顆":"颗","題":"题","額":"额","顎":"颚","顏":"颜","顒":"颙","顓":"颛","顔":"颜","顗":"𫖮","願":"愿","顙":"颡","顛":"颠","類":"类","顢":"颟","顣":"𫖹","顥":"颢","顧":"顾","顫":"颤","顬":"颥","顯":"显","顰":"颦","顱":"颅","顳":"颞","顴":"颧","風":"风","颭":"飐","颮":"飑","颯":"飒","颰":"𩙥","颱":"台","颳":"刮","颶":"飓","颷":"𩙪","颸":"飔","颺":"飏","颻":"飖","颼":"飕","颾":"𩙫","飀":"飗","飄":"飘","飆":"飙","飈":"飚","飋":"𫗋","飛":"飞","飠":"饣","飢":"饥","飣":"饤","飥":"饦","飦":"𫗞","飩":"饨","飪":"饪","飫":"饫","飭":"饬","飯":"饭","飱":"飧","飲":"饮","飴":"饴","飵":"𫗢","飶":"𫗣","飼":"饲","飽":"饱","飾":"饰","飿":"饳","餃":"饺","餄":"饸","餅":"饼","餈":"糍","餉":"饷","養":"养","餌":"饵","餎":"饹","餏":"饻","餑":"饽","餒":"馁","餓":"饿","餔":"𫗦","餕":"馂","餖":"饾","餗":"𫗧","餘":"余","餚":"肴","餛":"馄","餜":"馃","餞":"饯","餡":"馅","餦":"𫗠","餧":"𫗪","館":"馆","餪":"𫗬","餫":"𫗥","餬":"糊","餭":"𫗮","餱":"糇","餳":"饧","餵":"喂","餶":"馉","餷":"馇","餸":"𩠌","餺":"馎","餼":"饩","餾":"馏","餿":"馊","饁":"馌","饃":"馍","饅":"馒","饈":"馐","饉":"馑","饊":"馓","饋":"馈","饌":"馔","饑":"饥","饒":"饶","饗":"飨","饘":"𫗴","饜":"餍","饞":"馋","饟":"𫗵","饠":"𫗩","饢":"馕","馬":"马","馭":"驭","馮":"冯","馯":"𫘛","馱":"驮","馳":"驰","馴":"驯","馹":"驲","馼":"𫘜","駁":"驳","駃":"𫘝","駉":"𬳶","駊":"𫘟","駎":"𩧨","駐":"驻","駑":"驽","駒":"驹","駓":"𬳵","駔":"驵","駕":"驾","駘":"骀","駙":"驸","駚":"𩧫","駛":"驶","駝":"驼","駞":"𫘞","駟":"驷","駡":"骂","駢":"骈","駤":"𫘠","駧":"𩧲","駩":"𩧴","駪":"𬳽","駫":"𫘡","駭":"骇","駰":"骃","駱":"骆","駶":"𩧺","駸":"骎","駻":"𫘣","駼":"𬳿","駿":"骏","騁":"骋","騂":"骍","騃":"𫘤","騄":"𫘧","騅":"骓","騉":"𫘥","騊":"𫘦","騌":"骔","騍":"骒","騎":"骑","騏":"骐","騑":"𬴂","騔":"𩨀","騖":"骛","騙":"骗","騚":"𩨊","騜":"𫘩","騝":"𩨃","騞":"𬴃","騟":"𩨈","騠":"𫘨","騤":"骙","騧":"䯄","騪":"𩨄","騫":"骞","騭":"骘","騮":"骝","騰":"腾","騱":"𫘬","騴":"𫘫","騵":"𫘪","騶":"驺","騷":"骚","騸":"骟","騻":"𫘭","騼":"𫠋","騾":"骡","驀":"蓦","驁":"骜","驂":"骖","驃":"骠","驄":"骢","驅":"驱","驊":"骅","驋":"𩧯","驌":"骕","驍":"骁","驎":"𬴊","驏":"骣","驓":"𫘯","驕":"骄","驗":"验","驙":"𫘰","驚":"惊","驛":"驿","驟":"骤","驢":"驴","驤":"骧","驥":"骥","驦":"骦","驨":"𫘱","驪":"骊","驫":"骉","骯":"肮","髏":"髅","髒":"脏","體":"体","髕":"髌","髖":"髋","髮":"发","鬆":"松","鬍":"胡","鬖":"𩭹","鬚":"须","鬠":"𫘽","鬢":"鬓","鬥":"斗","鬧":"闹","鬨":"哄","鬩":"阋","鬮":"阄","鬱":"郁","鬹":"鬶","魎":"魉","魘":"魇","魚":"鱼","魛":"鱽","魟":"𫚉","魢":"鱾","魥":"𩽹","魦":"𫚌","魨":"鲀","魯":"鲁","魴":"鲂","魵":"𫚍","魷":"鱿","魺":"鲄","魽":"𫠐","鮀":"𬶍","鮁":"鲅","鮃":"鲆","鮄":"𫚒","鮅":"𫚑","鮆":"𫚖","鮈":"𬶋","鮊":"鲌","鮋":"鲉","鮍":"鲏","鮎":"鲇","鮐":"鲐","鮑":"鲍","鮒":"鲋","鮓":"鲊","鮚":"鲒","鮜":"鲘","鮝":"鲞","鮞":"鲕","鮟":"𩽾","鮠":"𬶏","鮡":"𬶐","鮣":"䲟","鮤":"𫚓","鮦":"鲖","鮪":"鲔","鮫":"鲛","鮭":"鲑","鮮":"鲜","鮯":"𫚗","鮰":"𫚔","鮳":"鲓","鮵":"𫚛","鮶":"鲪","鮸":"3","鮺":"鲝","鮿":"𫚚","鯀":"鲧","鯁":"鲠","鯄":"𩾁","鯆":"𫚙","鯇":"鲩","鯉":"鲤","鯊":"鲨","鯒":"鲬","鯔":"鲻","鯕":"鲯","鯖":"鲭","鯗":"鲞","鯛":"鲷","鯝":"鲴","鯞":"𫚡","鯡":"鲱","鯢":"鲵","鯤":"鲲","鯧":"鲳","鯨":"鲸","鯪":"鲮","鯫":"鲰","鯬":"𫚞","鯰":"鲶","鯱":"𩾇","鯴":"鲺","鯶":"𩽼","鯷":"鳀","鯻":"𬶟","鯽":"鲫","鯾":"𫚣","鯿":"鳊","鰁":"鳈","鰂":"鲗","鰃":"鳂","鰆":"䲠","鰈":"鲽","鰉":"鳇","鰊":"𬶠","鰋":"𫚢","鰌":"䲡","鰍":"鳅","鰏":"鲾","鰐":"鳄","鰑":"𫚊","鰒":"鳆","鰓":"鳃","鰕":"𫚥","鰛":"鳁","鰜":"鳒","鰟":"鳑","鰠":"鳋","鰣":"鲥","鰤":"𫚕","鰥":"鳏","鰦":"𫚤","鰧":"䲢","鰨":"鳎","鰩":"鳐","鰫":"𫚦","鰭":"鳍","鰮":"鳁","鰱":"鲢","鰲":"鳌","鰳":"鳓","鰵":"鳘","鰶":"𬶭","鰷":"鲦","鰹":"鲣","鰺":"鲹","鰻":"鳗","鰼":"鳛","鰽":"𫚧","鰾":"鳔","鱀":"𬶨","鱂":"鳉","鱄":"𫚋","鱅":"鳙","鱆":"𫠒","鱇":"𩾌","鱈":"鳕","鱉":"鳖","鱊":"𫚪","鱒":"鳟","鱔":"鳝","鱖":"鳜","鱗":"鳞","鱘":"鲟","鱚":"𬶮","鱝":"鲼","鱟":"鲎","鱠":"鲙","鱢":"𫚫","鱣":"鳣","鱤":"鳡","鱧":"鳢","鱨":"鲿","鱭":"鲚","鱮":"𫚈","鱯":"鳠","鱲":"𫚭","鱷":"鳄","鱸":"鲈","鱺":"鲡","鳥":"鸟","鳧":"凫","鳩":"鸠","鳬":"凫","鳲":"鸤","鳳":"凤","鳴":"鸣","鳶":"鸢","鳷":"𫛛","鳼":"𪉃","鳽":"𫛚","鳾":"䴓","鴀":"𫛜","鴃":"𫛞","鴅":"𫛝","鴆":"鸩","鴇":"鸨","鴉":"鸦","鴐":"𫛤","鴒":"鸰","鴔":"𫛡","鴕":"鸵","鴗":"𫁡","鴛":"鸳","鴜":"𪉈","鴝":"鸲","鴞":"鸮","鴟":"鸱","鴣":"鸪","鴥":"𫛣","鴦":"鸯","鴨":"鸭","鴮":"𫛦","鴯":"鸸","鴰":"鸹","鴲":"𪉆","鴳":"𫛩","鴴":"鸻","鴷":"䴕","鴻":"鸿","鴽":"𫛪","鴿":"鸽","鵁":"䴔","鵂":"鸺","鵃":"鸼","鵊":"𫛥","鵏":"𬷕","鵐":"鹀","鵑":"鹃","鵒":"鹆","鵓":"鹁","鵚":"𪉍","鵜":"鹈","鵝":"鹅","鵟":"𫛭","鵠":"鹄","鵡":"鹉","鵧":"𫛨","鵩":"𫛳","鵪":"鹌","鵫":"𫛱","鵬":"鹏","鵮":"鹐","鵯":"鹎","鵰":"雕","鵲":"鹊","鵷":"鹓","鵾":"鹍","鶄":"䴖","鶇":"鸫","鶉":"鹑","鶊":"鹒","鶌":"𫛵","鶒":"𫛶","鶓":"鹋","鶖":"鹙","鶗":"𫛸","鶘":"鹕","鶚":"鹗","鶠":"𬸘","鶡":"鹖","鶥":"鹛","鶦":"𫛷","鶩":"鹜","鶪":"䴗","鶬":"鸧","鶭":"𫛯","鶯":"莺","鶰":"𫛫","鶱":"𬸣","鶲":"鹟","鶴":"鹤","鶹":"鹠","鶺":"鹡","鶻":"鹘","鶼":"鹣","鶿":"鹚","鷀":"鹚","鷁":"鹢","鷂":"鹞","鷄":"鸡","鷅":"𫛽","鷉":"䴘","鷊":"鹝","鷐":"𫜀","鷓":"鹧","鷔":"𪉑","鷖":"鹥","鷗":"鸥","鷙":"鸷","鷚":"鹨","鷟":"𬸦","鷣":"𫜃","鷤":"𫛴","鷥":"鸶","鷦":"鹪","鷨":"𪉊","鷩":"𫜁","鷫":"鹔","鷭":"𬸪","鷯":"鹩","鷲":"鹫","鷳":"鹇","鷴":"鹇","鷷":"𫜄","鷸":"鹬","鷹":"鹰","鷺":"鹭","鷽":"鸴","鷿":"𬸯","鸂":"㶉","鸇":"鹯","鸊":"䴙","鸋":"𫛢","鸌":"鹱","鸏":"鹲","鸑":"𬸚","鸕":"鸬","鸗":"𫛟","鸘":"鹴","鸚":"鹦","鸛":"鹳","鸝":"鹂","鸞":"鸾","鹵":"卤","鹹":"咸","鹺":"鹾","鹼":"碱","鹽":"盐","麗":"丽","麥":"麦","麨":"𪎊","麩":"麸","麪":"面","麫":"面","麬":"𤿲","麯":"曲","麲":"𪎉","麳":"𪎌","麴":"曲","麵":"面","麷":"𫜑","麼":"么","麽":"么","黃":"黄","黌":"黉","點":"点","黨":"党","黲":"黪","黴":"霉","黶":"黡","黷":"黩","黽":"黾","黿":"鼋","鼂":"鼌","鼉":"鼍","鼕":"冬","鼴":"鼹","齊":"齐","齋":"斋","齎":"赍","齏":"齑","齒":"齿","齔":"龀","齕":"龁","齗":"龂","齘":"𬹼","齙":"龅","齜":"龇","齟":"龃","齠":"龆","齡":"龄","齣":"出","齦":"龈","齧":"啮","齩":"𫜪","齪":"龊","齬":"龉","齭":"𫜭","齮":"𬺈","齯":"𫠜","齰":"𫜬","齲":"龋","齴":"𫜮","齶":"腭","齷":"龌","齼":"𬺓","齾":"𫜰","龍":"龙","龎":"厐","龐":"庞","龑":"䶮","龓":"𫜲","龔":"龚","龕":"龛","龜":"龟","龭":"𩨎","龯":"𨱆","鿁":"䜤","鿓":"鿒","𠁞":"𠀾","𠌥":"𠆿","𠏢":"𠉗","𠐊":"𫝋","𠗣":"㓆","𠞆":"𠛆","𠠎":"𠚳","𠬙":"𪠡","𠽃":"𪠺","𠿕":"𪜎","𡂡":"𪢒","𡃄":"𪡺","𡃕":"𠴛","𡃤":"𪢐","𡄔":"𠴢","𡄣":"𠵸","𡅏":"𠲥","𡅯":"𪢖","𡑍":"𫭼","𡑭":"𡋗","𡓁":"𪤄","𡓾":"𡋀","𡔖":"𡍣","𡞵":"㛟","𡟫":"𫝪","𡠹":"㛿","𡢃":"㛠","𡮉":"𡭜","𡮣":"𡭬","𡳳":"𡳃","𡸗":"𪨩","𡹬":"𪨹","𡻕":"岁","𡽗":"𡸃","𡾱":"㟜","𡿖":"𪩛","𢍰":"𪪴","𢠼":"𢙑","𢣐":"𪬚","𢣚":"𢘝","𢣭":"𢘞","𢤩":"𪫡","𢤱":"𢘙","𢤿":"𪬯","𢯷":"𪭝","𢶒":"𪭯","𢶫":"𢫞","𢷮":"𢫊","𢹿":"𢬦","𢺳":"𪮳","𣈶":"暅","𣋋":"𣈣","𣍐":"𫧃","𣙎":"㭣","𣜬":"𪳗","𣝕":"𣘷","𣞻":"𣘓","𣠩":"𣞎","𣠲":"𣑶","𣯩":"𣯣","𣯴":"𣭤","𣯶":"毶","𣽏":"𪶮","𣾷":"㳢","𣿉":"𣶫","𤁣":"𣺽","𤄷":"𪶒","𤅶":"𣷷","𤑳":"𤎻","𤑹":"𪹀","𤒎":"𤊀","𤒻":"𪹹","𤓌":"𪹠","𤓎":"𤎺","𤓩":"𤊰","𤘀":"𪺣","𤛮":"𤙯","𤛱":"𫞢","𤜆":"𪺪","𤠮":"𪺸","𤢟":"𤝢","𤢻":"𢢐","𤩂":"𫞧","𤪺":"㻘","𤫩":"㻏","𤬅":"𪼴","𤳷":"𪽝","𤳸":"𤳄","𤷃":"𪽭","𤸫":"𤶧","𤺔":"𪽴","𥊝":"𥅿","𥌃":"𥅘","𥏝":"𪿊","𥕥":"𥐰","𥖅":"𥐯","𥖲":"𪿞","𥗇":"𪿵","𥗽":"𬒗","𥜐":"𫀓","𥜰":"𫀌","𥞵":"𥞦","𥢢":"䅪","𥢶":"𫞷","𥢷":"𫀮","𥨐":"𥧂","𥪂":"𥩺","𥯤":"𫁳","𥴨":"𫂖","𥴼":"𫁺","𥵃":"𥱔","𥵊":"𥭉","𥶽":"𫁱","𥸠":"𥮋","𥻦":"𫂿","𥼽":"𥹥","𥽖":"𥺇","𥾯":"𫄝","𥿊":"𦈈","𦀖":"𫄦","𦂅":"𦈒","𦃄":"𦈗","𦃩":"𫄯","𦅇":"𫄪","𦅈":"𫄵","𦆲":"𫟇","𦒀":"𫅥","𦔖":"𫅼","𦘧":"𡳒","𦟼":"𫆝","𦠅":"𫞅","𦡝":"𫆫","𦢈":"𣍨","𦣎":"𦟗","𦧺":"𫇘","𦪙":"䑽","𦪽":"𦨩","𦱌":"𫇪","𦾟":"𦶻","𧎈":"𧌥","𧒯":"𫊹","𧔥":"𧒭","𧕟":"𧉐","𧜗":"䘞","𧜵":"䙊","𧝞":"䘛","𧞫":"𫌋","𧟀":"𧝧","𧡴":"𫌫","𧢄":"𫌬","𧦝":"𫍞","𧦧":"𫍟","𧩕":"𫍭","𧩙":"𬣥","𧩼":"𫍶","𧫝":"𫍺","𧬤":"𫍼","𧭈":"𫍾","𧭹":"𫍐","𧳟":"𧳕","𧵳":"䞌","𧶔":"𧹓","𧶧":"䞎","𧷎":"𪠀","𧸘":"𫎨","𧹈":"𪥠","𧽯":"𫎸","𨂐":"𫏌","𨄣":"𨀱","𨅍":"𨁴","𨆪":"𫏕","𨇁":"𧿈","𨇞":"𨅫","𨇤":"𫏨","𨇰":"𫏞","𨇽":"𫏑","𨈊":"𨂺","𨈌":"𨄄","𨊰":"䢀","𨊸":"䢁","𨊻":"𨐆","𨋢":"䢂","𨌈":"𫐍","𨍰":"𫐔","𨎌":"𫐋","𨎮":"𨐉","𨏠":"𨐇","𨏥":"𨐊","𨞺":"𫟫","𨟊":"𫟬","𨢿":"𨡙","𨣈":"𨡺","𨣞":"𨟳","𨣧":"𨠨","𨤻":"𨤰","𨥛":"𨱀","𨥟":"𫓫","𨦫":"䦀","𨧀":"𬭊","𨧜":"䦁","𨧰":"𫟽","𨧱":"𨱊","𨨏":"𬭛","𨨛":"𫓼","𨨢":"𫓽","𨩰":"𫟾","𨪕":"𫓮","𨫒":"𨱐","𨬖":"𫔏","𨭆":"𬭶","𨭎":"𬭳","𨭖":"𫔑","𨭸":"𫔐","𨮂":"𨱕","𨮳":"𫔒","𨯅":"䥿","𨯟":"𫔓","𨰃":"𫔉","𨰋":"𫓳","𨰥":"𫔕","𨰲":"𫔃","𨲳":"𫔖","𨳑":"𨸁","𨳕":"𨸀","𨴗":"𨸅","𨴹":"𫔲","𨵩":"𨸆","𨵸":"𨸇","𨶀":"𨸉","𨶏":"𨸊","𨶮":"𨸌","𨶲":"𨸋","𨷲":"𨸎","𨼳":"𫔽","𨽏":"𨸘","𩀨":"𫕚","𩅙":"𫕨","𩎖":"𫖑","𩎢":"𩏾","𩏂":"𫖓","𩏠":"𫖖","𩏪":"𩏽","𩏷":"𫃗","𩑔":"𫖪","𩒎":"𫖭","𩓣":"𩖕","𩓥":"𫖵","𩔑":"𫖷","𩔳":"𫖴","𩖰":"𫠇","𩗀":"𩙦","𩗓":"𫗈","𩗴":"𫗉","𩘀":"𩙩","𩘝":"𩙭","𩘹":"𩙨","𩘺":"𩙬","𩙈":"𩙰","𩚛":"𩟿","𩚥":"𩠀","𩚩":"𫗡","𩚵":"𩠁","𩛆":"𩠂","𩛌":"𫗤","𩛡":"𫗨","𩛩":"𩠃","𩜇":"𩠉","𩜦":"𩠆","𩜵":"𩠊","𩝔":"𩠋","𩝽":"𫗳","𩞄":"𩠎","𩞦":"𩠏","𩞯":"䭪","𩟐":"𩠅","𩟗":"𫗚","𩠴":"𩠠","𩡣":"𩡖","𩡺":"𩧦","𩢡":"𩧬","𩢴":"𩧵","𩢸":"𩧳","𩢾":"𩧮","𩣏":"𩧶","𩣑":"䯃","𩣫":"𩧸","𩣵":"𩧻","𩣺":"𩧼","𩤊":"𩧩","𩤙":"𩨆","𩤲":"𩨉","𩤸":"𩨅","𩥄":"𩨋","𩥇":"𩨍","𩥉":"𩧱","𩥑":"𩨌","𩦠":"𫠌","𩧆":"𩨐","𩭙":"𩬣","𩯁":"𫙂","𩯳":"𩯒","𩰀":"𩬤","𩰹":"𩰰","𩳤":"𩲒","𩴵":"𩴌","𩵦":"𫠏","𩵩":"𩽺","𩵹":"𩽻","𩶁":"𫚎","𩶘":"䲞","𩶰":"𩽿","𩶱":"𩽽","𩷰":"𩾄","𩸃":"𩾅","𩸄":"𫚝","𩸡":"𫚟","𩸦":"𩾆","𩻗":"𫚨","𩻬":"𫚩","𩻮":"𫚘","𩼶":"𫚬","𩽇":"𩾎","𩿅":"𫠖","𩿤":"𫛠","𩿪":"𪉄","𪀖":"𫛧","𪀦":"𪉅","𪀾":"𪉋","𪁈":"𪉉","𪁖":"𪉌","𪂆":"𪉎","𪃍":"𪉐","𪃏":"𪉏","𪃒":"𫛻","𪃧":"𫛹","𪄆":"𪉔","𪄕":"𪉒","𪅂":"𫜂","𪆷":"𫛾","𪇳":"𪉕","𪈼":"𱊜","𪉸":"𫜊","𪋿":"𫧮","𪌭":"𫜓","𪍠":"𫜕","𪓰":"𫜟","𪔵":"𪔭","𪘀":"𪚏","𪘯":"𪚐","𪙏":"𫜯","𪟖":"𠛾","𪷓":"𣶭","𫒡":"𫓷","𫜦":"𫜫"};function De(e,t){var i,r,n,o,s="";if(o=t?Me:Se,"string"!=typeof e)return e;for(i=0;i13312&&n<40899||n>63744&&n<64106)){s+=r;continue}let t=o[r];s+=t||r}return s}var ke=function(e){return De(e,!0)},Ae=function(e){return De(e,!1)};let Ee=["章","节","回","節","卷","部","輯","辑","話","集","话","篇"," "," "],Ie=[],Ne=["CHAPTER","Chapter","序章","前言","声明","写在前面的话","后记","楔子","后序","章节目录","尾声","聲明","寫在前面的話","後記","後序","章節目錄","尾聲"];const Re=e=>e.trim().replace(/(\r\n|\n|\r|\t)/gm,"").substring(0,100).split("").filter((e=>"="!==e&&"-"!==e&&"_"!==e&&"+"!==e)).join(""),Pe=(e,t="")=>t?new RegExp(t).test(e):e&&e.length<40&&!Oe(e)&&(Be(e)||e.startsWith("第")&&Fe(e)||e.startsWith("卷")&&He(e)||e.indexOf("第")>-1&&e.lastIndexOf("第")<7&&Fe(e.substr(e.indexOf("第")))),Oe=e=>Ie.filter((t=>e.indexOf(t)>-1)).length>0,Be=e=>Ne.filter((t=>e.startsWith(t))).length>0,Fe=e=>{let t=!1;for(let i=0;i!(!/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(e.substring(1,e.indexOf(" ")))&&!/^\d+$/.test(e.substring(1,e.indexOf(" "))))||(!(!/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(e.substring(1,e.indexOf(" ")))&&!/^\d+$/.test(e.substring(1,e.indexOf(" "))))||!(!/^[\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\u767e\u5343\u4e07\u842c\u96f6]+$/.test(e.substring(1))&&!/^\d+$/.test(e.substring(1))));let je=!1;const We=e=>Array.from(e.querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,li,dt,dd,pre,blockquote,address,kookitmarker")),$e=(e,t,i,r,n,o,s)=>c(void 0,void 0,void 0,(function*(){let a=Math.floor(e.clientWidth/12),l=a%2==0?a:a-1;const c=e.clientWidth;if("mimical"===t&&"yes"!==s){let e=document.getElementById("book");e&&(e.style.display="block",i>0?o():i<0&&n(),setTimeout((()=>{if(!e)return{};e.style.display="none"}),1e3))}const h=r.body.scrollLeft,d=c+l;if(i>0){const e=Math.round(h/d),i=Math.max(0,e-1)*d;r.body.scrollTo({top:0,left:i,behavior:"sliding"===t&&"yes"!==s?"smooth":"auto"})}else if(i<0){const e=(Math.round(h/d)+1)*d;r.body.scrollTo({top:0,left:e,behavior:"sliding"===t&&"yes"!==s?"smooth":"auto"})}})),Ue=(t,i,r,n)=>{let o=e.findLastIndex(r,(e=>e.href===i||e.href&&e.href.includes("#")&&e.href.includes(i)));return i&&e.findLastIndex(r,(e=>e.href===i||e.href&&e.href.includes("#")&&e.href.includes(i)))>-1||(o=t),"prev"===n?Object.assign(Object.assign({},r[o-1]),{index:o-1}):Object.assign(Object.assign({},r[o+1]),{index:o+1})},qe=(e,t,i,r,n,o,s,a)=>c(void 0,void 0,void 0,(function*(){let t=parseInt(o.chapterDocIndex||"0"),l=o.chapterHref||"";if(0===t)return;let c=Ue(t,l,i,"prev");c&&(o.text="prevChapter",o.page="",yield ze(c.index,c.label,c.href,i,e,r,n,o,s,a))})),ze=(t,i,r,n,o,s,a,l,h,u)=>c(void 0,void 0,void 0,(function*(){if(h.body.innerHTML="",u.height="0px",h.body.scrollTo(0,0),i&&!t||n[t]&&n[t].label&&i&&i!==n[t].label&&-1===r.indexOf("#")){let r=e.findLastIndex(n,{label:i});-1!==r&&(t=r)}if(-1===t&&r.indexOf("#")>-1){let i=r.split("#")[0],o=e.findLastIndex(n,(e=>e.href===i||e.href&&e.href.includes("#")&&e.href.includes(i)));-1!==o&&(t=o)}(-1===t||t>n.length-1)&&(t=0);let f=yield d(n[t].text,!1),p=function(e){const t=e.match(/]*)>/i);if(!t)return{};const i=t[1],r={},n=/([\w-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^>\s]+))/g;let o;for(;null!==(o=n.exec(i));){const e=o[2]||o[3]||o[4]||"";r[o[1]]=e}return r}(f);h.body.innerHTML=f,p.style?h.body.setAttribute("style",h.body.getAttribute("style")||""):p.class?h.body.setAttribute("class",p.class):p.id?h.body.setAttribute("id",p.id):p.class?p.id||h.body.removeAttribute("id"):h.body.removeAttribute("class"),yield Ve(h),l.chapterTitle=i,l.chapterHref=r,l.chapterDocIndex=t+"",l.percentage=n.slice(0,t).map((e=>e.text&&e.text.size||1)).reduce(((e,t)=>e+t),0)/n.map((e=>e.text&&e.text.size||1)).reduce(((e,t)=>e+t),0)+"",l.text="",yield((e,t,i,r,n)=>c(void 0,void 0,void 0,(function*(){if(yield Promise.race([Promise.all(Array.from([...n.images,...n.querySelectorAll("image")]).map((e=>e.complete?Promise.resolve(0!==e.naturalHeight):new Promise((t=>{e.addEventListener("load",(()=>t(!0))),e.addEventListener("error",(()=>t(!1)))}))))),new Promise(((e,t)=>{setTimeout((()=>{e("image load timeout")}),10)}))]),yield v(e,t,i,n),y(n),"scroll"!==t){if(r.height=e.clientHeight+"px","double"===t){let t=Math.floor(e.clientWidth/12),i=t%2==0?t:t-1,r=(e.clientWidth+i)/2;if((n.body.scrollWidth-n.body.clientWidth)/r%2==1){let e=document.createElement("div");e.setAttribute("style","height: "+n.body.clientHeight+"px; display: inline-block; width: "+(r-i)+"px"),n.body.appendChild(e)}}}else r.height=n.body.scrollHeight+"px",r.height=n.body.scrollHeight+300+"px"})))(o,s,a,u,h),yield Xe(o,s,"","","","",h)}));const Ve=e=>c(void 0,void 0,void 0,(function*(){let t=Array.from(e.getElementsByTagName("link"));if(0===t.length)return;let i=[];for(let e=0;e{r.addEventListener("load",e)})))}try{yield Promise.race([Promise.all(i),new Promise(((e,t)=>{setTimeout((()=>{e("css load timeout")}),10)}))])}catch(e){console.error(e)}})),Xe=(e,t,i,r,n,o,s)=>c(void 0,void 0,void 0,(function*(){let a=0,l=s.body;if(o&&"scroll"!==t){let t=Math.floor(e.clientWidth/12),i=t%2==0?t:t-1;a=((c=getComputedStyle(e).width,parseFloat(c.substring(0,c.length-2)))+i)*(parseInt(o)-1)}else if(i){let n=We(s.body).filter(((e,t)=>Re(e.textContent)&&(Re(e.textContent)===Re(i)||Re(e.textContent)===Ae(Re(i))||Re(e.textContent)===ke(Re(i)))&&(Math.abs(t-parseInt(r))<2||"search"===r||"ignore"===r||"next"===r)));if(0===n.length)return;l=Je(n[0],e,t),a=l?h(l.offsetLeft)-h(l.marginLeft||parseFloat(getComputedStyle(l).marginLeft)):"prevChapter"===i?s.body.scrollWidth:0}else if(n&&n.indexOf("#")>-1){let i=CSS.escape(n.split("#").reverse()[0]);if(!s.body.querySelector("#"+i))return;l=Je(s.body.querySelector("#"+i)||s.body,e,t),a=l?h(l.offsetLeft)-h(l.marginLeft||parseFloat(getComputedStyle(l).marginLeft)):0}var c;"scroll"!==t?s.body.scrollTo(a,0):l.scrollIntoView()})),Je=(e,t,i)=>{let r=Math.floor(t.clientWidth/12),n=r%2==0?r:r-1,o=h(e.offsetLeft)-h(e.marginLeft||parseFloat(getComputedStyle(e).marginLeft));return"scroll"===i||"scroll"!==i&&Ge(parseInt(o+""),(t.clientWidth+n)/2)?e:e.parentElement?Je(e.parentElement,t,i):e},Ge=(e,t)=>{for(let i=e-10;i<=e+10;i++)if(i%t==0)return!0;return!1},_e=(e,t,i,r,n,o,s)=>c(void 0,void 0,void 0,(function*(){var a,l;if(je)return;let c=We(o.body),h=c.filter((i=>it(e,i,t)&&(i.textContent||"").trim())),d=h[0];s&&(d=s);let u=0;for(let i=0;ie.text&&e.text.size||1)).reduce(((e,t)=>e+t),0);n.percentage=r.slice(0,parseInt(n.chapterDocIndex)).map((e=>e.text&&e.text.size||1)).reduce(((e,t)=>e+t),0)/e+((null===(a=r.find(((e,t)=>t===parseInt(n.chapterDocIndex))))||void 0===a?void 0:a.text.size)||0)/e*(u/c.length)+""}else n.page=(null===(l=yield m(t,o,e))||void 0===l?void 0:l.currentPage)+"";je=!0,setTimeout((()=>{je=!1}),100)})),Ye=(e,t,i)=>{let r=Math.floor(t.clientWidth/12),n=r%2==0?r:r-1;return Math.abs(e.offsetLeft-Je(e,t,i).offsetLeft)>(t.clientWidth+n)/2},Ze=(t,i,r)=>{let n=r.chapterHref||"",o=n.lastIndexOf("#"),s="";s=-1===o?n:n.substring(0,o);for(let n=0;n-1&&(r.chapterHref=t,r.chapterTitle=i[n].label)}}},Ke=(e,t,i,r,n,o,s,a)=>c(void 0,void 0,void 0,(function*(){let t=parseInt(o.chapterDocIndex||"0"),l=o.chapterHref||"";if(t>=i.length-1)return void(o.percentage="1");let c=Ue(t,l,i,"next");c&&(o.page="",yield ze(c.index,c.label,c.href,i,e,r,n,o,s,a))})),Qe=(e,t,i)=>{let r=We(i.body).filter((e=>!tt(e))),n=r.filter((i=>it(e,i,t)&&(i.textContent||"").trim()));return n=n.filter((e=>{if(!(e.textContent||"").trim())return!1;let t=e.parentElement;for(;t&&t!==i.body;){if(r.includes(t))return!1;t=t.parentElement}return!0})),n.filter((e=>{var t;return"img"!==e.textContent&&!(null===(t=e.textContent)||void 0===t?void 0:t.startsWith("img"))})).map((e=>e.textContent))},et=(e,t,i)=>{if(i.querySelectorAll('span[data-highlight="true"]').forEach((e=>{const t=e.parentNode;t&&t.replaceChild(i.createTextNode(e.textContent||""),e)})),!e.trim())return;let r=Array.from(i.body.querySelectorAll("span, p, div, h1, h2, h3, h4, h5, h6 ")).filter((t=>{const i=t.textContent||"";return i.trim()&&i.indexOf(e)>-1}));if(r.length>0){const n=r=>{var n;if(r.nodeType===Node.TEXT_NODE){const o=r.textContent||"",s=o.indexOf(e);if(s>-1){const a=o.substring(0,s),l=o.substring(s+e.length),c=i.createElement("span");c.setAttribute("style",t),c.setAttribute("data-highlight","true"),c.textContent=e;const h=i.createDocumentFragment();return a&&h.appendChild(i.createTextNode(a)),h.appendChild(c),l&&h.appendChild(i.createTextNode(l)),null===(n=r.parentNode)||void 0===n||n.replaceChild(h,r),!0}}return!1},o=e=>{if(n(e))return!0;const t=Array.from(e.childNodes);for(const e of t)if(o(e))return!0;return!1};for(let e=0;e{var t=e.children;let i=!1;var r=/^(address|kookitmarker|section|blockquote|body|center|dir|div|dl|fieldset|form|h[1-6]|hr|isindex|menu|noframes|noscript|ol|p|pre|table|ul|dd|dt|frameset|li|tbody|td|tfoot|th|thead|tr|html)$/i;if(Array.from(t).filter((e=>r.test(e.nodeName))).length<3)return!1;for(var n=0;n{var r=!1,n=t.getBoundingClientRect();if("scroll"!==i&&t.textContent&&t.textContent.trim()){let t=n.left;r=t>-10&&t<=e.clientWidth}else if("scroll"===i&&t.textContent&&t.textContent.trim()){let t=n.top;r=t>=e.scrollTop&&t<=e.scrollTop+e.clientHeight}else if("scroll"!==i){let t=n.left;r=t>=0&&t<=e.clientWidth}return r};class rt{constructor(){this.callbacks={},this.callbacks.base={}}on(e,t){const i=this;if(void 0===e||""===e)return console.warn("wrong names"),!1;if(void 0===t)return console.warn("wrong callback"),!1;return this.resolveNames(e).forEach((function(e){const r=i.resolveName(e);i.callbacks[r.namespace]instanceof Object||(i.callbacks[r.namespace]={}),i.callbacks[r.namespace][r.value]instanceof Array||(i.callbacks[r.namespace][r.value]=[]),i.callbacks[r.namespace][r.value].push(t)})),this}off(e){const t=this;if(void 0===e||""===e)return console.warn("wrong name"),!1;return this.resolveNames(e).forEach((function(e){const i=t.resolveName(e);if("base"!==i.namespace&&""===i.value)delete t.callbacks[i.namespace];else if("base"===i.namespace)for(const e in t.callbacks)t.callbacks[e]instanceof Object&&t.callbacks[e][i.value]instanceof Array&&(delete t.callbacks[e][i.value],0===Object.keys(t.callbacks[e]).length&&delete t.callbacks[e]);else t.callbacks[i.namespace]instanceof Object&&t.callbacks[i.namespace][i.value]instanceof Array&&(delete t.callbacks[i.namespace][i.value],0===Object.keys(t.callbacks[i.namespace]).length&&delete t.callbacks[i.namespace])})),this}trigger(e,t=[]){if(void 0===e||""===e)return console.warn("wrong name"),!1;const i=this;const r=t instanceof Array?t:[];let n=this.resolveNames(e);n=this.resolveName(n[0]),setTimeout((()=>{if("base"===n.namespace)for(const e in i.callbacks){if(i.callbacks[e]instanceof Object&&i.callbacks[e][n.value]instanceof Array&&i.callbacks[e][n.value])i.callbacks[e][n.value].forEach((function(e){e.apply(i,r)}));else if(this.callbacks[n.namespace]instanceof Object&&i.callbacks[n.namespace][n.value]){if(""===n.value)return console.warn("wrong name"),this;i.callbacks[n.namespace][n.value].forEach((function(e){e.apply(i,r)}))}return null}}),100)}resolveNames(e){let t=e;return t=t.replace(/[^a-zA-Z0-9 ,/.]/g,""),t=t.replace(/[,/]+/g," "),t=t.split(" "),t}resolveName(e){const t={},i=e.split(".");return t.original=e,t.value=i[0],t.namespace="base",i.length>1&&""!==i[1]&&(t.namespace=i[1]),t}}const nt=Node.ELEMENT_NODE,ot=Node.TEXT_NODE,st=Node.CDATA_SECTION_NODE;function at(e,t,i){let r,n,o,s=0,a=0,l=!0;for(n=0;nthis.compare(e,t)))}static compare(e,t){let i=e.get(),r=t.get();if(e.isRange||t.isRange){if(e.isRange&&t.isRange){const e=this.comparePath(i.from,r.from);return e||this.comparePath(i.to,r.to)}return e.isRange&&(i=i.from),t.isRange&&(r=r.from),this.comparePath(i,r)}return this.comparePath(i,r)}static compareParts(e,t){const i=Math.max(e.length,t.length);let r,n,o,s;for(r=0;r=i)return"img"===a.tagName.toLowerCase()&&r?{node:a,offset:r}:{node:a,offset:0}}else{if(l+=1,l===i)return"img"===a.tagName.toLowerCase()&&r?{node:a,offset:r}:{node:a,offset:0};if(l>i)return o?{node:o,offset:this.trueLength(e,o.textContent)}:{node:t,offset:0}}o=a;break;case ot:case st:if(0!==l&&l%2!=0||(l+=1),l===i){const t=this.trueLength(e,a.textContent);if(!(r>=t))return{node:a,offset:r};r-=t}o=a;break;default:continue}if(i>l){const i={relativeToNode:"after",offset:0};return i.node=o||t,this.isTextNode(i.node)&&(i.offset=this.trueLength(e,i.node.textContent.length)),i}}isTextNode(e){return!!e&&(e.nodeType===ot||e.nodeType===st)}correctOffset(e,t,i,r){let n,o=t;if("string"==typeof r?n=this.decodeEntities(e,r):(r.pre=this.decodeEntities(e,r.pre),r.post=this.decodeEntities(e,r.post),n=r.pre+"."+r.post),!this.isTextNode(t))return{node:t,offset:0};for(;this.isTextNode(o.previousSibling);)o=o.previousSibling;const s=o;let a;const l=[];let c="",h=0;for(;this.isTextNode(o)&&(a=this.decodeEntities(e,o.textContent),l[h]=a.length,c+=a,o.nextSibling);)o=o.nextSibling,h++;const d=r.pre?r.pre.length:0,u=function(e,t,i){i=i||0;const r=[];let n,o=0;do{if(n=e.match(t),!n)break;r.push(n.index+i),o+=n.index+n.length,e=e.slice(n.index+n.length)}while(o=l[h];){if(f-=l[h],f<0)return{node:t,offset:i};const e=[];if(!o.nextSibling||h+1>=e.length)return{node:t,offset:i};h++,o=o.nextSibling}return{node:o,offset:f}}resolveNode(e,t,i,r){if(r=Object.assign({},r||{}),!i)throw new Error("Missing DOM argument");let n;if(0===e&&(n=i.querySelector("package")),!n)for(const e of i.childNodes)if(e.nodeType===nt){n=e;break}if(n=i,!n)throw new Error("Document incompatible with CFIs");let o,s,a=n,l=0;for(o=t.length-1;o>=0;o--)if(s=t[o],!r.ignoreIDs&&s.nodeID&&(a=i.getElementById(s.nodeID))){l=o+1;break}a||(a=n);let c={node:a,offset:0};for(o=l;othis.parts.length-2)throw new Error("index is out of bounds");const r=this.parts[e];if(!r)throw new Error("Missing CFI part for index: "+e);let n=this.resolveNode(e,r,t,i).node;const o=n.tagName.toLowerCase();if("itemref"===o&&"spine"===n.parentNode.tagName.toLowerCase()){const e=n.getAttribute("idref");if(!e)throw new Error("Referenced node had not 'idref' attribute");if(n=t.getElementById(e),!n)throw new Error("Specified node is missing from manifest");const i=n.getAttribute("href");if(!i)throw new Error("Manifest item is missing href attribute");return i}if("iframe"===o||"embed"===o){const e=n.getAttribute("src");if(!e)throw new Error(o+" element is missing 'src' attribute");return e}if("object"===o){const e=n.getAttribute("data");if(!e)throw new Error(o+" element is missing 'data' attribute");return e}if("image"===o||"use"===o){const e=n.getAttribute("xlink:href");if(!e)throw new Error(o+" element is missing 'xlink:href' attribute");return e}throw new Error("No URI found")}deepClone(e){return JSON.parse(JSON.stringify(e))}resolveLocation(e,t){const i=t.length-1,r=t[i];if(!r)throw new Error("Missing CFI part for index: "+i);const n=this.resolveNode(i,r,e),o=this.deepClone(r[r.length-1]);return delete o.nodeIndex,o.offset||delete n.offset,Object.assign(Object.assign({},o),n)}resolveLast(e,t){if(t=Object.assign({range:!1},t||{}),!this.isRange)return this.resolveLocation(e,this.parts);if(t.range){const t=e.createRange(),i=this.getFrom();"before"===i.relativeToNode?t.setStartBefore(i.node,i.offset):"after"===i.relativeToNode?t.setStartAfter(i.node,i.offset):t.setStart(i.node,i.offset);const r=this.getTo();return"before"===r.relativeToNode?t.setEndBefore(r.node,r.offset):"after"===r.relativeToNode?t.setEndAfter(r.node,r.offset):t.setEnd(r.node,r.offset),t}return{from:this.resolveLocation(e,this.getFrom()),to:this.resolveLocation(e,this.getTo()),isRange:!0}}resolve(e,t){return this.resolveLast(e,t)}}const dt=["color-0","color-1","color-2","color-3","line-0","line-1","line-2","line-3"],ut=["#FEF3CD","#FBFACC","#CEFACD","#CDE9FA"],ft=["#FF0000","#000080","#0000FF","#2EFF2E"],pt=["#fac106","#ebe702","#0be603","#0493e6"],gt=(e,i,r,n,o,s)=>{var a,l;let c=dt[i],h=s.contentWindow||(null===(a=s.contentDocument)||void 0===a?void 0:a.defaultView),d=e;d=[d];let u=t.getSelection(s);u.restoreCharacterRanges(o,d);let f=u.getRangeAt(0);bt(f,c,r,n,o),h&&h.getSelection()&&(null===(l=h.getSelection())||void 0===l||l.empty())},mt=(e,t,i,r,n,o,s)=>{let a=dt[t],l=s.querySelector(".noteLayer"),c=s.querySelector("#koodoPDFLayer");var h=n.getViewport({scale:o});let d=[];for(let t=0;t{e.heighte.width-t.width)),g=[];for(let e=0;e{if(n===e)return!1;const o=r.bottom;return Math.abs(i-o)=t.right||r.left<=t.left&&Math.abs(r.right-t.right)<5||Math.abs(r.left-t.left)<5&&r.right>=t.right)}))||g.push(t)}for(let e=0;e-1?"background-color: ":"border-bottom: ")+(a.indexOf("color")>-1?pt[a.split("-")[1]]:`2px solid ${ft[a.split("-")[1]]}`)+"; left:"+(t.left+parseFloat(getComputedStyle(c).marginLeft))+"px; top:"+t.top+"px;width:"+t.width+"px; height:"+t.height+"px; z-index: 1; cursor: pointer; opacity: "+(a.indexOf("color")>-1?.3:1)+";"),null==m||m.setAttribute("data-key",i),null==m||m.setAttribute("class","kookit-note"),null==m||m.addEventListener("click",(e=>{e&&e.target&&e.target.dataset&&e.target.dataset.key&&r(e)})),m.ontouchend=e=>{window.isSwiping||(e&&e.target&&e.target.dataset&&e.target.dataset.key&&r(e),e.preventDefault(),e.stopPropagation())},l.appendChild(m))}},yt=e=>{const t=e.querySelectorAll(".kookit-note");for(let e=0;e{const o=e.nativeRange.getClientRects(),s=[],a=Array.from(o).sort(((e,t)=>e.width-t.width)),l=a.length?Math.max(...Array.from(o).map((e=>e.width))):0;for(let e=0;eMath.abs(t.bottom-e.bottom)<5&&t.width===l))||s.push(t)}for(let e=0;e-1?"background-color: ":"border-bottom: ")+(t.indexOf("color")>-1?ut[t.split("-")[1]]+";opacity: 1":`2px solid ${ft[t.split("-")[1]]}`)+";left:"+(Math.min(o.left,o.x)+n.body.scrollLeft)+"px; top:"+(Math.min(o.top,o.y)+n.body.scrollTop)+"px;width:"+o.width+"px; height:"+o.height+"px; z-index:-1;opacity: "+(t.indexOf("color")>-1?.8:1)+"; cursor: pointer;"),c.setAttribute("class"," kookit-note"),c.setAttribute("data-key",i),n.body.appendChild(c);var h=document.createElement("span");null==h||h.setAttribute("style","position: absolute;left:"+(Math.min(o.left,o.x)+n.body.scrollLeft)+"px; top:"+(Math.min(o.top,o.y)+n.body.scrollTop)+"px;width:"+o.width+"px; height:"+o.height+"px; z-index:1;"),h.setAttribute("class"," kookit-note"),h.setAttribute("data-key",i),h.addEventListener("click",(e=>{e&&e.target&&e.target.dataset&&e.target.dataset.key&&r(e)})),h.ontouchend=e=>{window.isSwiping||(e&&e.target&&e.target.dataset&&e.target.dataset.key&&r(e),e.preventDefault(),e.stopPropagation())},n.body.appendChild(h)}};const vt=(e,t,i)=>{!function(e){let t=document.getElementById("book");t&&t.remove();const i=document.createElement("div");i.id="book";const r=document.createElement("canvas");r.id="pageflip-canvas";const n=document.createElement("div");n.id="pages";for(let t=0;t{u+1{u-1>=0&&(m[u-1].target=1,u=Math.max(u-1,0))},mouseDownHandler:C,mouseUpHandler:T,mouseMoveHandler:x}}}function x(e){if(!y)return;const t=e.touches[0],i=t.screenX,r=t.screenY;g.x=i-y.offsetLeft-o/2,g.y=r-y.offsetTop}function C(e){const t=e.touches[0];c=t.screenX,t.screenX=0?m[u-1].dragging=!0:t.screenX>window.screen.width/2&&u+1a/4*1&&h-c>0?(m[i].target=1,u=Math.max(u-1,0)):i===u?m[i].target=1:i===u-1&&(m[i].target=-1)),m[i].dragging=!1}function L(e){var r=1-Math.abs(e.progress),n=.5*a*(1-e.progress),s=a*e.progress+n,c=20*r,h=.5*a*Math.max(Math.min(1-e.progress,.5),0),u=.5*a*Math.max(Math.min(r,.5),0),f=.5*a*Math.max(Math.min(r,.5),0);e.page.style.width=Math.max(s,0)+"px",p.save(),p.translate(0+o/2,d+0),p.strokeStyle=("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+.05*r+")",p.lineWidth=30*r,p.beginPath(),p.moveTo(s-n,.5*-c),p.lineTo(s-n,l+.5*c),p.stroke();var g=p.createLinearGradient(s,0,s+u,0);g.addColorStop(0,("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+.2*r+")"),g.addColorStop(.8,("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+"0.0)"),p.fillStyle=g,p.beginPath(),p.moveTo(s,0),p.lineTo(s+u,0),p.lineTo(s+u,l),p.lineTo(s,l),p.fill();var m=p.createLinearGradient(s-n-f,0,s-n,0);m.addColorStop(0,("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+"0.0)"),m.addColorStop(1,("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+.15*+r+")"),p.fillStyle=m,p.beginPath(),p.moveTo(s-n-f,0),p.lineTo(s-n,0),p.lineTo(s-n,l),p.lineTo(s-n-f,l),p.fill();var y=p.createLinearGradient(s-h,0,s,0);i?(y.addColorStop(.35,i),y.addColorStop(.73,i),y.addColorStop(.9,i),y.addColorStop(1,i)):"no"===t?(y.addColorStop(.35,"#fafafa"),y.addColorStop(.73,"#eeeeee"),y.addColorStop(.9,"#fafafa"),y.addColorStop(1,"#e2e2e2")):(y.addColorStop(.35,"#333"),y.addColorStop(.73,"#444"),y.addColorStop(.9,"#333"),y.addColorStop(1,"#444")),p.fillStyle=y,p.strokeStyle=("no"===t?"rgba(0,0,0,":"rgba(255,255,255,")+"0.06)",p.lineWidth=.5,p.beginPath(),p.moveTo(s,0),p.lineTo(s,l),p.quadraticCurveTo(s,l+2*c,s-n,l+c),p.lineTo(s-n,-c),p.quadraticCurveTo(s,2*-c,s,0),p.fill(),p.stroke(),p.restore()}},wt=(e,t,i,r,n)=>c(void 0,void 0,void 0,(function*(){let{width:o,height:s}=yield i[r].text.getDimension(),a=n.body.clientWidth,l=e.clientHeight,c=Math.min(a/o,l/s);return"scroll"===t&&(c=a/o),c})),xt=(e,t)=>{var i;const r=t.getElementById("pdf-container-"+e);if(!r)return;let n=document.createElement("iframe");n.style.position="absolute",n.style.top="0",n.style.left="0",n.style.width="100%",n.style.height="100%",n.style.border="0",n.style.margin="0",n.style.padding="0",n.style.fontSize="100%",n.style.font="inherit",n.scrolling="no",n.tabIndex=0,n.id="pdf-iframe-"+e;let o=document.createElement("style");return o.id="default-style",o.textContent="p,empty-line{display: inherit;margin-block-start: inherit;margin-block-end: inherit;margin-inline-start: inherit;margin-inline-end: inherit;}body{margin: 0px}",r.appendChild(n),null===(i=n.contentDocument)||void 0===i||i.head.appendChild(o),n},Ct=(e,t,i)=>c(void 0,void 0,void 0,(function*(){let r=i.getElementById("pdf-container-"+e);if(r){if("scroll"!==t){let e=r?h(r.offsetLeft)-h(r.marginLeft||parseFloat(getComputedStyle(r).marginLeft)):0;i.body.scrollTo(e,0)}else r.scrollIntoView();r.scrollIntoView()}})),Tt=(e,t,i,r)=>{var n=!1,o=t.getBoundingClientRect();if("scroll"!==i){let e=o.left;n=e>-10&&e<=r.body.clientWidth}else{let t=o.top,i=o.bottom;n=t-10>=e.scrollTop&&t+10<=e.scrollTop+e.clientHeight||i-10>=e.scrollTop&&i+10<=e.scrollTop+e.clientHeight||t+10<=e.scrollTop&&i-10>=e.scrollTop+e.clientHeight}return n},Lt=(e,t,i)=>{if(i.querySelectorAll('span[data-highlight="true"]').forEach((e=>{const t=(e.getAttribute("style")||"").replace(/background(?:-color)?\s*:[^;]+;?/gi,"").trim();t?e.setAttribute("style",t):e.removeAttribute("style"),e.removeAttribute("data-highlight")})),!e.trim())return;let r=i.querySelectorAll("p,span"),n=Array.from(r).filter(((t,i)=>(t.textContent||"").trim()&&t.textContent===e));n.length>0&&(n[0].setAttribute("style",(n[0].getAttribute("style")||"")+t),n[0].setAttribute("data-highlight","true"))},Mt=(e,t,i,r,n,o,s,a,l)=>c(void 0,void 0,void 0,(function*(){let e=Math.floor(r.body.clientWidth/12),c=e%2==0?e:e-1;const h=r.body.clientWidth;if("mimical"===t&&"yes"!==s){let e=document.getElementById("book");e&&(e.style.display="block",i>0?o():i<0&&n(),setTimeout((()=>{if(!e)return{};e.style.display="none"}),1e3))}if(i>0)if("single"===l){let e=r.querySelector("#pdf-container-"+(a-1));e&&e.scrollIntoView()}else r.body.scrollBy(-(h+c)/2,0);else if(i<0)if("single"===l){let e=r.querySelector("#pdf-container-"+(a+1));e&&e.scrollIntoView()}else r.body.scrollBy((h+c)/2,0)})),St=e=>c(void 0,void 0,void 0,(function*(){const t=1600,i=e.getViewport({scale:1}),r=document.createElement("canvas"),n=r.getContext("2d");r.width=t,r.height=t/i.width*i.height;const o={canvasContext:n,viewport:e.getViewport({scale:t/i.width})};yield e.render(o).promise;const s=r.toDataURL("image/jpeg",1),a=function(e){const t=e.length-23;return Math.ceil(.75*t)}(s);return{imageURL:s,size:a}}));function Dt(e){return c(this,void 0,void 0,(function*(){try{const t=yield fetch(e),i=yield t.blob();return yield new Promise(((e,t)=>{const r=new FileReader;r.onloadend=()=>e(r.result),r.onerror=t,r.readAsDataURL(i)}))}catch(e){throw console.error("转换失败:",e),e}}))}function kt(){return window.visualViewport?window.visualViewport.offsetLeft:window.pageXOffset||document.documentElement.scrollLeft||0}function At(){return window.visualViewport?window.visualViewport.offsetTop:window.pageYOffset||document.documentElement.scrollTop||0}const Et=(e,t,i)=>c(void 0,void 0,void 0,(function*(){const r=e.target;if(!r)return;const n=It(r);if(n){e.preventDefault(),e.stopPropagation();let r=n.getAttribute("href");if(r&&r.startsWith("kindle:")){let e=i.resolveChapter(r);if(e)return yield i.goToChapter(e.index,e.href,e.label),!0;r="#"+(yield i.resolveHref(r)).id}let o="";if(r&&r.indexOf("#")>-1){let n=r.split("#").reverse()[0],s=t.body.querySelector("#"+n);if(!s){if(r.indexOf("filepos")>-1){let e=i.resolveChapter(r);return yield i.goToChapter(e.index,e.href,e.label),!0}if(0!==r.indexOf("#")){for(;r.startsWith(".");)r=r.substring(1);let e=i.resolveChapter(r);e&&(yield i.goToChapter(e.index,e.href,e.label))}if(s=t.body.querySelector("#"+CSS.escape(n)),!s)return!1;yield i.goToNode(t.body.querySelector("#"+CSS.escape(n)))}if((s.textContent.trim()===e.target.textContent.trim()||!s.textContent.trim())&&s.parentElement){if("BODY"===s.parentElement.tagName)return!1;s=s.parentElement}o=s.textContent}else if(r){let e=i.resolveChapter(r);e&&(yield i.goToChapter(e.index,e.href,e.label))}return window.ReactNativeWebView.postMessage(JSON.stringify({event:"link-clicked",href:r,footnote:o})),!1}}));function It(e){if("A"===e.tagName)return e;let t=e;for(;t&&"BODY"!==t.tagName;){if("A"===t.tagName)return t;t=t.parentElement}return null}function Nt(e,t,i){const r=3*t+e+1;return i.layout.A.area.includes(r)?i.touchControlA:i.layout.B.area.includes(r)?i.touchControlB:i.layout.C.area.includes(r)?i.touchControlC:"right"}const Rt=(e,i,r,n,o,s,a,l)=>{var h;let d=i.contentWindow||(null===(h=i.contentDocument)||void 0===h?void 0:h.defaultView),u=l.getDocument(),f=0,p=0,g=0,m=0;const y=30;let b=Math.floor(r.clientWidth/12),v=b%2==0?b:b-1,w=r.clientWidth+v,x=null,C=!1,T=0;e.addEventListener("touchend",(function(t){window.isSwiping=!1,window.isTouchNavigation=!0,x&&clearTimeout(x),x=setTimeout((()=>{window.isTouchNavigation=!1,x=null}),4e3);let i=(new Date).getTime();if(i-m<=300)return void t.preventDefault();m=i;const c=t.changedTouches[0],h=Date.now();let b=c.screenX,T=c.screenY;const M=h-f,S=b-p,D=T-g;if(C&&"mimical"===o&&"scroll"!==n)return C=!1,l.mouseUpHandler(t),c.screenXwindow.screen.width/4*1&&b-p>0&&(l.prev(),C=!1),void setTimeout((()=>{let e=document.getElementById("book");e&&(e.style.display="none")}),400);if(C&&"sliding"===o&&"scroll"!==n){let R="PDF"===s?u:e;if(window.scrollAnimationId&&cancelAnimationFrame(window.scrollAnimationId),Math.abs(R.body.scrollWidth-R.body.scrollLeft-r.clientWidth)<10)return L&&clearTimeout(L),void(L=setTimeout((()=>{l.next(),C=!1}),300));if(0===R.body.scrollLeft)return L&&clearTimeout(L),void(L=setTimeout((()=>{l.prev(),C=!1}),300));R.body.style.transform="";let P,O=R.body.scrollLeft;const B=Math.round(O/w),F=Math.abs(S)/window.screen.width,H=.1;P=S>0&&F>H?(B-1)*w:S<0&&F>H?(B+1)*w:B*w,P=Math.max(0,Math.min(P,R.body.scrollWidth-w)),R.body.scrollWidth-P1-Math.pow(1-e,3);function z(e){const t=e-j;if(t>=U)return R.body.scrollLeft=P,R.body.style.willChange="auto",l.record(),void(C=!1);const i=q(t/U),r=W+$*i;R.body.scrollLeft=r,window.scrollAnimationId=requestAnimationFrame(z)}window.scrollAnimationId=requestAnimationFrame(z)}else{var k=d.getSelection().toString(),A=Math.abs(S)>=y||Math.abs(D)>=y;if(!k||"PDF"===s&&("PDF"!==s||A)){if(M>500){const V=t.target;if(!V)return;if("IMG"===V.tagName||"image"===V.tagName){const X=V.src||V.getAttribute("xlink:href");return void(X.startsWith("blob:")&&Dt(X).then((e=>{window.ReactNativeWebView.postMessage(JSON.stringify({event:"view-image",imgSrc:e}))})))}}if(M<500&&Math.abs(S)=y||Math.abs(D)>=y)&&(window.ReactNativeWebView.postMessage(JSON.stringify({event:"swipe"})),"scroll"===n&&Math.abs(r.scrollHeight-r.scrollTop-r.clientHeight)<10&&window.ReactNativeWebView.postMessage(JSON.stringify({event:"scroll-bottom"})),"scroll"===n&&0===r.scrollTop&&window.ReactNativeWebView.postMessage(JSON.stringify({event:"scroll-top"})))}else window.ReactNativeWebView.postMessage(JSON.stringify({event:"select-text-after-touch",selectedText:k}))}}),!1),e.addEventListener("touchstart",(function(e){f=Date.now();const t=e.target;if(!t)return;if(It(t))return;e.touches.length>1&&e.preventDefault();const i=e.touches[0];p=i.screenX,g=i.screenY}),!1),e.addEventListener("touchmove",(function(t){if(!C&&Math.abs(t.touches[0].screenX-p)<=10)return;if(t.preventDefault(),window.visualViewport.scale>1&&"PDF"===s)return void t.preventDefault();const i=t.touches[0],r=i.screenX,a=i.screenY,c=r-p,h=a-g;if((Math.abs(c)>10||Math.abs(h)>10)&&(window.isSwiping=!0),!C&&Math.abs(c)>Math.abs(h)&&Math.abs(c)>10){if(C=!0,T=r,e.body.style.transform="translateZ(0)","mimical"===o&&"scroll"!==n){let e=document.getElementById("book");e&&(e.style.display="block",l.mouseDownHandler(t))}}else if(C&&"mimical"===o&&"scroll"!==n&&l.mouseMoveHandler(t),C&&"sliding"===o&&"scroll"!==n){let t="PDF"===s?u:e;const i=r-T,n=t.body.scrollLeft;t.body.scrollLeft=n-i,T=r,requestAnimationFrame((()=>{}))}}),!1),e.addEventListener("click",(t=>{Et(t,e,l)}),!0);let L=null,M=0,S=0,D=e=>c(void 0,void 0,void 0,(function*(){var i,n;const o=d.getSelection().toString().trim();if(o){var a=d.getSelection().getRangeAt(0);let u=l.getPageSize();var c=a.getBoundingClientRect();if("PDF"===s){let e=a.getClientRects();if(e.length>0){e=Array.from(e).filter((e=>Math.abs(e.height-u.sectionHeight)>10&&Math.abs(e.width-u.sectionWidth)>10&&e.height>0&&e.width>0));let t=1/0,i=1/0,r=-1/0,n=-1/0;for(let o=0;o{1===S&&D(e)}),600):D(e),e.preventDefault(),e.stopPropagation(),!1;{const e=t.src||t.getAttribute("xlink:href");e.startsWith("blob:")&&Dt(e).then((e=>{window.ReactNativeWebView.postMessage(JSON.stringify({event:"view-image",imgSrc:e}))}))}}};let k=0,A=0,E=0;e.addEventListener("selectstart",(t=>{S=0,M=Date.now(),A=kt(),E=At(),"scroll"!==n&&(k=e.body.scrollLeft)}),!1);let I=0;e.addEventListener("selectionchange",(t=>{if(!d.getSelection().toString().trim())return;k>0&&(e.body.scrollLeft=k),S++;const i=Date.now();i-I>=3e3&&(I=i,window.ReactNativeWebView.postMessage(JSON.stringify({event:"selection-change"})))}),!1)},Pt=(e,i,r,n,o,s,a,l)=>{var h;let d=i.contentWindow||(null===(h=i.contentDocument)||void 0===h?void 0:h.defaultView),u=l.getDocument(),f=0,p=0,g=0,m=0;let y=Math.floor(r.clientWidth/12),b=y%2==0?y:y-1,v=null,w=!1,x=0;e.addEventListener("touchend",(function(i){var h,y;return c(this,void 0,void 0,(function*(){window.isSwiping=!1,window.isTouchNavigation=!0,v&&clearTimeout(v),v=setTimeout((()=>{window.isTouchNavigation=!1,v=null}),4e3);let c=(new Date).getTime();if(c-m<=300)return void i.preventDefault();m=c;const x=i.changedTouches[0],T=Date.now(),L=x.screenX,M=x.screenY,S=T-f,D=L-p,k=M-g;if(w&&"mimical"===o&&"scroll"!==n)return w=!1,l.mouseUpHandler(i),Lwindow.screen.width/4*1&&L-p>0&&(l.prev(),w=!1),void setTimeout((()=>{let e=document.getElementById("book");e&&(e.style.display="none")}),400);if(w&&"sliding"===o&&"scroll"!==n){let R="PDF"===s?u:e;if(window.scrollAnimationId&&cancelAnimationFrame(window.scrollAnimationId),Math.abs(R.body.scrollWidth-R.body.scrollLeft-r.clientWidth)<10)return C&&clearTimeout(C),void(C=setTimeout((()=>{l.next(),w=!1}),300));if(0===R.body.scrollLeft)return C&&clearTimeout(C),void(C=setTimeout((()=>{l.prev(),w=!1}),300));R.body.style.transform="";let P,O=r.clientWidth+b,B=R.body.scrollLeft;const F=Math.round(B/O),H=Math.abs(D)/window.screen.width,j=.1;P=D>0&&H>j?(F-1)*O:D<0&&H>j?(F+1)*O:F*O,P=Math.max(0,Math.min(P,R.body.scrollWidth-O)),R.body.scrollWidth-P1-Math.pow(1-e,3);function V(e){const t=e-W;if(t>=q)return R.body.scrollLeft=P,R.body.style.willChange="auto",l.record(),void(w=!1);const i=z(t/q),r=$+U*i;R.body.scrollLeft=r,window.scrollAnimationId=requestAnimationFrame(V)}return void(window.scrollAnimationId=requestAnimationFrame(V))}const A=d.getSelection().toString().trim();if(A){var E=d.getSelection().getRangeAt(0).getBoundingClientRect(),I=l.getPageSize(),N={top:E.top-r.scrollTop,left:E.left,width:E.width,height:E.height,screenWidth:window.innerWidth,screenHeight:window.innerHeight,sectionHeight:I.sectionHeight,sectionWidth:I.sectionWidth,gap:I.gap,scale:window.visualViewport.scale,offsetLeft:kt(),offsetTop:At()};t.init();let X=null;if("PDF"===s){let J=i.target.ownerDocument,G=null===(h=null==J?void 0:J.defaultView)||void 0===h?void 0:h.frameElement,_=(null==G?void 0:G.getAttribute("id"))||"",Y=_?parseInt(_.split("-").reverse()[0]):0;N.chapterDocIndex=Y,X=yield l.getHightlightCoords(Y);let Z=G.parentElement;Z&&(N.top=N.top+parseFloat(getComputedStyle(Z).top))}else X=yield l.getHightlightCoords();window.ReactNativeWebView.postMessage(JSON.stringify({event:"select-text",selectedText:A,position:N,range:X}))}else{if(S>500){const K=i.target;if(!K)return;if("IMG"===K.tagName||"image"===K.tagName){const Q=K.src||K.getAttribute("xlink:href");return void(Q.startsWith("blob:")&&Dt(Q).then((e=>{window.ReactNativeWebView.postMessage(JSON.stringify({event:"view-image",imgSrc:e}))})))}}if(S<500&&Math.abs(D)<30&&Math.abs(k)<30){const ee=document.documentElement.clientWidth,te=document.documentElement.clientHeight;let ie=Math.min(Math.max(L,0),ee),re=Math.min(Math.max(M,0),te);if("PDF"===s&&"double"===n){let ae=i.target.ownerDocument,le=null===(y=null==ae?void 0:ae.defaultView)||void 0===y?void 0:y.frameElement,ce=(null==le?void 0:le.getAttribute("id"))||"";(ce?parseInt(ce.split("-").reverse()[0]):0)%2==1&&(ie+=ee/2)}const ne=ee/3,oe=te/3;let se=Nt(Math.min(Math.floor(ie/ne),2),Math.min(Math.floor(re/oe),2),a);window.ReactNativeWebView.postMessage(JSON.stringify({event:se}))}else(Math.abs(D)>=30||Math.abs(k)>=30)&&(window.ReactNativeWebView.postMessage(JSON.stringify({event:"swipe"})),"scroll"===n&&Math.abs(r.scrollHeight-r.scrollTop-r.clientHeight)<10&&window.ReactNativeWebView.postMessage(JSON.stringify({event:"scroll-bottom"})),"scroll"===n&&0===r.scrollTop&&window.ReactNativeWebView.postMessage(JSON.stringify({event:"scroll-top"})))}}))}),{passive:!1}),e.addEventListener("touchstart",(function(e){const t=e.target;if(!t)return;if(It(t))return;const i=e.touches[0];f=Date.now(),p=i.screenX,g=i.screenY}),{passive:!1}),e.addEventListener("touchmove",(function(t){const i=d.getSelection().toString().trim();if(!w&&Math.abs(t.touches[0].screenX-p)<=10||i)return;if(window.visualViewport.scale>1&&"PDF"===s)return;const r=t.touches[0],a=r.screenX,c=r.screenY,h=a-p,f=c-g;if(!w&&Math.abs(h)>Math.abs(f)&&Math.abs(h)>10){if(w=!0,x=a,"mimical"===o&&"scroll"!==n){window.isSwiping=!0;let e=document.getElementById("book");e&&(e.style.display="block",l.mouseDownHandler(t))}}else if(w&&"mimical"===o&&"scroll"!==n&&l.mouseMoveHandler(t),w&&"sliding"===o&&"scroll"!==n){window.isSwiping=!0;let t="PDF"===s?u:e;const i=a-x,r=t.body.scrollLeft;t.body.scrollLeft=r-i,x=a,requestAnimationFrame((()=>{}))}}),{passive:!1}),e.addEventListener("click",(t=>{Et(t,e,l)}),!0);let C=null;e.body.oncontextmenu=function(e){return e.preventDefault(),e.stopPropagation(),!1};let T=0;e.addEventListener("selectionchange",(e=>{if(!d.getSelection().toString().trim())return;const t=Date.now();t-T>=3e3&&(T=t,window.ReactNativeWebView.postMessage(JSON.stringify({event:"selection-change"})))}),{passive:!1})};class Ot extends rt{constructor(e){super(),this.tranformText=()=>{let e=this.getDocument();e&&("Simplified To Traditional"===this.convertChinese?e.querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker").forEach((e=>{e.innerHTML=e.innerHTML.split("").map((e=>ke(e))).join("")})):"Traditional To Simplified"===this.convertChinese&&e.querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker").forEach((e=>{e.innerHTML=e.innerHTML.split("").map((e=>Ae(e))).join("")})),"yes"===this.isIndent&&e.querySelectorAll("h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address").forEach((e=>{for(let t of e.childNodes){if(t.nodeType===Node.TEXT_NODE){(t.nodeValue||"").match(/^(\s+)/)&&e.setAttribute("style",(e.getAttribute("style")||"")+"text-indent: 0em !important;");break}if(t.nodeType===Node.ELEMENT_NODE&&"img"===t.tagName.toLowerCase()){e.setAttribute("style",(e.getAttribute("style")||"")+"text-indent: 0em !important;");break}}})))},this.addPageAnimation=e=>{if("mimical"===this.animation){let t=this.getProgress();if(!t)return;const i=vt(t.totalPage,this.isDarkMode,e);i&&(this.flipToNextPage=i.flipToNextPage,this.flipToPrevPage=i.flipToPrevPage,this.mouseDownHandler=i.mouseDownHandler,this.mouseUpHandler=i.mouseUpHandler,this.mouseMoveHandler=i.mouseMoveHandler)}},this.readerMode=e.readerMode,this.animation=e.animation,this.format=e.format,this.convertChinese=e.convertChinese,this.isIndent=e.isIndent,this.isDarkMode=e.isDarkMode,this.isMobile=e.isMobile,this.chapterList=[],this.chapterDocList=[],this.flattenChapters=[],this.book="",this.element="",this.tempLocation={},this.flipToNextPage=()=>{},this.flipToPrevPage=()=>{},this.mouseDownHandler=()=>{},this.mouseUpHandler=()=>{},this.mouseMoveHandler=e=>{},this.touchEventSet={},"yes"===this.isMobile&&(console.log=function(...e){window.ReactNativeWebView.postMessage(e.map((e=>String(e))).join(", "))},console.info=function(...e){window.ReactNativeWebView.postMessage(e.map((e=>String(e))).join(", "))},console.error=function(...e){window.ReactNativeWebView.postMessage(e.map((e=>String(e))).join(", "))})}getPageSize(){let e="double"===this.readerMode?2:1,t=Math.floor(this.element.clientWidth/12),i=t%2==0?t:t-1,r=this.getIframe();if(!r)return;let n=null==r?void 0:r.getBoundingClientRect().height;return{width:this.element.clientWidth,height:this.element.clientHeight,left:this.element.offsetLeft,top:this.element.offsetTop,scrollTop:this.element.scrollTop,sectionWidth:(this.element.clientWidth-i)/e,sectionHeight:n,gap:i}}scrollToText(e){let t=this.getDocument();if(!t)return;let i=We(t.body).filter((e=>!tt(e))).filter((t=>(t.textContent||"").indexOf(e)>-1));if(i.length>0){let e=i[0],r=e?h(e.offsetLeft)-h(e.marginLeft||parseFloat(getComputedStyle(e).marginLeft)):0,n=e?h(e.offsetTop)-h(e.marginTop||parseFloat(getComputedStyle(e).marginTop)):0;"scroll"!==this.readerMode?t.body.scrollTo(r,0):this.element.scrollTo(0,n)}}goToPage(e){return c(this,void 0,void 0,(function*(){if("scroll"===this.readerMode){e<0&&(e=1);let t=(e-1)*(this.element.clientHeight-50);this.element.scrollTo(0,t)}else{let t=this.getDocument();if(!t)return;let i=Math.floor(this.element.clientWidth/12),r=i%2==0?i:i-1;const n=this.element.clientWidth+r;"double"===this.readerMode?e=(e%2==0?e-2:e-1)/2:e-=1,e<0&&(e=0);const o=e*n;t.body.scrollTo({top:0,left:o,behavior:"sliding"===this.animation&&"yes"!==this.isMobile?"smooth":"auto"})}yield this.record()}))}resolveChapter(e){let t=e,i=-1;for(let e=0;e-1)return this.flattenChapters[i];{let r=e.split("#")[0];for(let e=0;e-1)return this.flattenChapters[i];for(let e=0;e-1?{label:"",href:"",index:i}:null}}flatChapter(e){let t=[];for(let i=0;i0?(t.push(e[i]),t=t.concat(this.flatChapter(e[i].subitems))):t.push(e[i]);return this.flattenChapters=t,t}getChapter(){return this.chapterList}getChapterDoc(){return this.chapterDocList}goToPercentage(e){return c(this,void 0,void 0,(function*(){if(this.flattenChapters.length>0){let t=1===e?this.flattenChapters.length-1:Math.floor(this.flattenChapters.length*e);yield this.goToChapter(this.flattenChapters[t].index.toString(),this.flattenChapters[t].href,this.flattenChapters[t].label)}}))}goToChapterIndex(e){return c(this,void 0,void 0,(function*(){this.flattenChapters.length>0&&(yield this.goToChapter(this.flattenChapters[e].index,this.flattenChapters[e].href,this.flattenChapters[e].label))}))}goToChapterDocIndex(e){return c(this,void 0,void 0,(function*(){this.chapterDocList.length>0&&(yield this.goToChapter(e,this.chapterDocList[e].href,this.chapterDocList[e].label))}))}goToChapter(e,t,i){return c(this,void 0,void 0,(function*(){let r=this.getDocument(),n=this.getIframe();r&&n&&(yield ze(parseInt(e),i,t,this.chapterDocList,this.element,this.readerMode,this.format,this.tempLocation,r,n),t&&t.indexOf("#")>-1&&(yield Xe(this.element,this.readerMode,"","",t,"",r)),yield this.record(),this.trigger("rendered"))}))}goToPosition(e){return c(this,void 0,void 0,(function*(){let i=this.getDocument(),r=this.getIframe();if(!i||!r)return;let n=JSON.parse(e);this.tempLocation={text:n.text,chapterTitle:n.chapterTitle,chapterDocIndex:n.chapterDocIndex,chapterHref:n.chapterHref,count:n.count,page:n.page,percentage:n.percentage};let{text:o,chapterTitle:s,chapterDocIndex:a,chapterHref:l,count:c,page:h,cfi:d}=n;if(yield ze(parseInt(a),s,l,this.chapterDocList,this.element,this.readerMode,this.format,this.tempLocation,i,r),d){const e=new ht(d,{});let t=this.getDocument();if(!t)return;const{node:i,offset:r}=e.resolve(t,{});if(i){let e=null,t=i;for(;t;){const i=t;if(i.tagName&&"h1,h2,h3,h4,h5,h6,p,div,ul,dl,ol,pre,li,dt,dd,blockquote,address,kookitmarker".indexOf(i.tagName.toLowerCase())>-1){e=i;break}t=t.parentNode}e&&(c="ignore",o=e.textContent)}}yield Xe(this.element,this.readerMode,o,c,"",h,i),t.init(),yield this.record(),this.trigger("rendered")}))}getDocument(){let e=document.getElementById("page-area");if(!e)return null;let t=e.getElementsByTagName("iframe")[0];if(!t)return null;let i=t.contentDocument;return i||null}getIframe(){let e=document.getElementById("page-area");if(!e)return null;let t=e.getElementsByTagName("iframe")[0];return t||null}goToNode(e){return c(this,void 0,void 0,(function*(){let t=this.getDocument();if(!t)return;let i=Je(e,this.element,this.readerMode),r=i?h(i.offsetLeft)-h(i.marginLeft||parseFloat(getComputedStyle(i).marginLeft)):0,n=i?h(i.offsetTop)-h(i.marginTop||parseFloat(getComputedStyle(i).marginTop)):0;"scroll"!==this.readerMode?t.body.scrollTo(r,0):this.element.scrollTo(0,n),yield this.record(),this.trigger("rendered")}))}removeContent(){this.element.innerHTML=""}prev(){return c(this,void 0,void 0,(function*(){let e=this.getDocument(),t=this.getIframe();if(e&&t){if("scroll"===this.readerMode&&0===h(this.element.scrollTop)||"scroll"!==this.readerMode&&0===h(e.body.scrollLeft)){if("0"===this.tempLocation.chapterDocIndex)return;"mimical"===this.animation&&"scroll"!==this.readerMode&&"yes"===this.isMobile&&(yield new Promise((e=>setTimeout(e,500)))),yield qe(this.element,this.flatChapter(this.chapterList),this.chapterDocList,this.readerMode,this.format,this.tempLocation,e,t),parseInt(this.tempLocation.chapterDocIndex||"-1")>-1&&("scroll"===this.readerMode?this.element.scrollTo(0,e.body.scrollHeight):e.body.scrollTo(e.body.scrollWidth,0)),this.trigger("rendered")}else"scroll"===this.readerMode?this.element.scrollBy({left:0,top:-(this.element.clientHeight-50),behavior:"smooth"}):yield $e(this.element,this.animation,1,e,this.flipToNextPage,this.flipToPrevPage,this.isMobile);yield this.record()}}))}next(){return c(this,void 0,void 0,(function*(){let e=this.getDocument(),t=this.getIframe();e&&t&&(Math.abs(e.body.scrollWidth-h(e.body.scrollLeft)-e.body.clientWidth)<50&&"scroll"!==this.readerMode||Math.abs(this.element.scrollHeight-h(this.element.scrollTop)-this.element.clientHeight)<20&&"scroll"===this.readerMode?("mimical"===this.animation&&"scroll"!==this.readerMode&&"yes"===this.isMobile&&(yield new Promise((e=>setTimeout(e,500)))),yield Ke(this.element,this.flatChapter(this.chapterList),this.chapterDocList,this.readerMode,this.format,this.tempLocation,e,t),this.trigger("rendered")):"scroll"===this.readerMode?Math.abs(this.element.scrollHeight-h(this.element.scrollTop)-this.element.clientHeight)-(this.element.clientHeight-50)<20&&Math.abs(this.element.scrollHeight-h(this.element.scrollTop)-this.element.clientHeight)>20?this.element.scrollTo({left:0,top:this.element.scrollHeight-20,behavior:"smooth"}):this.element.scrollBy({left:0,top:this.element.clientHeight-50,behavior:"smooth"}):yield $e(this.element,this.animation,-1,e,this.flipToNextPage,this.flipToPrevPage,this.isMobile),yield this.record())}))}prevChapter(){return c(this,void 0,void 0,(function*(){let e=this.getDocument(),t=this.getIframe();e&&t&&(yield qe(this.element,this.flatChapter(this.chapterList),this.chapterDocList,this.readerMode,this.format,this.tempLocation,e,t),yield this.record(),this.trigger("rendered"))}))}nextChapter(){return c(this,void 0,void 0,(function*(){let e=this.getDocument(),t=this.getIframe();e&&t&&(yield Ke(this.element,this.flatChapter(this.chapterList),this.chapterDocList,this.readerMode,this.format,this.tempLocation,e,t),yield this.record(),this.trigger("rendered"))}))}visibleText(){return c(this,void 0,void 0,(function*(){let e=this.getDocument();return e?Qe(this.element,this.readerMode,e):""}))}audioText(){return c(this,void 0,void 0,(function*(){let e=this.getDocument();return e?((e,t,i)=>{let r=We(i.body).filter((e=>!tt(e))),n=r.filter((e=>{if(!(e.textContent||"").trim())return!1;let t=e.parentElement;for(;t&&t!==i.body;){if(r.includes(t))return!1;t=t.parentElement}return!0})).filter((e=>{var t;return"img"!==e.textContent&&!(null===(t=e.textContent)||void 0===t?void 0:t.startsWith("img"))})).map((e=>e.textContent)),o=0,s=Qe(e,t,i);if(s&&s.length>0){let e=s[0];o=n.indexOf(e)}return n.slice(o)})(this.element,this.readerMode,e):""}))}chapterText(){return c(this,void 0,void 0,(function*(){let e=this.getDocument();return e&&e.body.textContent||""}))}autoScroll(e,t){if(!this.getDocument())return;if(this.scrollTimer&&(cancelAnimationFrame(this.scrollTimer),this.scrollTimer=null),this.recordTimer&&(clearInterval(this.recordTimer),this.recordTimer=null),"no"===t||"scroll"!==this.readerMode)return;let i=0,r=0;const n=()=>{if(i+=e,r++,Math.abs(e)<1){if((Math.abs(i)>=.5||r%Math.max(1,Math.floor(30/Math.abs(e)))==0)&&Math.abs(i)>=.1){const e=Math.round(10*i)/10;this.element.scrollBy({left:0,top:e,behavior:"auto"}),i=0,r=0}}else if(Math.abs(i)>=1){const e=Math.floor(i);this.element.scrollBy({left:0,top:e,behavior:"auto"}),i-=e}this.scrollTimer=requestAnimationFrame(n)};this.scrollTimer=requestAnimationFrame(n),this.recordTimer=setInterval((()=>{"scroll"===this.readerMode&&Math.abs(this.element.scrollHeight-this.element.scrollTop-this.element.clientHeight)<10&&this.nextChapter(),this.record()}),3e3)}autoScrollIOS(e,t){let i=this.getDocument();if(!i)return;if(this.scrollTimer&&(clearInterval(this.scrollTimer),this.scrollTimer=null),this.recordTimer&&(clearInterval(this.recordTimer),this.recordTimer=null),"no"===t||"scroll"!==this.readerMode)return;let r=0,n=this.element.scrollTop;this.scrollTimer=setInterval((()=>{r+=e,i&&(i.body.style.transform=`translateY(-${r}px)`,Math.abs(r)>=50&&(i.body.style.transform="translateY(0px)",n+=r,this.element.scrollTo({left:0,top:n,behavior:"auto"}),r=0))}),30),this.recordTimer=setInterval((()=>{"scroll"===this.readerMode&&Math.abs(this.element.scrollHeight-this.element.scrollTop-this.element.clientHeight)<10&&this.nextChapter(),this.record()}),3e3)}highlightSearchNode(e,t){let i=this.getDocument();i&&et(e,t,i)}highlightAudioNode(e,t){let i=this.getDocument();i&&((e,t,i,r,n)=>{if(i.querySelectorAll('span[data-highlight="true"]').forEach((e=>{const t=e.parentNode;t&&t.replaceChild(i.createTextNode(e.textContent||""),e)})),!e.trim())return;let o=We(i.body).filter((e=>it(r,e,n)&&(e.textContent||"").trim())).filter((t=>{const i=t.textContent||"";return i.trim()&&i.indexOf(e)>-1}));if(o.length>0){const r=r=>{var n;if(r.nodeType===Node.TEXT_NODE){const o=r.textContent||"",s=o.indexOf(e);if(s>-1){const a=o.substring(0,s),l=o.substring(s+e.length),c=i.createElement("span");c.setAttribute("style",t),c.setAttribute("data-highlight","true"),c.textContent=e;const h=i.createDocumentFragment();return a&&h.appendChild(i.createTextNode(a)),h.appendChild(c),l&&h.appendChild(i.createTextNode(l)),null===(n=r.parentNode)||void 0===n||n.replaceChild(h,r),!0}}return!1},n=e=>{if(r(e))return!0;const t=Array.from(e.childNodes);for(const e of t)if(n(e))return!0;return!1};n(o[0])}})(e,t,i,this.element,this.readerMode)}doSearch(t){return c(this,void 0,void 0,(function*(){return"PDF"===this.format?yield((t,i)=>c(void 0,void 0,void 0,(function*(){let r=[];for(let e=0;e{n.str.indexOf(t)>-1&&r.push({excerpt:n.str,cfi:JSON.stringify({text:n.str+"#"+e+"#"+o,chapterTitle:i[e].label,chapterDocIndex:e,chapterHref:i[e].href,count:"search",percentage:e/i.length,keyword:t})})}));return e.uniq(r,"excerpt")})))(t,this.chapterDocList):yield((t,i)=>c(void 0,void 0,void 0,(function*(){var r;let n=[];for(let e=0;e!tt(e)));for(let o=0;o-1&&n.push({excerpt:(null===(r=s[o].textContent)||void 0===r?void 0:r.substring(a-100,a+100))||"",cfi:JSON.stringify({text:s[o].textContent,chapterTitle:i[e].label,chapterDocIndex:e,chapterHref:i[e].href,count:"search",percentage:e/i.length,keyword:t})})}}return e.uniq(n,"excerpt")})))(t,this.chapterDocList)}))}getProgress(){let e=this.getDocument();if(e)return m(this.readerMode,e,this.element)}record(){return c(this,void 0,void 0,(function*(){""!==this.animation&&(yield new Promise((e=>setTimeout(e,1e3))));let e=this.getDocument();e&&(yield _e(this.element,this.readerMode,this.flatChapter(this.chapterList),this.chapterDocList,this.tempLocation,e,null),this.trigger("page-changed"))}))}getPosition(){return this.tempLocation}getNotePosition(){return c(this,void 0,void 0,(function*(){let e=this.getDocument();if(!e)return;let t=x(e);return t?(yield _e(this.element,this.readerMode,this.flatChapter(this.chapterList),this.chapterDocList,this.tempLocation,e,t),this.tempLocation):void 0}))}setStyle(e){let t=this.getDocument();if(t){var i=document.createElement("style");i.innerHTML=e,t.head.appendChild(i)}}getHightlightCoords(){return c(this,void 0,void 0,(function*(){let e=this.getDocument(),i=this.getIframe();if(e&&i)return t.getSelection(i).saveCharacterRanges(e.body)[0]}))}renderHighlighters(e,t){return c(this,void 0,void 0,(function*(){let i=this.getDocument(),r=this.getIframe();if(i&&r){yt(i);for(let n=0;nsetTimeout(e,5))),gt(JSON.parse(o.range),o.color,o.key,t,i,r)}catch(e){return void console.error(e,"Exception has been caught when restore character ranges.")}}}}))}removeOneNote(e,t){let i=this.getDocument();if(!i)return;const r=i.querySelectorAll(".kookit-note");for(let t=0;t{let t=e.contentDocument;t&&i.push(t)})),[e,...i]}getAllIframes(){let e=this.getIframe();if(!e)return[];if("PDF"!==this.format)return[e];let t=this.getDocument();if(!t)return[];let i=t.querySelectorAll("iframe"),r=[];return i.forEach((e=>{let t=e;r.push(t)})),[e,...r]}addTouchEvent(e,t){let i=this.getAllDocuments(),r=this.getAllIframes();for(let n=0;n{var t;null===(t=e.parentNode)||void 0===t||t.removeChild(e)}));let i=this.getIframe();if(!i)return;let r=window.charRange;r&&t.getSelection(i).restoreCharacterRanges(e,[r])}}const Bt={svg:"image/svg+xml",png:"image/png",jpg:"image/jpeg",jpeg:"image/jpeg",gif:"image/gif",webp:"image/webp",zip:"application/zip",rar:"application/x-rar-compressed","7z":"application/x-7z-compressed",tar:"application/x-tar",html:"text/html",htm:"text/html",xml:"text/xml",xhtml:"application/xhtml+xml",css:"text/css"},Ft={"image/svg+xml":"svg","image/png":"png","image/jpeg":"jpg","image/gif":"gif","image/webp":"webp","application/zip":"zip","application/x-rar-compressed":"rar","application/x-7z-compressed":"7z","application/x-tar":"tar","text/html":"html","text/xml":"xml","application/xhtml+xml":"xhtml","text/css":"css"},Ht=t=>c(void 0,void 0,void 0,(function*(){let r=yield i.loadAsync(t);var n=r.file("toc.json");let o=[];n&&(o=JSON.parse(yield n.async("string")));var s=r.file("sections.json");let a=[];s&&(a=JSON.parse(yield s.async("string")));const l={getCover:()=>""};return l.sections=a.map(((e,t)=>({id:e.href,load:()=>(e=>c(void 0,void 0,void 0,(function*(){var t=r.file("chapters/"+e+".html");let i="";return t&&(i=yield t.async("string")),URL.createObjectURL(new Blob([i],{type:"text/html"}))})))(t),unload:()=>{},loadAsset:e=>(e=>c(void 0,void 0,void 0,(function*(){var t=r.file(e);let i;return t&&(i=yield t.async("arraybuffer")),URL.createObjectURL(new Blob([i],{type:Bt[e.split(".").reverse()[0]]}))})))(e)}))),l.toc=o.map((e=>({label:e.label,href:e.href,subitems:e.subitems}))),l.rendition={layout:"pre-paginated"},l.resolveHref=t=>({index:e.findLastIndex(a,{href:t})}),l.splitTOCHref=e=>[e,null],l.getTOCFragment=e=>e.documentElement,l})),jt=e=>new Promise(((t,r)=>c(void 0,void 0,void 0,(function*(){let r=new T(e),n=yield r.getChapter(e.toc),o=yield r.getChapterDoc(),s=n,a=o.map((e=>({href:e.href,label:e.label}))),l=yield Promise.all(o.map((e=>c(void 0,void 0,void 0,(function*(){let t="";if(e.text&&e.text.load){let i=yield fetch(yield e.text.load()).then((e=>e.blob()));t=yield i.text()}return t}))))),h=new i;h.file("toc.json",JSON.stringify(s)),h.file("sections.json",JSON.stringify(a));let d=[];for(let e=0;ee.blob()));r.file(t+"."+Ft[o.type],o);let s="imgs/"+e+"/"+t+"."+Ft[o.type];i[t].src=s,i[t].getAttribute("xlink:href")&&i[t].setAttribute("xlink:href",s)}catch(e){console.error(e)}}let r=Array.from(t.getElementsByTagName("link"));for(let t=0;te.blob()));n.file(t+"."+Ft[r.type],r),i.href="css/"+e+"/"+t+"."+Ft[r.type]}catch(e){console.error(e)}}d.push(t.documentElement.innerHTML)}let f=h.folder("chapters");if(f){for(let e=0;ec(void 0,void 0,void 0,(function*(){t(yield new Response(e).arrayBuffer())})))).catch((e=>{t("err")}))}}))));class Wt extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"EPUB"})),this.epubBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}parse(){return c(this,void 0,void 0,(function*(){let e=new Blob([this.epubBuffer]),t=new File([e],"book",{lastModified:(new Date).getTime(),type:e.type});try{const e=yield this.makeZipLoader(t);this.book=yield new Le(e).init()}catch(e){throw console.error(e),e}}))}preCache(){return c(this,void 0,void 0,(function*(){try{return this.book||(yield this.parse()),yield jt(this.book)}catch(e){return""}}))}makeZipLoader(e){return c(this,void 0,void 0,(function*(){let t=yield i.loadAsync(e);const r=t.files;return{entries:Object.values(r).map((e=>({filename:e.name}))),loadText:e=>c(this,void 0,void 0,(function*(){let i=t.file(e);return i?i.async("string"):""})),loadBlob:e=>c(this,void 0,void 0,(function*(){let i=t.file(e);if(i){let e=yield i.async("arraybuffer");return new Blob([e])}return new Blob([new ArrayBuffer(0)])})),getSize:e=>{let i=t.file(e);if(i)return i._data.uncompressedSize||0}}}))}getMetadata(){return c(this,void 0,void 0,(function*(){try{this.book||(yield this.parse());let e=new T(this.book);return yield e.getMetadata()}catch(e){throw console.error(e,"error"),e}}))}}const $t=e=>{if(!e)return"";const t=document.createElement("textarea");return t.innerHTML=e,t.value},Ut={XML:"application/xml",XHTML:"application/xhtml+xml",HTML:"text/html",CSS:"text/css",SVG:"image/svg+xml"},qt={name:[0,32,"string"],type:[60,4,"string"],creator:[64,4,"string"],numRecords:[76,2,"uint"]},zt={compression:[0,2,"uint"],numTextRecords:[8,2,"uint"],recordSize:[10,2,"uint"],encryption:[12,2,"uint"]},Vt={magic:[16,4,"string"],length:[20,4,"uint"],type:[24,4,"uint"],encoding:[28,4,"uint"],uid:[32,4,"uint"],version:[36,4,"uint"],titleOffset:[84,4,"uint"],titleLength:[88,4,"uint"],localeRegion:[94,1,"uint"],localeLanguage:[95,1,"uint"],resourceStart:[108,4,"uint"],huffcdic:[112,4,"uint"],numHuffcdic:[116,4,"uint"],exthFlag:[128,4,"uint"],trailingFlags:[240,4,"uint"],indx:[244,4,"uint"]},Xt={resourceStart:[108,4,"uint"],fdst:[192,4,"uint"],numFdst:[196,4,"uint"],frag:[248,4,"uint"],skel:[252,4,"uint"],guide:[260,4,"uint"]},Jt={magic:[0,4,"string"],length:[4,4,"uint"],count:[8,4,"uint"]},Gt={magic:[0,4,"string"],length:[4,4,"uint"],type:[8,4,"uint"],idxt:[20,4,"uint"],numRecords:[24,4,"uint"],encoding:[28,4,"uint"],language:[32,4,"uint"],total:[36,4,"uint"],ordt:[40,4,"uint"],ligt:[44,4,"uint"],numLigt:[48,4,"uint"],numCncx:[52,4,"uint"]},_t={magic:[0,4,"string"],length:[4,4,"uint"],numControlBytes:[8,4,"uint"]},Yt={magic:[0,4,"string"],offset1:[8,4,"uint"],offset2:[12,4,"uint"]},Zt={magic:[0,4,"string"],length:[4,4,"uint"],numEntries:[8,4,"uint"],codeLength:[12,4,"uint"]},Kt={magic:[0,4,"string"],numEntries:[8,4,"uint"]},Qt={flags:[8,4,"uint"],dataStart:[12,4,"uint"],keyLength:[16,4,"uint"],keyStart:[20,4,"uint"]},ei={1252:"windows-1252",65001:"utf-8"},ti={100:["creator","string",!0],101:["publisher"],103:["description"],104:["isbn"],105:["subject","string",!0],106:["date"],108:["contributor","string",!0],109:["rights"],110:["subjectCode","string",!0],112:["source","string",!0],113:["asin"],121:["boundary","uint"],122:["fixedLayout"],125:["numResources","uint"],126:["originalResolution"],127:["zeroGutter"],128:["zeroMargin"],129:["coverURI"],132:["regionMagnification"],201:["coverOffset","uint"],202:["thumbnailOffset","uint"],503:["title"],524:["language","string",!0],527:["pageProgressionDirection"]},ii={1:["ar","ar-SA","ar-IQ","ar-EG","ar-LY","ar-DZ","ar-MA","ar-TN","ar-OM","ar-YE","ar-SY","ar-JO","ar-LB","ar-KW","ar-AE","ar-BH","ar-QA"],2:["bg"],3:["ca"],4:["zh","zh-TW","zh-CN","zh-HK","zh-SG"],5:["cs"],6:["da"],7:["de","de-DE","de-CH","de-AT","de-LU","de-LI"],8:["el"],9:["en","en-US","en-GB","en-AU","en-CA","en-NZ","en-IE","en-ZA","en-JM",null,"en-BZ","en-TT","en-ZW","en-PH"],10:["es","es-ES","es-MX",null,"es-GT","es-CR","es-PA","es-DO","es-VE","es-CO","es-PE","es-AR","es-EC","es-CL","es-UY","es-PY","es-BO","es-SV","es-HN","es-NI","es-PR"],11:["fi"],12:["fr","fr-FR","fr-BE","fr-CA","fr-CH","fr-LU","fr-MC"],13:["he"],14:["hu"],15:["is"],16:["it","it-IT","it-CH"],17:["ja"],18:["ko"],19:["nl","nl-NL","nl-BE"],20:["no","nb","nn"],21:["pl"],22:["pt","pt-BR","pt-PT"],23:["rm"],24:["ro"],25:["ru"],26:["hr",null,"sr"],27:["sk"],28:["sq"],29:["sv","sv-SE","sv-FI"],30:["th"],31:["tr"],32:["ur"],33:["id"],34:["uk"],35:["be"],36:["sl"],37:["et"],38:["lv"],39:["lt"],41:["fa"],42:["vi"],43:["hy"],44:["az"],45:["eu"],46:["hsb"],47:["mk"],48:["st"],49:["ts"],50:["tn"],52:["xh"],53:["zu"],54:["af"],55:["ka"],56:["fo"],57:["hi"],58:["mt"],59:["se"],62:["ms"],63:["kk"],65:["sw"],67:["uz",null,"uz-UZ"],68:["tt"],69:["bn"],70:["pa"],71:["gu"],72:["or"],73:["ta"],74:["te"],75:["kn"],76:["ml"],77:["as"],78:["mr"],79:["sa"],82:["cy","cy-GB"],83:["gl","gl-ES"],87:["kok"],97:["ne"],98:["fy"]},ri=(e,t)=>{const i=new e.constructor(e.length+t.length);return i.set(e),i.set(t,e.length),i},ni=(e,t,i)=>{const r=new e.constructor(e.length+t.length+i.length);return r.set(e),r.set(t,e.length),r.set(i,e.length+t.length),r},oi=new TextDecoder,si=e=>oi.decode(e),ai=e=>{if(!e)return;const t=e.byteLength,i=4===t?"getUint32":2===t?"getUint16":"getUint8";return new DataView(e)[i](0)},li=(e,t)=>Object.fromEntries(Array.from(Object.entries(e)).map((([e,[i,r,n]])=>[e,("string"===n?si:ai)(t.slice(i,i+r))]))),ci=e=>new TextDecoder(ei[e]),hi=(e,t=0)=>{let i=0,r=0;for(const n of e.subarray(t,t+4))if(i=i<<7|(127&n)>>>0,r++,128&n)break;return{value:i,length:r}},di=e=>{let t=0;for(const i of e.subarray(-4))128&i&&(t=0),t=t<<7|127&i;return t},ui=e=>{let t=0;for(;e>0;e>>=1)1&~e||t++;return t},fi=e=>{let t=0;for(;!(1&e);)e>>=1,t++;return t},pi=e=>{let t=[];for(let i=0;i>>3,s=3+(7&n);for(let e=0;e{const i=t+32,r=i>>3;let n=0n;for(let i=t>>3;i<=r;i++)n=n<<8n|BigInt(e[i]??0);return n>>8n-BigInt(7&i)&0xffffffffn},mi=async(e,t)=>{const i=await t(e),r=li(Gt,i);if("INDX"!==r.magic)throw new Error("Invalid INDX record");const n=ci(r.encoding),o=i.slice(r.length),s=li(_t,o);if("TAGX"!==s.magic)throw new Error("Invalid TAGX section");const a=(s.length-12)/4,l=Array.from({length:a},((e,t)=>new Uint8Array(o.slice(12+4*t,12+4*t+4)))),c={};let h=0;for(let i=0;i1){const{value:i,length:r}=hi(n,p);h.push([e,null,i,t]),p+=r}else h.push([e,1,null,t]);else h.push([e,a>>fi(i),null,t])}const g={};for(const[e,t,i,r]of h){const o=[];if(null!=t)for(let e=0;eai(i.slice(8*t,8*t+4)))).map(((e,t,i)=>[e,i[t+1]]))}loadRecord(e){const t=this.#h[e];if(!t)throw new RangeError("Record index out of bounds");return this.#c.slice(...t).arrayBuffer()}async loadMagic(e){const t=this.#h[e][0];return si(await this.#c.slice(t,t+4).arrayBuffer())}}class bi extends yi{#d=0;#u;#f;#p;#g;#m;constructor({unzlib:e}){super(),this.unzlib=e}async open(e){await super.open(e),this.headers=this.#y(await super.loadRecord(0)),this.#u=this.headers.mobi.resourceStart;let t=this.headers.mobi.version>=8;if(!t){const e=this.headers.exth?.boundary;if(e<4294967295)try{this.headers=this.#y(await super.loadRecord(e)),this.#d=e,t=!0}catch(e){console.warn(e),console.warn("Failed to open KF8; falling back to MOBI")}}return await this.#b(),t?new ki(this).init():new xi(this).init()}#y(e){const t=li(zt,e),i=li(Vt,e);if("MOBI"!==i.magic)throw new Error("Missing MOBI header");const{titleOffset:r,titleLength:n,localeLanguage:o,localeRegion:s}=i;i.title=e.slice(r,r+n);const a=ii[o];i.language=a?.[s>>2]??a?.[0];const l=64&i.exthFlag?((e,t)=>{const{magic:i,count:r}=li(Jt,e);if("EXTH"!==i)throw new Error("Invalid EXTH header");const n=ci(t),o={};let s=12;for(let t=0;t=8?li(Xt,e):null}}async#b(){const{palmdoc:e,mobi:t}=this.headers;this.#f=ci(t.encoding),this.#p=new TextEncoder;const{compression:i}=e;if(this.#g=1===i?e=>e:2===i?pi:17480===i?await(async(e,t)=>{const i=await t(e.huffcdic),{magic:r,offset1:n,offset2:o}=li(Yt,i);if("HUFF"!==r)throw new Error("Invalid HUFF record");const s=Array.from({length:256},((e,t)=>n+4*t)).map((e=>ai(i.slice(e,e+4)))).map((e=>[128&e,31&e,e>>>8])),a=[null].concat(Array.from({length:32},((e,t)=>o+8*t)).map((e=>[ai(i.slice(e,e+4)),ai(i.slice(e+4,e+8))]))),l=[];for(let i=1;i{let t=new Uint8Array;const i=8*e.byteLength;for(let r=0;r>>24];if(!o){for(;n>>>32-hi)break;const u=d-(n>>>32-h);let[f,p]=l[u];p||(f=c(f),l[u]=[f,!0]),t=ri(t,f)}return t};return c})(t,this.loadRecord.bind(this)):null,!this.#g)throw new Error("Unknown compression type");const{trailingFlags:r}=t,n=1&r,o=ui(r>>>1);this.#m=e=>{for(let t=0;tnew Uint8Array(e))).then(this.#m).then(this.#g)}async loadResource(e){const t=await super.loadRecord(this.#u+e),i=si(t.slice(0,4));return"FONT"===i?(async(e,t)=>{const{flags:i,dataStart:r,keyLength:n,keyStart:o}=li(Qt,e),s=new Uint8Array(e.slice(r));if(2&i){const t=16===n?1024:1040,i=new Uint8Array(e.slice(o,o+n)),r=Math.min(t,s.length);for(var a=0;a{const{table:i,cncx:r}=await mi(e,t),n=i.map((({tagMap:e},t)=>({index:t,offset:e[1]?.[0],size:e[2]?.[0],label:r[e[3]]??"",headingLevel:e[4]?.[0],pos:e[6],parent:e[21]?.[0],firstChild:e[22]?.[0],lastChild:e[23]?.[0]}))),o=e=>(null==e.firstChild||(e.children=n.filter((t=>t.parent===e.index)).map(o)),e);return n.filter((e=>0===e.headingLevel)).map(o)})(e,this.loadRecord.bind(this))}getMetadata(){const{mobi:e,exth:t}=this.headers;return{identifier:e.uid.toString(),title:$t(t?.title||this.decode(e.title)),author:t?.creator?.map($t),publisher:$t(t?.publisher),language:t?.language??e.language,published:t?.date,description:$t(t?.description),subject:t?.subject?.map($t),rights:$t(t?.rights)}}async getCover(){const{exth:e}=this.headers,t=e?.coverOffset<4294967295?e?.coverOffset:e?.thumbnailOffset<4294967295?e?.thumbnailOffset:null;if(null!=t){const e=await this.loadResource(t);return new Blob([e])}}}const vi=/<\s*(?:mbp:)?pagebreak[^>]*>/gi,wi=/<[^<>]+filepos=['"]{0,1}(\d+)[^<>]*>/gi;class xi{parser=new DOMParser;serializer=new XMLSerializer;#v=new Map;#w=new Map;#r=new Map;#x;#C=[];#T=Ut.HTML;constructor(e){this.mobi=e}async init(){let e=new Uint8Array;for(let t=0;tString.fromCharCode(e))).join("");this.#x=[0].concat(Array.from(t.matchAll(vi),(e=>e.index))).map(((e,i,r)=>t.slice(e,r[i+1]))).map((e=>Uint8Array.from(e,(e=>e.charCodeAt(0))))).map((e=>({book:this,raw:e}))).reduce(((e,t)=>{const i=e[e.length-1];return t.start=i?.end??0,t.end=t.start+t.raw.byteLength,e.concat(t)}),[]),this.sections=this.#x.map(((e,t)=>({id:t,load:()=>this.loadSection(e),createDocument:()=>this.createDocument(e),resolveHref:e=>this.resolveHref(e),size:e.end-e.start})));try{this.landmarks=await this.getGuide();const e=this.landmarks.find((({type:e})=>e?.includes("toc")))?.href;if(e){const{index:t}=this.resolveHref(e),i=await this.sections[t].createDocument();let r,n=0,o=0;const s=new Map,a=new Map;this.toc=Array.from(i.querySelectorAll("a[filepos]")).reduce(((e,t)=>{const i=(e=>{let t=0;for(;e;){const i=e.parentElement;if(i){const e=i.tagName.toLowerCase();"p"===e?t+=1.5:"blockquote"===e&&(t+=2)}e=i}return t})(t),l={label:t.innerText?.trim(),href:`#filepos${t.getAttribute("filepos")}`},c=i>o?n+1:i===o?n:s.get(i)??Math.max(0,n-1);if(c>n)r?(r.subitems??=[],r.subitems.push(l),a.set(c,r)):e.push(l);else{const t=a.get(c);t?t.subitems.push(l):e.push(l)}return r=l,n=c,o=i,s.set(i,c),e}),[])}}catch(e){console.warn(e)}return this.#C=[...new Set(Array.from(t.matchAll(wi),(e=>e[1])))].map((e=>({filepos:e,number:Number(e)}))).sort(((e,t)=>e.number-t.number)),this.metadata=this.mobi.getMetadata(),this.getCover=this.mobi.getCover.bind(this.mobi),this}async getGuide(){const e=await this.createDocument(this.#x[0]);return Array.from(e.getElementsByTagName("reference"),(e=>({label:e.getAttribute("title"),type:e.getAttribute("type")?.split(/\s/),href:`#filepos${e.getAttribute("filepos")}`})))}async loadResource(e){if(this.#v.has(e))return this.#v.get(e);const t=await this.mobi.loadResource(e),i=URL.createObjectURL(new Blob([t]));return this.#v.set(e,i),i}async loadRecindex(e){return this.loadResource(Number(e)-1)}async replaceResources(e){for(const t of e.querySelectorAll("img[recindex]")){const e=t.getAttribute("recindex");try{t.src=await this.loadRecindex(e)}catch(t){console.warn(`Failed to load image ${e}`)}}for(const t of e.querySelectorAll("[mediarecindex]")){const e=t.getAttribute("mediarecindex"),i=t.getAttribute("recindex");try{t.src=await this.loadRecindex(e),i&&(t.poster=await this.loadRecindex(i))}catch(t){console.warn(`Failed to load media ${e}`)}}for(const t of e.querySelectorAll("[filepos]")){const e=t.getAttribute("filepos");t.href=`#filepos${e}`}}async loadText(e){if(this.#w.has(e))return this.#w.get(e);const{raw:t}=e,i=this.#C.filter((({number:t})=>t>=e.start&&t({...t,offset:t.number-e.start})));let r=t;i.length&&(r=t.subarray(0,i[0].offset),i.forEach((({filepos:e,offset:n},o)=>{const s=i[o+1],a=this.mobi.encode(``);r=ni(r,a,t.subarray(n,s?.offset))})));const n=this.mobi.decode(r).replaceAll(vi,"");return this.#w.set(e,n),n}async createDocument(e){const t=await this.loadText(e);return this.parser.parseFromString(t,this.#T)}async loadSection(e){if(this.#r.has(e))return this.#r.get(e);const t=await this.createDocument(e),i=t.createElement("style");t.head.append(i),i.append(t.createTextNode("blockquote {\n margin-block-start: 0;\n margin-block-end: 0;\n margin-inline-start: 1em;\n margin-inline-end: 0;\n }")),await this.replaceResources(t);const r=this.serializer.serializeToString(t),n=URL.createObjectURL(new Blob([r],{type:this.#T}));return this.#r.set(e,n),n}resolveHref(e){const t=e.match(/#filepos(.*)/)[1],i=Number(t);return{index:this.#x.findIndex((e=>e.end>i)),anchor:e=>e.getElementById(`filepos${t}`)}}splitTOCHref(e){const t=e.match(/#filepos(.*)/)[1],i=Number(t);return[this.#x.findIndex((e=>e.end>i)),`filepos${t}`]}getTOCFragment(e,t){return e.getElementById(t)}isExternal(e){return/^(?!blob|filepos)\w+:/i.test(e)}destroy(){for(const e of this.#v.values())URL.revokeObjectURL(e);for(const e of this.#r.values())URL.revokeObjectURL(e)}}const Ci=/kindle:(flow|embed):(\w+)(?:\?mime=(\w+\/[-+.\w]+))?/,Ti=/kindle:pos:fid:(\w+):off:(\w+)/,Li=e=>{const[t,i]=e.match(Ti).slice(1);return{fid:parseInt(t,32),off:parseInt(i,32)}},Mi=(e=0,t=0)=>`kindle:pos:fid:${e.toString(32).toUpperCase().padStart(4,"0")}:off:${t.toString(32).toUpperCase().padStart(10,"0")}`,Si=e=>{const t=e.match(/\s(id|name|aid)\s*=\s*['"]([^'"]*)['"]/i);if(!t)return;const[,i,r]=t;return`[${i}="${CSS.escape(r)}"]`},Di=e=>{for(const t of e){if("page-spread-left"===t||"rendition:page-spread-left"===t)return"left";if("page-spread-right"===t||"rendition:page-spread-right"===t)return"right";if("rendition:page-spread-center"===t)return"center"}};class ki{parser=new DOMParser;serializer=new XMLSerializer;#r=new Map;#L=new Map;#M=new Map;#S={};#x;#D;#k=new Uint8Array;#A=new Uint8Array;#E=-1;#I=-1;#T=Ut.XHTML;#N=new Map;constructor(e){this.mobi=e}async init(){const e=this.mobi.loadRecord.bind(this.mobi),{kf8:t}=this.mobi.headers;try{const i=await e(t.fdst),r=li(Kt,i);if("FDST"!==r.magic)throw new Error("Missing FDST record");const n=Array.from({length:r.numEntries},((e,t)=>12+8*t)).map((e=>[ai(i.slice(e,e+4)),ai(i.slice(e+4,e+8))]));this.#S.fdstTable=n,this.#D=n[n.length-1][1]}catch{}const i=(await mi(t.skel,e)).table.map((({name:e,tagMap:t},i)=>({index:i,name:e,numFrag:t[1][0],offset:t[6][0],length:t[6][1]}))),r=await mi(t.frag,e),n=r.table.map((({name:e,tagMap:t})=>({insertOffset:parseInt(e),selector:r.cncx[t[2][0]],index:t[4][0],offset:t[6][0],length:t[6][1]})));this.#S.skelTable=i,this.#S.fragTable=n,this.#x=i.reduce(((e,t)=>{const i=e[e.length-1],r=i?.fragEnd??0,o=r+t.numFrag,s=n.slice(r,o),a=t.length+s.map((e=>e.length)).reduce(((e,t)=>e+t)),l=(i?.totalLength??0)+a;return e.concat({skel:t,frags:s,fragEnd:o,length:a,totalLength:l})}),[]);const o=await this.getResourcesByMagic(["RESC","PAGE"]),s=new Map;if(o.RESC){const e=await this.mobi.loadRecord(o.RESC),t=this.mobi.decode(e.slice(16)).replace(/\0/g,""),i=t.search(/\?>/),r=`${t.slice(i)}`,n=this.parser.parseFromString(r,Ut.XML);for(const e of n.querySelectorAll("spine > itemref")){const t=parseInt(e.getAttribute("skelid"));s.set(t,Di(e.getAttribute("properties")?.split(" ")??[]))}}this.sections=this.#x.map(((e,t)=>e.frags.length?{id:t,load:()=>this.loadSection(e),createDocument:()=>this.createDocument(e),resolveHref:e=>this.resolveHref(e),size:e.length,pageSpread:s.get(t)}:{linear:"no"}));try{const e=await this.mobi.getNCX(),t=({label:e,pos:i,children:r})=>{const[n,o]=i,s=Mi(n,o),a=this.#L.get(n);return a?a.push(o):this.#L.set(n,[o]),{label:$t(e),href:s,subitems:r?.map(t)}};this.toc=e?.map(t),this.landmarks=await this.getGuide()}catch(e){console.warn(e)}const{exth:a}=this.mobi.headers;return this.dir=a.pageProgressionDirection,this.rendition={layout:"true"===a.fixedLayout?"pre-paginated":"reflowable",viewport:Object.fromEntries(a.originalResolution?.split("x")?.slice(0,2)?.map(((e,t)=>[t?"height":"width",e]))??[])},this.metadata=this.mobi.getMetadata(),this.getCover=this.mobi.getCover.bind(this.mobi),this}async getResourcesByMagic(e){const t={},i=this.mobi.headers.kf8.resourceStart,r=this.mobi.pdb.numRecords;for(let n=i;ne===i));r&&(t[r]=n)}catch{}return t}async getGuide(){const e=this.mobi.headers.kf8.guide;if(e<4294967295){const t=this.mobi.loadRecord.bind(this.mobi),{table:i,cncx:r}=await mi(e,t);return i.map((({name:e,tagMap:t})=>({label:r[t[1][0]]??"",type:e?.split(/\s/),href:Mi(t[6]?.[0]??t[3]?.[0])})))}}async loadResourceBlob(e){let{resourceType:t,id:i,type:r}=(e=>{const[t,i,r]=e.match(Ci).slice(1);return{resourceType:t,id:parseInt(i,32),type:r}})(e);"image/jpg"===r&&(r="image/jpeg");const n="flow"===t?await this.loadFlow(i):await this.mobi.loadResource(i-1),o=[Ut.XHTML,Ut.HTML,Ut.CSS,Ut.SVG].includes(r)?await this.replaceResources(this.mobi.decode(n)):n,s=r===Ut.SVG?this.parser.parseFromString(o,r):null;return[new Blob([o],{type:r}),s?.getElementsByTagNameNS("http://www.w3.org/2000/svg","image")?.length?s.documentElement:null]}async loadResource(e){if(this.#r.has(e))return this.#r.get(e);const[t,i]=await this.loadResourceBlob(e),r=i?e:URL.createObjectURL(t);return i&&this.#N.set(r,i),this.#r.set(e,r),r}replaceResources(e){return(async(e,t,i)=>{const r=[];e.replace(t,((...e)=>(r.push(e),null)));const n=[];for(const e of r)n.push(await i(...e));return e.replace(t,(()=>n.shift()))})(e,new RegExp(Ci,"g"),this.loadResource.bind(this))}async loadRaw(e,t){const i=t-this.#k.length,r=null==this.#D?1/0:this.#D-this.#A.length-e;if(i<0||ie;){const e=this.mobi.headers.palmdoc.numTextRecords-1-++this.#I,t=await this.mobi.loadText(e);this.#A=ri(t,this.#A)}const n=this.#D-this.#A.length;return this.#A.slice(e-n,t-n)}loadFlow(e){if(e<4294967295)return this.loadRaw(...this.#S.fdstTable[e])}async loadText(e){const{skel:t,frags:i,length:r}=e,n=await this.loadRaw(t.offset,t.offset+r);let o=n.slice(0,t.length);for(const e of i){const i=e.insertOffset-t.offset,r=t.length+e.offset,s=n.slice(r,r+e.length);o=ni(o.slice(0,i),s,o.slice(i));const a=this.#L.get(e.index);if(a)for(const t of a){const i=this.mobi.decode(s).slice(t),r=Si(i);this.#R(e.index,t,r)}}return this.mobi.decode(o)}async createDocument(e){const t=await this.loadText(e);return this.parser.parseFromString(t,this.#T)}async loadSection(e){if(this.#r.has(e))return this.#r.get(e);const t=await this.loadText(e),i=await this.replaceResources(t);let r=this.parser.parseFromString(i,this.#T);r.querySelector("parsererror")&&(this.#T=Ut.HTML,r=this.parser.parseFromString(i,this.#T));for(const[e,t]of this.#N)for(const i of r.querySelectorAll(`img[src="${e}"]`))i.replaceWith(t);const n=URL.createObjectURL(new Blob([this.serializer.serializeToString(r)],{type:this.#T}));return this.#r.set(e,n),n}getIndexByFID(e){return this.#x.findIndex((t=>t.frags.some((t=>t.index===e))))}#R(e,t,i){const r=this.#M.get(e);if(r)r.set(t,i);else{const r=new Map;this.#M.set(e,r),r.set(t,i)}}async resolveHref(e){const{fid:t,off:i}=Li(e),r=this.getIndexByFID(t);if(r<0)return;const n=this.#M.get(t)?.get(i);if(n)return{index:r,anchor:e=>e.querySelector(n)};const{skel:o,frags:s}=this.#x[r],a=s.find((e=>e.index===t)),l=o.offset+o.length+a.offset,c=await this.loadRaw(l,l+a.length),h=this.mobi.decode(c.slice(i)),d=Si(h);this.#R(t,i,d);return{index:r,anchor:e=>e.querySelector(d)}}splitTOCHref(e){const t=Li(e);return[this.getIndexByFID(t.fid),t]}getTOCFragment(e,{fid:t,off:i}){const r=this.#M.get(t)?.get(i);return e.querySelector(r)}isExternal(e){return/^(?!blob|kindle)\w+:/i.test(e)}destroy(){for(const e of this.#r.values())URL.revokeObjectURL(e)}}class Ai extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"MOBI"})),this.mobiBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}resolveHref(e){return c(this,void 0,void 0,(function*(){let t=this.tempLocation.chapterDocIndex,i=this.chapterDocList[t];if(i){let r=yield i.text.resolveHref(e);if(!r)return{};if(r.index===parseInt(t)){let e=this.getDocument();if(!e)return r;let t=r.anchor(e);if(!t)return r;let i=t.getAttribute("id")||"";return Object.assign(Object.assign({},r),{id:i})}return r}return{}}))}parse(){return c(this,void 0,void 0,(function*(){try{let e=new Blob([this.mobiBuffer]),t=new File([e],"book",{lastModified:(new Date).getTime(),type:e.type});(yield(async e=>"BOOKMOBI"===si(await e.slice(60,68).arrayBuffer()))(t))&&(this.book=yield new bi({unzlib:r}).open(t))}catch(e){throw console.error(e),e}}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}getMetadata(){return c(this,void 0,void 0,(function*(){try{this.book||(yield this.parse());let e=new T(this.book);return yield e.getMetadata()}catch(e){throw console.error(e),e}}))}}const Ei=e=>`${Ri()?".":""}/lib/pdfjs/${e}`,Ii=window.pdfjsLib,Ni=async e=>await(await fetch(e)).text(),Ri=()=>"undefined"!=typeof window&&"object"==typeof window.process&&"renderer"===window.process.type||(!("undefined"==typeof process||"object"!=typeof process.versions||!process.versions.electron)||"object"==typeof navigator&&"string"==typeof navigator.userAgent&&navigator.userAgent.indexOf("Electron")>=0);function Pi(e,t="",i=""){return new Promise((r=>{vex.dialog.prompt({message:e,placeholder:t,value:i,callback:function(e){r(e)}})}))}const Oi=async()=>await Ni(Ei("text_layer_builder.css")),Bi=async()=>await Ni(Ei("annotation_layer_builder.css")),Fi=async(e,t)=>{const i=e.getViewport({scale:1});if(t){const t=document.createElement("canvas");t.height=i.height,t.width=i.width;const r=t.getContext("2d");return await e.render({canvasContext:r,viewport:i}).promise,new Promise((e=>t.toBlob(e)))}return URL.createObjectURL(new Blob([`\n \n \n \n \n \n
\n
\n
\n
\n
\n
\n `],{type:"text/html"}))},Hi=e=>({label:e.title,href:e.dest?JSON.stringify(e.dest):null,subitems:e.items.length?e.items.map(Hi):null});function ji(e="need"){return(navigator.language?.toLowerCase()||"en").startsWith("zh")?"need"===e?"请输入PDF密码:":"密码错误,请重新输入:":"need"===e?"Need password to open this PDF:":"Incorrect password, please try again:"}const Wi=async(e,t)=>{let i;for(;;){const r=new Ii.PDFDataRangeTransport(e.size,[]);r.requestDataRange=(t,i)=>{e.slice(t,i).arrayBuffer().then((e=>{r.onDataRange(t,e)}))};try{i=await Ii.getDocument({range:r,cMapUrl:Ei("cmaps/"),standardFontDataUrl:Ei("standard_fonts/"),isEvalSupported:!1,password:t}).promise;break}catch(e){if("PasswordException"!==e.name)throw e;if(e.code===Ii.PasswordResponses.NEED_PASSWORD?Ri()?(t=await Pi(ji("need"),"",""),vex.closeAll()):t=prompt(ji("need")):e.code===Ii.PasswordResponses.INCORRECT_PASSWORD&&(Ri()?(t=await Pi(ji("incorrect"),"",""),vex.closeAll()):t=prompt(ji("incorrect"))),!t)throw new Error("PDF loading failed: no password provided")}}let r=!1,n=i.numPages>0?await i.getPage(Math.floor(i.numPages/2)+1):null;if(n){const e=await n.getTextContent();if(r=0===e.items.length,e.items.length>0){r=e.items.reduce(((e,t)=>e+t.str.trim().length),0)<40}n.cleanup()}const o={rendition:{layout:"pre-paginated"}},{metadata:s,info:a}=await i.getMetadata()??{};o.metadata={title:s?.get("dc:title")??a?.Title,author:s?.get("dc:creator")??a?.Author,contributor:s?.get("dc:contributor"),description:s?.get("dc:description")??a?.Subject,language:s?.get("dc:language"),publisher:s?.get("dc:publisher"),subject:s?.get("dc:subject"),identifier:s?.get("dc:identifier"),source:s?.get("dc:source"),rights:s?.get("dc:rights")},o.metadata.description=(o.metadata.description?o.metadata.description:"")+(r?"\nscanned PDF":"")+(t?"\nprotected PDF: #"+t+"#":"");const l=await i.getOutline();o.toc=l?.map(Hi);const c=new Map;return o.sections=Array.from({length:i.numPages}).map(((e,t)=>({id:t,load:async()=>{const e=c.get(t);if(e)return e;const r=await Fi(await i.getPage(t+1));return c.set(t,r),r},unload:async()=>{(await i.getPage(t+1)).cleanup()},render:async(e,r,n,o)=>{await(async(e,t,i,r,n,o)=>{let s=window.devicePixelRatio*("yes"===n?1/r*1.5:1);const a=r*s;let l=i.querySelector("#koodoPDFLayer");l.style.visibility="hidden",l.style.transform=`scale(${1/s})`,l.style.transformOrigin="top left",l.style.setProperty("--scale-factor",a);const c=e.getViewport({scale:a}),h=document.createElement("canvas");l.style.width=`${c.width}px`,l.style.height=`${c.height}px`,h.height=c.height,h.width=c.width;const d=h.getContext("2d");await e.render({canvasContext:d,viewport:c,background:"rgba(0,0,0,0)"}).promise,i.querySelector("#canvas").replaceChildren(i.adoptNode(h)),l.style.overflow="hidden";const u=i.querySelector("#textLayer"),f=new Ii.TextLayer({textContentSource:await e.streamTextContent(),container:u,viewport:c});await f.render();for(const e of document.querySelectorAll(".hiddenCanvasElement"))Object.assign(e.style,{position:"absolute",top:"0",left:"0",width:"0",height:"0",display:"none"});const p=document.createElement("div");p.className="endOfContent",u.append(p);let g=!1,m=null;u.onpointerdown=()=>{let e=i?.defaultView;if(e.getSelection().toString().trim().length>0)return u.classList.remove("selecting"),g=!1,p.remove(),void u.append(p);u.classList.add("selecting"),g=!0},"yes"!==n?(u.onpointerup=()=>{u.classList.remove("selecting"),g=!1,p.remove(),u.append(p)},u.onpointermove=e=>{if(!g)return;let t=e.target.closest(".textLayer > span");const i=null!==t;u.style.cursor=i?"text":"default",i&&(m=t),p.remove(),u.insertBefore(p,m)}):i.addEventListener("selectionchange",(e=>{if(!g)return;let t=i?.defaultView;var r=t.getSelection().getRangeAt(0).endContainer;let n=r.nodeType===Node.TEXT_NODE?r.parentNode:r;n=n.closest(".textLayer > span");const o=null!==n;u.style.cursor=o?"text":"default",o&&(m=n),p.remove(),u.insertBefore(p,m.nextSibling?m.nextSibling:m)}));const y=i.querySelector("#annotationLayer");try{await new Ii.AnnotationLayer({page:e,viewport:c,div:y}).render({annotations:await e.getAnnotations(),linkService:{goToDestination:async e=>{try{const i="string"==typeof e?await t.getDestination(e):e;if(!i||!Array.isArray(i)||0===i.length)return void console.warn("Invalid destination:",e);const r=await t.getPageIndex(i[0]);o.goToChapterDocIndex(r)}catch(e){console.error("Error navigating to destination:",e)}},getDestinationHash:e=>JSON.stringify(e),addLinkAttributes:(e,t)=>e.href=t}})}catch(e){console.error(e)}})(await i.getPage(t+1),i,e,r,n,o)},getTextContent:async()=>{const e=await i.getPage(t+1);return await e.getTextContent()},size:1e3,getDimension:async()=>{let e=(await i.getPage(t+1)).getViewport({scale:1});return{width:e.width,height:e.height}},getPage:async()=>await i.getPage(t+1)}))),o.isExternal=e=>/^\w+:/i.test(e),o.resolveHref=async e=>{const t=JSON.parse(e),r="string"==typeof t?await i.getDestination(t):t;return{index:await i.getPageIndex(r[0])}},o.splitTOCHref=async e=>{const t=JSON.parse(e),r="string"==typeof t?await i.getDestination(t):t;return[await i.getPageIndex(r[0]),null]},o.getTOCFragment=e=>e.documentElement,o.getCover=async()=>Fi(await i.getPage(1),!0),o.destroy=()=>i.destroy(),o},$i=async e=>{const t=new Uint8Array(await e.slice(0,5).arrayBuffer());return 37===t[0]&&80===t[1]&&68===t[2]&&70===t[3]&&45===t[4]};class Ui extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{convertChinese:"Default",format:"PDF"})),this.isStartFromEven="no",this.password="",this.scale=1,this.pdfBuffer=e,this.isStartFromEven=t.isStartFromEven||"no",this.password=t.password||"",this.scale=t.scale||1,this.backgroundColor=t.backgroundColor||"#ffffff",this.isScannedPDF=t.isScannedPDF||"no",this.platform=t.platform||"web"}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),console.log(this.chapterDocList,this.chapterList),"yes"===this.isStartFromEven&&(this.chapterDocList=[{label:"",text:{load:()=>c(this,void 0,void 0,(function*(){return""})),render:()=>c(this,void 0,void 0,(function*(){})),unload:()=>c(this,void 0,void 0,(function*(){})),getPage:()=>c(this,void 0,void 0,(function*(){return null})),getDimension:()=>c(this,void 0,void 0,(function*(){return{width:0,height:0}})),getScale:()=>c(this,void 0,void 0,(function*(){return 1})),getPageCount:()=>c(this,void 0,void 0,(function*(){return 0}))},href:""},...this.chapterDocList]),document.body.clientWidth*Math.abs(this.scale)-.4*document.body.clientWidth>document.body.clientWidth&&"double"!==this.readerMode?g(e,this.scale):g(e);const r=yield this.chapterDocList[Math.floor(this.chapterDocList.length/2)].text.getDimension();let n=this.getDocument();if(!n)return;((e,t,i,r)=>{for(let n=0;n{o&&clearTimeout(o),o=setTimeout((()=>c(this,void 0,void 0,(function*(){yield this.handlePDFScrollEvent(n),yield this.record()}))),100)})):n.addEventListener("scroll",(e=>{o&&clearTimeout(o),o=setTimeout((()=>c(this,void 0,void 0,(function*(){yield this.handlePDFScrollEvent(n),yield this.record()}))),200)})),((e,t,i)=>{if("scroll"===t)return;let r="double"===t?2:1,n=Math.floor(i.body.clientWidth/12),o=n%2==0?n:n-1;i.body.setAttribute("style",e.getAttribute("style")+`height: 100%;overflow-y: hidden;overflow-X: hidden;padding-left: 0px;padding-right: 0px;margin: 0px;box-sizing: border-box;touch-action: manipulation; overscroll-behavior: none;max-width: inherit;column-fill: auto;column-gap: ${o}px; column-width: ${(i.body.clientWidth-o)/r}px;`)})(e,this.readerMode,n),t()}))))}handlePDFScrollEvent(e){return c(this,void 0,void 0,(function*(){let t=e.querySelectorAll(".pdf-container");for(let i=0;i0&&(yield this.goToChapter(e,this.chapterDocList[e].href,this.chapterDocList[e].label))}))}getPageSize(){let e=this.getDocument();if(!e)return;let t="double"===this.readerMode?2:1,i=Math.floor(e.body.clientWidth/12),r=i%2==0?i:i-1,n=e.querySelectorAll("iframe")[0],o=null==n?void 0:n.getBoundingClientRect().height;return{width:e.body.clientWidth,height:this.element.clientHeight,left:this.element.offsetLeft,top:this.element.offsetTop,scrollTop:this.element.scrollTop,sectionWidth:(e.body.clientWidth-r)/t,sectionHeight:o,gap:r}}goToChapter(e,t,i){return c(this,void 0,void 0,(function*(){"double"===this.readerMode&&e%2==1&&e--;let t=this.getDocument(),i=this.getIframe();t&&i&&(yield this.renderPdfPage(e,t),yield Ct(parseInt(e),this.readerMode,t),yield this.recordByChapter(e))}))}getPositionByChapter(e){return{percentage:e/this.chapterDocList.length,chapterDocIndex:e+"",chapterHref:this.chapterDocList[e].href,chapterTitle:this.chapterDocList[e].label,text:""}}goToPercentage(e){return c(this,void 0,void 0,(function*(){if(this.chapterDocList.length>0){let t=1===e?this.chapterDocList.length-1:Math.floor(this.chapterDocList.length*e);yield this.goToChapter(t,this.chapterDocList[t].href,this.chapterDocList[t].label)}}))}goToPosition(e){var i;return c(this,void 0,void 0,(function*(){let r=this.getDocument(),n=this.getIframe();if(!r||!n)return;let o=JSON.parse(e);void 0===o.chapterDocIndex&&(o.chapterDocIndex=0),this.tempLocation={text:o.text,chapterTitle:o.chapterTitle,chapterDocIndex:o.chapterDocIndex,chapterHref:o.chapterHref,count:o.count,page:o.page,percentage:o.percentage};let{chapterTitle:s,chapterDocIndex:a,chapterHref:l}=o;if("double"===this.readerMode&&a%2==1&&a--,yield this.renderPdfPage(parseInt(a),r),"scroll"===this.readerMode){let e=this.getSubIframe(void 0!==a?a:parseInt(this.tempLocation.chapterDocIndex));if(!e)return;let t=(null===(i=e.parentElement)||void 0===i?void 0:i.getBoundingClientRect().height)||0;n.style.height=t*this.chapterDocList.length+"px"}yield Ct(parseInt(a),this.readerMode,r),t.init(),yield this.recordByChapter(parseInt(a))}))}prev(e){return c(this,void 0,void 0,(function*(){let t=this.getDocument(),i=this.getIframe();t&&i&&("scroll"===this.readerMode?this.element.scrollBy({left:0,top:-(this.element.clientHeight-50),behavior:"smooth"}):("ios"===e?yield Mt(this.element,this.animation,1,t,this.flipToNextPage,this.flipToPrevPage,this.isMobile,parseInt(this.tempLocation.chapterDocIndex||"0"),this.readerMode):yield $e(this.element,this.animation,1,t,this.flipToNextPage,this.flipToPrevPage,this.isMobile),yield this.renderPdfPage(parseInt(this.tempLocation.chapterDocIndex)-("double"===this.readerMode?2:1),t)),yield this.record())}))}next(e){return c(this,void 0,void 0,(function*(){let t=this.getDocument(),i=this.getIframe();t&&i&&("scroll"===this.readerMode?this.element.scrollBy({left:0,top:this.element.clientHeight-50,behavior:"smooth"}):("ios"===e?yield Mt(this.element,this.animation,-1,t,this.flipToNextPage,this.flipToPrevPage,this.isMobile,parseInt(this.tempLocation.chapterDocIndex||"0"),this.readerMode):yield $e(this.element,this.animation,-1,t,this.flipToNextPage,this.flipToPrevPage,this.isMobile),yield this.renderPdfPage(parseInt(this.tempLocation.chapterDocIndex)+("double"===this.readerMode?2:1),t)),yield this.record())}))}prevChapter(){return c(this,void 0,void 0,(function*(){yield this.prev()}))}nextChapter(){return c(this,void 0,void 0,(function*(){yield this.next()}))}goToPage(e){return c(this,void 0,void 0,(function*(){let t=Math.floor(e-1);t>=this.chapterDocList.length&&(t=this.chapterDocList.length-1),t<0&&(t=0),yield this.goToChapter(t,this.chapterDocList[t].href,this.chapterDocList[t].label)}))}visibleText(){return c(this,void 0,void 0,(function*(){return this.getDocument()?yield(e=parseInt(this.tempLocation.chapterDocIndex||"0"),t=this.chapterDocList,i=this.readerMode,c(void 0,void 0,void 0,(function*(){let r=(yield t[e].text.getTextContent()).items.map((e=>e.str));if("double"===i){let i=(yield t[e+1].text.getTextContent()).items.map((e=>e.str));r=r.concat(i)}return r}))):"";var e,t,i}))}audioText(){return c(this,void 0,void 0,(function*(){return yield this.visibleText()}))}chapterText(){return c(this,void 0,void 0,(function*(){return(yield this.visibleText()).join(" ")}))}record(){return c(this,void 0,void 0,(function*(){""!==this.animation&&(yield new Promise((e=>setTimeout(e,1e3))));let e=this.getDocument();e&&(yield this.handlePDFRecord(e))}))}recordByChapter(e){return c(this,void 0,void 0,(function*(){""!==this.animation&&(yield new Promise((e=>setTimeout(e,1e3)))),e>=this.chapterDocList.length||e<0||(this.tempLocation.chapterDocIndex=e+"",this.tempLocation.percentage=e/(this.chapterDocList.length-1)+"",this.tempLocation.chapterHref=this.chapterDocList[e].href,this.tempLocation.chapterTitle=this.chapterDocList[e].label,this.tempLocation.text="",this.trigger("page-changed"))}))}handlePDFRecord(e){return c(this,void 0,void 0,(function*(){let t=e.querySelectorAll(".pdf-container");if(t.length>0&&Tt(this.element,t[t.length-1],this.readerMode,e))this.handleRecord(t[t.length-1]);else for(let i=0;ir[e].left&&(c[c.length-1].left=r[e].left),c[c.length-1].right=this.chapterDocList.length||e<0)return;let i=t.getElementById("pdf-iframe-"+e);i||(i=xt(e,t));let r=null==i?void 0:i.contentDocument;if(!r)return;if(r.body.innerHTML)return;r.body.innerHTML="";let n=yield fetch(yield this.chapterDocList[e].text.load()).then((e=>e.blob())),o=yield n.text();r.body.innerHTML=o;let s=yield wt(this.element,this.readerMode,this.chapterDocList,e,r);yield this.chapterDocList[e].text.render(r,s,this.isMobile,this);let a=r.querySelector("#koodoPDFLayer");if(a){if("yes"===this.isDarkMode&&(a.style.filter="invert(1) hue-rotate(180deg) contrast(0.95)"),"rgba(233, 216, 188,1)"===this.backgroundColor&&"yes"===this.isScannedPDF&&(a.style.filter="sepia(100%) contrast(0.95) brightness(0.95)"),"rgba(197, 231, 207,1)"===this.backgroundColor&&"yes"===this.isScannedPDF&&(a.style.filter="sepia(30%) hue-rotate(60deg) saturate(120%) brightness(95%)"),"single"===this.readerMode||"double"===this.readerMode){let e=this.element.clientHeight/2-a.getBoundingClientRect().height/2;a.style.marginTop=e+"px",i.style.height=a.getBoundingClientRect().height+e+"px";let t=r.querySelector(".noteLayer");t&&(t.style.position="relative")}"scroll"!==this.readerMode&&(a.style.marginLeft=`calc(50% - ${a.getBoundingClientRect().width/2}px)`),a.style.visibility="visible",window.chapterDocIndex=e,this.trigger("rendered")}}))}handleUnloadPDFChapter(e,t){return c(this,void 0,void 0,(function*(){if(e>=this.chapterDocList.length||e<0)return;let t=this.getSubDocument(e);t&&""!==t.body.innerHTML&&(yield this.chapterDocList[e].text.unload(),t.body.innerHTML="")}))}renderPdfPage(e,t){return c(this,void 0,void 0,(function*(){e>=this.chapterDocList.length||e<0||(e>2&&(yield this.handleUnloadPDFChapter(e-3,t)),yield this.handleRenderPDFChapter(e,t),yield this.handleRenderPDFChapter(e+1,t))}))}}class qi extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"PDFTEXT"})),this.password="",this.ocrLang="chi_sim",this.paraSpacingValue=1.5,this.titleSizeValue=1.2,this.isFinishOCR=!1,this.performOCR=e=>c(this,void 0,void 0,(function*(){try{if("tesseract"===this.ocrEngine){return(yield this.worker.recognize(e)).data.text}this.ocrEngine}catch(e){throw console.error("OCR Error:",e),e}})),this.pdfBuffer=e,this.password=t.password||"",this.isScannedPDF=t.isScannedPDF||"no",this.ocrLang=t.ocrLang||"chi_sim",this.paraSpacingValue=parseFloat(t.paraSpacingValue)||1.5,this.titleSizeValue=parseFloat(t.titleSizeValue)||1.2,this.cache={},this.serverRegion=t.serverRegion||"global",this.processingPromises=new Map,this.ocrEngine=t.ocrEngine||"tesseract"}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc();for(let e=0;ec(this,void 0,void 0,(function*(){if(this.cache[e])return"yes"===this.isScannedPDF&&this.preProcessNextChapters(e),this.cache[e];let i="";return"yes"===this.isScannedPDF?(i=yield this.processCurrentChapter(e),this.preProcessNextChapters(e)):(i=yield this.getTextFromDoc(t),this.cache[e]=i),i}))}g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}processCurrentChapter(e){return c(this,void 0,void 0,(function*(){if(this.cache[e])return this.cache[e];if(this.processingPromises.has(e))return yield this.processingPromises.get(e),this.cache[e];const t=this.chapterDocList[e],i=yield this.getTextByOCR(t);return this.cache[e]=i,i}))}preProcessNextChapters(e){const t=Math.min(e+3,this.chapterDocList.length-1);for(let i=e+1;i<=t;i++)if(!this.cache[i]&&!this.processingPromises.has(i)){const e=this.processChapterOCR(i);this.processingPromises.set(i,e),e.finally((()=>{this.processingPromises.delete(i)}))}}processChapterOCR(e){return c(this,void 0,void 0,(function*(){try{const t=this.chapterDocList[e],i=yield this.getTextByOCR(t);this.cache[e]=i}catch(t){console.error(`Failed to process OCR for chapter ${e}:`,t)}}))}getTextByOCR(e){return c(this,void 0,void 0,(function*(){let t=yield e.text.getPage(),{imageURL:i}=yield St(t);let r=(yield this.performOCR(i)).split("\n").filter((e=>""!==e.trim()));return URL.createObjectURL(new Blob([`\n \n \n \n \n
${r.map((e=>`

${e}

`)).join("")}
\n `],{type:"text/html"}))}))}getTextFromDoc(e){return c(this,void 0,void 0,(function*(){let t=yield e.text.getTextContent(),i=[];if(t&&t.items&&Array.isArray(t.items)){const e=t.items.filter((e=>e.str&&e.transform)).map((e=>e.transform[3]));let r=10;if(e.length>0){const t=e.reduce(((e,t)=>(e[t]=(e[t]||0)+1,e)),{});r=Object.keys(t).map(Number).reduce(((e,i)=>t[e]>t[i]?e:i))}let n={text:"",styles:new Set,y:0,tag:"p"},o=0;t.items.forEach((e=>{if(e.str){const t=Math.abs(e.transform[5]-o);let s="p",a=e.transform[3]>Number(r)*this.titleSizeValue;t>e.height*this.paraSpacingValue&&n.text.trim()?(i.push(n),n={text:"",styles:new Set,y:e.transform[5],tag:s,isBold:a}):n.hasOwnProperty("isBold")||(n.isBold=a);const l=e.str;e.hasEOL?l.endsWith("-")?n.text+=l.slice(0,-1):n.text+=l+" ":n.text+=l,o=e.transform[5]}})),n.text.trim()&&i.push(n)}return URL.createObjectURL(new Blob([`\n \n \n \n \n
${i.length>0?i.map((e=>`

${e.text.trim()}

`)).join(""):"Empty"}
\n `],{type:"text/html"}))}))}parse(){return c(this,void 0,void 0,(function*(){try{let t=new Blob([this.pdfBuffer]),i=new File([t],"book",{lastModified:(new Date).getTime(),type:t.type});if((yield $i(i))&&(this.book=yield Wi(i,this.password)),"yes"===this.isScannedPDF&&"tesseract"===this.ocrEngine){let t=yield(e=("undefined"!=typeof window&&"object"==typeof window.process&&"renderer"===window.process.type||"undefined"!=typeof process&&"object"==typeof process.versions&&process.versions.electron||"object"==typeof navigator&&"string"==typeof navigator.userAgent&&navigator.userAgent.indexOf("Electron")>=0?".":"")+"/lib/tesseractjs/worker.min.js",c(void 0,void 0,void 0,(function*(){return yield(yield fetch(e)).text()}))),i=URL.createObjectURL(new Blob([t],{type:"application/javascript"}));const r=yield window.Tesseract.createWorker([this.ocrLang],1,{workerPath:i,corePath:`https://${"global"===this.serverRegion?"storage.koodoreader.com":"storage.koodoreader.cn"}/tesseractjs/tesseract-core`,langPath:`https://${"global"===this.serverRegion?"storage.koodoreader.com":"storage.koodoreader.cn"}/tesseractjs/4.0.0-fast`,logger:e=>{"recognizing text"!==e.status||"number"!=typeof e.progress||this.isFinishOCR||((e=>{let t=document.getElementById("ocr-progress-bar");t||(t=document.createElement("progress"),t.id="ocr-progress-bar",t.max=1,t.value=0,t.style.position="fixed",t.style.top="10px",t.style.left="50%",t.style.transform="translateX(-50%)",t.style.width="300px",t.style.zIndex="9999",document.body.appendChild(t)),t.value=e,e>=1&&setTimeout((()=>{t.remove()}),1e3)})(e.progress),1===e.progress&&(this.isFinishOCR=!0))}});yield r.load(),this.worker=r}}catch(e){throw console.error(e),e}var e}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}getMetadata(){return c(this,void 0,void 0,(function*(){try{this.book||(yield this.parse());let e=new T(this.book);return yield e.getMetadata()}catch(e){throw console.error(e),e}}))}}const zi=(e,t=!1,i="",r)=>{const n=(new DOMParser).parseFromString(t?((e,t,i)=>{let r=e.split("\n");1===r.length&&(r=e.split("\r"));const n=[];let o=!1;if(i&&i.refresh&&(o=!0),r.length>1e4&&!o){i&&i.text||(i={text:r[0],chapterTitle:"",chapterDocIndex:0});let e=r.findIndex((e=>Re(e)===Re(i.text)));-1===e&&(e=0);const o=Math.max(e-1e3,0),s=Math.min(e+1e3,r.length),a=r.slice(o,s),l=a.filter((e=>{const i=Re(e);return i&&Pe(i,t)})),c=new Set(l.map((e=>Re(e))));let h=l.findIndex((e=>Re(e)===Re(i.chapterTitle)));if(-1===h&&(h=0),h0)for(let t=0;tChapter ${t}`),n.push(`

Chapter ${t}

`)}for(const e of a){const t=Re(e);t&&c.has(t)?n.push(`

${t}

`):n.push(`

${e}

`)}}else for(const e of r){const i=Re(e);i&&Pe(i,t)?n.push(`

${i}

`):n.push(`

${e}

`)}return n.join("")||`

Title

${e}

`})(e,i,r):e,"text/html");let o=Vi(n);0===o.length&&(o=_i(n));for(let e=0;e""};return h.sections=l.map((e=>({id:e.index,load:()=>{return t=e.index,c(void 0,void 0,void 0,(function*(){return URL.createObjectURL(new Blob([l[t].text],{type:"text/html"}))}));var t},unload:()=>{e.index},size:l[e.index].text.length}))),h.toc=l.map((e=>({label:e.label,href:"title"+e.index}))).filter((e=>""!==e.label)),h.rendition={layout:"pre-paginated"},h.resolveHref=e=>({index:parseInt(e.substring(5,e.length))}),h.splitTOCHref=e=>[e,null],h.getTOCFragment=e=>e.documentElement,h},Vi=e=>Array.from(e.querySelectorAll("h1,h2,h3,h4,h5,h6,title")),Xi=e=>{let t=[],i=e.split(" ").filter((e=>""!==e.trim())),r=i.map((e=>Ji(e)||Gi(e)));return t=i.map(((e,t)=>({index:t,label:r[t],text:e,href:"title"+t}))),t},Ji=e=>{var t;const i=(new DOMParser).parseFromString(e,"text/html").querySelector("h1, h2, h3, h4, h5, h6");return i&&(null===(t=i.textContent)||void 0===t?void 0:t.trim())||""},Gi=e=>{var t;const i=(new DOMParser).parseFromString(e,"text/html").querySelector("title");return i&&(null===(t=i.textContent)||void 0===t?void 0:t.trim())||""},_i=e=>{let t=e.getElementsByTagName("*"),i=Array.from(t).filter((e=>1===e.childNodes.length&&e.childNodes[0].nodeType===Node.TEXT_NODE&&Pe(Re(e.textContent)))),r=[];for(let e=0;ec(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse(t));let r=new T(this.book);this.chapterList=yield r.getChapter(this.book.toc),this.chapterDocList=yield r.getChapterDoc(),g(e);let n=this.getDocument();n&&(w(e,this.readerMode,n),i())}))))}parse(e){return c(this,void 0,void 0,(function*(){try{const t=new TextDecoder(this.charset),i=new Uint8Array(this.txtBuffer);let r=t.decode(i);this.book=zi(r,!0,this.parserRegex,e)}catch(e){throw console.error(e),e}}))}refreshContent(){return c(this,void 0,void 0,(function*(){yield this.parse({refresh:!0});let e=new T(this.book);return this.chapterList=yield e.getChapter(this.book.toc),this.chapterDocList=yield e.getChapterDoc(),this.chapterList}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse({refresh:!0})),yield jt(this.book)}))}getMetadata(e){return c(this,void 0,void 0,(function*(){try{const t=4096,i=e.byteLength,r=Math.min(i,t),o=new Uint8Array(e,0,r);const s=n.detect(o)||"utf8";return this.charset=s,{charset:s}}catch(e){return console.error("Error detecting charset:",e),this.charset="utf8",{charset:"utf8"}}}))}}const Zi=({entries:e,loadBlob:t,getSize:i},r,n)=>{const o=new Map,s=new Map,a=async(e,i)=>{if(o.has(e))return o.get(e);if(i){const r=URL.createObjectURL(await t(e)),n=URL.createObjectURL(await t(i)),a=URL.createObjectURL(new Blob([`
`],{type:"text/html"}));return s.set(e,[r,a]),o.set(e,a),a}{const i=URL.createObjectURL(await t(e)),r=URL.createObjectURL(new Blob([`
`],{type:"text/html"}));return s.set(e,[i,r]),o.set(e,r),r}},l=[".jpg",".jpeg",".png",".gif",".bmp",".webp",".svg"],c=e.map((e=>e.filename)).filter((e=>l.some((t=>e.endsWith(t))))).sort(((e,t)=>{const i=parseInt(e.replace(/\D/g,"")),r=parseInt(t.replace(/\D/g,""));return isNaN(i)||isNaN(r)?isNaN(i)?isNaN(r)?e.localeCompare(t):1:-1:i-r})),h={getCover:()=>t(c[0])};return h.metadata={title:r.name},h.sections=c.map(((e,t)=>({id:e,load:()=>{if("double"===n){const i=c[t+1];return a(e,i)}return a(e)},unload:()=>(e=>{s.get(e)?.forEach?.((e=>URL.revokeObjectURL(e))),s.delete(e),o.delete(e)})(e),size:i(e)}))).filter(((e,t)=>"double"!==n||t%2==0)),h.toc=c.map((e=>({label:e,href:e}))).filter(((e,t)=>"double"!==n||t%2==0)),h.rendition={layout:"pre-paginated"},h.resolveHref=e=>({index:h.sections.findIndex((t=>t.id===e))}),h.splitTOCHref=e=>[e,null],h.getTOCFragment=e=>e.documentElement,h};class Ki extends Ot{constructor(e,t){super(t),this.comicBuffer=e,this.readerMode=t.readerMode,this.format=t.format,this.chapterList=[],this.chapterDocList=[],this.book="",this.element="",this.rpc}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){if(this.element=e,g(e),!this.book)try{yield this.parse()}catch(e){console.error(e),i(e)}let r=new T(this.book);this.chapterList=yield r.getChapter(this.book.toc),this.chapterDocList=yield r.getChapterDoc();let n=this.getDocument();n&&(w(e,this.readerMode,n),t())}))))}parse(){return c(this,void 0,void 0,(function*(){try{let e=new Blob([this.comicBuffer]),t=new File([e],"book."+this.format.toLocaleLowerCase(),{lastModified:(new Date).getTime(),type:e.type});if("CBZ"===this.format){const e=yield this.makeZipLoader(t);this.book=Zi(e,t,this.readerMode)}else if("CBT"===this.format){const e=yield this.makeTarLoader();this.book=Zi(e,t,this.readerMode)}else if("CBR"===this.format){this.rpc=yield window.RPC.new("./lib/libunrar/worker.js",{loaded:function(){console.info("loaded")},progressShow:function(e,t,i){console.info(i)}}),yield new Promise((e=>setTimeout(e,200)));const e=yield this.makeRarLoader();this.book=Zi(e,t,this.readerMode)}else if("CB7"===this.format){const e=yield this.make7zLoader();this.book=Zi(e,t,this.readerMode)}}catch(e){throw console.error(e),e}}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}makeZipLoader(e){return c(this,void 0,void 0,(function*(){let t=yield i.loadAsync(e);const r=t.files;return{entries:Object.values(r).map((e=>({filename:e.name}))),loadText:e=>c(this,void 0,void 0,(function*(){let i=t.file(e);return i?i.async("string"):""})),loadBlob:e=>c(this,void 0,void 0,(function*(){let i=t.file(e);if(i){let e=yield i.async("arraybuffer");return new Blob([e])}return new Blob([new ArrayBuffer(0)])})),getSize:e=>{let i=t.file(e);if(i)return i._data.uncompressedSize||1}}}))}makeTarLoader(){return c(this,void 0,void 0,(function*(){const e=yield o(this.comicBuffer),t=new Map(e.map((e=>[e.name,e]))),i=e=>(i,...r)=>t.has(i)?e(t.get(i),...r):null,r=i((e=>e.readAsString())),n=i(((e,t)=>e.blob));return{entries:e.map((e=>({filename:e.name}))),loadText:r,loadBlob:n,getSize:e=>{var i,r;return null!==(r=null===(i=t.get(e))||void 0===i?void 0:i.size)&&void 0!==r?r:1}}}))}makeRarLoader(){return c(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{var i=[this.comicBuffer],r=[{name:"book.rar",content:this.comicBuffer}];this.rpc.transferables=i,this.rpc.unrar(r,null,0).then((t=>{let i=this.getRarEntries(t.ls);const r=new Map(Object.values(i).map((e=>[e.fullFileName,e]))),n=e=>(t,...i)=>r.has(t)?e(r.get(t),...i):null,o=n((e=>e.fullFileName)),s=n(((e,t)=>new Blob([e.fileContent])));e({entries:Object.values(i).map((e=>({filename:e.fullFileName}))),loadText:o,loadBlob:s,getSize:e=>{var t,i;return null!==(i=null===(t=r.get(e))||void 0===t?void 0:t.fileSize)&&void 0!==i?i:1}})})).catch((e=>{console.error(e),t(e)}))}))}))}make7zLoader(){return c(this,void 0,void 0,(function*(){const e="./lib/7z-wasm/7zz.wasm";if(!window.wasmBinary){const t=yield fetch(e,{credentials:"same-origin"});if(!t.ok)throw"failed to load wasm binary file at '"+e+"'";window.wasmBinary=yield t.arrayBuffer()}const t=yield window.SevenZip({wasmBinary:window.wasmBinary}),i=new Uint8Array(this.comicBuffer),r="archive.cb7",n=t.FS.open(r,"w+");t.FS.write(n,i,0,i.length),t.FS.close(n),t.callMain(["x",r]);const o=t.FS,s=this.get7zEntries(o.lookupPath("/").node),a=new Map(s.map((e=>[e.name,e]))),l=e=>(t,...i)=>a.has(t)?e(a.get(t),...i):null,c=l((e=>e.name)),h=l(((e,t)=>new Blob([e.buffer])));return{entries:s.map((e=>({filename:e.name}))),loadText:c,loadBlob:h,getSize:e=>{var t,i;return null!==(i=null===(t=a.get(e))||void 0===t?void 0:t.size)&&void 0!==i?i:1}}}))}getRarEntries(e){const t=Object.keys(e);let i=[];for(let r=0;r"archive.cb7"!=e&&"dev"!=e&&"home"!=e&&"proc"!=e&&"tmp"!=e));let r=[];for(let e=0;ec(this,void 0,void 0,(function*(){try{this.book||(yield this.parse());const t=yield this.book.getCover();var i=new FileReader;i.readAsDataURL(t),i.onloadend=()=>{e({cover:i.result})}}catch(e){console.error(e),t(e)}}))))}))}}const Qi=e=>e?.trim()?.replace(/\s{2,}/g," "),er=e=>Qi(e?.textContent),tr={XLINK:"http://www.w3.org/1999/xlink",EPUB:"http://www.idpf.org/2007/ops"},ir="application/xml",rr="application/xhtml+xml",nr={strong:["strong","self"],emphasis:["em","self"],style:["span","self"],a:"anchor",strikethrough:["s","self"],sub:["sub","self"],sup:["sup","self"],code:["code","self"],image:"image"},or={epigraph:["blockquote"],subtitle:["h2",nr],"text-author":["p",nr],date:["p",nr],stanza:"stanza"},sr={title:["header",{p:["h1",nr],"empty-line":["br"]}],epigraph:["blockquote","self"],image:"image",annotation:["aside"],section:["section","self"],p:["p",nr],poem:["blockquote",or],subtitle:["h2",nr],cite:["blockquote","self"],"empty-line":["br"],table:["table",{tr:["tr",["align"]],th:["th",["colspan","rowspan","align","valign"]],td:["td",["colspan","rowspan","align","valign"]]}],"text-author":["p",nr]};or.epigraph.push(sr);const ar={image:"image",title:["section",{p:["h1",nr],"empty-line":["br"]}],epigraph:["section",sr],section:["section",sr]},lr=e=>{const t=e.getAttributeNS(tr.XLINK,"href"),[,i]=t.split("#"),r=e.getRootNode().getElementById(i);return r?`data:${r.getAttribute("content-type")};base64,${r.textContent}`:t};class cr{constructor(e){this.fb2=e,this.doc=document.implementation.createDocument(tr.XHTML,"html")}image(e){const t=this.doc.createElement("img");return t.alt=e.getAttribute("alt"),t.title=e.getAttribute("title"),t.setAttribute("src",lr(e)),t}anchor(e){const t=this.convert(e,{a:["a",nr]});return t.setAttribute("href",e.getAttributeNS(tr.XLINK,"href")),"note"===e.getAttribute("type")&&t.setAttributeNS(tr.EPUB,"epub:type","noteref"),t}stanza(e){const t=this.convert(e,{stanza:["p",{title:["header",{p:["strong",nr],"empty-line":["br"]}],subtitle:["p",nr]}]});for(const i of e.children)"v"===i.nodeName&&(t.append(this.doc.createTextNode(i.textContent)),t.append(this.doc.createElement("br")));return t}convert(e,t){if(3===e.nodeType)return this.doc.createTextNode(e.textContent);if(4===e.nodeType)return this.doc.createCDATASection(e.textContent);if(8===e.nodeType)return this.doc.createComment(e.textContent);const i=t?.[e.nodeName];if(!i)return null;if("string"==typeof i)return this[i](e);const[r,n]=i,o=this.doc.createElement(r);if(e.id&&(o.id=e.id),o.classList.add(e.nodeName),Array.isArray(n))for(const t of n)o.setAttribute(t,e.getAttribute(t));const s="self"===n?t:Array.isArray(n)?null:n;let a=e.firstChild;for(;a;){const e=this.convert(a,s);e&&o.append(e),a=a.nextSibling}return o}}const hr=URL.createObjectURL(new Blob(['\n@namespace epub "http://www.idpf.org/2007/ops";\nbody > img, section > img {\n display: block;\n margin: auto;\n}\n.title {\n text-align: center;\n}\nbody > section > .title, body.notesBodyType > .title {\n margin: 3em 0;\n}\nbody.notesBodyType > section .title {\n text-align: left;\n margin: 1em 0;\n}\np {\n text-indent: 1em;\n margin: 0;\n}\n:not(p) + p, p:first-child {\n text-indent: 0;\n}\n.poem p {\n text-indent: 0;\n margin: 1em 0;\n}\n.text-author, .date {\n text-align: end;\n}\n.text-author:before {\n content: "—";\n}\ntable {\n border-collapse: collapse;\n}\ntd, th {\n padding: .25em;\n}\na[epub|type~="noteref"] {\n font-size: .75em;\n vertical-align: super;\n}\nbody:not(.notesBodyType) > .title, body:not(.notesBodyType) > .epigraph {\n margin: 3em 0;\n}\n'],{type:"text/css"})),dr="data-foliate-id",ur=async e=>{const t={},i=await(async e=>{const t=await e.arrayBuffer(),i=new TextDecoder("utf-8").decode(t),r=new DOMParser,n=r.parseFromString(i,ir),o=n.xmlEncoding||i.match(/^<\?xml\s+version\s*=\s*["']1.\d+"\s+encoding\s*=\s*["']([A-Za-z0-9._-]*)["']/)?.[1];if(o&&"utf-8"!==o.toLowerCase()){const e=new TextDecoder(o).decode(t);return r.parseFromString(e,ir)}return n})(e),r=new cr(i),n=e=>i.querySelector(e),o=e=>[...i.querySelectorAll(e)],s=e=>{const t=er(e.querySelector("nickname"));if(t)return t;const i=er(e.querySelector("first-name")),r=er(e.querySelector("middle-name")),n=er(e.querySelector("last-name"));return{name:[i,r,n].filter((e=>e)).join(" "),sortAs:n?[n,[i,r].filter((e=>e)).join(" ")].join(", "):null}},a=e=>e?.getAttribute("value")??er(e),l=n("title-info annotation");t.metadata={title:er(n("title-info book-title")),identifier:er(n("document-info id")),language:er(n("title-info lang")),author:o("title-info author").map(s),translator:o("title-info translator").map(s),producer:o("document-info author").map(s).concat(o("document-info program-used").map(er)),publisher:er(n("publish-info publisher")),published:a(n("title-info date")),modified:a(n("document-info date")),description:l?r.convert(l,{annotation:["div",sr]}).innerHTML:null,subject:o("title-info genre").map(er)},t.getCover=()=>fetch(lr(n("coverpage image"))).then((e=>e.blob()));const c=Array.from(i.querySelectorAll("body"),(e=>{const t=r.convert(e,{body:["body",ar]});return[Array.from(t.children,(e=>{const t=[e,...e.querySelectorAll("[id]")].map((e=>e.id));return{el:e,ids:t}})),t]})),h=c[0][0].map((({el:e,ids:t})=>({ids:t,titles:Array.from(e.querySelectorAll(":scope > section > .title"),((e,t)=>(e.setAttribute(dr,t),{title:er(e),index:t}))),el:e}))).concat(c.slice(1).map((([e,t])=>{const i=e.map((e=>e.ids)).flat();return t.classList.add("notesBodyType"),{ids:i,el:t,linear:"no"}}))).map((({ids:e,titles:t,el:i,linear:r})=>{const n=(o=i.outerHTML,`\n\n \n ${o}\n`);var o;const s=new Blob([n],{type:rr}),a=URL.createObjectURL(s);return{ids:e,title:Qi(i.querySelector(".title, .subtitle, p")?.textContent??(i.classList.contains("title")?i.textContent:"")),titles:t,load:()=>a,createDocument:()=>(new DOMParser).parseFromString(n,rr),size:s.size-Array.from(i.querySelectorAll("[src]"),(e=>e.getAttribute("src")?.length??0)).reduce(((e,t)=>e+t),0),linear:r}})),d=new Map;return t.sections=h.map(((e,t)=>{const{ids:i,load:r,createDocument:n,size:o,linear:s}=e;for(const e of i)e&&d.set(e,t);return{id:t,load:r,createDocument:n,size:o,linear:s}})),t.toc=h.map((({title:e,titles:t},i)=>{const r=i.toString();return{label:e,href:r,subitems:t?.length?t.map((({title:e,index:t})=>({label:e,href:`${r}#${t}`}))):null}})).filter((e=>e)),t.resolveHref=e=>{const[t,i]=e.split("#");return t?{index:Number(t),anchor:e=>e.querySelector(`[${dr}="${i}"]`)}:{index:d.get(i),anchor:e=>e.getElementById(i)}},t.splitTOCHref=e=>e?.split("#")?.map((e=>Number(e)))??[],t.getTOCFragment=(e,t)=>e.querySelector(`[${dr}="${t}"]`),t};class fr extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"FB2"})),this.fb2Buffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}parse(){return c(this,void 0,void 0,(function*(){try{let e=new Blob([this.fb2Buffer]);this.book=yield ur(e)}catch(e){throw console.error(e),e}}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}getMetadata(){return c(this,void 0,void 0,(function*(){try{this.book||(yield this.parse());let e=new T(this.book);return yield e.getMetadata()}catch(e){throw console.error(e),e}}))}}class pr extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"CACHE"})),this.cacheBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book=yield Ht(this.cacheBuffer);let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}}class gr extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"DOCX"})),this.docxBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}parse(){return c(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{try{s.convertToHtml({arrayBuffer:this.docxBuffer}).then((t=>c(this,void 0,void 0,(function*(){this.book=zi(t.value,!1),e()}))))}catch(e){console.error(e),t(e)}}))}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}}class mr extends Ot{constructor(e,t){super(Object.assign(Object.assign({},t),{format:"MD"})),this.mdBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}parse(){return c(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{try{var i=new Blob([this.mdBuffer],{type:"text/plain"}),r=new FileReader;r.onload=t=>c(this,void 0,void 0,(function*(){var i;let r=yield a(null===(i=t.target)||void 0===i?void 0:i.result);this.book=zi(r,!1),e()})),r.readAsText(i,"UTF-8")}catch(e){console.error(e),t(e)}}))}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}}class yr extends Ot{constructor(e,t){super(t),this.htmlBuffer=e}renderTo(e){return new Promise(((t,i)=>c(this,void 0,void 0,(function*(){this.element=e,this.book||(yield this.parse());let i=new T(this.book);this.chapterList=yield i.getChapter(this.book.toc),this.chapterDocList=yield i.getChapterDoc(),g(e);let r=this.getDocument();r&&(w(e,this.readerMode,r),t())}))))}parse(){return c(this,void 0,void 0,(function*(){return new Promise(((e,t)=>{try{var i=new Blob([this.htmlBuffer],{type:Bt[this.format.toLocaleLowerCase()]}),r=new FileReader;r.onload=t=>c(this,void 0,void 0,(function*(){var i;let r=null===(i=t.target)||void 0===i?void 0:i.result;"MHTML"===this.format&&(r=l.convert(r).window.document.documentElement.innerHTML),this.book=zi(r,!1),e()})),r.readAsText(i,"UTF-8")}catch(e){console.error(e),t(e)}}))}))}preCache(){return c(this,void 0,void 0,(function*(){return this.book||(yield this.parse()),yield jt(this.book)}))}}export{pr as CacheRender,Ki as ComicRender,gr as DocxRender,Wt as EpubRender,fr as Fb2Render,yr as HtmlRender,mr as MdRender,Ai as MobiRender,Ui as PdfRender,qi as PdfTextRender,Yi as TxtRender}; diff --git a/src/components/popups/popupRefer/index.tsx b/src/components/popups/popupRefer/index.tsx index bab97e62..79afd456 100644 --- a/src/components/popups/popupRefer/index.tsx +++ b/src/components/popups/popupRefer/index.tsx @@ -1,13 +1,7 @@ -import { - handleSelection, - handleOpenMenu, - handleMenuMode, - handleNoteKey, - handleRenderNoteFunc, -} from "../../../store/actions"; +import { handleSelection } from "../../../store/actions"; import { connect } from "react-redux"; import { stateType } from "../../../store"; -import PopupMenu from "./component"; +import PopupRefer from "./component"; import { withTranslation } from "react-i18next"; const mapStateToProps = (state: stateType) => { @@ -24,4 +18,4 @@ const actionCreator = { export default connect( mapStateToProps, actionCreator -)(withTranslation()(PopupMenu as any) as any); +)(withTranslation()(PopupRefer as any) as any); diff --git a/src/containers/viewer/component.tsx b/src/containers/viewer/component.tsx index 35610ca6..04776768 100644 --- a/src/containers/viewer/component.tsx +++ b/src/containers/viewer/component.tsx @@ -494,7 +494,7 @@ class Viewer extends React.Component { }} /> ) : null} - {this.props.htmlBook && this.props.currentBook.format !== "PDF" ? ( + {this.props.htmlBook ? (