@files-preview-app/preview-file 1.2.9 → 1.3.1
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/dist/angular.cjs +668 -154
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.js +667 -154
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +711 -154
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +709 -155
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +668 -154
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +667 -154
- package/dist/react.js.map +1 -1
- package/dist/vue.cjs +668 -154
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.js +667 -154
- package/dist/vue.js.map +1 -1
- package/package.json +1 -1
package/dist/react.cjs
CHANGED
|
@@ -18,6 +18,7 @@ var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
|
|
|
18
18
|
var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
|
|
19
19
|
var jsxRuntime = require('react/jsx-runtime');
|
|
20
20
|
|
|
21
|
+
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
21
22
|
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
22
23
|
|
|
23
24
|
function _interopNamespace(e) {
|
|
@@ -41,6 +42,7 @@ function _interopNamespace(e) {
|
|
|
41
42
|
var DOMPurify6__default = /*#__PURE__*/_interopDefault(DOMPurify6);
|
|
42
43
|
var pdfjsLib__namespace = /*#__PURE__*/_interopNamespace(pdfjsLib);
|
|
43
44
|
var docx__namespace = /*#__PURE__*/_interopNamespace(docx);
|
|
45
|
+
var fflate__namespace = /*#__PURE__*/_interopNamespace(fflate);
|
|
44
46
|
var XLSX__namespace = /*#__PURE__*/_interopNamespace(XLSX);
|
|
45
47
|
var hljs__default = /*#__PURE__*/_interopDefault(hljs);
|
|
46
48
|
var THREE__namespace = /*#__PURE__*/_interopNamespace(THREE);
|
|
@@ -370,16 +372,21 @@ async function sourceToArrayBuffer(source, signal) {
|
|
|
370
372
|
metadata.mimeType = source.type || void 0;
|
|
371
373
|
metadata.extension = extractExtension(source.name);
|
|
372
374
|
buffer = await source.arrayBuffer();
|
|
373
|
-
} else if (source instanceof Blob) {
|
|
375
|
+
} else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
|
|
374
376
|
metadata.size = source.size;
|
|
375
377
|
metadata.mimeType = source.type || void 0;
|
|
378
|
+
if (source.name) {
|
|
379
|
+
metadata.name = source.name;
|
|
380
|
+
metadata.extension = extractExtension(source.name);
|
|
381
|
+
}
|
|
376
382
|
buffer = await source.arrayBuffer();
|
|
377
|
-
} else if (source instanceof ArrayBuffer) {
|
|
383
|
+
} else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
|
|
378
384
|
buffer = source;
|
|
379
|
-
} else if (source instanceof Uint8Array) {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
385
|
+
} else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
|
|
386
|
+
const view = source;
|
|
387
|
+
buffer = view.buffer.slice(
|
|
388
|
+
view.byteOffset,
|
|
389
|
+
view.byteOffset + view.byteLength
|
|
383
390
|
);
|
|
384
391
|
} else {
|
|
385
392
|
throw new Error("Unsupported file source type");
|
|
@@ -464,6 +471,45 @@ function createElement(tag, attrs, ...children) {
|
|
|
464
471
|
}
|
|
465
472
|
return el;
|
|
466
473
|
}
|
|
474
|
+
var DB_NAME = "PreviewFileTransferDB";
|
|
475
|
+
var DB_STORE = "transfers";
|
|
476
|
+
function openDB() {
|
|
477
|
+
return new Promise((resolve, reject) => {
|
|
478
|
+
if (typeof indexedDB === "undefined") {
|
|
479
|
+
return reject(new Error("IndexedDB is not available"));
|
|
480
|
+
}
|
|
481
|
+
const req = indexedDB.open(DB_NAME, 1);
|
|
482
|
+
req.onupgradeneeded = () => {
|
|
483
|
+
const db = req.result;
|
|
484
|
+
if (!db.objectStoreNames.contains(DB_STORE)) {
|
|
485
|
+
db.createObjectStore(DB_STORE, { keyPath: "id" });
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
req.onsuccess = () => resolve(req.result);
|
|
489
|
+
req.onerror = () => reject(req.error);
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
async function saveTransferPayload(id, payload) {
|
|
493
|
+
if (typeof window !== "undefined") {
|
|
494
|
+
try {
|
|
495
|
+
window[id] = payload;
|
|
496
|
+
window.__lastTransfer = payload;
|
|
497
|
+
} catch {
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
try {
|
|
501
|
+
const db = await openDB();
|
|
502
|
+
return new Promise((resolve, reject) => {
|
|
503
|
+
const tx = db.transaction(DB_STORE, "readwrite");
|
|
504
|
+
const store = tx.objectStore(DB_STORE);
|
|
505
|
+
store.put({ id, ...payload, timestamp: Date.now() });
|
|
506
|
+
tx.oncomplete = () => resolve();
|
|
507
|
+
tx.onerror = () => reject(tx.error);
|
|
508
|
+
});
|
|
509
|
+
} catch (e) {
|
|
510
|
+
console.warn("[saveTransferPayload] IndexedDB store warning:", e);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
467
513
|
var ICON_ZOOM_IN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
|
|
468
514
|
var ICON_ZOOM_OUT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
|
|
469
515
|
var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><polyline points="11 9 8 12 11 15"></polyline><polyline points="13 9 16 12 13 15"></polyline></svg>`;
|
|
@@ -483,6 +529,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
|
|
|
483
529
|
var ICON_FAST_FORWARD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
|
|
484
530
|
var ICON_REWIND = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
|
|
485
531
|
var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
|
|
532
|
+
var ICON_EXTERNAL_WINDOW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`;
|
|
486
533
|
var ICON_MAP = {
|
|
487
534
|
"zoom-in": ICON_ZOOM_IN,
|
|
488
535
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -509,7 +556,9 @@ var ICON_MAP = {
|
|
|
509
556
|
"forward-10": ICON_FAST_FORWARD,
|
|
510
557
|
"rewind": ICON_REWIND,
|
|
511
558
|
"replay-10": ICON_REWIND,
|
|
512
|
-
"speed": ICON_SPEED
|
|
559
|
+
"speed": ICON_SPEED,
|
|
560
|
+
"open-window": ICON_EXTERNAL_WINDOW,
|
|
561
|
+
"external-window": ICON_EXTERNAL_WINDOW
|
|
513
562
|
};
|
|
514
563
|
var ToolbarController = class {
|
|
515
564
|
el;
|
|
@@ -652,9 +701,11 @@ var ToolbarController = class {
|
|
|
652
701
|
type: "button",
|
|
653
702
|
"data-action-id": id
|
|
654
703
|
});
|
|
655
|
-
const
|
|
656
|
-
if (
|
|
657
|
-
btn.innerHTML =
|
|
704
|
+
const internalSvg = ICON_MAP[iconHtml] || ICON_MAP[id];
|
|
705
|
+
if (internalSvg) {
|
|
706
|
+
btn.innerHTML = internalSvg;
|
|
707
|
+
} else if (iconHtml && iconHtml.startsWith("<svg")) {
|
|
708
|
+
btn.innerHTML = sanitizeSVG(iconHtml);
|
|
658
709
|
} else {
|
|
659
710
|
btn.textContent = title || id;
|
|
660
711
|
}
|
|
@@ -726,7 +777,7 @@ var ThumbnailPanel = class {
|
|
|
726
777
|
}
|
|
727
778
|
}
|
|
728
779
|
};
|
|
729
|
-
var FilePreviewViewer = class {
|
|
780
|
+
var FilePreviewViewer = class _FilePreviewViewer {
|
|
730
781
|
plugins = [];
|
|
731
782
|
activeInstance = null;
|
|
732
783
|
abortController = null;
|
|
@@ -763,6 +814,7 @@ var FilePreviewViewer = class {
|
|
|
763
814
|
* Preview a file in the given container element.
|
|
764
815
|
*/
|
|
765
816
|
async preview(container, source, options = {}) {
|
|
817
|
+
this.currentOptions = options;
|
|
766
818
|
this.abort();
|
|
767
819
|
this.abortController = new AbortController();
|
|
768
820
|
const { signal } = this.abortController;
|
|
@@ -772,7 +824,10 @@ var FilePreviewViewer = class {
|
|
|
772
824
|
this.showLoading();
|
|
773
825
|
try {
|
|
774
826
|
const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
|
|
775
|
-
|
|
827
|
+
if (options.metadata) {
|
|
828
|
+
Object.assign(metadata, options.metadata);
|
|
829
|
+
}
|
|
830
|
+
this.currentBuffer = buffer.slice(0);
|
|
776
831
|
this.currentMetadata = metadata;
|
|
777
832
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
778
833
|
const fileInfo = { metadata, buffer };
|
|
@@ -802,6 +857,7 @@ var FilePreviewViewer = class {
|
|
|
802
857
|
}
|
|
803
858
|
});
|
|
804
859
|
this.activeInstance = instance;
|
|
860
|
+
instance.openInSeparateWindow = () => this.openInSeparateWindow();
|
|
805
861
|
this.hideLoading();
|
|
806
862
|
this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
|
|
807
863
|
if (options.showToolbar !== false && this.toolbar) {
|
|
@@ -811,26 +867,16 @@ var FilePreviewViewer = class {
|
|
|
811
867
|
actions.push({
|
|
812
868
|
id: "fullscreen",
|
|
813
869
|
icon: "fullscreen",
|
|
814
|
-
label: "
|
|
870
|
+
label: "Fullscreen",
|
|
815
871
|
type: "button",
|
|
816
872
|
group: "view",
|
|
817
|
-
execute:
|
|
873
|
+
execute: () => {
|
|
818
874
|
try {
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
if (this.wrapperEl?.requestFullscreen) {
|
|
823
|
-
await this.wrapperEl.requestFullscreen().catch(() => {
|
|
824
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
825
|
-
});
|
|
826
|
-
} else {
|
|
827
|
-
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
828
|
-
}
|
|
875
|
+
if (!document.fullscreenElement) {
|
|
876
|
+
this.wrapperEl?.requestFullscreen?.();
|
|
877
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
829
878
|
} else {
|
|
830
|
-
|
|
831
|
-
await document.exitFullscreen().catch(() => {
|
|
832
|
-
});
|
|
833
|
-
}
|
|
879
|
+
document.exitFullscreen?.();
|
|
834
880
|
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
835
881
|
}
|
|
836
882
|
} catch {
|
|
@@ -842,6 +888,28 @@ var FilePreviewViewer = class {
|
|
|
842
888
|
}
|
|
843
889
|
});
|
|
844
890
|
}
|
|
891
|
+
const openWinAction = actions.find((a) => a.id === "open-window");
|
|
892
|
+
if (openWinAction) {
|
|
893
|
+
if (options?._isSeparateWindow) {
|
|
894
|
+
const idx = actions.indexOf(openWinAction);
|
|
895
|
+
if (idx !== -1) actions.splice(idx, 1);
|
|
896
|
+
} else {
|
|
897
|
+
openWinAction.execute = () => {
|
|
898
|
+
this.openInSeparateWindow();
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
} else if (!options?._isSeparateWindow) {
|
|
902
|
+
actions.push({
|
|
903
|
+
id: "open-window",
|
|
904
|
+
icon: "open-window",
|
|
905
|
+
label: "Open in Separate Full Window",
|
|
906
|
+
type: "button",
|
|
907
|
+
group: "actions",
|
|
908
|
+
execute: () => {
|
|
909
|
+
this.openInSeparateWindow();
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
}
|
|
845
913
|
this.toolbar.update(actions);
|
|
846
914
|
this.toolbar.show();
|
|
847
915
|
}
|
|
@@ -874,6 +942,104 @@ var FilePreviewViewer = class {
|
|
|
874
942
|
throw error;
|
|
875
943
|
}
|
|
876
944
|
}
|
|
945
|
+
/**
|
|
946
|
+
* Opens the current file preview in a separate full browser window.
|
|
947
|
+
*/
|
|
948
|
+
openInSeparateWindow() {
|
|
949
|
+
if (!this.currentBuffer) {
|
|
950
|
+
console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
|
|
951
|
+
return null;
|
|
952
|
+
}
|
|
953
|
+
if (this.currentOptions.onOpenSeparateWindow) {
|
|
954
|
+
return this.currentOptions.onOpenSeparateWindow({
|
|
955
|
+
buffer: this.currentBuffer,
|
|
956
|
+
metadata: this.currentMetadata || { name: "Document" },
|
|
957
|
+
options: this.currentOptions
|
|
958
|
+
});
|
|
959
|
+
}
|
|
960
|
+
const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
|
|
961
|
+
let clonedBuffer;
|
|
962
|
+
try {
|
|
963
|
+
clonedBuffer = this.currentBuffer.slice(0);
|
|
964
|
+
} catch {
|
|
965
|
+
clonedBuffer = this.currentBuffer;
|
|
966
|
+
}
|
|
967
|
+
const payload = {
|
|
968
|
+
buffer: clonedBuffer,
|
|
969
|
+
metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
|
|
970
|
+
options: { ...this.currentOptions, _isSeparateWindow: true }
|
|
971
|
+
};
|
|
972
|
+
if (typeof window !== "undefined") {
|
|
973
|
+
try {
|
|
974
|
+
window[transferId] = payload;
|
|
975
|
+
window.__lastTransfer = payload;
|
|
976
|
+
} catch {
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
saveTransferPayload(transferId, payload).catch((err) => {
|
|
980
|
+
console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
|
|
981
|
+
});
|
|
982
|
+
let targetUrl = null;
|
|
983
|
+
if (this.currentOptions.standaloneViewerUrl) {
|
|
984
|
+
const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
|
|
985
|
+
u.searchParams.set("mode", "fullscreen");
|
|
986
|
+
u.searchParams.set("transferId", transferId);
|
|
987
|
+
targetUrl = u.toString();
|
|
988
|
+
} else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
|
|
989
|
+
const u = new URL(window.location.href);
|
|
990
|
+
u.searchParams.set("mode", "fullscreen");
|
|
991
|
+
u.searchParams.set("transferId", transferId);
|
|
992
|
+
targetUrl = u.toString();
|
|
993
|
+
}
|
|
994
|
+
if (targetUrl) {
|
|
995
|
+
const newWin2 = window.open(targetUrl, "_blank");
|
|
996
|
+
if (!newWin2) {
|
|
997
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
998
|
+
return null;
|
|
999
|
+
}
|
|
1000
|
+
return newWin2;
|
|
1001
|
+
}
|
|
1002
|
+
const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
|
|
1003
|
+
const newWin = window.open("", "_blank");
|
|
1004
|
+
if (!newWin) {
|
|
1005
|
+
alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
|
|
1006
|
+
return null;
|
|
1007
|
+
}
|
|
1008
|
+
newWin.document.title = title;
|
|
1009
|
+
newWin.document.body.style.margin = "0";
|
|
1010
|
+
newWin.document.body.style.padding = "0";
|
|
1011
|
+
newWin.document.body.style.width = "100vw";
|
|
1012
|
+
newWin.document.body.style.height = "100vh";
|
|
1013
|
+
newWin.document.body.style.overflow = "hidden";
|
|
1014
|
+
newWin.document.body.style.backgroundColor = "#f8fafc";
|
|
1015
|
+
const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
|
|
1016
|
+
headNodes.forEach((node) => {
|
|
1017
|
+
newWin.document.head.appendChild(node.cloneNode(true));
|
|
1018
|
+
});
|
|
1019
|
+
const root = newWin.document.createElement("div");
|
|
1020
|
+
root.id = "full-window-preview-root";
|
|
1021
|
+
root.style.width = "100%";
|
|
1022
|
+
root.style.height = "100%";
|
|
1023
|
+
root.style.overflow = "hidden";
|
|
1024
|
+
newWin.document.body.appendChild(root);
|
|
1025
|
+
const separateViewer = new _FilePreviewViewer();
|
|
1026
|
+
for (const plugin of this.plugins) {
|
|
1027
|
+
separateViewer.registerPlugin(plugin);
|
|
1028
|
+
}
|
|
1029
|
+
separateViewer.preview(root, this.currentBuffer.slice(0), {
|
|
1030
|
+
...this.currentOptions,
|
|
1031
|
+
showToolbar: true,
|
|
1032
|
+
toolbarPosition: "top",
|
|
1033
|
+
metadata: this.currentMetadata || void 0,
|
|
1034
|
+
_isSeparateWindow: true
|
|
1035
|
+
}).catch((err) => {
|
|
1036
|
+
console.error("[FilePreviewViewer] Error rendering in separate window:", err);
|
|
1037
|
+
});
|
|
1038
|
+
newWin.addEventListener("beforeunload", () => {
|
|
1039
|
+
separateViewer.destroy();
|
|
1040
|
+
});
|
|
1041
|
+
return newWin;
|
|
1042
|
+
}
|
|
877
1043
|
/**
|
|
878
1044
|
* Subscribe to viewer events.
|
|
879
1045
|
*/
|
|
@@ -1279,8 +1445,20 @@ var CfbfReader = class {
|
|
|
1279
1445
|
}
|
|
1280
1446
|
};
|
|
1281
1447
|
if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
|
|
1282
|
-
if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
|
|
1283
|
-
|
|
1448
|
+
if (!pdfjsLib__namespace.GlobalWorkerOptions.workerPort && !pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
|
|
1449
|
+
const customWorker = window.__PDF_WORKER_SRC__;
|
|
1450
|
+
if (customWorker) {
|
|
1451
|
+
pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = customWorker;
|
|
1452
|
+
} else {
|
|
1453
|
+
try {
|
|
1454
|
+
pdfjsLib__namespace.GlobalWorkerOptions.workerPort = new Worker(
|
|
1455
|
+
new URL("pdfjs-dist/build/pdf.worker.min.mjs", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('react.cjs', document.baseURI).href))),
|
|
1456
|
+
{ type: "module" }
|
|
1457
|
+
);
|
|
1458
|
+
} catch {
|
|
1459
|
+
pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1284
1462
|
}
|
|
1285
1463
|
}
|
|
1286
1464
|
var PdfPlugin = class {
|
|
@@ -1374,6 +1552,14 @@ var PdfPlugin = class {
|
|
|
1374
1552
|
type: "button",
|
|
1375
1553
|
group: "actions",
|
|
1376
1554
|
execute: () => instance.print?.()
|
|
1555
|
+
},
|
|
1556
|
+
{
|
|
1557
|
+
id: "open-window",
|
|
1558
|
+
icon: "open-window",
|
|
1559
|
+
label: "Open in Separate Full Window",
|
|
1560
|
+
type: "button",
|
|
1561
|
+
group: "actions",
|
|
1562
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
1377
1563
|
}
|
|
1378
1564
|
];
|
|
1379
1565
|
}
|
|
@@ -1423,18 +1609,21 @@ var PdfPlugin = class {
|
|
|
1423
1609
|
indicator.style.pointerEvents = "none";
|
|
1424
1610
|
container.appendChild(indicator);
|
|
1425
1611
|
ctx.container.appendChild(container);
|
|
1612
|
+
const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
|
|
1613
|
+
const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
|
|
1426
1614
|
const loadingTask = pdfjsLib__namespace.getDocument({
|
|
1427
|
-
data: new Uint8Array(ctx.buffer),
|
|
1428
|
-
cMapUrl:
|
|
1615
|
+
data: new Uint8Array(ctx.buffer.slice(0)),
|
|
1616
|
+
cMapUrl: cmapsUrl,
|
|
1429
1617
|
cMapPacked: true,
|
|
1430
|
-
standardFontDataUrl:
|
|
1618
|
+
standardFontDataUrl: standardFontsUrl,
|
|
1619
|
+
verbosity: 0
|
|
1431
1620
|
});
|
|
1432
1621
|
const pdfDoc = await loadingTask.promise;
|
|
1433
1622
|
const totalPages = Math.max(1, pdfDoc.numPages);
|
|
1434
1623
|
let currentPage = 1;
|
|
1435
1624
|
let zoomScale = 1;
|
|
1436
1625
|
let rotation = 0;
|
|
1437
|
-
let fitMode = "
|
|
1626
|
+
let fitMode = "page";
|
|
1438
1627
|
let currentRenderTask = null;
|
|
1439
1628
|
const renderPage = async (pageNum) => {
|
|
1440
1629
|
if (currentRenderTask) {
|
|
@@ -1454,15 +1643,15 @@ var PdfPlugin = class {
|
|
|
1454
1643
|
const containerWidth = container.clientWidth || 900;
|
|
1455
1644
|
const containerHeight = container.clientHeight || 700;
|
|
1456
1645
|
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1457
|
-
const availWidth = Math.max(
|
|
1458
|
-
const availHeight = Math.max(
|
|
1646
|
+
const availWidth = Math.max(320, containerWidth - 48);
|
|
1647
|
+
const availHeight = Math.max(550, containerHeight - 88);
|
|
1459
1648
|
const scaleW = availWidth / unscaledVp.width;
|
|
1460
1649
|
const scaleH = availHeight / unscaledVp.height;
|
|
1461
1650
|
let fitScale;
|
|
1462
1651
|
if (fitMode === "page") {
|
|
1463
|
-
fitScale = Math.max(0.
|
|
1652
|
+
fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
|
|
1464
1653
|
} else {
|
|
1465
|
-
fitScale = Math.max(0.65, Math.min(1.
|
|
1654
|
+
fitScale = Math.max(0.65, Math.min(1.25, scaleW));
|
|
1466
1655
|
}
|
|
1467
1656
|
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1468
1657
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
@@ -1488,7 +1677,7 @@ var PdfPlugin = class {
|
|
|
1488
1677
|
currentRenderTask = null;
|
|
1489
1678
|
}
|
|
1490
1679
|
};
|
|
1491
|
-
|
|
1680
|
+
renderPage(1);
|
|
1492
1681
|
let resizeTimer = null;
|
|
1493
1682
|
const resizeObserver = new ResizeObserver(() => {
|
|
1494
1683
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
@@ -1532,7 +1721,7 @@ var PdfPlugin = class {
|
|
|
1532
1721
|
renderPage(currentPage);
|
|
1533
1722
|
},
|
|
1534
1723
|
fitToPage: () => {
|
|
1535
|
-
fitMode = fitMode === "
|
|
1724
|
+
fitMode = fitMode === "page" ? "width" : "page";
|
|
1536
1725
|
zoomScale = 1;
|
|
1537
1726
|
rotation = 0;
|
|
1538
1727
|
renderPage(currentPage);
|
|
@@ -2009,6 +2198,14 @@ var DocxPlugin = class {
|
|
|
2009
2198
|
type: "button",
|
|
2010
2199
|
group: "actions",
|
|
2011
2200
|
execute: () => instance.print?.()
|
|
2201
|
+
},
|
|
2202
|
+
{
|
|
2203
|
+
id: "open-window",
|
|
2204
|
+
icon: "open-window",
|
|
2205
|
+
label: "Open in Separate Full Window",
|
|
2206
|
+
type: "button",
|
|
2207
|
+
group: "actions",
|
|
2208
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
2012
2209
|
}
|
|
2013
2210
|
);
|
|
2014
2211
|
return actions;
|
|
@@ -2068,6 +2265,31 @@ var DocxPlugin = class {
|
|
|
2068
2265
|
}
|
|
2069
2266
|
if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
|
|
2070
2267
|
renderedSuccessfully = true;
|
|
2268
|
+
try {
|
|
2269
|
+
const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
|
|
2270
|
+
const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
|
|
2271
|
+
if (chartKeys.length > 0) {
|
|
2272
|
+
const allDivs = Array.from(wrapper.querySelectorAll("div"));
|
|
2273
|
+
const emptyContainers = allDivs.filter((div) => {
|
|
2274
|
+
const st = div.getAttribute("style") || "";
|
|
2275
|
+
return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
|
|
2276
|
+
});
|
|
2277
|
+
chartKeys.forEach((cKey, idx) => {
|
|
2278
|
+
const target = emptyContainers[idx];
|
|
2279
|
+
if (target) {
|
|
2280
|
+
const xmlStr = fflate.strFromU8(unzipped[cKey]);
|
|
2281
|
+
const svg = this.parseAndRenderChartSvg(xmlStr);
|
|
2282
|
+
if (svg) {
|
|
2283
|
+
target.innerHTML = svg;
|
|
2284
|
+
target.style.display = "block";
|
|
2285
|
+
target.style.margin = "12px auto";
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
} catch (chartErr) {
|
|
2291
|
+
console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
|
|
2292
|
+
}
|
|
2071
2293
|
}
|
|
2072
2294
|
} catch (err) {
|
|
2073
2295
|
console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
|
|
@@ -2099,64 +2321,74 @@ var DocxPlugin = class {
|
|
|
2099
2321
|
}
|
|
2100
2322
|
let sections = Array.from(wrapper.querySelectorAll("section.docx"));
|
|
2101
2323
|
const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
|
|
2102
|
-
if (sections.length
|
|
2103
|
-
const
|
|
2104
|
-
const
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
const
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
nextSec.
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
nextArticle
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
nextSec.appendChild(
|
|
2324
|
+
if (sections.length > 0 && cards.length === 0) {
|
|
2325
|
+
const finalSections = [];
|
|
2326
|
+
for (const singleSec of sections) {
|
|
2327
|
+
const contentContainer = singleSec.querySelector("article") || singleSec;
|
|
2328
|
+
const children = Array.from(contentContainer.children);
|
|
2329
|
+
const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
|
|
2330
|
+
const secH = singleSec.scrollHeight || singleSec.offsetHeight;
|
|
2331
|
+
if (secH > pageH * 1.25 && children.length > 1) {
|
|
2332
|
+
const childHeights = children.map((c) => {
|
|
2333
|
+
const rectH = c.getBoundingClientRect().height;
|
|
2334
|
+
const offH = c.offsetHeight;
|
|
2335
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
2336
|
+
const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
|
|
2337
|
+
return Math.max(rectH, offH, estH);
|
|
2338
|
+
});
|
|
2339
|
+
const parent = singleSec.parentElement || wrapper;
|
|
2340
|
+
const headerEl = singleSec.querySelector("header");
|
|
2341
|
+
const footerEl = singleSec.querySelector("footer");
|
|
2342
|
+
contentContainer.innerHTML = "";
|
|
2343
|
+
singleSec.style.minHeight = `${pageH}px`;
|
|
2344
|
+
singleSec.style.boxSizing = "border-box";
|
|
2345
|
+
let curContent = contentContainer;
|
|
2346
|
+
let curSec = singleSec;
|
|
2347
|
+
let curH = 0;
|
|
2348
|
+
const maxH = pageH - 140;
|
|
2349
|
+
finalSections.push(singleSec);
|
|
2350
|
+
for (let i = 0; i < children.length; i++) {
|
|
2351
|
+
const child = children[i];
|
|
2352
|
+
const chH = childHeights[i];
|
|
2353
|
+
curContent.appendChild(child);
|
|
2354
|
+
curH += chH;
|
|
2355
|
+
if (curH >= maxH && i < children.length - 1) {
|
|
2356
|
+
const nextSec = document.createElement("section");
|
|
2357
|
+
nextSec.className = singleSec.className;
|
|
2358
|
+
nextSec.style.cssText = singleSec.style.cssText;
|
|
2359
|
+
nextSec.style.minHeight = `${pageH}px`;
|
|
2360
|
+
nextSec.style.boxSizing = "border-box";
|
|
2361
|
+
nextSec.style.backgroundColor = "#ffffff";
|
|
2362
|
+
nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
2363
|
+
nextSec.style.borderRadius = "4px";
|
|
2364
|
+
nextSec.style.marginBottom = "24px";
|
|
2365
|
+
if (headerEl) {
|
|
2366
|
+
nextSec.appendChild(headerEl.cloneNode(true));
|
|
2367
|
+
}
|
|
2368
|
+
const nextArticle = document.createElement("article");
|
|
2369
|
+
if (contentContainer.tagName.toLowerCase() === "article") {
|
|
2370
|
+
nextArticle.style.cssText = contentContainer.style.cssText;
|
|
2371
|
+
}
|
|
2372
|
+
nextSec.appendChild(nextArticle);
|
|
2373
|
+
if (footerEl) {
|
|
2374
|
+
nextSec.appendChild(footerEl.cloneNode(true));
|
|
2375
|
+
}
|
|
2376
|
+
if (curSec.nextSibling) {
|
|
2377
|
+
parent.insertBefore(nextSec, curSec.nextSibling);
|
|
2378
|
+
} else {
|
|
2379
|
+
parent.appendChild(nextSec);
|
|
2380
|
+
}
|
|
2381
|
+
finalSections.push(nextSec);
|
|
2382
|
+
curSec = nextSec;
|
|
2383
|
+
curContent = nextArticle;
|
|
2384
|
+
curH = 0;
|
|
2151
2385
|
}
|
|
2152
|
-
parent.appendChild(nextSec);
|
|
2153
|
-
newSections.push(nextSec);
|
|
2154
|
-
curContent = nextArticle;
|
|
2155
|
-
curH = 0;
|
|
2156
2386
|
}
|
|
2387
|
+
} else {
|
|
2388
|
+
finalSections.push(singleSec);
|
|
2157
2389
|
}
|
|
2158
|
-
sections = newSections;
|
|
2159
2390
|
}
|
|
2391
|
+
sections = finalSections;
|
|
2160
2392
|
}
|
|
2161
2393
|
const pageElements = sections.length > 0 ? sections : cards;
|
|
2162
2394
|
const totalPages = Math.max(1, pageElements.length);
|
|
@@ -2548,6 +2780,88 @@ var DocxPlugin = class {
|
|
|
2548
2780
|
}
|
|
2549
2781
|
return result;
|
|
2550
2782
|
}
|
|
2783
|
+
parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
|
|
2784
|
+
const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
|
|
2785
|
+
let categories = [];
|
|
2786
|
+
if (catMatches.length > 0) {
|
|
2787
|
+
categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
|
|
2788
|
+
}
|
|
2789
|
+
if (categories.length === 0) {
|
|
2790
|
+
categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
|
|
2791
|
+
}
|
|
2792
|
+
const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
|
|
2793
|
+
const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
|
|
2794
|
+
const series = [];
|
|
2795
|
+
sers.forEach((s, sIdx) => {
|
|
2796
|
+
const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
|
|
2797
|
+
const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
|
|
2798
|
+
const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
|
|
2799
|
+
const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
|
|
2800
|
+
const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
|
|
2801
|
+
let values = [];
|
|
2802
|
+
if (valMatch) {
|
|
2803
|
+
values = [...valMatch[1].matchAll(/<c:pt\s+idx="(\d+)">\s*<c:v>([^<]+)<\/c:v>/g)].sort((a, b) => parseInt(a[1], 10) - parseInt(b[1], 10)).map((m) => parseFloat(m[2]) || 0);
|
|
2804
|
+
}
|
|
2805
|
+
series.push({ title, color, values });
|
|
2806
|
+
});
|
|
2807
|
+
if (series.length === 0) return "";
|
|
2808
|
+
let maxVal = 10;
|
|
2809
|
+
series.forEach((s) => s.values.forEach((v) => {
|
|
2810
|
+
if (v > maxVal) maxVal = v;
|
|
2811
|
+
}));
|
|
2812
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
2813
|
+
if (maxVal % 2 !== 0) maxVal++;
|
|
2814
|
+
const padLeft = 45;
|
|
2815
|
+
const padBottom = 55;
|
|
2816
|
+
const padTop = 20;
|
|
2817
|
+
const padRight = 20;
|
|
2818
|
+
const plotW = width - padLeft - padRight;
|
|
2819
|
+
const plotH = height - padTop - padBottom;
|
|
2820
|
+
const yTicks = 5;
|
|
2821
|
+
let gridLines = "";
|
|
2822
|
+
for (let i = 0; i <= yTicks; i++) {
|
|
2823
|
+
const val = maxVal / yTicks * i;
|
|
2824
|
+
const y = padTop + plotH - val / maxVal * plotH;
|
|
2825
|
+
gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
|
|
2826
|
+
gridLines += `<text x="${padLeft - 8}" y="${y + 4}" text-anchor="end" font-size="11" fill="#64748b" font-family="Calibri, sans-serif">${Math.round(val)}</text>`;
|
|
2827
|
+
}
|
|
2828
|
+
const numCats = categories.length;
|
|
2829
|
+
const numSers = series.length;
|
|
2830
|
+
const groupW = plotW / numCats;
|
|
2831
|
+
const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
|
|
2832
|
+
const groupPad = (groupW - barW * numSers) / 2;
|
|
2833
|
+
let bars = "";
|
|
2834
|
+
let catLabels = "";
|
|
2835
|
+
for (let c = 0; c < numCats; c++) {
|
|
2836
|
+
const catX = padLeft + c * groupW;
|
|
2837
|
+
catLabels += `<text x="${catX + groupW / 2}" y="${padTop + plotH + 18}" text-anchor="middle" font-size="11" fill="#334155" font-family="Calibri, sans-serif">${categories[c]}</text>`;
|
|
2838
|
+
for (let s = 0; s < numSers; s++) {
|
|
2839
|
+
const val = series[s].values[c] ?? 0;
|
|
2840
|
+
const bH = Math.max(0, val / maxVal * plotH);
|
|
2841
|
+
const bX = catX + groupPad + s * barW;
|
|
2842
|
+
const bY = padTop + plotH - bH;
|
|
2843
|
+
bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
|
|
2844
|
+
}
|
|
2845
|
+
}
|
|
2846
|
+
let legend = "";
|
|
2847
|
+
const legY = height - 12;
|
|
2848
|
+
let legX = padLeft + (plotW - numSers * 100) / 2;
|
|
2849
|
+
series.forEach((s) => {
|
|
2850
|
+
legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
|
|
2851
|
+
legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
|
|
2852
|
+
legX += 95;
|
|
2853
|
+
});
|
|
2854
|
+
return `
|
|
2855
|
+
<svg viewBox="0 0 ${width} ${height}" width="100%" height="100%" style="background:#ffffff; border-radius:4px; overflow:visible;" xmlns="http://www.w3.org/2000/svg">
|
|
2856
|
+
${gridLines}
|
|
2857
|
+
<line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2858
|
+
<line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
|
|
2859
|
+
${bars}
|
|
2860
|
+
${catLabels}
|
|
2861
|
+
${legend}
|
|
2862
|
+
</svg>
|
|
2863
|
+
`.trim();
|
|
2864
|
+
}
|
|
2551
2865
|
};
|
|
2552
2866
|
function docxPlugin() {
|
|
2553
2867
|
return new DocxPlugin();
|
|
@@ -3114,6 +3428,16 @@ var CodePlugin = class {
|
|
|
3114
3428
|
execute: () => {
|
|
3115
3429
|
instance.print?.();
|
|
3116
3430
|
}
|
|
3431
|
+
},
|
|
3432
|
+
{
|
|
3433
|
+
id: "open-window",
|
|
3434
|
+
icon: "open-window",
|
|
3435
|
+
label: "Open in Separate Full Window",
|
|
3436
|
+
type: "button",
|
|
3437
|
+
group: "actions",
|
|
3438
|
+
execute: () => {
|
|
3439
|
+
instance.openInSeparateWindow?.();
|
|
3440
|
+
}
|
|
3117
3441
|
}
|
|
3118
3442
|
);
|
|
3119
3443
|
return actions;
|
|
@@ -4282,6 +4606,14 @@ var RtfPlugin = class {
|
|
|
4282
4606
|
type: "button",
|
|
4283
4607
|
group: "actions",
|
|
4284
4608
|
execute: () => instance.print?.()
|
|
4609
|
+
},
|
|
4610
|
+
{
|
|
4611
|
+
id: "open-window",
|
|
4612
|
+
icon: "open-window",
|
|
4613
|
+
label: "Open in Separate Full Window",
|
|
4614
|
+
type: "button",
|
|
4615
|
+
group: "actions",
|
|
4616
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4285
4617
|
}
|
|
4286
4618
|
);
|
|
4287
4619
|
return actions;
|
|
@@ -4334,59 +4666,54 @@ var RtfPlugin = class {
|
|
|
4334
4666
|
}
|
|
4335
4667
|
const doc = new RTFJS__namespace.Document(ctx.buffer, {});
|
|
4336
4668
|
const htmlElements = await doc.render();
|
|
4337
|
-
|
|
4338
|
-
|
|
4339
|
-
|
|
4340
|
-
|
|
4341
|
-
const secH = singleEl.offsetHeight || singleEl.scrollHeight;
|
|
4342
|
-
if (secH > 1300 && children.length > 1) {
|
|
4343
|
-
const childHeights = children.map((c) => {
|
|
4344
|
-
const rectH = c.getBoundingClientRect().height;
|
|
4345
|
-
const offH = c.offsetHeight;
|
|
4346
|
-
const textLen = c.textContent?.trim().length || 0;
|
|
4347
|
-
const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
|
|
4348
|
-
return Math.max(rectH, offH, estH);
|
|
4349
|
-
});
|
|
4350
|
-
wrapper.innerHTML = "";
|
|
4351
|
-
const createRtfCard = () => {
|
|
4352
|
-
const card = document.createElement("div");
|
|
4353
|
-
card.className = "fp-rtf-page-card";
|
|
4354
|
-
card.style.backgroundColor = "#ffffff";
|
|
4355
|
-
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
4356
|
-
card.style.borderRadius = "4px";
|
|
4357
|
-
card.style.padding = "72px 56px";
|
|
4358
|
-
card.style.width = "816px";
|
|
4359
|
-
card.style.minHeight = "1056px";
|
|
4360
|
-
card.style.boxSizing = "border-box";
|
|
4361
|
-
card.style.marginBottom = "24px";
|
|
4362
|
-
return card;
|
|
4363
|
-
};
|
|
4364
|
-
let curCard = createRtfCard();
|
|
4365
|
-
wrapper.appendChild(curCard);
|
|
4366
|
-
pageElements = [curCard];
|
|
4367
|
-
let curH = 0;
|
|
4368
|
-
const maxH = 920;
|
|
4369
|
-
for (let i = 0; i < children.length; i++) {
|
|
4370
|
-
const child = children[i];
|
|
4371
|
-
const chH = childHeights[i];
|
|
4372
|
-
curCard.appendChild(child);
|
|
4373
|
-
curH += chH;
|
|
4374
|
-
if (curH >= maxH && i < children.length - 1) {
|
|
4375
|
-
curCard = createRtfCard();
|
|
4376
|
-
wrapper.appendChild(curCard);
|
|
4377
|
-
pageElements.push(curCard);
|
|
4378
|
-
curH = 0;
|
|
4379
|
-
}
|
|
4380
|
-
}
|
|
4669
|
+
const contentNodes = [];
|
|
4670
|
+
for (const item of htmlElements) {
|
|
4671
|
+
if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
|
|
4672
|
+
contentNodes.push(...Array.from(item.children));
|
|
4381
4673
|
} else {
|
|
4382
|
-
|
|
4674
|
+
contentNodes.push(item);
|
|
4383
4675
|
}
|
|
4384
|
-
}
|
|
4385
|
-
|
|
4386
|
-
|
|
4387
|
-
|
|
4388
|
-
|
|
4389
|
-
|
|
4676
|
+
}
|
|
4677
|
+
wrapper.innerHTML = "";
|
|
4678
|
+
contentNodes.forEach((node) => wrapper.appendChild(node));
|
|
4679
|
+
const childHeights = contentNodes.map((c) => {
|
|
4680
|
+
const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
|
|
4681
|
+
const offH = c.offsetHeight || 0;
|
|
4682
|
+
const textLen = c.textContent?.trim().length || 0;
|
|
4683
|
+
const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
|
|
4684
|
+
return Math.max(rectH, offH, estH);
|
|
4685
|
+
});
|
|
4686
|
+
wrapper.innerHTML = "";
|
|
4687
|
+
const createRtfCard = () => {
|
|
4688
|
+
const card = document.createElement("div");
|
|
4689
|
+
card.className = "fp-rtf-page-card";
|
|
4690
|
+
card.style.backgroundColor = "#ffffff";
|
|
4691
|
+
card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
4692
|
+
card.style.borderRadius = "4px";
|
|
4693
|
+
card.style.padding = "72px 56px";
|
|
4694
|
+
card.style.width = "816px";
|
|
4695
|
+
card.style.minHeight = "1056px";
|
|
4696
|
+
card.style.boxSizing = "border-box";
|
|
4697
|
+
card.style.marginBottom = "24px";
|
|
4698
|
+
card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4699
|
+
card.style.lineHeight = "1.6";
|
|
4700
|
+
return card;
|
|
4701
|
+
};
|
|
4702
|
+
let curCard = createRtfCard();
|
|
4703
|
+
wrapper.appendChild(curCard);
|
|
4704
|
+
pageElements = [curCard];
|
|
4705
|
+
let curH = 0;
|
|
4706
|
+
const maxH = 912;
|
|
4707
|
+
for (let i = 0; i < contentNodes.length; i++) {
|
|
4708
|
+
const child = contentNodes[i];
|
|
4709
|
+
const chH = childHeights[i];
|
|
4710
|
+
curCard.appendChild(child);
|
|
4711
|
+
curH += chH;
|
|
4712
|
+
if (curH >= maxH && i < contentNodes.length - 1) {
|
|
4713
|
+
curCard = createRtfCard();
|
|
4714
|
+
wrapper.appendChild(curCard);
|
|
4715
|
+
pageElements.push(curCard);
|
|
4716
|
+
curH = 0;
|
|
4390
4717
|
}
|
|
4391
4718
|
}
|
|
4392
4719
|
} catch (err) {
|
|
@@ -4755,6 +5082,14 @@ var OpenDocumentPlugin = class {
|
|
|
4755
5082
|
type: "button",
|
|
4756
5083
|
group: "actions",
|
|
4757
5084
|
execute: () => instance.print?.()
|
|
5085
|
+
},
|
|
5086
|
+
{
|
|
5087
|
+
id: "open-window",
|
|
5088
|
+
icon: "open-window",
|
|
5089
|
+
label: "Open in Separate Full Window",
|
|
5090
|
+
type: "button",
|
|
5091
|
+
group: "actions",
|
|
5092
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
4758
5093
|
}
|
|
4759
5094
|
);
|
|
4760
5095
|
return actions;
|
|
@@ -5343,6 +5678,14 @@ var DocPlugin = class {
|
|
|
5343
5678
|
type: "button",
|
|
5344
5679
|
group: "actions",
|
|
5345
5680
|
execute: () => instance.print?.()
|
|
5681
|
+
},
|
|
5682
|
+
{
|
|
5683
|
+
id: "open-window",
|
|
5684
|
+
icon: "open-window",
|
|
5685
|
+
label: "Open in Separate Full Window",
|
|
5686
|
+
type: "button",
|
|
5687
|
+
group: "actions",
|
|
5688
|
+
execute: () => instance.openInSeparateWindow?.()
|
|
5346
5689
|
}
|
|
5347
5690
|
);
|
|
5348
5691
|
return actions;
|
|
@@ -5362,8 +5705,17 @@ var DocPlugin = class {
|
|
|
5362
5705
|
let scale = 1;
|
|
5363
5706
|
let extractedRawText = "";
|
|
5364
5707
|
let isFallback = false;
|
|
5708
|
+
let chartSvg = "";
|
|
5365
5709
|
try {
|
|
5366
5710
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
5711
|
+
try {
|
|
5712
|
+
const pkg = cfbf.readStream("package_stream");
|
|
5713
|
+
if (pkg && pkg.length > 100) {
|
|
5714
|
+
chartSvg = this.parseOdfChartToSvg(pkg);
|
|
5715
|
+
}
|
|
5716
|
+
} catch (chartErr) {
|
|
5717
|
+
console.warn("[DocPlugin] Chart stream parsing info:", chartErr);
|
|
5718
|
+
}
|
|
5367
5719
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
5368
5720
|
if (!wordDocStream || wordDocStream.length < 512) {
|
|
5369
5721
|
throw new Error("WordDocument stream not found or invalid in CFBF archive");
|
|
@@ -5381,7 +5733,7 @@ var DocPlugin = class {
|
|
|
5381
5733
|
extractedRawText = fallback;
|
|
5382
5734
|
isFallback = true;
|
|
5383
5735
|
}
|
|
5384
|
-
const rawPages = this.splitIntoPages(extractedRawText);
|
|
5736
|
+
const rawPages = this.splitIntoPages(extractedRawText, chartSvg);
|
|
5385
5737
|
const totalPages = Math.max(1, rawPages.length);
|
|
5386
5738
|
let currentPage = 1;
|
|
5387
5739
|
const pageCards = [];
|
|
@@ -5623,7 +5975,7 @@ var DocPlugin = class {
|
|
|
5623
5975
|
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
5624
5976
|
const trimmed = run.trim();
|
|
5625
5977
|
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
5626
|
-
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
5978
|
+
if (!trimmed.includes("Normal.dot") && !trimmed.includes("Microsoft Word") && !trimmed.includes("Times New Roman") && !trimmed.startsWith("\xD0\xCF\xE0\xA1\xB1\xE1") && !/^EMBED\b/i.test(trimmed) && !trimmed.includes("ChartDocument") && !/^[\W_0-9]+$/.test(trimmed)) {
|
|
5627
5979
|
seen.add(trimmed);
|
|
5628
5980
|
candidateLines.push(trimmed);
|
|
5629
5981
|
}
|
|
@@ -5634,12 +5986,138 @@ var DocPlugin = class {
|
|
|
5634
5986
|
heuristicTextExtraction(buffer) {
|
|
5635
5987
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
5636
5988
|
}
|
|
5637
|
-
|
|
5989
|
+
/**
|
|
5990
|
+
* Parses an embedded OpenDocument Chart package into a vector SVG bar/column chart
|
|
5991
|
+
*/
|
|
5992
|
+
parseOdfChartToSvg(zipBytes) {
|
|
5993
|
+
try {
|
|
5994
|
+
const unzipped = fflate__namespace.unzipSync(zipBytes);
|
|
5995
|
+
const contentXml = unzipped["content.xml"] ? new TextDecoder("utf-8").decode(unzipped["content.xml"]) : "";
|
|
5996
|
+
if (!contentXml) return "";
|
|
5997
|
+
const rowsMatch = contentXml.match(/<table:table-row[\s\S]*?<\/table:table-row>/g) || [];
|
|
5998
|
+
if (rowsMatch.length < 2) return "";
|
|
5999
|
+
const headers = [];
|
|
6000
|
+
const firstRow = rowsMatch[0];
|
|
6001
|
+
const headerCells = firstRow ? firstRow.match(/<text:p>([^<]+)<\/text:p>/g) || [] : [];
|
|
6002
|
+
for (const h of headerCells) {
|
|
6003
|
+
headers.push(h.replace(/<\/?text:p>/g, "").trim());
|
|
6004
|
+
}
|
|
6005
|
+
const categories = [];
|
|
6006
|
+
const seriesValues = headers.map(() => []);
|
|
6007
|
+
for (let r = 1; r < rowsMatch.length; r++) {
|
|
6008
|
+
const rowStr = rowsMatch[r];
|
|
6009
|
+
if (!rowStr) continue;
|
|
6010
|
+
const cells = rowStr.match(/<table:table-cell[\s\S]*?<\/table:table-cell>/g) || [];
|
|
6011
|
+
if (cells.length > 0 && cells[0]) {
|
|
6012
|
+
const catMatch = cells[0].match(/<text:p>([^<]+)<\/text:p>/);
|
|
6013
|
+
categories.push(catMatch ? catMatch[1] : "Row " + r);
|
|
6014
|
+
for (let c = 1; c < cells.length && c - 1 < headers.length; c++) {
|
|
6015
|
+
const cellStr = cells[c];
|
|
6016
|
+
if (!cellStr) continue;
|
|
6017
|
+
const valMatch = cellStr.match(/office:value="([0-9.]+)"/) || cellStr.match(/<text:p>([0-9.]+)<\/text:p>/);
|
|
6018
|
+
const series = seriesValues[c - 1];
|
|
6019
|
+
if (series) {
|
|
6020
|
+
series.push(valMatch ? parseFloat(valMatch[1]) : 0);
|
|
6021
|
+
}
|
|
6022
|
+
}
|
|
6023
|
+
}
|
|
6024
|
+
}
|
|
6025
|
+
const colors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021"];
|
|
6026
|
+
const colorMatches = contentXml.matchAll(/draw:fill-color="(#[0-9a-fA-F]{6})"/g);
|
|
6027
|
+
let cIdx = 0;
|
|
6028
|
+
for (const cm of colorMatches) {
|
|
6029
|
+
if (cIdx < colors.length) colors[cIdx] = cm[1];
|
|
6030
|
+
cIdx++;
|
|
6031
|
+
}
|
|
6032
|
+
let maxVal = 10;
|
|
6033
|
+
for (const s of seriesValues) {
|
|
6034
|
+
for (const v of s) {
|
|
6035
|
+
if (v > maxVal) maxVal = v;
|
|
6036
|
+
}
|
|
6037
|
+
}
|
|
6038
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
6039
|
+
const width = 560;
|
|
6040
|
+
const height = 280;
|
|
6041
|
+
const padLeft = 45;
|
|
6042
|
+
const padRight = 100;
|
|
6043
|
+
const padTop = 20;
|
|
6044
|
+
const padBottom = 40;
|
|
6045
|
+
const chartW = width - padLeft - padRight;
|
|
6046
|
+
const chartH = height - padTop - padBottom;
|
|
6047
|
+
let svg = `<svg viewBox="0 0 ${width} ${height}" width="100%" height="auto" style="max-width: 560px; height: 280px; margin: 16px auto; display: block; font-family: Calibri, sans-serif; background: #ffffff; border: 1px solid #e2e8f0; border-radius: 6px; box-shadow: 0 1px 4px rgba(0,0,0,0.05);">`;
|
|
6048
|
+
for (let step = 0; step <= 4; step++) {
|
|
6049
|
+
const yVal = (maxVal / 4 * step).toFixed(1);
|
|
6050
|
+
const yPos = padTop + chartH - step / 4 * chartH;
|
|
6051
|
+
svg += `<line x1="${padLeft}" y1="${yPos}" x2="${padLeft + chartW}" y2="${yPos}" stroke="#e2e8f0" stroke-dasharray="2,2" />`;
|
|
6052
|
+
svg += `<text x="${padLeft - 8}" y="${yPos + 4}" font-size="11" fill="#64748b" text-anchor="end">${yVal}</text>`;
|
|
6053
|
+
}
|
|
6054
|
+
const numCats = categories.length;
|
|
6055
|
+
const numSeries = headers.length;
|
|
6056
|
+
const groupW = chartW / numCats;
|
|
6057
|
+
const barW = Math.max(8, groupW * 0.7 / numSeries);
|
|
6058
|
+
const groupPad = (groupW - barW * numSeries) / 2;
|
|
6059
|
+
for (let catIdx = 0; catIdx < numCats; catIdx++) {
|
|
6060
|
+
const groupX = padLeft + catIdx * groupW + groupPad;
|
|
6061
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6062
|
+
const val = seriesValues[sIdx][catIdx] || 0;
|
|
6063
|
+
const barH = val / maxVal * chartH;
|
|
6064
|
+
const barX = groupX + sIdx * barW;
|
|
6065
|
+
const barY = padTop + chartH - barH;
|
|
6066
|
+
const col = colors[sIdx % colors.length];
|
|
6067
|
+
svg += `<rect x="${barX}" y="${barY}" width="${barW - 2}" height="${barH}" fill="${col}" rx="2"><title>${headers[sIdx]}: ${val}</title></rect>`;
|
|
6068
|
+
}
|
|
6069
|
+
const catX = padLeft + catIdx * groupW + groupW / 2;
|
|
6070
|
+
svg += `<text x="${catX}" y="${padTop + chartH + 18}" font-size="11" fill="#475569" text-anchor="middle">${categories[catIdx]}</text>`;
|
|
6071
|
+
}
|
|
6072
|
+
let legendY = padTop + 20;
|
|
6073
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6074
|
+
const col = colors[sIdx % colors.length];
|
|
6075
|
+
svg += `<rect x="${padLeft + chartW + 15}" y="${legendY}" width="12" height="12" fill="${col}" rx="2" />`;
|
|
6076
|
+
svg += `<text x="${padLeft + chartW + 32}" y="${legendY + 10}" font-size="11" fill="#334155">${headers[sIdx]}</text>`;
|
|
6077
|
+
legendY += 20;
|
|
6078
|
+
}
|
|
6079
|
+
svg += "</svg>";
|
|
6080
|
+
return svg;
|
|
6081
|
+
} catch (e) {
|
|
6082
|
+
console.warn("[DocPlugin] Error generating chart SVG:", e);
|
|
6083
|
+
return "";
|
|
6084
|
+
}
|
|
6085
|
+
}
|
|
6086
|
+
cleanWordDocFields(text, chartSvg = "") {
|
|
6087
|
+
if (!text) return "";
|
|
6088
|
+
let cleaned = text.replace(
|
|
6089
|
+
/\x13\s*EMBED\b[\s\S]*?\x15/gi,
|
|
6090
|
+
() => chartSvg ? `
|
|
6091
|
+
|
|
6092
|
+
${chartSvg}
|
|
6093
|
+
|
|
6094
|
+
` : ""
|
|
6095
|
+
);
|
|
6096
|
+
cleaned = cleaned.replace(
|
|
6097
|
+
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
6098
|
+
(_match, url, label) => {
|
|
6099
|
+
const cleanUrl = url.trim();
|
|
6100
|
+
const cleanLabel = label.trim() || cleanUrl;
|
|
6101
|
+
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
6102
|
+
}
|
|
6103
|
+
);
|
|
6104
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, (_m, res) => {
|
|
6105
|
+
if (/[\x00-\x1F]/.test(res)) return "";
|
|
6106
|
+
return res.trim();
|
|
6107
|
+
});
|
|
6108
|
+
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6109
|
+
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6110
|
+
cleaned = cleaned.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
6111
|
+
cleaned = cleaned.replace(/EMBED\s+LibreOffice\.ChartDocument\.[0-9]+/gi, chartSvg || "");
|
|
6112
|
+
return cleaned;
|
|
6113
|
+
}
|
|
6114
|
+
splitIntoPages(text, chartSvg = "") {
|
|
5638
6115
|
if (!text) return [""];
|
|
5639
|
-
const
|
|
6116
|
+
const cleanedText = this.cleanWordDocFields(text, chartSvg);
|
|
6117
|
+
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
5640
6118
|
const explicitParts = normalized.split(/[\x0C\f]|\n\s*[-=_]{3,}\s*(?:PAGE|Page|page break)[\s\d\w-]*[-=_]{3,}\s*\n/i).map((p) => p.trim()).filter((p) => p.length > 0);
|
|
5641
6119
|
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
5642
|
-
const maxLinesPerPage =
|
|
6120
|
+
const maxLinesPerPage = 34;
|
|
5643
6121
|
const charsPerLine = 80;
|
|
5644
6122
|
const finalPages = [];
|
|
5645
6123
|
for (const part of explicitParts) {
|
|
@@ -5647,7 +6125,8 @@ var DocPlugin = class {
|
|
|
5647
6125
|
let currentLines = [];
|
|
5648
6126
|
let count = 0;
|
|
5649
6127
|
for (const line of lines) {
|
|
5650
|
-
const
|
|
6128
|
+
const isSvg = line.includes("<svg");
|
|
6129
|
+
const vLines = isSvg ? 12 : Math.max(1, Math.ceil((line.replace(/<[^>]+>/g, "").length || 1) / charsPerLine));
|
|
5651
6130
|
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
5652
6131
|
finalPages.push(currentLines.join("\n"));
|
|
5653
6132
|
currentLines = [];
|
|
@@ -5687,9 +6166,44 @@ var DocPlugin = class {
|
|
|
5687
6166
|
tableLines = [];
|
|
5688
6167
|
}
|
|
5689
6168
|
};
|
|
6169
|
+
const sanitizeOptions = {
|
|
6170
|
+
ADD_TAGS: ["a", "svg", "g", "path", "line", "rect", "circle", "text", "title"],
|
|
6171
|
+
ADD_ATTR: [
|
|
6172
|
+
"href",
|
|
6173
|
+
"target",
|
|
6174
|
+
"rel",
|
|
6175
|
+
"style",
|
|
6176
|
+
"viewBox",
|
|
6177
|
+
"width",
|
|
6178
|
+
"height",
|
|
6179
|
+
"x",
|
|
6180
|
+
"y",
|
|
6181
|
+
"x1",
|
|
6182
|
+
"y1",
|
|
6183
|
+
"x2",
|
|
6184
|
+
"y2",
|
|
6185
|
+
"fill",
|
|
6186
|
+
"stroke",
|
|
6187
|
+
"stroke-width",
|
|
6188
|
+
"stroke-dasharray",
|
|
6189
|
+
"rx",
|
|
6190
|
+
"font-size",
|
|
6191
|
+
"text-anchor"
|
|
6192
|
+
]
|
|
6193
|
+
};
|
|
5690
6194
|
let i = 0;
|
|
5691
6195
|
while (i < lines.length) {
|
|
5692
6196
|
let line = lines[i];
|
|
6197
|
+
if (line.includes("<svg")) {
|
|
6198
|
+
if (inList) {
|
|
6199
|
+
html += "</ul>";
|
|
6200
|
+
inList = false;
|
|
6201
|
+
}
|
|
6202
|
+
flushTable();
|
|
6203
|
+
html += line;
|
|
6204
|
+
i++;
|
|
6205
|
+
continue;
|
|
6206
|
+
}
|
|
5693
6207
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
5694
6208
|
if (tabCount > 0) {
|
|
5695
6209
|
let j = i;
|
|
@@ -5728,20 +6242,20 @@ var DocPlugin = class {
|
|
|
5728
6242
|
html += "</ul>";
|
|
5729
6243
|
inList = false;
|
|
5730
6244
|
}
|
|
5731
|
-
html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line)}</h2>`;
|
|
6245
|
+
html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</h2>`;
|
|
5732
6246
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
5733
6247
|
if (!inList) {
|
|
5734
6248
|
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
5735
6249
|
inList = true;
|
|
5736
6250
|
}
|
|
5737
6251
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
5738
|
-
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText)}</li>`;
|
|
6252
|
+
html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
|
|
5739
6253
|
} else {
|
|
5740
6254
|
if (inList) {
|
|
5741
6255
|
html += "</ul>";
|
|
5742
6256
|
inList = false;
|
|
5743
6257
|
}
|
|
5744
|
-
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line)}</p>`;
|
|
6258
|
+
html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
|
|
5745
6259
|
}
|
|
5746
6260
|
i++;
|
|
5747
6261
|
}
|