@l1yp/file-viewer 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/components/AnsiText.d.ts +47 -0
- package/dist/components/AnsiText.js +41 -0
- package/dist/components/AnsiText.js.map +1 -0
- package/dist/components/ArchiveBrowser.js +1 -1
- package/dist/components/ArchiveBrowser.js.map +1 -1
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js +105 -94
- package/dist/components/ArchiveBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/CodeView.js +1 -1
- package/dist/components/CodeView.js.map +1 -1
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js +80 -71
- package/dist/components/CodeView.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/DocxEmbeddedFiles.js +9 -0
- package/dist/components/DocxEmbeddedFiles.js.map +1 -0
- package/dist/components/DocxEmbeddedFiles.vue.d.ts +15 -0
- package/dist/components/DocxEmbeddedFiles.vue_vue_type_script_setup_true_lang.js +69 -0
- package/dist/components/DocxEmbeddedFiles.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/DocxViewer.js +1 -1
- package/dist/components/DocxViewer.js.map +1 -1
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js +180 -50
- package/dist/components/DocxViewer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/FileViewerBody.js +1 -1
- package/dist/components/FileViewerBody.js.map +1 -1
- package/dist/components/FileViewerBody.vue.d.ts +1 -0
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js +117 -114
- package/dist/components/FileViewerBody.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/FileViewerPanel.js +1 -1
- package/dist/components/FileViewerPanel.js.map +1 -1
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js +18 -18
- package/dist/components/FileViewerPanel.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ImageLightbox.js +1 -1
- package/dist/components/ImageLightbox.js.map +1 -1
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js +101 -55
- package/dist/components/ImageLightbox.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/LightboxGalleryStrip.js +9 -0
- package/dist/components/LightboxGalleryStrip.js.map +1 -0
- package/dist/components/LightboxGalleryStrip.vue.d.ts +12 -0
- package/dist/components/LightboxGalleryStrip.vue_vue_type_script_setup_true_lang.js +60 -0
- package/dist/components/LightboxGalleryStrip.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/LogViewer.js +1 -1
- package/dist/components/LogViewer.js.map +1 -1
- package/dist/components/LogViewer.vue.d.ts +3 -1
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js +284 -280
- package/dist/components/LogViewer.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/SevenZipBrowser.js +1 -1
- package/dist/components/SevenZipBrowser.js.map +1 -1
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js +157 -146
- package/dist/components/SevenZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ZipBrowser.js +1 -1
- package/dist/components/ZipBrowser.js.map +1 -1
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js +70 -61
- package/dist/components/ZipBrowser.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/components/ZoomImage.js +1 -1
- package/dist/components/ZoomImage.js.map +1 -1
- package/dist/components/ZoomImage.vue.d.ts +2 -0
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js +94 -85
- package/dist/components/ZoomImage.vue_vue_type_script_setup_true_lang.js.map +1 -1
- package/dist/composables/useArchiveImageGallery.d.ts +15 -0
- package/dist/composables/useArchiveImageGallery.js +62 -0
- package/dist/composables/useArchiveImageGallery.js.map +1 -0
- package/dist/composables/useLightbox.d.ts +29 -21
- package/dist/composables/useLightbox.js +72 -12
- package/dist/composables/useLightbox.js.map +1 -1
- package/dist/lib/ansi.d.ts +44 -0
- package/dist/lib/ansi.js +224 -0
- package/dist/lib/ansi.js.map +1 -0
- package/dist/lib/archiveImages.d.ts +14 -0
- package/dist/lib/archiveImages.js +15 -0
- package/dist/lib/archiveImages.js.map +1 -0
- package/dist/lib/cfb.d.ts +37 -0
- package/dist/lib/cfb.js +112 -0
- package/dist/lib/cfb.js.map +1 -0
- package/dist/lib/docxEmbeddedDom.d.ts +22 -0
- package/dist/lib/docxEmbeddedDom.js +128 -0
- package/dist/lib/docxEmbeddedDom.js.map +1 -0
- package/dist/lib/docxEmbeddings.d.ts +41 -0
- package/dist/lib/docxEmbeddings.js +226 -0
- package/dist/lib/docxEmbeddings.js.map +1 -0
- package/dist/lib/msi.js +62 -154
- package/dist/lib/msi.js.map +1 -1
- package/dist/lib/olePackage.d.ts +8 -0
- package/dist/lib/olePackage.js +37 -0
- package/dist/lib/olePackage.js.map +1 -0
- package/dist/lib/pptxFirstSlide.d.ts +11 -0
- package/dist/lib/pptxFirstSlide.js +34 -0
- package/dist/lib/pptxFirstSlide.js.map +1 -0
- package/dist/style.css +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docxEmbeddedDom.js","names":[],"sources":["../../src/lib/docxEmbeddedDom.ts"],"sourcesContent":["import {\n extractDocxEmbeddedFile,\n extractDocxEmbeddedPreview,\n type DocxEmbeddedFile,\n} from '@/lib/docxEmbeddings'\nimport type { PptxFirstSlideController } from '@/lib/pptxFirstSlide'\n\nexport interface DocxEmbeddedDomController {\n /** Number of embedded objects mounted into their original document position. */\n mounted: number\n /** Resolves after every inline snapshot/slide has either rendered or fallen back to a file card. */\n ready: Promise<void>\n dispose: () => void\n}\n\ninterface MountDocxEmbeddedPreviewsOptions {\n root: HTMLElement\n buffer: ArrayBuffer\n files: readonly DocxEmbeddedFile[]\n onOpen: (file: DocxEmbeddedFile) => void\n}\n\nfunction normalizedText(element: Element): string {\n return (element.textContent ?? '').replace(/\\s+/g, ' ').trim()\n}\n\nfunction isEmptyParagraph(paragraph: HTMLParagraphElement): boolean {\n return !normalizedText(paragraph) && !paragraph.querySelector('img, table, svg, button, object')\n}\n\nfunction findHostParagraphs(\n root: HTMLElement,\n files: readonly DocxEmbeddedFile[],\n): Array<{ file: DocxEmbeddedFile; host: HTMLParagraphElement }> {\n const paragraphs = Array.from(root.querySelectorAll<HTMLParagraphElement>('section.docx p'))\n const result: Array<{ file: DocxEmbeddedFile; host: HTMLParagraphElement }> = []\n const used = new Set<HTMLParagraphElement>()\n let cursor = 0\n\n for (const file of files) {\n let headingIndex = -1\n if (file.label) {\n for (let i = cursor; i < paragraphs.length; i++) {\n if (normalizedText(paragraphs[i]) === file.label) {\n headingIndex = i\n break\n }\n }\n }\n\n let host: HTMLParagraphElement | undefined\n if (headingIndex >= 0) {\n // docx-preview drops `w:object` itself but preserves its containing paragraph as the first empty <p>\n // after the caption. Stop at the next non-empty paragraph so a distant layout spacer is never hijacked.\n for (let i = headingIndex + 1; i < paragraphs.length; i++) {\n const paragraph = paragraphs[i]\n if (isEmptyParagraph(paragraph) && !used.has(paragraph)) {\n host = paragraph\n cursor = i + 1\n break\n }\n if (normalizedText(paragraph)) break\n }\n }\n\n if (!host) {\n // Caption-less fallback: accept only an empty paragraph immediately followed by Word's usual OLE hint.\n for (let i = cursor; i < paragraphs.length - 1; i++) {\n const paragraph = paragraphs[i]\n const nextText = normalizedText(paragraphs[i + 1])\n if (\n isEmptyParagraph(paragraph) &&\n !used.has(paragraph) &&\n /^(?:点击|双击).*(?:查看|打开).*(?:文件|表格)/.test(nextText)\n ) {\n host = paragraph\n cursor = i + 1\n break\n }\n }\n }\n\n if (!host && file.storage === 'ole-package') {\n // A caption-less OLE object at the document tail still leaves a non-empty DOM shell (typically its\n // bookmark spans). Prefer that over truly empty spacer paragraphs and generated heading anchors.\n for (let i = cursor; i < paragraphs.length; i++) {\n const paragraph = paragraphs[i]\n if (\n isEmptyParagraph(paragraph) &&\n paragraph.childElementCount > 0 &&\n !paragraph.querySelector('[id^=\"heading_\"]') &&\n !used.has(paragraph)\n ) {\n host = paragraph\n cursor = i + 1\n break\n }\n }\n }\n\n if (host) {\n used.add(host)\n result.push({ file, host })\n }\n }\n return result\n}\n\nfunction extension(name: string): string {\n const i = name.lastIndexOf('.')\n return i > 0 ? name.slice(i + 1).toUpperCase() : 'FILE'\n}\n\nfunction isPptx(file: DocxEmbeddedFile): boolean {\n return (\n file.contentType ===\n 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ||\n /\\.pptx$/i.test(file.name)\n )\n}\n\nfunction mountFallback(previewElement: HTMLElement, file: DocxEmbeddedFile): void {\n previewElement.classList.remove(\n 'docx-embedded-preview--image',\n 'docx-embedded-preview--pptx',\n 'docx-embedded-preview--loading',\n )\n previewElement.removeAttribute('aria-busy')\n previewElement.style.aspectRatio = ''\n const badge = document.createElement('span')\n badge.className = 'docx-embedded-preview__badge'\n badge.textContent = extension(file.name)\n const label = document.createElement('span')\n label.className = 'docx-embedded-preview__fallback-label'\n label.textContent = `${file.label || file.name} · 点击预览完整文件`\n previewElement.replaceChildren(badge, label)\n}\n\n/**\n * Put Word/WPS's own OLE snapshots, or a PPTX first-slide fallback, into the empty paragraphs left by\n * docx-preview.\n * The third-party renderer owns this subtree imperatively, so this adapter also uses explicit DOM nodes and\n * listeners; dispose removes every listener and Blob URL before the renderer replaces the document.\n */\nexport function mountDocxEmbeddedPreviews(\n options: MountDocxEmbeddedPreviewsOptions,\n): DocxEmbeddedDomController {\n const mounts = findHostParagraphs(options.root, options.files)\n const urls: string[] = []\n const cleanups: Array<() => void> = []\n let disposed = false\n\n const loads = mounts.map(async ({ file, host }) => {\n host.classList.add('docx-embedded-host')\n const previewElement = document.createElement('div')\n previewElement.className = 'docx-embedded-preview'\n previewElement.dataset.docxEmbeddedId = file.id\n previewElement.title = `预览 ${file.name}`\n previewElement.setAttribute('role', 'button')\n previewElement.setAttribute('aria-label', `预览内嵌文件 ${file.name}`)\n previewElement.tabIndex = 0\n if (file.preview?.widthPt) previewElement.style.maxWidth = `${file.preview.widthPt}pt`\n const open = (): void => options.onOpen(file)\n const openFromKeyboard = (event: KeyboardEvent): void => {\n if (event.key !== 'Enter' && event.key !== ' ') return\n event.preventDefault()\n open()\n }\n let pptxAbort: AbortController | null = null\n let pptxController: PptxFirstSlideController | null = null\n previewElement.addEventListener('click', open)\n previewElement.addEventListener('keydown', openFromKeyboard)\n mountFallback(previewElement, file)\n host.replaceChildren(previewElement)\n\n cleanups.push(() => {\n pptxAbort?.abort()\n pptxController?.dispose()\n previewElement.removeEventListener('click', open)\n previewElement.removeEventListener('keydown', openFromKeyboard)\n if (host.contains(previewElement)) host.replaceChildren()\n host.classList.remove('docx-embedded-host')\n })\n\n try {\n const preview = await extractDocxEmbeddedPreview(options.buffer, file)\n if (disposed) return\n if (preview) {\n if (file.preview?.widthPt && file.preview.heightPt) {\n previewElement.style.aspectRatio = `${file.preview.widthPt} / ${file.preview.heightPt}`\n }\n const url = URL.createObjectURL(new Blob([preview.bytes], { type: preview.contentType }))\n if (disposed) {\n URL.revokeObjectURL(url)\n return\n }\n urls.push(url)\n const image = document.createElement('img')\n image.className = 'docx-embedded-preview__image'\n image.src = url\n image.alt = `${file.label || file.name} 内容预览`\n image.draggable = false\n const hint = document.createElement('span')\n hint.className = 'docx-embedded-preview__hint'\n hint.textContent = `${extension(file.name)} · 点击预览完整文件`\n previewElement.classList.add('docx-embedded-preview--image')\n previewElement.replaceChildren(image, hint)\n return\n }\n\n if (!isPptx(file)) return\n previewElement.classList.add('docx-embedded-preview--pptx', 'docx-embedded-preview--loading')\n previewElement.setAttribute('aria-busy', 'true')\n previewElement.style.aspectRatio =\n file.preview?.widthPt && file.preview.heightPt\n ? `${file.preview.widthPt} / ${file.preview.heightPt}`\n : '16 / 9'\n const slide = document.createElement('div')\n slide.className = 'docx-embedded-preview__pptx-slide'\n const hint = document.createElement('span')\n hint.className = 'docx-embedded-preview__pptx-status'\n hint.textContent = '正在渲染 PPTX 第一页…'\n previewElement.replaceChildren(slide, hint)\n\n pptxAbort = new AbortController()\n const [bytes, renderer] = await Promise.all([\n extractDocxEmbeddedFile(options.buffer, file),\n import('@/lib/pptxFirstSlide'),\n ])\n if (disposed) return\n pptxController = await renderer.mountPptxFirstSlide({\n container: slide,\n bytes,\n signal: pptxAbort.signal,\n })\n if (disposed) {\n pptxController.dispose()\n pptxController = null\n return\n }\n previewElement.classList.remove('docx-embedded-preview--loading')\n previewElement.removeAttribute('aria-busy')\n hint.className = 'docx-embedded-preview__hint'\n hint.textContent = 'PPTX · 点击预览完整文件'\n } catch {\n // The actual embedded file remains previewable from this fallback card and from the top toolbar.\n if (!disposed) mountFallback(previewElement, file)\n }\n })\n\n return {\n mounted: mounts.length,\n ready: Promise.allSettled(loads).then(() => undefined),\n dispose: () => {\n if (disposed) return\n disposed = true\n for (const cleanup of cleanups) cleanup()\n for (const url of urls) URL.revokeObjectURL(url)\n urls.length = 0\n cleanups.length = 0\n },\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type ZipEntry } from '@/lib/zip';
|
|
2
|
+
export interface DocxEmbeddedFile {
|
|
3
|
+
/** Stable relationship id inside word/document.xml. */
|
|
4
|
+
id: string;
|
|
5
|
+
/** Human-facing caption inferred from the nearest preceding Word paragraph. */
|
|
6
|
+
label: string;
|
|
7
|
+
/** Friendly download name (caption + original extension when a caption exists). */
|
|
8
|
+
name: string;
|
|
9
|
+
/** Original package basename, e.g. Microsoft_Excel_Worksheet1.xlsx. */
|
|
10
|
+
originalName: string;
|
|
11
|
+
/** Resolved package path inside the DOCX ZIP. */
|
|
12
|
+
path: string;
|
|
13
|
+
size: number;
|
|
14
|
+
contentType: string;
|
|
15
|
+
storage: 'package' | 'ole-package';
|
|
16
|
+
/** Word/WPS-generated visual snapshot for the embedded object, when present. */
|
|
17
|
+
preview?: DocxEmbeddedPreview;
|
|
18
|
+
/** Internal ZIP descriptor retained so extraction stays on-demand. */
|
|
19
|
+
entry: ZipEntry;
|
|
20
|
+
}
|
|
21
|
+
export interface DocxEmbeddedPreview {
|
|
22
|
+
path: string;
|
|
23
|
+
size: number;
|
|
24
|
+
contentType: string;
|
|
25
|
+
widthPt?: number;
|
|
26
|
+
heightPt?: number;
|
|
27
|
+
entry: ZipEntry;
|
|
28
|
+
}
|
|
29
|
+
export interface ExtractedDocxPreview {
|
|
30
|
+
bytes: Uint8Array;
|
|
31
|
+
contentType: string;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Discover embedded package relationships (`w:object` / `o:OLEObject`) in document order.
|
|
35
|
+
* `docx-preview` currently drops these nodes, so the caller can expose the original bytes separately.
|
|
36
|
+
*/
|
|
37
|
+
export declare function readDocxEmbeddedFiles(buffer: ArrayBuffer): Promise<DocxEmbeddedFile[]>;
|
|
38
|
+
/** Extract one discovered embedded package on demand, with the same per-file ceiling as server exports. */
|
|
39
|
+
export declare function extractDocxEmbeddedFile(buffer: ArrayBuffer, file: DocxEmbeddedFile): Promise<Uint8Array>;
|
|
40
|
+
/** Extract the inert Word/WPS snapshot paired with an embedded object. */
|
|
41
|
+
export declare function extractDocxEmbeddedPreview(buffer: ArrayBuffer, file: DocxEmbeddedFile): Promise<ExtractedDocxPreview | null>;
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { extractZipEntry as e, readZip as t } from "./zip.js";
|
|
2
|
+
import { extractOlePackage as n } from "./olePackage.js";
|
|
3
|
+
//#region src/lib/docxEmbeddings.ts
|
|
4
|
+
var r = "word/document.xml", i = "word/_rels/document.xml.rels", a = "[Content_Types].xml", o = 100 * 1024 * 1024, s = 20 * 1024 * 1024, c = {
|
|
5
|
+
docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
6
|
+
pdf: "application/pdf",
|
|
7
|
+
pptx: "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
8
|
+
xls: "application/vnd.ms-excel",
|
|
9
|
+
xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
10
|
+
};
|
|
11
|
+
function l(e, t) {
|
|
12
|
+
let n = new DOMParser().parseFromString(e, "application/xml"), r = Array.from(n.getElementsByTagNameNS("*", "parsererror"))[0];
|
|
13
|
+
if (r) throw Error(`${t} XML 无法解析:${r.textContent?.trim() || "格式错误"}`);
|
|
14
|
+
return n;
|
|
15
|
+
}
|
|
16
|
+
function u(e, t) {
|
|
17
|
+
return Array.from(e.getElementsByTagNameNS("*", t));
|
|
18
|
+
}
|
|
19
|
+
function d(e, t) {
|
|
20
|
+
return Array.from(e.attributes).find((e) => e.localName === t)?.value ?? "";
|
|
21
|
+
}
|
|
22
|
+
function f(e) {
|
|
23
|
+
return new TextDecoder("utf-8").decode(e);
|
|
24
|
+
}
|
|
25
|
+
function p(e) {
|
|
26
|
+
let t = e.lastIndexOf(".");
|
|
27
|
+
return t > 0 && t < e.length - 1 ? e.slice(t + 1).toLowerCase() : "";
|
|
28
|
+
}
|
|
29
|
+
function m(e) {
|
|
30
|
+
return e.slice(e.lastIndexOf("/") + 1);
|
|
31
|
+
}
|
|
32
|
+
function h(e) {
|
|
33
|
+
for (let t of e) if (t.charCodeAt(0) < 32) return !0;
|
|
34
|
+
return !1;
|
|
35
|
+
}
|
|
36
|
+
function g(e) {
|
|
37
|
+
let t = "";
|
|
38
|
+
for (let n of e) t += n.charCodeAt(0) < 32 || "<>:\"/\\|?*".includes(n) ? "_" : n;
|
|
39
|
+
return t;
|
|
40
|
+
}
|
|
41
|
+
function _(e, t) {
|
|
42
|
+
let n = t.split(/[?#]/, 1)[0]?.replace(/\\/g, "/") ?? "";
|
|
43
|
+
if (!n || h(n)) return "";
|
|
44
|
+
let r = n.startsWith("/") ? [] : e.split("/").slice(0, -1);
|
|
45
|
+
for (let e of n.split("/")) {
|
|
46
|
+
let t;
|
|
47
|
+
try {
|
|
48
|
+
t = decodeURIComponent(e);
|
|
49
|
+
} catch {
|
|
50
|
+
return "";
|
|
51
|
+
}
|
|
52
|
+
if (!(!t || t === ".")) {
|
|
53
|
+
if (t === "..") {
|
|
54
|
+
if (r.length === 0) return "";
|
|
55
|
+
r.pop();
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (t.includes("/") || t.includes("\\")) return "";
|
|
59
|
+
r.push(t);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return r.join("/");
|
|
63
|
+
}
|
|
64
|
+
function v(e) {
|
|
65
|
+
return u(e, "t").map((e) => e.textContent ?? "").join("").replace(/\s+/g, " ").trim();
|
|
66
|
+
}
|
|
67
|
+
function y(e) {
|
|
68
|
+
let t = e.replace(/\s+/g, " ").trim();
|
|
69
|
+
return /^(?:点击|双击).*(?:查看|打开).*(?:文件|表格)/.test(t) ? "" : t.slice(0, 100);
|
|
70
|
+
}
|
|
71
|
+
function b(e, t) {
|
|
72
|
+
if (!e) return t;
|
|
73
|
+
let n = g(e).replace(/[. ]+$/g, "").trim().slice(0, 120);
|
|
74
|
+
if (!n) return t;
|
|
75
|
+
let r = p(t);
|
|
76
|
+
return r && !n.toLowerCase().endsWith(`.${r}`) ? `${n}.${r}` : n;
|
|
77
|
+
}
|
|
78
|
+
function x(e) {
|
|
79
|
+
let t = l(e, "DOCX relationship"), n = /* @__PURE__ */ new Map();
|
|
80
|
+
for (let e of u(t, "Relationship")) {
|
|
81
|
+
let t = e.getAttribute("Id") ?? "", r = e.getAttribute("Target") ?? "";
|
|
82
|
+
!t || !r || n.set(t, {
|
|
83
|
+
target: r,
|
|
84
|
+
type: e.getAttribute("Type") ?? "",
|
|
85
|
+
external: (e.getAttribute("TargetMode") ?? "").toLowerCase() === "external"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return n;
|
|
89
|
+
}
|
|
90
|
+
function S(e) {
|
|
91
|
+
let t = /* @__PURE__ */ new Map(), n = /* @__PURE__ */ new Map();
|
|
92
|
+
if (!e) return {
|
|
93
|
+
defaults: t,
|
|
94
|
+
overrides: n
|
|
95
|
+
};
|
|
96
|
+
let r = l(e, "DOCX content types");
|
|
97
|
+
for (let e of u(r, "Default")) {
|
|
98
|
+
let n = (e.getAttribute("Extension") ?? "").toLowerCase(), r = e.getAttribute("ContentType") ?? "";
|
|
99
|
+
n && r && t.set(n, r);
|
|
100
|
+
}
|
|
101
|
+
for (let e of u(r, "Override")) {
|
|
102
|
+
let t = (e.getAttribute("PartName") ?? "").replace(/^\/+/, ""), r = e.getAttribute("ContentType") ?? "";
|
|
103
|
+
t && r && n.set(t, r);
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
defaults: t,
|
|
107
|
+
overrides: n
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
function C(e, t) {
|
|
111
|
+
let n = p(e);
|
|
112
|
+
return t.overrides.get(e) ?? t.defaults.get(n) ?? c[n] ?? "application/octet-stream";
|
|
113
|
+
}
|
|
114
|
+
function w(e) {
|
|
115
|
+
let t = e.toLowerCase();
|
|
116
|
+
return t === "powerpoint.show.12" || t === "powerpoint.slide.12" ? {
|
|
117
|
+
extension: "pptx",
|
|
118
|
+
contentType: c.pptx,
|
|
119
|
+
label: "PowerPoint 演示文稿"
|
|
120
|
+
} : t === "excel.sheet.12" ? {
|
|
121
|
+
extension: "xlsx",
|
|
122
|
+
contentType: c.xlsx,
|
|
123
|
+
label: "Excel 工作簿"
|
|
124
|
+
} : t === "word.document.12" ? {
|
|
125
|
+
extension: "docx",
|
|
126
|
+
contentType: c.docx,
|
|
127
|
+
label: "Word 文档"
|
|
128
|
+
} : null;
|
|
129
|
+
}
|
|
130
|
+
function T(e, t) {
|
|
131
|
+
let n = e;
|
|
132
|
+
for (; n;) {
|
|
133
|
+
if (n.localName === t) return n;
|
|
134
|
+
n = n.parentElement;
|
|
135
|
+
}
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
function E(e, t) {
|
|
139
|
+
let n = e.match(RegExp(`(?:^|;)\\s*${t}\\s*:\\s*([0-9]+(?:\\.[0-9]+)?)(pt|px|in|cm|mm)`, "i"));
|
|
140
|
+
if (!n) return;
|
|
141
|
+
let r = Number(n[1]), i = n[2]?.toLowerCase(), a = i === "pt" ? r : i === "px" ? r * .75 : i === "in" ? r * 72 : i === "cm" ? r * 72 / 2.54 : r * 72 / 25.4;
|
|
142
|
+
return Number.isFinite(a) && a > 0 && a <= 2e3 ? a : void 0;
|
|
143
|
+
}
|
|
144
|
+
function D(e, t, n, i) {
|
|
145
|
+
let a = T(e, "object");
|
|
146
|
+
if (!a) return;
|
|
147
|
+
let o = u(a, "imagedata")[0], s = o ? d(o, "id") : "", c = t.get(s);
|
|
148
|
+
if (!s || !c || c.external || !c.type.toLowerCase().endsWith("/image")) return;
|
|
149
|
+
let l = _(r, c.target), f = n.get(l);
|
|
150
|
+
if (!l || !f) return;
|
|
151
|
+
let p = u(a, "shape")[0]?.getAttribute("style") ?? "";
|
|
152
|
+
return {
|
|
153
|
+
path: l,
|
|
154
|
+
size: f.size,
|
|
155
|
+
contentType: C(l, i),
|
|
156
|
+
widthPt: E(p, "width"),
|
|
157
|
+
heightPt: E(p, "height"),
|
|
158
|
+
entry: f
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
function O(e, t) {
|
|
162
|
+
return e.length >= 8 && e[0] === 137 && e[1] === 80 && e[2] === 78 && e[3] === 71 ? "image/png" : e.length >= 3 && e[0] === 255 && e[1] === 216 && e[2] === 255 ? "image/jpeg" : e.length >= 6 && e[0] === 71 && e[1] === 73 && e[2] === 70 && e[3] === 56 ? "image/gif" : e.length >= 2 && e[0] === 66 && e[1] === 77 ? "image/bmp" : e.length >= 12 && e[0] === 82 && e[1] === 73 && e[2] === 70 && e[3] === 70 && e[8] === 87 && e[9] === 69 && e[10] === 66 && e[11] === 80 ? "image/webp" : t.startsWith("image/") ? t : "application/octet-stream";
|
|
163
|
+
}
|
|
164
|
+
async function k(n) {
|
|
165
|
+
let o = t(n), s = new Map(o.filter((e) => !e.isDir).map((e) => [e.path, e])), c = s.get(r), p = s.get(i);
|
|
166
|
+
if (!c || !p) return [];
|
|
167
|
+
let [h, g, T] = await Promise.all([
|
|
168
|
+
e(n, c).then(f),
|
|
169
|
+
e(n, p).then(f),
|
|
170
|
+
s.has(a) ? e(n, s.get(a)).then(f) : Promise.resolve(null)
|
|
171
|
+
]), E = l(h, "DOCX document"), O = x(g), k = S(T), A = [], j = /* @__PURE__ */ new Set(), M = "";
|
|
172
|
+
for (let e of u(E, "p")) {
|
|
173
|
+
let t = v(e);
|
|
174
|
+
for (let n of u(e, "OLEObject")) {
|
|
175
|
+
let e = d(n, "id"), i = O.get(e);
|
|
176
|
+
if (!e || !i || i.external) continue;
|
|
177
|
+
let a = i.type.toLowerCase(), o = a.endsWith("/package"), c = a.endsWith("/oleobject");
|
|
178
|
+
if (!o && !c) continue;
|
|
179
|
+
let l = n.getAttribute("ProgID") ?? d(n, "ProgID"), u = c ? w(l) : null;
|
|
180
|
+
if (c && !u) continue;
|
|
181
|
+
let f = _(r, i.target), p = s.get(f);
|
|
182
|
+
if (!f || !p || j.has(f)) continue;
|
|
183
|
+
j.add(f);
|
|
184
|
+
let h = m(f), g = u ? `${u.label}.${u.extension}` : h, v = y(t || (u ? u.label : M));
|
|
185
|
+
A.push({
|
|
186
|
+
id: e,
|
|
187
|
+
label: v,
|
|
188
|
+
name: b(v, g),
|
|
189
|
+
originalName: g,
|
|
190
|
+
path: f,
|
|
191
|
+
size: p.size,
|
|
192
|
+
contentType: u?.contentType ?? C(f, k),
|
|
193
|
+
storage: u ? "ole-package" : "package",
|
|
194
|
+
preview: D(n, O, s, k),
|
|
195
|
+
entry: p
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
t && (M = t);
|
|
199
|
+
}
|
|
200
|
+
return A;
|
|
201
|
+
}
|
|
202
|
+
async function A(t, r) {
|
|
203
|
+
if (r.size > o) throw Error(`内嵌文件超过 ${o / 1024 / 1024} MiB,已拒绝在浏览器中展开`);
|
|
204
|
+
let i = await e(t, r.entry);
|
|
205
|
+
return r.storage === "ole-package" ? n(i, o) : i;
|
|
206
|
+
}
|
|
207
|
+
async function j(t, n) {
|
|
208
|
+
let r = n.preview;
|
|
209
|
+
if (!r) return null;
|
|
210
|
+
if (r.size > s) throw Error(`内嵌预览图超过 ${s / 1024 / 1024} MiB,已跳过正文预览`);
|
|
211
|
+
let i = await e(t, r.entry), a = O(i, r.contentType);
|
|
212
|
+
return [
|
|
213
|
+
"image/png",
|
|
214
|
+
"image/jpeg",
|
|
215
|
+
"image/gif",
|
|
216
|
+
"image/bmp",
|
|
217
|
+
"image/webp"
|
|
218
|
+
].includes(a) ? {
|
|
219
|
+
bytes: i,
|
|
220
|
+
contentType: a
|
|
221
|
+
} : null;
|
|
222
|
+
}
|
|
223
|
+
//#endregion
|
|
224
|
+
export { A as extractDocxEmbeddedFile, j as extractDocxEmbeddedPreview, k as readDocxEmbeddedFiles };
|
|
225
|
+
|
|
226
|
+
//# sourceMappingURL=docxEmbeddings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docxEmbeddings.js","names":[],"sources":["../../src/lib/docxEmbeddings.ts"],"sourcesContent":["import { extractZipEntry, readZip, type ZipEntry } from '@/lib/zip'\nimport { extractOlePackage } from '@/lib/olePackage'\n\nconst DOCUMENT_PART = 'word/document.xml'\nconst DOCUMENT_RELS_PART = 'word/_rels/document.xml.rels'\nconst CONTENT_TYPES_PART = '[Content_Types].xml'\nconst MAX_EMBEDDED_FILE_BYTES = 100 * 1024 * 1024\nconst MAX_PREVIEW_IMAGE_BYTES = 20 * 1024 * 1024\n\nconst MIME_BY_EXTENSION: Record<string, string> = {\n docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',\n pdf: 'application/pdf',\n pptx: 'application/vnd.openxmlformats-officedocument.presentationml.presentation',\n xls: 'application/vnd.ms-excel',\n xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',\n}\n\nexport interface DocxEmbeddedFile {\n /** Stable relationship id inside word/document.xml. */\n id: string\n /** Human-facing caption inferred from the nearest preceding Word paragraph. */\n label: string\n /** Friendly download name (caption + original extension when a caption exists). */\n name: string\n /** Original package basename, e.g. Microsoft_Excel_Worksheet1.xlsx. */\n originalName: string\n /** Resolved package path inside the DOCX ZIP. */\n path: string\n size: number\n contentType: string\n storage: 'package' | 'ole-package'\n /** Word/WPS-generated visual snapshot for the embedded object, when present. */\n preview?: DocxEmbeddedPreview\n /** Internal ZIP descriptor retained so extraction stays on-demand. */\n entry: ZipEntry\n}\n\nexport interface DocxEmbeddedPreview {\n path: string\n size: number\n contentType: string\n widthPt?: number\n heightPt?: number\n entry: ZipEntry\n}\n\nexport interface ExtractedDocxPreview {\n bytes: Uint8Array\n contentType: string\n}\n\ninterface Relationship {\n target: string\n type: string\n external: boolean\n}\n\ninterface OlePackageKind {\n extension: string\n contentType: string\n label: string\n}\n\nfunction parseXml(xml: string, label: string): XMLDocument {\n const document = new DOMParser().parseFromString(xml, 'application/xml')\n const error = Array.from(document.getElementsByTagNameNS('*', 'parsererror'))[0]\n if (error) throw new Error(`${label} XML 无法解析:${error.textContent?.trim() || '格式错误'}`)\n return document\n}\n\nfunction elements(root: Document | Element, localName: string): Element[] {\n return Array.from(root.getElementsByTagNameNS('*', localName))\n}\n\nfunction attrByLocalName(element: Element, localName: string): string {\n return (\n Array.from(element.attributes).find((attribute) => attribute.localName === localName)?.value ??\n ''\n )\n}\n\nfunction decodeEntry(bytes: Uint8Array): string {\n return new TextDecoder('utf-8').decode(bytes)\n}\n\nfunction extension(name: string): string {\n const i = name.lastIndexOf('.')\n return i > 0 && i < name.length - 1 ? name.slice(i + 1).toLowerCase() : ''\n}\n\nfunction basename(path: string): string {\n return path.slice(path.lastIndexOf('/') + 1)\n}\n\nfunction hasControlCharacters(value: string): boolean {\n for (const character of value) if (character.charCodeAt(0) < 0x20) return true\n return false\n}\n\nfunction sanitizeFileName(value: string): string {\n const invalid = '<>:\"/\\\\|?*'\n let result = ''\n for (const character of value) {\n result += character.charCodeAt(0) < 0x20 || invalid.includes(character) ? '_' : character\n }\n return result\n}\n\n/** Resolve an OPC relationship target relative to its source part without allowing it above the package root. */\nfunction resolvePartTarget(sourcePart: string, target: string): string {\n const raw = target.split(/[?#]/, 1)[0]?.replace(/\\\\/g, '/') ?? ''\n if (!raw || hasControlCharacters(raw)) return ''\n const parts = raw.startsWith('/') ? [] : sourcePart.split('/').slice(0, -1)\n for (const encoded of raw.split('/')) {\n let segment: string\n try {\n segment = decodeURIComponent(encoded)\n } catch {\n return ''\n }\n if (!segment || segment === '.') continue\n if (segment === '..') {\n if (parts.length === 0) return ''\n parts.pop()\n continue\n }\n if (segment.includes('/') || segment.includes('\\\\')) return ''\n parts.push(segment)\n }\n return parts.join('/')\n}\n\nfunction paragraphText(paragraph: Element): string {\n return elements(paragraph, 't')\n .map((node) => node.textContent ?? '')\n .join('')\n .replace(/\\s+/g, ' ')\n .trim()\n}\n\nfunction cleanLabel(value: string): string {\n const label = value.replace(/\\s+/g, ' ').trim()\n if (/^(?:点击|双击).*(?:查看|打开).*(?:文件|表格)/.test(label)) return ''\n return label.slice(0, 100)\n}\n\nfunction friendlyName(label: string, originalName: string): string {\n if (!label) return originalName\n const safe = sanitizeFileName(label)\n .replace(/[. ]+$/g, '')\n .trim()\n .slice(0, 120)\n if (!safe) return originalName\n const ext = extension(originalName)\n return ext && !safe.toLowerCase().endsWith(`.${ext}`) ? `${safe}.${ext}` : safe\n}\n\nfunction readRelationships(xml: string): Map<string, Relationship> {\n const document = parseXml(xml, 'DOCX relationship')\n const out = new Map<string, Relationship>()\n for (const node of elements(document, 'Relationship')) {\n const id = node.getAttribute('Id') ?? ''\n const target = node.getAttribute('Target') ?? ''\n if (!id || !target) continue\n out.set(id, {\n target,\n type: node.getAttribute('Type') ?? '',\n external: (node.getAttribute('TargetMode') ?? '').toLowerCase() === 'external',\n })\n }\n return out\n}\n\nfunction readContentTypes(xml: string | null): {\n defaults: Map<string, string>\n overrides: Map<string, string>\n} {\n const defaults = new Map<string, string>()\n const overrides = new Map<string, string>()\n if (!xml) return { defaults, overrides }\n const document = parseXml(xml, 'DOCX content types')\n for (const node of elements(document, 'Default')) {\n const ext = (node.getAttribute('Extension') ?? '').toLowerCase()\n const type = node.getAttribute('ContentType') ?? ''\n if (ext && type) defaults.set(ext, type)\n }\n for (const node of elements(document, 'Override')) {\n const path = (node.getAttribute('PartName') ?? '').replace(/^\\/+/, '')\n const type = node.getAttribute('ContentType') ?? ''\n if (path && type) overrides.set(path, type)\n }\n return { defaults, overrides }\n}\n\nfunction contentTypeFor(path: string, contentTypes: ReturnType<typeof readContentTypes>): string {\n const ext = extension(path)\n return (\n contentTypes.overrides.get(path) ??\n contentTypes.defaults.get(ext) ??\n MIME_BY_EXTENSION[ext] ??\n 'application/octet-stream'\n )\n}\n\nfunction olePackageKind(programId: string): OlePackageKind | null {\n const normalized = programId.toLowerCase()\n if (normalized === 'powerpoint.show.12' || normalized === 'powerpoint.slide.12') {\n return {\n extension: 'pptx',\n contentType: MIME_BY_EXTENSION.pptx,\n label: 'PowerPoint 演示文稿',\n }\n }\n if (normalized === 'excel.sheet.12') {\n return { extension: 'xlsx', contentType: MIME_BY_EXTENSION.xlsx, label: 'Excel 工作簿' }\n }\n if (normalized === 'word.document.12') {\n return { extension: 'docx', contentType: MIME_BY_EXTENSION.docx, label: 'Word 文档' }\n }\n return null\n}\n\nfunction ancestorByLocalName(element: Element, localName: string): Element | null {\n let current: Element | null = element\n while (current) {\n if (current.localName === localName) return current\n current = current.parentElement\n }\n return null\n}\n\nfunction vmlLengthInPoints(style: string, property: 'width' | 'height'): number | undefined {\n const match = style.match(\n new RegExp(`(?:^|;)\\\\s*${property}\\\\s*:\\\\s*([0-9]+(?:\\\\.[0-9]+)?)(pt|px|in|cm|mm)`, 'i'),\n )\n if (!match) return undefined\n const value = Number(match[1])\n const unit = match[2]?.toLowerCase()\n const points =\n unit === 'pt'\n ? value\n : unit === 'px'\n ? value * 0.75\n : unit === 'in'\n ? value * 72\n : unit === 'cm'\n ? (value * 72) / 2.54\n : (value * 72) / 25.4\n return Number.isFinite(points) && points > 0 && points <= 2000 ? points : undefined\n}\n\nfunction embeddedPreview(\n object: Element,\n relationships: ReadonlyMap<string, Relationship>,\n byPath: ReadonlyMap<string, ZipEntry>,\n contentTypes: ReturnType<typeof readContentTypes>,\n): DocxEmbeddedPreview | undefined {\n const wrapper = ancestorByLocalName(object, 'object')\n if (!wrapper) return undefined\n const imageData = elements(wrapper, 'imagedata')[0]\n const relationshipId = imageData ? attrByLocalName(imageData, 'id') : ''\n const relationship = relationships.get(relationshipId)\n if (\n !relationshipId ||\n !relationship ||\n relationship.external ||\n !relationship.type.toLowerCase().endsWith('/image')\n )\n return undefined\n const path = resolvePartTarget(DOCUMENT_PART, relationship.target)\n const entry = byPath.get(path)\n if (!path || !entry) return undefined\n const shape = elements(wrapper, 'shape')[0]\n const style = shape?.getAttribute('style') ?? ''\n return {\n path,\n size: entry.size,\n contentType: contentTypeFor(path, contentTypes),\n widthPt: vmlLengthInPoints(style, 'width'),\n heightPt: vmlLengthInPoints(style, 'height'),\n entry,\n }\n}\n\nfunction sniffImageContentType(bytes: Uint8Array, declared: string): string {\n if (\n bytes.length >= 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47\n )\n return 'image/png'\n if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff)\n return 'image/jpeg'\n if (\n bytes.length >= 6 &&\n bytes[0] === 0x47 &&\n bytes[1] === 0x49 &&\n bytes[2] === 0x46 &&\n bytes[3] === 0x38\n )\n return 'image/gif'\n if (bytes.length >= 2 && bytes[0] === 0x42 && bytes[1] === 0x4d) return 'image/bmp'\n if (\n bytes.length >= 12 &&\n bytes[0] === 0x52 &&\n bytes[1] === 0x49 &&\n bytes[2] === 0x46 &&\n bytes[3] === 0x46 &&\n bytes[8] === 0x57 &&\n bytes[9] === 0x45 &&\n bytes[10] === 0x42 &&\n bytes[11] === 0x50\n )\n return 'image/webp'\n return declared.startsWith('image/') ? declared : 'application/octet-stream'\n}\n\n/**\n * Discover embedded package relationships (`w:object` / `o:OLEObject`) in document order.\n * `docx-preview` currently drops these nodes, so the caller can expose the original bytes separately.\n */\nexport async function readDocxEmbeddedFiles(buffer: ArrayBuffer): Promise<DocxEmbeddedFile[]> {\n const entries = readZip(buffer)\n const byPath = new Map(\n entries.filter((entry) => !entry.isDir).map((entry) => [entry.path, entry]),\n )\n const documentEntry = byPath.get(DOCUMENT_PART)\n const relationshipsEntry = byPath.get(DOCUMENT_RELS_PART)\n if (!documentEntry || !relationshipsEntry) return []\n\n const [documentXml, relationshipsXml, contentTypesXml] = await Promise.all([\n extractZipEntry(buffer, documentEntry).then(decodeEntry),\n extractZipEntry(buffer, relationshipsEntry).then(decodeEntry),\n byPath.has(CONTENT_TYPES_PART)\n ? extractZipEntry(buffer, byPath.get(CONTENT_TYPES_PART) as ZipEntry).then(decodeEntry)\n : Promise.resolve(null),\n ])\n const document = parseXml(documentXml, 'DOCX document')\n const relationships = readRelationships(relationshipsXml)\n const contentTypes = readContentTypes(contentTypesXml)\n const result: DocxEmbeddedFile[] = []\n const seenPaths = new Set<string>()\n let previousText = ''\n\n for (const paragraph of elements(document, 'p')) {\n const currentText = paragraphText(paragraph)\n for (const object of elements(paragraph, 'OLEObject')) {\n const relationshipId = attrByLocalName(object, 'id')\n const relationship = relationships.get(relationshipId)\n if (!relationshipId || !relationship || relationship.external) continue\n const relationshipType = relationship.type.toLowerCase()\n const directPackage = relationshipType.endsWith('/package')\n const oleObject = relationshipType.endsWith('/oleobject')\n if (!directPackage && !oleObject) continue\n const programId = object.getAttribute('ProgID') ?? attrByLocalName(object, 'ProgID')\n const oleKind = oleObject ? olePackageKind(programId) : null\n if (oleObject && !oleKind) continue\n\n const path = resolvePartTarget(DOCUMENT_PART, relationship.target)\n const entry = byPath.get(path)\n if (!path || !entry || seenPaths.has(path)) continue\n seenPaths.add(path)\n\n const sourceName = basename(path)\n const originalName = oleKind ? `${oleKind.label}.${oleKind.extension}` : sourceName\n const label = cleanLabel(currentText || (oleKind ? oleKind.label : previousText))\n result.push({\n id: relationshipId,\n label,\n name: friendlyName(label, originalName),\n originalName,\n path,\n size: entry.size,\n contentType: oleKind?.contentType ?? contentTypeFor(path, contentTypes),\n storage: oleKind ? 'ole-package' : 'package',\n preview: embeddedPreview(object, relationships, byPath, contentTypes),\n entry,\n })\n }\n if (currentText) previousText = currentText\n }\n return result\n}\n\n/** Extract one discovered embedded package on demand, with the same per-file ceiling as server exports. */\nexport async function extractDocxEmbeddedFile(\n buffer: ArrayBuffer,\n file: DocxEmbeddedFile,\n): Promise<Uint8Array> {\n if (file.size > MAX_EMBEDDED_FILE_BYTES) {\n throw new Error(\n `内嵌文件超过 ${MAX_EMBEDDED_FILE_BYTES / 1024 / 1024} MiB,已拒绝在浏览器中展开`,\n )\n }\n const bytes = await extractZipEntry(buffer, file.entry)\n return file.storage === 'ole-package' ? extractOlePackage(bytes, MAX_EMBEDDED_FILE_BYTES) : bytes\n}\n\n/** Extract the inert Word/WPS snapshot paired with an embedded object. */\nexport async function extractDocxEmbeddedPreview(\n buffer: ArrayBuffer,\n file: DocxEmbeddedFile,\n): Promise<ExtractedDocxPreview | null> {\n const preview = file.preview\n if (!preview) return null\n if (preview.size > MAX_PREVIEW_IMAGE_BYTES) {\n throw new Error(`内嵌预览图超过 ${MAX_PREVIEW_IMAGE_BYTES / 1024 / 1024} MiB,已跳过正文预览`)\n }\n const bytes = await extractZipEntry(buffer, preview.entry)\n const contentType = sniffImageContentType(bytes, preview.contentType)\n if (!['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/webp'].includes(contentType)) {\n return null\n }\n return { bytes, contentType }\n}\n"],"mappings":";;;AAGA,IAAM,IAAgB,qBAChB,IAAqB,gCACrB,IAAqB,uBACrB,IAA0B,MAAM,OAAO,MACvC,IAA0B,KAAK,OAAO,MAEtC,IAA4C;CAChD,MAAM;CACN,KAAK;CACL,MAAM;CACN,KAAK;CACL,MAAM;AACR;AAgDA,SAAS,EAAS,GAAa,GAA4B;CACzD,IAAM,IAAW,IAAI,UAAU,CAAC,CAAC,gBAAgB,GAAK,iBAAiB,GACjE,IAAQ,MAAM,KAAK,EAAS,uBAAuB,KAAK,aAAa,CAAC,CAAC,CAAC;CAC9E,IAAI,GAAO,MAAU,MAAM,GAAG,EAAM,YAAY,EAAM,aAAa,KAAK,KAAK,QAAQ;CACrF,OAAO;AACT;AAEA,SAAS,EAAS,GAA0B,GAA8B;CACxE,OAAO,MAAM,KAAK,EAAK,uBAAuB,KAAK,CAAS,CAAC;AAC/D;AAEA,SAAS,EAAgB,GAAkB,GAA2B;CACpE,OACE,MAAM,KAAK,EAAQ,UAAU,CAAC,CAAC,MAAM,MAAc,EAAU,cAAc,CAAS,CAAC,EAAE,SACvF;AAEJ;AAEA,SAAS,EAAY,GAA2B;CAC9C,OAAO,IAAI,YAAY,OAAO,CAAC,CAAC,OAAO,CAAK;AAC9C;AAEA,SAAS,EAAU,GAAsB;CACvC,IAAM,IAAI,EAAK,YAAY,GAAG;CAC9B,OAAO,IAAI,KAAK,IAAI,EAAK,SAAS,IAAI,EAAK,MAAM,IAAI,CAAC,CAAC,CAAC,YAAY,IAAI;AAC1E;AAEA,SAAS,EAAS,GAAsB;CACtC,OAAO,EAAK,MAAM,EAAK,YAAY,GAAG,IAAI,CAAC;AAC7C;AAEA,SAAS,EAAqB,GAAwB;CACpD,KAAK,IAAM,KAAa,GAAO,IAAI,EAAU,WAAW,CAAC,IAAI,IAAM,OAAO;CAC1E,OAAO;AACT;AAEA,SAAS,EAAiB,GAAuB;CAC/C,IACI,IAAS;CACb,KAAK,IAAM,KAAa,GACtB,KAAU,EAAU,WAAW,CAAC,IAAI,MAAQ,cAAQ,SAAS,CAAS,IAAI,MAAM;CAElF,OAAO;AACT;AAGA,SAAS,EAAkB,GAAoB,GAAwB;CACrE,IAAM,IAAM,EAAO,MAAM,QAAQ,CAAC,CAAC,CAAC,EAAE,EAAE,QAAQ,OAAO,GAAG,KAAK;CAC/D,IAAI,CAAC,KAAO,EAAqB,CAAG,GAAG,OAAO;CAC9C,IAAM,IAAQ,EAAI,WAAW,GAAG,IAAI,CAAC,IAAI,EAAW,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,EAAE;CAC1E,KAAK,IAAM,KAAW,EAAI,MAAM,GAAG,GAAG;EACpC,IAAI;EACJ,IAAI;GACF,IAAU,mBAAmB,CAAO;EACtC,QAAQ;GACN,OAAO;EACT;EACI,OAAC,KAAW,MAAY,MAC5B;OAAI,MAAY,MAAM;IACpB,IAAI,EAAM,WAAW,GAAG,OAAO;IAC/B,EAAM,IAAI;IACV;GACF;GACA,IAAI,EAAQ,SAAS,GAAG,KAAK,EAAQ,SAAS,IAAI,GAAG,OAAO;GAC5D,EAAM,KAAK,CAAO;EAFlB;CAGF;CACA,OAAO,EAAM,KAAK,GAAG;AACvB;AAEA,SAAS,EAAc,GAA4B;CACjD,OAAO,EAAS,GAAW,GAAG,CAAC,CAC5B,KAAK,MAAS,EAAK,eAAe,EAAE,CAAC,CACrC,KAAK,EAAE,CAAC,CACR,QAAQ,QAAQ,GAAG,CAAC,CACpB,KAAK;AACV;AAEA,SAAS,EAAW,GAAuB;CACzC,IAAM,IAAQ,EAAM,QAAQ,QAAQ,GAAG,CAAC,CAAC,KAAK;CAE9C,OADI,mCAAmC,KAAK,CAAK,IAAU,KACpD,EAAM,MAAM,GAAG,GAAG;AAC3B;AAEA,SAAS,EAAa,GAAe,GAA8B;CACjE,IAAI,CAAC,GAAO,OAAO;CACnB,IAAM,IAAO,EAAiB,CAAK,CAAC,CACjC,QAAQ,WAAW,EAAE,CAAC,CACtB,KAAK,CAAC,CACN,MAAM,GAAG,GAAG;CACf,IAAI,CAAC,GAAM,OAAO;CAClB,IAAM,IAAM,EAAU,CAAY;CAClC,OAAO,KAAO,CAAC,EAAK,YAAY,CAAC,CAAC,SAAS,IAAI,GAAK,IAAI,GAAG,EAAK,GAAG,MAAQ;AAC7E;AAEA,SAAS,EAAkB,GAAwC;CACjE,IAAM,IAAW,EAAS,GAAK,mBAAmB,GAC5C,oBAAM,IAAI,IAA0B;CAC1C,KAAK,IAAM,KAAQ,EAAS,GAAU,cAAc,GAAG;EACrD,IAAM,IAAK,EAAK,aAAa,IAAI,KAAK,IAChC,IAAS,EAAK,aAAa,QAAQ,KAAK;EAC1C,CAAC,KAAM,CAAC,KACZ,EAAI,IAAI,GAAI;GACV;GACA,MAAM,EAAK,aAAa,MAAM,KAAK;GACnC,WAAW,EAAK,aAAa,YAAY,KAAK,GAAA,CAAI,YAAY,MAAM;EACtE,CAAC;CACH;CACA,OAAO;AACT;AAEA,SAAS,EAAiB,GAGxB;CACA,IAAM,oBAAW,IAAI,IAAoB,GACnC,oBAAY,IAAI,IAAoB;CAC1C,IAAI,CAAC,GAAK,OAAO;EAAE;EAAU;CAAU;CACvC,IAAM,IAAW,EAAS,GAAK,oBAAoB;CACnD,KAAK,IAAM,KAAQ,EAAS,GAAU,SAAS,GAAG;EAChD,IAAM,KAAO,EAAK,aAAa,WAAW,KAAK,GAAA,CAAI,YAAY,GACzD,IAAO,EAAK,aAAa,aAAa,KAAK;EACjD,AAAI,KAAO,KAAM,EAAS,IAAI,GAAK,CAAI;CACzC;CACA,KAAK,IAAM,KAAQ,EAAS,GAAU,UAAU,GAAG;EACjD,IAAM,KAAQ,EAAK,aAAa,UAAU,KAAK,GAAA,CAAI,QAAQ,QAAQ,EAAE,GAC/D,IAAO,EAAK,aAAa,aAAa,KAAK;EACjD,AAAI,KAAQ,KAAM,EAAU,IAAI,GAAM,CAAI;CAC5C;CACA,OAAO;EAAE;EAAU;CAAU;AAC/B;AAEA,SAAS,EAAe,GAAc,GAA2D;CAC/F,IAAM,IAAM,EAAU,CAAI;CAC1B,OACE,EAAa,UAAU,IAAI,CAAI,KAC/B,EAAa,SAAS,IAAI,CAAG,KAC7B,EAAkB,MAClB;AAEJ;AAEA,SAAS,EAAe,GAA0C;CAChE,IAAM,IAAa,EAAU,YAAY;CAczC,OAbI,MAAe,wBAAwB,MAAe,wBACjD;EACL,WAAW;EACX,aAAa,EAAkB;EAC/B,OAAO;CACT,IAEE,MAAe,mBACV;EAAE,WAAW;EAAQ,aAAa,EAAkB;EAAM,OAAO;CAAY,IAElF,MAAe,qBACV;EAAE,WAAW;EAAQ,aAAa,EAAkB;EAAM,OAAO;CAAU,IAE7E;AACT;AAEA,SAAS,EAAoB,GAAkB,GAAmC;CAChF,IAAI,IAA0B;CAC9B,OAAO,IAAS;EACd,IAAI,EAAQ,cAAc,GAAW,OAAO;EAC5C,IAAU,EAAQ;CACpB;CACA,OAAO;AACT;AAEA,SAAS,EAAkB,GAAe,GAAkD;CAC1F,IAAM,IAAQ,EAAM,MACd,OAAO,cAAc,EAAS,kDAAkD,GAAG,CACzF;CACA,IAAI,CAAC,GAAO;CACZ,IAAM,IAAQ,OAAO,EAAM,EAAE,GACvB,IAAO,EAAM,EAAE,EAAE,YAAY,GAC7B,IACJ,MAAS,OACL,IACA,MAAS,OACP,IAAQ,MACR,MAAS,OACP,IAAQ,KACR,MAAS,OACN,IAAQ,KAAM,OACd,IAAQ,KAAM;CAC3B,OAAO,OAAO,SAAS,CAAM,KAAK,IAAS,KAAK,KAAU,MAAO,IAAS,KAAA;AAC5E;AAEA,SAAS,EACP,GACA,GACA,GACA,GACiC;CACjC,IAAM,IAAU,EAAoB,GAAQ,QAAQ;CACpD,IAAI,CAAC,GAAS;CACd,IAAM,IAAY,EAAS,GAAS,WAAW,CAAC,CAAC,IAC3C,IAAiB,IAAY,EAAgB,GAAW,IAAI,IAAI,IAChE,IAAe,EAAc,IAAI,CAAc;CACrD,IACE,CAAC,KACD,CAAC,KACD,EAAa,YACb,CAAC,EAAa,KAAK,YAAY,CAAC,CAAC,SAAS,QAAQ,GAElD;CACF,IAAM,IAAO,EAAkB,GAAe,EAAa,MAAM,GAC3D,IAAQ,EAAO,IAAI,CAAI;CAC7B,IAAI,CAAC,KAAQ,CAAC,GAAO;CAErB,IAAM,IADQ,EAAS,GAAS,OAAO,CAAC,CAAC,EAC3B,EAAO,aAAa,OAAO,KAAK;CAC9C,OAAO;EACL;EACA,MAAM,EAAM;EACZ,aAAa,EAAe,GAAM,CAAY;EAC9C,SAAS,EAAkB,GAAO,OAAO;EACzC,UAAU,EAAkB,GAAO,QAAQ;EAC3C;CACF;AACF;AAEA,SAAS,EAAsB,GAAmB,GAA0B;CAgC1E,OA9BE,EAAM,UAAU,KAChB,EAAM,OAAO,OACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,KAEN,cACL,EAAM,UAAU,KAAK,EAAM,OAAO,OAAQ,EAAM,OAAO,OAAQ,EAAM,OAAO,MACvE,eAEP,EAAM,UAAU,KAChB,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,KAEN,cACL,EAAM,UAAU,KAAK,EAAM,OAAO,MAAQ,EAAM,OAAO,KAAa,cAEtE,EAAM,UAAU,MAChB,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,OAAO,MACb,EAAM,QAAQ,MACd,EAAM,QAAQ,KAEP,eACF,EAAS,WAAW,QAAQ,IAAI,IAAW;AACpD;AAMA,eAAsB,EAAsB,GAAkD;CAC5F,IAAM,IAAU,EAAQ,CAAM,GACxB,IAAS,IAAI,IACjB,EAAQ,QAAQ,MAAU,CAAC,EAAM,KAAK,CAAC,CAAC,KAAK,MAAU,CAAC,EAAM,MAAM,CAAK,CAAC,CAC5E,GACM,IAAgB,EAAO,IAAI,CAAa,GACxC,IAAqB,EAAO,IAAI,CAAkB;CACxD,IAAI,CAAC,KAAiB,CAAC,GAAoB,OAAO,CAAC;CAEnD,IAAM,CAAC,GAAa,GAAkB,KAAmB,MAAM,QAAQ,IAAI;EACzE,EAAgB,GAAQ,CAAa,CAAC,CAAC,KAAK,CAAW;EACvD,EAAgB,GAAQ,CAAkB,CAAC,CAAC,KAAK,CAAW;EAC5D,EAAO,IAAI,CAAkB,IACzB,EAAgB,GAAQ,EAAO,IAAI,CAAkB,CAAa,CAAC,CAAC,KAAK,CAAW,IACpF,QAAQ,QAAQ,IAAI;CAC1B,CAAC,GACK,IAAW,EAAS,GAAa,eAAe,GAChD,IAAgB,EAAkB,CAAgB,GAClD,IAAe,EAAiB,CAAe,GAC/C,IAA6B,CAAC,GAC9B,oBAAY,IAAI,IAAY,GAC9B,IAAe;CAEnB,KAAK,IAAM,KAAa,EAAS,GAAU,GAAG,GAAG;EAC/C,IAAM,IAAc,EAAc,CAAS;EAC3C,KAAK,IAAM,KAAU,EAAS,GAAW,WAAW,GAAG;GACrD,IAAM,IAAiB,EAAgB,GAAQ,IAAI,GAC7C,IAAe,EAAc,IAAI,CAAc;GACrD,IAAI,CAAC,KAAkB,CAAC,KAAgB,EAAa,UAAU;GAC/D,IAAM,IAAmB,EAAa,KAAK,YAAY,GACjD,IAAgB,EAAiB,SAAS,UAAU,GACpD,IAAY,EAAiB,SAAS,YAAY;GACxD,IAAI,CAAC,KAAiB,CAAC,GAAW;GAClC,IAAM,IAAY,EAAO,aAAa,QAAQ,KAAK,EAAgB,GAAQ,QAAQ,GAC7E,IAAU,IAAY,EAAe,CAAS,IAAI;GACxD,IAAI,KAAa,CAAC,GAAS;GAE3B,IAAM,IAAO,EAAkB,GAAe,EAAa,MAAM,GAC3D,IAAQ,EAAO,IAAI,CAAI;GAC7B,IAAI,CAAC,KAAQ,CAAC,KAAS,EAAU,IAAI,CAAI,GAAG;GAC5C,EAAU,IAAI,CAAI;GAElB,IAAM,IAAa,EAAS,CAAI,GAC1B,IAAe,IAAU,GAAG,EAAQ,MAAM,GAAG,EAAQ,cAAc,GACnE,IAAQ,EAAW,MAAgB,IAAU,EAAQ,QAAQ,EAAa;GAChF,EAAO,KAAK;IACV,IAAI;IACJ;IACA,MAAM,EAAa,GAAO,CAAY;IACtC;IACA;IACA,MAAM,EAAM;IACZ,aAAa,GAAS,eAAe,EAAe,GAAM,CAAY;IACtE,SAAS,IAAU,gBAAgB;IACnC,SAAS,EAAgB,GAAQ,GAAe,GAAQ,CAAY;IACpE;GACF,CAAC;EACH;EACA,AAAI,MAAa,IAAe;CAClC;CACA,OAAO;AACT;AAGA,eAAsB,EACpB,GACA,GACqB;CACrB,IAAI,EAAK,OAAO,GACd,MAAU,MACR,UAAU,IAA0B,OAAO,KAAK,gBAClD;CAEF,IAAM,IAAQ,MAAM,EAAgB,GAAQ,EAAK,KAAK;CACtD,OAAO,EAAK,YAAY,gBAAgB,EAAkB,GAAO,CAAuB,IAAI;AAC9F;AAGA,eAAsB,EACpB,GACA,GACsC;CACtC,IAAM,IAAU,EAAK;CACrB,IAAI,CAAC,GAAS,OAAO;CACrB,IAAI,EAAQ,OAAO,GACjB,MAAU,MAAM,WAAW,IAA0B,OAAO,KAAK,aAAa;CAEhF,IAAM,IAAQ,MAAM,EAAgB,GAAQ,EAAQ,KAAK,GACnD,IAAc,EAAsB,GAAO,EAAQ,WAAW;CAIpE,OAHK;EAAC;EAAa;EAAc;EAAa;EAAa;CAAY,CAAC,CAAC,SAAS,CAAW,IAGtF;EAAE;EAAO;CAAY,IAFnB;AAGX"}
|