@files-preview-app/preview-file 1.3.0 → 1.3.2
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 +69 -0
- package/dist/angular.cjs +1477 -482
- package/dist/angular.cjs.map +1 -1
- package/dist/angular.d.cts +1 -1
- package/dist/angular.d.ts +1 -1
- package/dist/angular.js +1477 -482
- package/dist/angular.js.map +1 -1
- package/dist/index.cjs +1477 -482
- package/dist/index.cjs.map +1 -1
- package/dist/index.d-B79v0jxi.d.cts +570 -0
- package/dist/index.d-B79v0jxi.d.ts +570 -0
- package/dist/index.d.cts +3227 -19
- package/dist/index.d.ts +3227 -19
- package/dist/index.js +1477 -482
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +1477 -482
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -16
- package/dist/react.d.ts +1 -16
- package/dist/react.js +1477 -482
- package/dist/react.js.map +1 -1
- package/dist/styles.css +195 -14
- package/dist/vue.cjs +1477 -482
- package/dist/vue.cjs.map +1 -1
- package/dist/vue.d.cts +1 -1
- package/dist/vue.d.ts +1 -1
- package/dist/vue.js +1477 -482
- package/dist/vue.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import DOMPurify6 from 'dompurify';
|
|
2
2
|
import * as pdfjsLib from 'pdfjs-dist';
|
|
3
3
|
import * as docx from 'docx-preview';
|
|
4
|
+
import * as fflate from 'fflate';
|
|
4
5
|
import { unzipSync, strFromU8, unzip } from 'fflate';
|
|
5
6
|
import * as XLSX from 'xlsx';
|
|
6
7
|
import hljs from 'highlight.js';
|
|
@@ -577,8 +578,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
|
|
|
577
578
|
var ICON_COPY = `<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="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
|
|
578
579
|
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>`;
|
|
579
580
|
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>`;
|
|
580
|
-
var
|
|
581
|
-
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>`;
|
|
581
|
+
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"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="3" y1="8" x2="21" y2="8"></line><circle cx="6" cy="5.5" r="0.75" fill="currentColor"></circle><circle cx="8.5" cy="5.5" r="0.75" fill="currentColor"></circle><polyline points="14 13 18 13 18 17"></polyline><line x1="11" y1="20" x2="18" y2="13"></line></svg>`;
|
|
582
582
|
var ICON_MAP = {
|
|
583
583
|
"zoom-in": ICON_ZOOM_IN,
|
|
584
584
|
"zoom-out": ICON_ZOOM_OUT,
|
|
@@ -605,28 +605,177 @@ var ICON_MAP = {
|
|
|
605
605
|
"forward-10": ICON_FAST_FORWARD,
|
|
606
606
|
"rewind": ICON_REWIND,
|
|
607
607
|
"replay-10": ICON_REWIND,
|
|
608
|
-
"speed": ICON_SPEED,
|
|
609
608
|
"open-window": ICON_EXTERNAL_WINDOW,
|
|
610
|
-
"external-window": ICON_EXTERNAL_WINDOW
|
|
609
|
+
"external-window": ICON_EXTERNAL_WINDOW,
|
|
610
|
+
"openWindow": ICON_EXTERNAL_WINDOW,
|
|
611
|
+
"openSeparateWindow": ICON_EXTERNAL_WINDOW
|
|
611
612
|
};
|
|
612
613
|
var ToolbarController = class {
|
|
613
614
|
el;
|
|
614
615
|
toolbarEl;
|
|
615
616
|
actions = [];
|
|
617
|
+
config = {};
|
|
616
618
|
pageInputEl = null;
|
|
617
619
|
pageLabelEl = null;
|
|
618
|
-
|
|
620
|
+
prevPageBtn = null;
|
|
621
|
+
nextPageBtn = null;
|
|
622
|
+
constructor(container, config) {
|
|
619
623
|
this.el = container;
|
|
624
|
+
if (config) this.config = { ...config };
|
|
620
625
|
this.toolbarEl = createElement("div", { className: "fp-toolbar" });
|
|
621
626
|
this.el.appendChild(this.toolbarEl);
|
|
622
627
|
}
|
|
628
|
+
setConfig(config) {
|
|
629
|
+
this.config = { ...config };
|
|
630
|
+
this.render();
|
|
631
|
+
}
|
|
632
|
+
getConfig() {
|
|
633
|
+
return { ...this.config };
|
|
634
|
+
}
|
|
635
|
+
hideAction(actionId) {
|
|
636
|
+
this.config[actionId] = false;
|
|
637
|
+
if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
|
|
638
|
+
if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
|
|
639
|
+
if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
|
|
640
|
+
this.config.fitPage = false;
|
|
641
|
+
this.config.fitToPage = false;
|
|
642
|
+
this.config.fitWidth = false;
|
|
643
|
+
}
|
|
644
|
+
if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
|
|
645
|
+
if (actionId === "fullscreen") this.config.fullscreen = false;
|
|
646
|
+
if (actionId === "download") this.config.download = false;
|
|
647
|
+
if (actionId === "print") this.config.print = false;
|
|
648
|
+
if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
|
|
649
|
+
this.config.openWindow = false;
|
|
650
|
+
this.config.openSeparateWindow = false;
|
|
651
|
+
}
|
|
652
|
+
if (actionId === "copy") this.config.copy = false;
|
|
653
|
+
if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
|
|
654
|
+
this.config.pageNav = false;
|
|
655
|
+
this.config.pagination = false;
|
|
656
|
+
}
|
|
657
|
+
this.render();
|
|
658
|
+
}
|
|
659
|
+
showAction(actionId) {
|
|
660
|
+
this.config[actionId] = true;
|
|
661
|
+
if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
|
|
662
|
+
if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
|
|
663
|
+
if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
|
|
664
|
+
this.config.fitPage = true;
|
|
665
|
+
this.config.fitToPage = true;
|
|
666
|
+
this.config.fitWidth = true;
|
|
667
|
+
}
|
|
668
|
+
if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
|
|
669
|
+
if (actionId === "fullscreen") this.config.fullscreen = true;
|
|
670
|
+
if (actionId === "download") this.config.download = true;
|
|
671
|
+
if (actionId === "print") this.config.print = true;
|
|
672
|
+
if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
|
|
673
|
+
this.config.openWindow = true;
|
|
674
|
+
this.config.openSeparateWindow = true;
|
|
675
|
+
}
|
|
676
|
+
if (actionId === "copy") this.config.copy = true;
|
|
677
|
+
if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
|
|
678
|
+
this.config.pageNav = true;
|
|
679
|
+
this.config.pagination = true;
|
|
680
|
+
}
|
|
681
|
+
this.render();
|
|
682
|
+
}
|
|
683
|
+
normalizeActionId(actionId) {
|
|
684
|
+
if (actionId === "zoomIn") return "zoom-in";
|
|
685
|
+
if (actionId === "zoomOut") return "zoom-out";
|
|
686
|
+
if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
|
|
687
|
+
if (actionId === "rotateCW") return "rotate-cw";
|
|
688
|
+
if (actionId === "rotateCCW") return "rotate-ccw";
|
|
689
|
+
if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
|
|
690
|
+
if (actionId === "pageNav") return "page-nav";
|
|
691
|
+
return actionId;
|
|
692
|
+
}
|
|
693
|
+
enableAction(actionId) {
|
|
694
|
+
const norm = this.normalizeActionId(actionId);
|
|
695
|
+
const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
|
|
696
|
+
if (btn) {
|
|
697
|
+
btn.disabled = false;
|
|
698
|
+
btn.classList.remove("disabled");
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
disableAction(actionId) {
|
|
702
|
+
const norm = this.normalizeActionId(actionId);
|
|
703
|
+
const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
|
|
704
|
+
if (btn) {
|
|
705
|
+
btn.disabled = true;
|
|
706
|
+
btn.classList.add("disabled");
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
setActionActive(actionId, active) {
|
|
710
|
+
const norm = this.normalizeActionId(actionId);
|
|
711
|
+
const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
|
|
712
|
+
if (btn) {
|
|
713
|
+
if (active) {
|
|
714
|
+
btn.classList.add("active");
|
|
715
|
+
} else {
|
|
716
|
+
btn.classList.remove("active");
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
}
|
|
720
|
+
isActionEnabled(action) {
|
|
721
|
+
const c = this.config;
|
|
722
|
+
const id = action.id;
|
|
723
|
+
if (c[id] === false) return false;
|
|
724
|
+
if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
|
|
725
|
+
if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
|
|
726
|
+
if ((id === "fit-page" || id === "fit-width" || id === "fit-slide") && (c.fitPage === false || c.fitToPage === false || c.fitWidth === false || c["fit-page"] === false || c["fit-width"] === false)) {
|
|
727
|
+
return false;
|
|
728
|
+
}
|
|
729
|
+
if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
if (id === "fullscreen" && c.fullscreen === false) return false;
|
|
733
|
+
if (id === "download" && c.download === false) return false;
|
|
734
|
+
if (id === "print" && c.print === false) return false;
|
|
735
|
+
if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
|
|
736
|
+
return false;
|
|
737
|
+
}
|
|
738
|
+
if (id === "copy" && c.copy === false) return false;
|
|
739
|
+
if (id === "thumbnails" && c.thumbnails === false) return false;
|
|
740
|
+
if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
|
|
741
|
+
return false;
|
|
742
|
+
}
|
|
743
|
+
if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
|
|
744
|
+
return false;
|
|
745
|
+
}
|
|
746
|
+
if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
|
|
747
|
+
return false;
|
|
748
|
+
}
|
|
749
|
+
if (id === "play" && c.play === false) return false;
|
|
750
|
+
if (id === "pause" && c.pause === false) return false;
|
|
751
|
+
if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
|
|
752
|
+
return false;
|
|
753
|
+
}
|
|
754
|
+
if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
|
|
755
|
+
return false;
|
|
756
|
+
}
|
|
757
|
+
if (id === "speed" && c.speed === false) return false;
|
|
758
|
+
return true;
|
|
759
|
+
}
|
|
623
760
|
setPage(page, max) {
|
|
624
761
|
if (this.pageInputEl) {
|
|
625
762
|
this.pageInputEl.value = page.toString();
|
|
763
|
+
if (max !== void 0) {
|
|
764
|
+
this.pageInputEl.max = max.toString();
|
|
765
|
+
}
|
|
626
766
|
}
|
|
627
767
|
if (max !== void 0 && this.pageLabelEl) {
|
|
628
768
|
this.pageLabelEl.textContent = ` / ${max}`;
|
|
629
769
|
}
|
|
770
|
+
const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
|
|
771
|
+
if (this.prevPageBtn) {
|
|
772
|
+
this.prevPageBtn.disabled = page <= 1;
|
|
773
|
+
this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
|
|
774
|
+
}
|
|
775
|
+
if (this.nextPageBtn) {
|
|
776
|
+
this.nextPageBtn.disabled = page >= currentMax;
|
|
777
|
+
this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
|
|
778
|
+
}
|
|
630
779
|
}
|
|
631
780
|
update(actions) {
|
|
632
781
|
this.actions = actions;
|
|
@@ -649,8 +798,9 @@ var ToolbarController = class {
|
|
|
649
798
|
view: [],
|
|
650
799
|
actions: []
|
|
651
800
|
};
|
|
652
|
-
const
|
|
653
|
-
const
|
|
801
|
+
const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
|
|
802
|
+
const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
|
|
803
|
+
const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
|
|
654
804
|
for (const action of effectiveActions) {
|
|
655
805
|
if (groups[action.group]) {
|
|
656
806
|
groups[action.group].push(action);
|
|
@@ -710,6 +860,13 @@ var ToolbarController = class {
|
|
|
710
860
|
action.execute("go", val);
|
|
711
861
|
});
|
|
712
862
|
const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
|
|
863
|
+
const curVal = action.value ?? 1;
|
|
864
|
+
prevBtn.disabled = curVal <= 1;
|
|
865
|
+
prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
|
|
866
|
+
nextBtn.disabled = curVal >= max;
|
|
867
|
+
nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
|
|
868
|
+
this.prevPageBtn = prevBtn;
|
|
869
|
+
this.nextPageBtn = nextBtn;
|
|
713
870
|
this.pageInputEl = input;
|
|
714
871
|
this.pageLabelEl = label;
|
|
715
872
|
groupEl.appendChild(prevBtn);
|
|
@@ -750,9 +907,11 @@ var ToolbarController = class {
|
|
|
750
907
|
type: "button",
|
|
751
908
|
"data-action-id": id
|
|
752
909
|
});
|
|
753
|
-
const
|
|
754
|
-
if (
|
|
755
|
-
btn.innerHTML =
|
|
910
|
+
const internalSvg = ICON_MAP[iconHtml] || ICON_MAP[id];
|
|
911
|
+
if (internalSvg) {
|
|
912
|
+
btn.innerHTML = internalSvg;
|
|
913
|
+
} else if (iconHtml && iconHtml.startsWith("<svg")) {
|
|
914
|
+
btn.innerHTML = sanitizeSVG(iconHtml);
|
|
756
915
|
} else {
|
|
757
916
|
btn.textContent = title || id;
|
|
758
917
|
}
|
|
@@ -763,25 +922,63 @@ var ToolbarController = class {
|
|
|
763
922
|
var ThumbnailPanel = class {
|
|
764
923
|
el;
|
|
765
924
|
panelEl;
|
|
925
|
+
listEl;
|
|
926
|
+
headerEl;
|
|
766
927
|
thumbnails = [];
|
|
767
928
|
onSelect;
|
|
929
|
+
onToggleCallback;
|
|
768
930
|
activeIndex = 0;
|
|
769
|
-
|
|
931
|
+
observer;
|
|
932
|
+
renderedIndices = /* @__PURE__ */ new Set();
|
|
933
|
+
constructor(container, onToggle) {
|
|
770
934
|
this.el = container;
|
|
935
|
+
this.onToggleCallback = onToggle;
|
|
771
936
|
this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
|
|
937
|
+
this.panelEl.style.width = "0px";
|
|
938
|
+
this.panelEl.style.minWidth = "0px";
|
|
939
|
+
this.panelEl.style.opacity = "0";
|
|
940
|
+
this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
|
|
941
|
+
const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
|
|
942
|
+
const closeBtn = createElement("button", {
|
|
943
|
+
className: "fp-thumbnail-close-btn",
|
|
944
|
+
title: "Close Thumbnails",
|
|
945
|
+
type: "button"
|
|
946
|
+
});
|
|
947
|
+
closeBtn.innerHTML = ICON_CLOSE;
|
|
948
|
+
closeBtn.addEventListener("click", (e) => {
|
|
949
|
+
e.stopPropagation();
|
|
950
|
+
this.hide();
|
|
951
|
+
});
|
|
952
|
+
this.headerEl.appendChild(title);
|
|
953
|
+
this.headerEl.appendChild(closeBtn);
|
|
954
|
+
this.listEl = createElement("div", { className: "fp-thumbnail-list" });
|
|
955
|
+
this.panelEl.appendChild(this.headerEl);
|
|
956
|
+
this.panelEl.appendChild(this.listEl);
|
|
772
957
|
this.el.appendChild(this.panelEl);
|
|
773
958
|
}
|
|
959
|
+
isOpen() {
|
|
960
|
+
return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
|
|
961
|
+
}
|
|
774
962
|
update(thumbnails, onSelect) {
|
|
775
963
|
this.thumbnails = thumbnails;
|
|
776
964
|
this.onSelect = onSelect;
|
|
965
|
+
this.renderedIndices.clear();
|
|
966
|
+
const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
|
|
967
|
+
if (titleEl) {
|
|
968
|
+
titleEl.textContent = `Pages (${thumbnails.length})`;
|
|
969
|
+
}
|
|
777
970
|
this.render();
|
|
971
|
+
if (this.isOpen()) {
|
|
972
|
+
this.renderAllVisible();
|
|
973
|
+
}
|
|
778
974
|
}
|
|
779
975
|
setActive(index) {
|
|
780
976
|
this.activeIndex = index;
|
|
781
|
-
const items = this.
|
|
977
|
+
const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
|
|
782
978
|
items.forEach((item, i) => {
|
|
783
979
|
if (i === index) {
|
|
784
980
|
item.classList.add("active");
|
|
981
|
+
item.scrollIntoView({ block: "nearest", behavior: "smooth" });
|
|
785
982
|
} else {
|
|
786
983
|
item.classList.remove("active");
|
|
787
984
|
}
|
|
@@ -789,37 +986,102 @@ var ThumbnailPanel = class {
|
|
|
789
986
|
}
|
|
790
987
|
show() {
|
|
791
988
|
this.panelEl.classList.remove("hidden");
|
|
989
|
+
this.panelEl.style.width = "200px";
|
|
990
|
+
this.panelEl.style.minWidth = "200px";
|
|
991
|
+
this.panelEl.style.opacity = "1";
|
|
992
|
+
this.panelEl.style.display = "flex";
|
|
993
|
+
this.onToggleCallback?.(true);
|
|
994
|
+
this.renderAllVisible();
|
|
792
995
|
}
|
|
793
996
|
hide() {
|
|
794
997
|
this.panelEl.classList.add("hidden");
|
|
998
|
+
this.panelEl.style.width = "0px";
|
|
999
|
+
this.panelEl.style.minWidth = "0px";
|
|
1000
|
+
this.panelEl.style.opacity = "0";
|
|
1001
|
+
this.onToggleCallback?.(false);
|
|
795
1002
|
}
|
|
796
1003
|
toggle() {
|
|
797
|
-
this.
|
|
1004
|
+
if (this.isOpen()) {
|
|
1005
|
+
this.hide();
|
|
1006
|
+
} else {
|
|
1007
|
+
this.show();
|
|
1008
|
+
}
|
|
798
1009
|
}
|
|
799
1010
|
destroy() {
|
|
1011
|
+
if (this.observer) {
|
|
1012
|
+
this.observer.disconnect();
|
|
1013
|
+
this.observer = void 0;
|
|
1014
|
+
}
|
|
1015
|
+
this.renderedIndices.clear();
|
|
800
1016
|
this.el.innerHTML = "";
|
|
801
1017
|
}
|
|
802
|
-
|
|
803
|
-
this.
|
|
1018
|
+
renderAllVisible() {
|
|
1019
|
+
const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
|
|
1020
|
+
items.forEach((item) => {
|
|
1021
|
+
const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
|
|
1022
|
+
if (idx >= 0 && !this.renderedIndices.has(idx)) {
|
|
1023
|
+
this.renderThumbnailItem(idx, item);
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
}
|
|
1027
|
+
renderThumbnailItem(idx, itemEl) {
|
|
1028
|
+
if (this.renderedIndices.has(idx)) return;
|
|
1029
|
+
this.renderedIndices.add(idx);
|
|
1030
|
+
const canvas = itemEl.querySelector("canvas");
|
|
1031
|
+
const thumb = this.thumbnails[idx];
|
|
1032
|
+
if (canvas && thumb) {
|
|
1033
|
+
Promise.resolve(thumb.render(canvas)).catch((err) => {
|
|
1034
|
+
console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
render() {
|
|
1039
|
+
if (this.observer) {
|
|
1040
|
+
this.observer.disconnect();
|
|
1041
|
+
}
|
|
1042
|
+
this.listEl.innerHTML = "";
|
|
1043
|
+
const useObserver = typeof IntersectionObserver !== "undefined";
|
|
1044
|
+
if (useObserver) {
|
|
1045
|
+
this.observer = new IntersectionObserver(
|
|
1046
|
+
(entries) => {
|
|
1047
|
+
for (const entry of entries) {
|
|
1048
|
+
if (entry.isIntersecting) {
|
|
1049
|
+
const target = entry.target;
|
|
1050
|
+
const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
|
|
1051
|
+
if (idx >= 0) {
|
|
1052
|
+
this.renderThumbnailItem(idx, target);
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
root: this.listEl,
|
|
1059
|
+
rootMargin: "120px 0px 120px 0px"
|
|
1060
|
+
}
|
|
1061
|
+
);
|
|
1062
|
+
}
|
|
804
1063
|
for (let i = 0; i < this.thumbnails.length; i++) {
|
|
805
1064
|
const thumb = this.thumbnails[i];
|
|
806
|
-
const itemEl = createElement("div", {
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1065
|
+
const itemEl = createElement("div", {
|
|
1066
|
+
className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
|
|
1067
|
+
});
|
|
1068
|
+
itemEl.dataset.thumbIndex = i.toString();
|
|
810
1069
|
itemEl.addEventListener("click", () => {
|
|
811
1070
|
this.setActive(i);
|
|
812
1071
|
this.onSelect?.(i);
|
|
813
1072
|
});
|
|
814
|
-
const
|
|
815
|
-
const
|
|
816
|
-
|
|
1073
|
+
const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
|
|
1074
|
+
const canvas = createElement("canvas", { width: "130", height: "170" });
|
|
1075
|
+
canvasWrapper.appendChild(canvas);
|
|
1076
|
+
const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
|
|
1077
|
+
itemEl.appendChild(canvasWrapper);
|
|
817
1078
|
itemEl.appendChild(label);
|
|
818
|
-
this.
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
}
|
|
822
|
-
|
|
1079
|
+
this.listEl.appendChild(itemEl);
|
|
1080
|
+
if (this.observer) {
|
|
1081
|
+
this.observer.observe(itemEl);
|
|
1082
|
+
}
|
|
1083
|
+
if (i < 4) {
|
|
1084
|
+
this.renderThumbnailItem(i, itemEl);
|
|
823
1085
|
}
|
|
824
1086
|
}
|
|
825
1087
|
}
|
|
@@ -840,6 +1102,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
840
1102
|
currentOptions = {};
|
|
841
1103
|
resizeObserver = null;
|
|
842
1104
|
fullscreenHandler = null;
|
|
1105
|
+
titleBarEl = null;
|
|
843
1106
|
/**
|
|
844
1107
|
* Register a preview plugin.
|
|
845
1108
|
*/
|
|
@@ -876,6 +1139,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
876
1139
|
}
|
|
877
1140
|
this.currentBuffer = buffer.slice(0);
|
|
878
1141
|
this.currentMetadata = metadata;
|
|
1142
|
+
this.updateTitleBar(options, metadata);
|
|
879
1143
|
if (signal.aborted) throw new DOMException("Aborted", "AbortError");
|
|
880
1144
|
const fileInfo = { metadata, buffer };
|
|
881
1145
|
const matchedPlugin = await this.findPlugin(fileInfo);
|
|
@@ -899,15 +1163,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
899
1163
|
if (event === "page-change" && payload && typeof payload.page === "number") {
|
|
900
1164
|
const total = payload.total ?? payload.totalPages;
|
|
901
1165
|
this.toolbar?.setPage(payload.page, total);
|
|
1166
|
+
this.thumbnailPanel?.setActive(payload.page - 1);
|
|
902
1167
|
}
|
|
903
1168
|
this.eventEmitter.emit(event, payload);
|
|
904
|
-
}
|
|
1169
|
+
},
|
|
1170
|
+
toggleThumbnails: () => this.toggleThumbnails()
|
|
905
1171
|
});
|
|
906
1172
|
this.activeInstance = instance;
|
|
907
1173
|
instance.openInSeparateWindow = () => this.openInSeparateWindow();
|
|
1174
|
+
instance.toggleThumbnails = () => this.toggleThumbnails();
|
|
908
1175
|
this.hideLoading();
|
|
909
1176
|
this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
|
|
910
|
-
|
|
1177
|
+
const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
|
|
1178
|
+
if (isToolbarVisible && this.toolbar) {
|
|
1179
|
+
const toolbarConfig = this.extractToolbarConfig(options);
|
|
1180
|
+
this.toolbar.setConfig(toolbarConfig);
|
|
911
1181
|
const actions = matchedPlugin.getToolbarActions(instance);
|
|
912
1182
|
const hasFullscreen = actions.some((a) => a.id === "fullscreen");
|
|
913
1183
|
if (!hasFullscreen && this.wrapperEl) {
|
|
@@ -957,6 +1227,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
957
1227
|
}
|
|
958
1228
|
});
|
|
959
1229
|
}
|
|
1230
|
+
const thumbAction = actions.find((a) => a.id === "thumbnails");
|
|
1231
|
+
if (thumbAction) {
|
|
1232
|
+
thumbAction.execute = () => {
|
|
1233
|
+
this.toggleThumbnails();
|
|
1234
|
+
};
|
|
1235
|
+
}
|
|
960
1236
|
this.toolbar.update(actions);
|
|
961
1237
|
this.toolbar.show();
|
|
962
1238
|
}
|
|
@@ -1041,7 +1317,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
1041
1317
|
if (targetUrl) {
|
|
1042
1318
|
const newWin2 = window.open(targetUrl, "_blank");
|
|
1043
1319
|
if (!newWin2) {
|
|
1044
|
-
|
|
1320
|
+
console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
|
|
1045
1321
|
return null;
|
|
1046
1322
|
}
|
|
1047
1323
|
return newWin2;
|
|
@@ -1049,7 +1325,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
1049
1325
|
const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
|
|
1050
1326
|
const newWin = window.open("", "_blank");
|
|
1051
1327
|
if (!newWin) {
|
|
1052
|
-
|
|
1328
|
+
console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
|
|
1053
1329
|
return null;
|
|
1054
1330
|
}
|
|
1055
1331
|
newWin.document.title = title;
|
|
@@ -1126,6 +1402,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
1126
1402
|
this.currentBuffer = null;
|
|
1127
1403
|
this.currentMetadata = null;
|
|
1128
1404
|
this.currentContainer = null;
|
|
1405
|
+
this.titleBarEl = null;
|
|
1129
1406
|
}
|
|
1130
1407
|
/**
|
|
1131
1408
|
* Get the currently active preview instance.
|
|
@@ -1133,7 +1410,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
1133
1410
|
getInstance() {
|
|
1134
1411
|
return this.activeInstance;
|
|
1135
1412
|
}
|
|
1413
|
+
// --- Toolbar Configuration & Feature Toggles ---
|
|
1414
|
+
/**
|
|
1415
|
+
* Configure toolbar features dynamically through options or methods.
|
|
1416
|
+
* e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
|
|
1417
|
+
*/
|
|
1418
|
+
setToolbarConfig(config) {
|
|
1419
|
+
this.toolbar?.setConfig(config);
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Get current toolbar configuration.
|
|
1423
|
+
*/
|
|
1424
|
+
getToolbarConfig() {
|
|
1425
|
+
return this.toolbar?.getConfig() ?? {};
|
|
1426
|
+
}
|
|
1427
|
+
/**
|
|
1428
|
+
* Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
|
|
1429
|
+
*/
|
|
1430
|
+
hideToolbarAction(actionId) {
|
|
1431
|
+
this.toolbar?.hideAction(actionId);
|
|
1432
|
+
}
|
|
1433
|
+
/**
|
|
1434
|
+
* Show a specific toolbar action by ID or alias.
|
|
1435
|
+
*/
|
|
1436
|
+
showToolbarAction(actionId) {
|
|
1437
|
+
this.toolbar?.showAction(actionId);
|
|
1438
|
+
}
|
|
1439
|
+
/**
|
|
1440
|
+
* Enable a specific toolbar action button.
|
|
1441
|
+
*/
|
|
1442
|
+
enableToolbarAction(actionId) {
|
|
1443
|
+
this.toolbar?.enableAction(actionId);
|
|
1444
|
+
}
|
|
1445
|
+
/**
|
|
1446
|
+
* Disable a specific toolbar action button.
|
|
1447
|
+
*/
|
|
1448
|
+
disableToolbarAction(actionId) {
|
|
1449
|
+
this.toolbar?.disableAction(actionId);
|
|
1450
|
+
}
|
|
1451
|
+
// --- Programmatic Toolbar Action Methods ---
|
|
1452
|
+
/**
|
|
1453
|
+
* Fit document to page so it fills the frame width with minimal margins.
|
|
1454
|
+
*/
|
|
1455
|
+
fitToPage() {
|
|
1456
|
+
this.activeInstance?.fitToPage?.();
|
|
1457
|
+
}
|
|
1458
|
+
/**
|
|
1459
|
+
* Zoom in.
|
|
1460
|
+
*/
|
|
1461
|
+
zoomIn() {
|
|
1462
|
+
this.activeInstance?.zoomIn?.();
|
|
1463
|
+
}
|
|
1464
|
+
/**
|
|
1465
|
+
* Zoom out.
|
|
1466
|
+
*/
|
|
1467
|
+
zoomOut() {
|
|
1468
|
+
this.activeInstance?.zoomOut?.();
|
|
1469
|
+
}
|
|
1470
|
+
/**
|
|
1471
|
+
* Set specific zoom level.
|
|
1472
|
+
*/
|
|
1473
|
+
setZoom(level) {
|
|
1474
|
+
this.activeInstance?.setZoom?.(level);
|
|
1475
|
+
}
|
|
1476
|
+
/**
|
|
1477
|
+
* Get current zoom level.
|
|
1478
|
+
*/
|
|
1479
|
+
getZoom() {
|
|
1480
|
+
return this.activeInstance?.getZoom?.() ?? 1;
|
|
1481
|
+
}
|
|
1482
|
+
/**
|
|
1483
|
+
* Rotate 90 degrees clockwise.
|
|
1484
|
+
*/
|
|
1485
|
+
rotateCW() {
|
|
1486
|
+
this.activeInstance?.rotateCW?.();
|
|
1487
|
+
}
|
|
1488
|
+
/**
|
|
1489
|
+
* Rotate 90 degrees counter-clockwise.
|
|
1490
|
+
*/
|
|
1491
|
+
rotateCCW() {
|
|
1492
|
+
this.activeInstance?.rotateCCW?.();
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Navigate to a specific page number.
|
|
1496
|
+
*/
|
|
1497
|
+
goToPage(page) {
|
|
1498
|
+
this.activeInstance?.goToPage?.(page);
|
|
1499
|
+
this.toolbar?.setPage(page);
|
|
1500
|
+
}
|
|
1501
|
+
/**
|
|
1502
|
+
* Navigate to next page.
|
|
1503
|
+
*/
|
|
1504
|
+
nextPage() {
|
|
1505
|
+
const cur = this.getCurrentPage();
|
|
1506
|
+
const max = this.getPageCount();
|
|
1507
|
+
if (cur < max) {
|
|
1508
|
+
this.goToPage(cur + 1);
|
|
1509
|
+
}
|
|
1510
|
+
}
|
|
1511
|
+
/**
|
|
1512
|
+
* Navigate to previous page.
|
|
1513
|
+
*/
|
|
1514
|
+
prevPage() {
|
|
1515
|
+
const cur = this.getCurrentPage();
|
|
1516
|
+
if (cur > 1) {
|
|
1517
|
+
this.goToPage(cur - 1);
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
/**
|
|
1521
|
+
* Get total page count.
|
|
1522
|
+
*/
|
|
1523
|
+
getPageCount() {
|
|
1524
|
+
return this.activeInstance?.getPageCount?.() ?? 1;
|
|
1525
|
+
}
|
|
1526
|
+
/**
|
|
1527
|
+
* Get current page number.
|
|
1528
|
+
*/
|
|
1529
|
+
getCurrentPage() {
|
|
1530
|
+
return this.activeInstance?.getCurrentPage?.() ?? 1;
|
|
1531
|
+
}
|
|
1532
|
+
/**
|
|
1533
|
+
* Download the current document.
|
|
1534
|
+
*/
|
|
1535
|
+
download() {
|
|
1536
|
+
this.activeInstance?.download?.();
|
|
1537
|
+
}
|
|
1538
|
+
/**
|
|
1539
|
+
* Print the current document.
|
|
1540
|
+
*/
|
|
1541
|
+
print() {
|
|
1542
|
+
this.activeInstance?.print?.();
|
|
1543
|
+
}
|
|
1544
|
+
/**
|
|
1545
|
+
* Toggle fullscreen mode.
|
|
1546
|
+
*/
|
|
1547
|
+
toggleFullscreen() {
|
|
1548
|
+
try {
|
|
1549
|
+
if (!document.fullscreenElement) {
|
|
1550
|
+
this.wrapperEl?.requestFullscreen?.();
|
|
1551
|
+
this.wrapperEl?.classList.add("fp-fullscreen-active");
|
|
1552
|
+
} else {
|
|
1553
|
+
document.exitFullscreen?.();
|
|
1554
|
+
this.wrapperEl?.classList.remove("fp-fullscreen-active");
|
|
1555
|
+
}
|
|
1556
|
+
} catch {
|
|
1557
|
+
this.wrapperEl?.classList.toggle("fp-fullscreen-active");
|
|
1558
|
+
}
|
|
1559
|
+
setTimeout(() => {
|
|
1560
|
+
this.activeInstance?.fitToPage?.();
|
|
1561
|
+
}, 120);
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* Toggle thumbnails sidebar panel.
|
|
1565
|
+
*/
|
|
1566
|
+
toggleThumbnails() {
|
|
1567
|
+
if (!this.thumbnailPanel) return;
|
|
1568
|
+
this.thumbnailPanel.toggle();
|
|
1569
|
+
const isOpen = this.thumbnailPanel.isOpen();
|
|
1570
|
+
this.toolbar?.setActionActive("thumbnails", isOpen);
|
|
1571
|
+
setTimeout(() => {
|
|
1572
|
+
this.activeInstance?.fitToPage?.();
|
|
1573
|
+
}, 260);
|
|
1574
|
+
}
|
|
1575
|
+
/**
|
|
1576
|
+
* Set whether to display the file name / title bar above the toolbar.
|
|
1577
|
+
*/
|
|
1578
|
+
setShowFileName(show) {
|
|
1579
|
+
if (this.currentOptions) {
|
|
1580
|
+
this.currentOptions.showFileName = show;
|
|
1581
|
+
}
|
|
1582
|
+
this.updateTitleBar(this.currentOptions, this.currentMetadata);
|
|
1583
|
+
}
|
|
1584
|
+
/**
|
|
1585
|
+
* Set or override the displayed file name in the preview panel.
|
|
1586
|
+
*/
|
|
1587
|
+
setFileName(name) {
|
|
1588
|
+
if (this.currentOptions) {
|
|
1589
|
+
this.currentOptions.fileName = name;
|
|
1590
|
+
}
|
|
1591
|
+
this.updateTitleBar(this.currentOptions, this.currentMetadata);
|
|
1592
|
+
}
|
|
1136
1593
|
// --- Private methods ---
|
|
1594
|
+
updateTitleBar(options, metadata) {
|
|
1595
|
+
if (!this.titleBarEl) return;
|
|
1596
|
+
const show = options?.showFileName !== false;
|
|
1597
|
+
if (!show) {
|
|
1598
|
+
this.titleBarEl.style.display = "none";
|
|
1599
|
+
return;
|
|
1600
|
+
}
|
|
1601
|
+
const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
|
|
1602
|
+
if (!name) {
|
|
1603
|
+
this.titleBarEl.style.display = "none";
|
|
1604
|
+
return;
|
|
1605
|
+
}
|
|
1606
|
+
this.titleBarEl.style.display = "flex";
|
|
1607
|
+
const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
|
|
1608
|
+
const badge = ext ? ext.replace(".", "").toUpperCase() : "";
|
|
1609
|
+
this.titleBarEl.innerHTML = `
|
|
1610
|
+
<div class="fp-titlebar-info">
|
|
1611
|
+
<span class="fp-titlebar-icon">\u{1F4C4}</span>
|
|
1612
|
+
<span class="fp-titlebar-name" title="${name}">${name}</span>
|
|
1613
|
+
${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
|
|
1614
|
+
</div>
|
|
1615
|
+
`;
|
|
1616
|
+
}
|
|
1617
|
+
extractToolbarConfig(options) {
|
|
1618
|
+
if (!options) return {};
|
|
1619
|
+
const nested = typeof options.toolbar === "object" ? options.toolbar : {};
|
|
1620
|
+
return {
|
|
1621
|
+
...options,
|
|
1622
|
+
...nested
|
|
1623
|
+
};
|
|
1624
|
+
}
|
|
1137
1625
|
abort() {
|
|
1138
1626
|
if (this.abortController) {
|
|
1139
1627
|
this.abortController.abort();
|
|
@@ -1176,17 +1664,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
|
|
|
1176
1664
|
bodyEl.className = "fp-body";
|
|
1177
1665
|
bodyEl.appendChild(thumbnailEl);
|
|
1178
1666
|
bodyEl.appendChild(this.contentEl);
|
|
1667
|
+
const titleBarEl = document.createElement("div");
|
|
1668
|
+
titleBarEl.className = "fp-titlebar";
|
|
1669
|
+
this.titleBarEl = titleBarEl;
|
|
1670
|
+
this.updateTitleBar(options, this.currentMetadata);
|
|
1179
1671
|
if (toolbarPos === "top") {
|
|
1672
|
+
this.wrapperEl.appendChild(titleBarEl);
|
|
1180
1673
|
this.wrapperEl.appendChild(toolbarEl);
|
|
1181
1674
|
this.wrapperEl.appendChild(bodyEl);
|
|
1182
1675
|
} else {
|
|
1676
|
+
this.wrapperEl.appendChild(titleBarEl);
|
|
1183
1677
|
this.wrapperEl.appendChild(bodyEl);
|
|
1184
1678
|
this.wrapperEl.appendChild(toolbarEl);
|
|
1185
1679
|
}
|
|
1186
1680
|
container.innerHTML = "";
|
|
1187
1681
|
container.appendChild(this.wrapperEl);
|
|
1188
|
-
|
|
1189
|
-
this.
|
|
1682
|
+
const initialToolbarConfig = this.extractToolbarConfig(options);
|
|
1683
|
+
this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
|
|
1684
|
+
this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
|
|
1685
|
+
this.toolbar?.setActionActive("thumbnails", isOpen);
|
|
1686
|
+
setTimeout(() => {
|
|
1687
|
+
this.activeInstance?.fitToPage?.();
|
|
1688
|
+
}, 260);
|
|
1689
|
+
});
|
|
1190
1690
|
this.setupKeyboardShortcuts();
|
|
1191
1691
|
this.setupDragAndDrop(container, options);
|
|
1192
1692
|
this.setupResizeAndFullscreenListeners();
|
|
@@ -1576,6 +2076,14 @@ var PdfPlugin = class {
|
|
|
1576
2076
|
group: "zoom",
|
|
1577
2077
|
execute: () => instance.fitToPage?.()
|
|
1578
2078
|
},
|
|
2079
|
+
{
|
|
2080
|
+
id: "fit-width",
|
|
2081
|
+
icon: "fit-width",
|
|
2082
|
+
label: "Fit to Width",
|
|
2083
|
+
type: "button",
|
|
2084
|
+
group: "zoom",
|
|
2085
|
+
execute: () => instance.fitToWidth?.()
|
|
2086
|
+
},
|
|
1579
2087
|
{
|
|
1580
2088
|
id: "rotate-cw",
|
|
1581
2089
|
icon: "rotate-cw",
|
|
@@ -1584,6 +2092,14 @@ var PdfPlugin = class {
|
|
|
1584
2092
|
group: "view",
|
|
1585
2093
|
execute: () => instance.rotateCW?.()
|
|
1586
2094
|
},
|
|
2095
|
+
{
|
|
2096
|
+
id: "rotate-ccw",
|
|
2097
|
+
icon: "rotate-ccw",
|
|
2098
|
+
label: "Rotate Counter-Clockwise",
|
|
2099
|
+
type: "button",
|
|
2100
|
+
group: "view",
|
|
2101
|
+
execute: () => instance.rotateCCW?.()
|
|
2102
|
+
},
|
|
1587
2103
|
{
|
|
1588
2104
|
id: "download",
|
|
1589
2105
|
icon: "download",
|
|
@@ -1615,12 +2131,13 @@ var PdfPlugin = class {
|
|
|
1615
2131
|
container.className = "fp-pdf-container";
|
|
1616
2132
|
container.style.width = "100%";
|
|
1617
2133
|
container.style.height = "100%";
|
|
1618
|
-
container.style.
|
|
2134
|
+
container.style.overflowX = "hidden";
|
|
2135
|
+
container.style.overflowY = "auto";
|
|
1619
2136
|
container.style.display = "flex";
|
|
1620
2137
|
container.style.flexDirection = "column";
|
|
1621
2138
|
container.style.alignItems = "center";
|
|
1622
2139
|
container.style.justifyContent = "flex-start";
|
|
1623
|
-
container.style.padding = "
|
|
2140
|
+
container.style.padding = "16px 8px";
|
|
1624
2141
|
container.style.backgroundColor = "#0f172a";
|
|
1625
2142
|
container.style.boxSizing = "border-box";
|
|
1626
2143
|
container.style.position = "relative";
|
|
@@ -1631,30 +2148,12 @@ var PdfPlugin = class {
|
|
|
1631
2148
|
pageCard.style.borderRadius = "4px";
|
|
1632
2149
|
pageCard.style.overflow = "hidden";
|
|
1633
2150
|
pageCard.style.lineHeight = "0";
|
|
1634
|
-
pageCard.style.transition = "transform 0.15s ease";
|
|
1635
2151
|
pageCard.style.position = "relative";
|
|
1636
2152
|
pageCard.style.flexShrink = "0";
|
|
2153
|
+
pageCard.style.transition = "box-shadow 0.2s ease";
|
|
1637
2154
|
let canvas = document.createElement("canvas");
|
|
1638
2155
|
pageCard.appendChild(canvas);
|
|
1639
2156
|
container.appendChild(pageCard);
|
|
1640
|
-
const indicator = document.createElement("div");
|
|
1641
|
-
indicator.className = "fp-pdf-page-indicator";
|
|
1642
|
-
indicator.style.position = "sticky";
|
|
1643
|
-
indicator.style.bottom = "16px";
|
|
1644
|
-
indicator.style.marginTop = "16px";
|
|
1645
|
-
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
1646
|
-
indicator.style.backdropFilter = "blur(8px)";
|
|
1647
|
-
indicator.style.color = "#f8fafc";
|
|
1648
|
-
indicator.style.fontSize = "12px";
|
|
1649
|
-
indicator.style.fontWeight = "600";
|
|
1650
|
-
indicator.style.padding = "5px 14px";
|
|
1651
|
-
indicator.style.borderRadius = "20px";
|
|
1652
|
-
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
1653
|
-
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
1654
|
-
indicator.style.zIndex = "10";
|
|
1655
|
-
indicator.style.userSelect = "none";
|
|
1656
|
-
indicator.style.pointerEvents = "none";
|
|
1657
|
-
container.appendChild(indicator);
|
|
1658
2157
|
ctx.container.appendChild(container);
|
|
1659
2158
|
const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
|
|
1660
2159
|
const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
|
|
@@ -1670,8 +2169,9 @@ var PdfPlugin = class {
|
|
|
1670
2169
|
let currentPage = 1;
|
|
1671
2170
|
let zoomScale = 1;
|
|
1672
2171
|
let rotation = 0;
|
|
1673
|
-
let fitMode = "
|
|
2172
|
+
let fitMode = ctx?.options?.fitMode || "width";
|
|
1674
2173
|
let currentRenderTask = null;
|
|
2174
|
+
let textLayerDiv = null;
|
|
1675
2175
|
const renderPage = async (pageNum) => {
|
|
1676
2176
|
if (currentRenderTask) {
|
|
1677
2177
|
try {
|
|
@@ -1683,30 +2183,37 @@ var PdfPlugin = class {
|
|
|
1683
2183
|
const newCanvas = document.createElement("canvas");
|
|
1684
2184
|
pageCard.replaceChild(newCanvas, canvas);
|
|
1685
2185
|
canvas = newCanvas;
|
|
2186
|
+
if (textLayerDiv) {
|
|
2187
|
+
textLayerDiv.remove();
|
|
2188
|
+
textLayerDiv = null;
|
|
2189
|
+
}
|
|
1686
2190
|
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
1687
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
1688
2191
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
1689
2192
|
const page = await pdfDoc.getPage(currentPage);
|
|
1690
2193
|
const containerWidth = container.clientWidth || 900;
|
|
1691
2194
|
const containerHeight = container.clientHeight || 700;
|
|
1692
2195
|
const unscaledVp = page.getViewport({ scale: 1, rotation });
|
|
1693
|
-
const availWidth = Math.max(
|
|
1694
|
-
const availHeight = Math.max(
|
|
2196
|
+
const availWidth = Math.max(280, containerWidth - 36);
|
|
2197
|
+
const availHeight = Math.max(280, containerHeight - 32);
|
|
1695
2198
|
const scaleW = availWidth / unscaledVp.width;
|
|
1696
2199
|
const scaleH = availHeight / unscaledVp.height;
|
|
1697
2200
|
let fitScale;
|
|
1698
2201
|
if (fitMode === "page") {
|
|
1699
|
-
fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
|
|
2202
|
+
fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
|
|
1700
2203
|
} else {
|
|
1701
|
-
fitScale = Math.max(0.
|
|
2204
|
+
fitScale = Math.max(0.2, Math.min(4, scaleW));
|
|
1702
2205
|
}
|
|
1703
2206
|
const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
|
|
1704
2207
|
const pixelRatio = window.devicePixelRatio || 1;
|
|
1705
2208
|
const viewport = page.getViewport({ scale: effectiveScale, rotation });
|
|
2209
|
+
const displayWidth = Math.floor(viewport.width);
|
|
2210
|
+
const displayHeight = Math.floor(viewport.height);
|
|
1706
2211
|
canvas.width = Math.floor(viewport.width * pixelRatio);
|
|
1707
2212
|
canvas.height = Math.floor(viewport.height * pixelRatio);
|
|
1708
|
-
canvas.style.width = `${
|
|
1709
|
-
canvas.style.height = `${
|
|
2213
|
+
canvas.style.width = `${displayWidth}px`;
|
|
2214
|
+
canvas.style.height = `${displayHeight}px`;
|
|
2215
|
+
pageCard.style.width = `${displayWidth}px`;
|
|
2216
|
+
pageCard.style.height = `${displayHeight}px`;
|
|
1710
2217
|
const canvasCtx = canvas.getContext("2d");
|
|
1711
2218
|
if (!canvasCtx) return;
|
|
1712
2219
|
canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
|
|
@@ -1720,9 +2227,31 @@ var PdfPlugin = class {
|
|
|
1720
2227
|
if (err?.name !== "RenderingCancelledException") {
|
|
1721
2228
|
console.warn("[PdfPlugin] Page render warning:", err);
|
|
1722
2229
|
}
|
|
2230
|
+
return;
|
|
1723
2231
|
} finally {
|
|
1724
2232
|
currentRenderTask = null;
|
|
1725
2233
|
}
|
|
2234
|
+
try {
|
|
2235
|
+
textLayerDiv = document.createElement("div");
|
|
2236
|
+
textLayerDiv.className = "textLayer";
|
|
2237
|
+
textLayerDiv.style.width = `${displayWidth}px`;
|
|
2238
|
+
textLayerDiv.style.height = `${displayHeight}px`;
|
|
2239
|
+
textLayerDiv.style.position = "absolute";
|
|
2240
|
+
textLayerDiv.style.top = "0";
|
|
2241
|
+
textLayerDiv.style.left = "0";
|
|
2242
|
+
textLayerDiv.style.lineHeight = "1";
|
|
2243
|
+
pageCard.appendChild(textLayerDiv);
|
|
2244
|
+
if (pdfjsLib.TextLayer) {
|
|
2245
|
+
const textLayer = new pdfjsLib.TextLayer({
|
|
2246
|
+
textContentSource: page.streamTextContent(),
|
|
2247
|
+
container: textLayerDiv,
|
|
2248
|
+
viewport
|
|
2249
|
+
});
|
|
2250
|
+
await textLayer.render();
|
|
2251
|
+
}
|
|
2252
|
+
} catch (err) {
|
|
2253
|
+
console.debug("[PdfPlugin] TextLayer notice:", err);
|
|
2254
|
+
}
|
|
1726
2255
|
};
|
|
1727
2256
|
renderPage(1);
|
|
1728
2257
|
let resizeTimer = null;
|
|
@@ -1732,11 +2261,48 @@ var PdfPlugin = class {
|
|
|
1732
2261
|
if (Math.abs(zoomScale - 1) < 0.05) {
|
|
1733
2262
|
renderPage(currentPage);
|
|
1734
2263
|
}
|
|
1735
|
-
},
|
|
2264
|
+
}, 120);
|
|
1736
2265
|
});
|
|
1737
2266
|
resizeObserver.observe(container);
|
|
2267
|
+
const onWheel = (e) => {
|
|
2268
|
+
if (e.ctrlKey || e.metaKey) {
|
|
2269
|
+
e.preventDefault();
|
|
2270
|
+
const delta = e.deltaY > 0 ? -0.15 : 0.15;
|
|
2271
|
+
zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
|
|
2272
|
+
renderPage(currentPage);
|
|
2273
|
+
}
|
|
2274
|
+
};
|
|
2275
|
+
container.addEventListener("wheel", onWheel, { passive: false });
|
|
2276
|
+
const onKeyDown = (e) => {
|
|
2277
|
+
const tag = e.target?.tagName?.toLowerCase();
|
|
2278
|
+
if (tag === "input" || tag === "textarea" || tag === "select") return;
|
|
2279
|
+
if (e.key === "ArrowRight" || e.key === "PageDown") {
|
|
2280
|
+
if (currentPage < totalPages) {
|
|
2281
|
+
e.preventDefault();
|
|
2282
|
+
container.scrollTop = 0;
|
|
2283
|
+
renderPage(currentPage + 1);
|
|
2284
|
+
}
|
|
2285
|
+
} else if (e.key === "ArrowLeft" || e.key === "PageUp") {
|
|
2286
|
+
if (currentPage > 1) {
|
|
2287
|
+
e.preventDefault();
|
|
2288
|
+
container.scrollTop = 0;
|
|
2289
|
+
renderPage(currentPage - 1);
|
|
2290
|
+
}
|
|
2291
|
+
} else if (e.key === "Home") {
|
|
2292
|
+
e.preventDefault();
|
|
2293
|
+
container.scrollTop = 0;
|
|
2294
|
+
renderPage(1);
|
|
2295
|
+
} else if (e.key === "End") {
|
|
2296
|
+
e.preventDefault();
|
|
2297
|
+
container.scrollTop = 0;
|
|
2298
|
+
renderPage(totalPages);
|
|
2299
|
+
}
|
|
2300
|
+
};
|
|
2301
|
+
window.addEventListener("keydown", onKeyDown);
|
|
1738
2302
|
const cleanup = () => {
|
|
1739
2303
|
resizeObserver.disconnect();
|
|
2304
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
2305
|
+
container.removeEventListener("wheel", onWheel);
|
|
1740
2306
|
if (resizeTimer) clearTimeout(resizeTimer);
|
|
1741
2307
|
if (currentRenderTask) {
|
|
1742
2308
|
try {
|
|
@@ -1755,22 +2321,26 @@ var PdfPlugin = class {
|
|
|
1755
2321
|
const instance = {
|
|
1756
2322
|
destroy: cleanup,
|
|
1757
2323
|
zoomIn: () => {
|
|
1758
|
-
zoomScale = Math.min(
|
|
2324
|
+
zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
|
|
1759
2325
|
renderPage(currentPage);
|
|
1760
2326
|
},
|
|
1761
2327
|
zoomOut: () => {
|
|
1762
|
-
zoomScale = Math.max(0.
|
|
2328
|
+
zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
|
|
1763
2329
|
renderPage(currentPage);
|
|
1764
2330
|
},
|
|
1765
2331
|
getZoom: () => zoomScale,
|
|
1766
2332
|
setZoom: (level) => {
|
|
1767
|
-
zoomScale = Math.max(0.
|
|
2333
|
+
zoomScale = Math.max(0.25, Math.min(4, level));
|
|
1768
2334
|
renderPage(currentPage);
|
|
1769
2335
|
},
|
|
1770
2336
|
fitToPage: () => {
|
|
1771
|
-
fitMode =
|
|
2337
|
+
fitMode = "page";
|
|
2338
|
+
zoomScale = 1;
|
|
2339
|
+
renderPage(currentPage);
|
|
2340
|
+
},
|
|
2341
|
+
fitToWidth: () => {
|
|
2342
|
+
fitMode = "width";
|
|
1772
2343
|
zoomScale = 1;
|
|
1773
|
-
rotation = 0;
|
|
1774
2344
|
renderPage(currentPage);
|
|
1775
2345
|
},
|
|
1776
2346
|
rotateCW: () => {
|
|
@@ -1788,6 +2358,9 @@ var PdfPlugin = class {
|
|
|
1788
2358
|
container.scrollTop = 0;
|
|
1789
2359
|
renderPage(page);
|
|
1790
2360
|
},
|
|
2361
|
+
toggleThumbnails: () => {
|
|
2362
|
+
ctx?.toggleThumbnails?.();
|
|
2363
|
+
},
|
|
1791
2364
|
download: () => {
|
|
1792
2365
|
const blob = new Blob([ctx.buffer], { type: "application/pdf" });
|
|
1793
2366
|
const url = URL.createObjectURL(blob);
|
|
@@ -1821,7 +2394,7 @@ var PdfPlugin = class {
|
|
|
1821
2394
|
},
|
|
1822
2395
|
getThumbnails: async () => {
|
|
1823
2396
|
const thumbnails = [];
|
|
1824
|
-
const count =
|
|
2397
|
+
const count = totalPages;
|
|
1825
2398
|
for (let i = 1; i <= count; i++) {
|
|
1826
2399
|
thumbnails.push({
|
|
1827
2400
|
index: i,
|
|
@@ -1829,9 +2402,10 @@ var PdfPlugin = class {
|
|
|
1829
2402
|
render: async (thumbCanvas) => {
|
|
1830
2403
|
try {
|
|
1831
2404
|
const p = await pdfDoc.getPage(i);
|
|
1832
|
-
const baseVp = p.getViewport({ scale: 1 });
|
|
1833
|
-
const
|
|
1834
|
-
const
|
|
2405
|
+
const baseVp = p.getViewport({ scale: 1, rotation });
|
|
2406
|
+
const targetW = thumbCanvas.width || 130;
|
|
2407
|
+
const thumbScale = targetW / baseVp.width;
|
|
2408
|
+
const thumbVp = p.getViewport({ scale: thumbScale, rotation });
|
|
1835
2409
|
thumbCanvas.height = Math.floor(thumbVp.height);
|
|
1836
2410
|
const tCtx = thumbCanvas.getContext("2d");
|
|
1837
2411
|
if (tCtx) {
|
|
@@ -2175,28 +2749,26 @@ var DocxPlugin = class {
|
|
|
2175
2749
|
getToolbarActions(instance) {
|
|
2176
2750
|
const totalPages = instance.getPageCount?.() ?? 1;
|
|
2177
2751
|
const actions = [];
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
if (
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
instance.goToPage?.(page);
|
|
2196
|
-
}
|
|
2752
|
+
actions.push({
|
|
2753
|
+
id: "page-nav",
|
|
2754
|
+
icon: "",
|
|
2755
|
+
label: "Page Navigation",
|
|
2756
|
+
type: "page-nav",
|
|
2757
|
+
group: "navigation",
|
|
2758
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
2759
|
+
max: totalPages,
|
|
2760
|
+
execute: (action, page) => {
|
|
2761
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
2762
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
2763
|
+
if (action === "prev") {
|
|
2764
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
2765
|
+
} else if (action === "next") {
|
|
2766
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
2767
|
+
} else if (typeof page === "number") {
|
|
2768
|
+
instance.goToPage?.(page);
|
|
2197
2769
|
}
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2770
|
+
}
|
|
2771
|
+
});
|
|
2200
2772
|
actions.push(
|
|
2201
2773
|
{
|
|
2202
2774
|
id: "zoom-out",
|
|
@@ -2262,11 +2834,17 @@ var DocxPlugin = class {
|
|
|
2262
2834
|
wrapper.className = "fp-docx-wrapper";
|
|
2263
2835
|
wrapper.style.transformOrigin = "top center";
|
|
2264
2836
|
wrapper.style.transition = "transform 0.2s ease";
|
|
2265
|
-
wrapper.style.padding = "
|
|
2266
|
-
wrapper.style.maxWidth = "
|
|
2837
|
+
wrapper.style.padding = "0";
|
|
2838
|
+
wrapper.style.maxWidth = "none";
|
|
2839
|
+
wrapper.style.width = "816px";
|
|
2267
2840
|
wrapper.style.margin = "0 auto";
|
|
2841
|
+
wrapper.style.display = "flex";
|
|
2842
|
+
wrapper.style.flexDirection = "column";
|
|
2843
|
+
wrapper.style.alignItems = "center";
|
|
2268
2844
|
wrapper.style.boxSizing = "border-box";
|
|
2269
|
-
ctx.container.style.
|
|
2845
|
+
ctx.container.style.overflowX = "hidden";
|
|
2846
|
+
ctx.container.style.overflowY = "auto";
|
|
2847
|
+
ctx.container.style.padding = "16px 8px";
|
|
2270
2848
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
2271
2849
|
ctx.container.appendChild(wrapper);
|
|
2272
2850
|
const styleOverride = document.createElement("style");
|
|
@@ -2440,39 +3018,13 @@ var DocxPlugin = class {
|
|
|
2440
3018
|
const pageElements = sections.length > 0 ? sections : cards;
|
|
2441
3019
|
const totalPages = Math.max(1, pageElements.length);
|
|
2442
3020
|
let currentPage = 1;
|
|
2443
|
-
let indicator = null;
|
|
2444
|
-
if (totalPages > 1) {
|
|
2445
|
-
indicator = document.createElement("div");
|
|
2446
|
-
indicator.className = "fp-docx-page-indicator";
|
|
2447
|
-
indicator.style.position = "sticky";
|
|
2448
|
-
indicator.style.bottom = "16px";
|
|
2449
|
-
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
2450
|
-
indicator.style.backdropFilter = "blur(8px)";
|
|
2451
|
-
indicator.style.color = "#f8fafc";
|
|
2452
|
-
indicator.style.fontSize = "12px";
|
|
2453
|
-
indicator.style.fontWeight = "600";
|
|
2454
|
-
indicator.style.padding = "5px 14px";
|
|
2455
|
-
indicator.style.borderRadius = "20px";
|
|
2456
|
-
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
2457
|
-
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
2458
|
-
indicator.style.zIndex = "10";
|
|
2459
|
-
indicator.style.userSelect = "none";
|
|
2460
|
-
indicator.style.pointerEvents = "none";
|
|
2461
|
-
indicator.style.textAlign = "center";
|
|
2462
|
-
indicator.style.width = "fit-content";
|
|
2463
|
-
indicator.style.margin = "16px auto 0";
|
|
2464
|
-
ctx.container.appendChild(indicator);
|
|
2465
|
-
}
|
|
2466
3021
|
const showPage = (pageNum) => {
|
|
2467
3022
|
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
2468
3023
|
if (pageElements.length > 1) {
|
|
2469
3024
|
pageElements.forEach((sec, idx) => {
|
|
2470
|
-
sec.style.display = idx + 1 === currentPage ? "
|
|
3025
|
+
sec.style.display = idx + 1 === currentPage ? "" : "none";
|
|
2471
3026
|
});
|
|
2472
3027
|
}
|
|
2473
|
-
if (indicator) {
|
|
2474
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
2475
|
-
}
|
|
2476
3028
|
ctx.container.scrollTop = 0;
|
|
2477
3029
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
2478
3030
|
};
|
|
@@ -2481,31 +3033,46 @@ var DocxPlugin = class {
|
|
|
2481
3033
|
}
|
|
2482
3034
|
scale = 1;
|
|
2483
3035
|
let rotation = 0;
|
|
2484
|
-
let fitMode = "
|
|
2485
|
-
|
|
2486
|
-
|
|
3036
|
+
let fitMode = "page";
|
|
3037
|
+
let isUserZoomed = false;
|
|
3038
|
+
const calculateFitScale = (_mode = fitMode) => {
|
|
3039
|
+
const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
|
|
2487
3040
|
const elW = activeEl.offsetWidth || 816;
|
|
2488
|
-
const
|
|
2489
|
-
const availW = Math.max(280, ctx.container.clientWidth - 48);
|
|
2490
|
-
const availH = Math.max(280, ctx.container.clientHeight - 80);
|
|
3041
|
+
const availW = Math.max(280, ctx.container.clientWidth - 32);
|
|
2491
3042
|
const sW = availW / elW;
|
|
2492
|
-
|
|
2493
|
-
if (mode === "page") {
|
|
2494
|
-
return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
|
|
2495
|
-
}
|
|
2496
|
-
return Math.max(0.65, Math.min(1.05, sW));
|
|
3043
|
+
return Math.max(0.35, Math.min(3, sW));
|
|
2497
3044
|
};
|
|
2498
3045
|
const applyTransform = () => {
|
|
2499
3046
|
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
2500
3047
|
wrapper.style.transformOrigin = "top center";
|
|
3048
|
+
const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
|
|
3049
|
+
const elH = activeEl.offsetHeight || 1056;
|
|
3050
|
+
if (scale > 1) {
|
|
3051
|
+
wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
|
|
3052
|
+
} else {
|
|
3053
|
+
wrapper.style.marginBottom = "32px";
|
|
3054
|
+
}
|
|
2501
3055
|
};
|
|
3056
|
+
let resizeObserver = null;
|
|
3057
|
+
if (typeof ResizeObserver !== "undefined") {
|
|
3058
|
+
resizeObserver = new ResizeObserver(() => {
|
|
3059
|
+
if (!isUserZoomed) {
|
|
3060
|
+
const newFit = calculateFitScale("page");
|
|
3061
|
+
if (Math.abs(scale - newFit) > 0.015) {
|
|
3062
|
+
scale = newFit;
|
|
3063
|
+
applyTransform();
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
});
|
|
3067
|
+
resizeObserver.observe(ctx.container);
|
|
3068
|
+
}
|
|
2502
3069
|
setTimeout(() => {
|
|
2503
|
-
|
|
2504
|
-
scale = calculateFitScale("width");
|
|
3070
|
+
scale = calculateFitScale("page");
|
|
2505
3071
|
applyTransform();
|
|
2506
|
-
},
|
|
3072
|
+
}, 40);
|
|
2507
3073
|
const cleanup = () => {
|
|
2508
|
-
|
|
3074
|
+
resizeObserver?.disconnect();
|
|
3075
|
+
resizeObserver = null;
|
|
2509
3076
|
for (const url of createdBlobUrls) {
|
|
2510
3077
|
URL.revokeObjectURL(url);
|
|
2511
3078
|
}
|
|
@@ -2522,21 +3089,24 @@ var DocxPlugin = class {
|
|
|
2522
3089
|
showPage(page);
|
|
2523
3090
|
},
|
|
2524
3091
|
zoomIn: () => {
|
|
2525
|
-
|
|
3092
|
+
isUserZoomed = true;
|
|
3093
|
+
scale = Math.min(3.5, scale + 0.15);
|
|
2526
3094
|
applyTransform();
|
|
2527
3095
|
},
|
|
2528
3096
|
zoomOut: () => {
|
|
3097
|
+
isUserZoomed = true;
|
|
2529
3098
|
scale = Math.max(0.2, scale - 0.15);
|
|
2530
3099
|
applyTransform();
|
|
2531
3100
|
},
|
|
2532
3101
|
getZoom: () => scale,
|
|
2533
3102
|
setZoom: (level) => {
|
|
3103
|
+
isUserZoomed = true;
|
|
2534
3104
|
scale = level;
|
|
2535
3105
|
applyTransform();
|
|
2536
3106
|
},
|
|
2537
3107
|
fitToPage: () => {
|
|
2538
|
-
|
|
2539
|
-
scale = calculateFitScale(
|
|
3108
|
+
isUserZoomed = false;
|
|
3109
|
+
scale = calculateFitScale("page");
|
|
2540
3110
|
rotation = 0;
|
|
2541
3111
|
applyTransform();
|
|
2542
3112
|
},
|
|
@@ -2926,7 +3496,7 @@ var ExcelPlugin = class {
|
|
|
2926
3496
|
"application/vnd.ms-excel.template.macroEnabled.12",
|
|
2927
3497
|
"application/vnd.oasis.opendocument.spreadsheet"
|
|
2928
3498
|
];
|
|
2929
|
-
weight =
|
|
3499
|
+
weight = 85;
|
|
2930
3500
|
supports(file) {
|
|
2931
3501
|
const ext = file.metadata.extension?.toLowerCase();
|
|
2932
3502
|
const mime = file.metadata.mimeType?.toLowerCase();
|
|
@@ -2938,6 +3508,14 @@ var ExcelPlugin = class {
|
|
|
2938
3508
|
getToolbarActions(instance) {
|
|
2939
3509
|
const totalSheets = instance.getPageCount?.() ?? 1;
|
|
2940
3510
|
const actions = [];
|
|
3511
|
+
actions.push({
|
|
3512
|
+
id: "thumbnails",
|
|
3513
|
+
icon: "thumbnails",
|
|
3514
|
+
label: "Sheet Thumbnails",
|
|
3515
|
+
type: "button",
|
|
3516
|
+
group: "navigation",
|
|
3517
|
+
execute: () => instance.toggleThumbnails?.()
|
|
3518
|
+
});
|
|
2941
3519
|
if (totalSheets > 1) {
|
|
2942
3520
|
actions.push({
|
|
2943
3521
|
id: "page-nav",
|
|
@@ -3020,6 +3598,16 @@ var ExcelPlugin = class {
|
|
|
3020
3598
|
execute: () => {
|
|
3021
3599
|
instance.print?.();
|
|
3022
3600
|
}
|
|
3601
|
+
},
|
|
3602
|
+
{
|
|
3603
|
+
id: "open-window",
|
|
3604
|
+
icon: "open-window",
|
|
3605
|
+
label: "Open in Separate Full Window",
|
|
3606
|
+
type: "button",
|
|
3607
|
+
group: "actions",
|
|
3608
|
+
execute: () => {
|
|
3609
|
+
instance.openInSeparateWindow?.();
|
|
3610
|
+
}
|
|
3023
3611
|
}
|
|
3024
3612
|
);
|
|
3025
3613
|
return actions;
|
|
@@ -3185,6 +3773,63 @@ var ExcelPlugin = class {
|
|
|
3185
3773
|
},
|
|
3186
3774
|
getPageCount: () => sheetNames.length,
|
|
3187
3775
|
getCurrentPage: () => currentSheetIndex,
|
|
3776
|
+
getThumbnails: () => {
|
|
3777
|
+
return sheetNames.map((name, idx) => ({
|
|
3778
|
+
index: idx,
|
|
3779
|
+
label: name,
|
|
3780
|
+
render: async (canvas) => {
|
|
3781
|
+
canvas.width = 140;
|
|
3782
|
+
canvas.height = 100;
|
|
3783
|
+
const c = canvas.getContext("2d");
|
|
3784
|
+
if (!c) return;
|
|
3785
|
+
c.fillStyle = "#ffffff";
|
|
3786
|
+
c.fillRect(0, 0, canvas.width, canvas.height);
|
|
3787
|
+
c.fillStyle = "#107c41";
|
|
3788
|
+
c.fillRect(0, 0, canvas.width, 16);
|
|
3789
|
+
c.fillStyle = "#ffffff";
|
|
3790
|
+
c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
|
3791
|
+
c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
|
|
3792
|
+
const startY = 17;
|
|
3793
|
+
const rowH = 13;
|
|
3794
|
+
const colW = 30;
|
|
3795
|
+
const colHeaders = ["A", "B", "C", "D"];
|
|
3796
|
+
c.fillStyle = "#f1f5f9";
|
|
3797
|
+
c.fillRect(0, startY, canvas.width, rowH);
|
|
3798
|
+
c.strokeStyle = "#cbd5e1";
|
|
3799
|
+
c.lineWidth = 0.8;
|
|
3800
|
+
c.strokeRect(0, startY, canvas.width, rowH);
|
|
3801
|
+
c.fillStyle = "#64748b";
|
|
3802
|
+
c.font = "bold 7px sans-serif";
|
|
3803
|
+
for (let ci = 0; ci < colHeaders.length; ci++) {
|
|
3804
|
+
c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
|
|
3805
|
+
}
|
|
3806
|
+
const ws = wb?.Sheets[name];
|
|
3807
|
+
for (let ri = 1; ri <= 5; ri++) {
|
|
3808
|
+
const y = startY + ri * rowH;
|
|
3809
|
+
if (y > canvas.height - 2) break;
|
|
3810
|
+
c.fillStyle = "#f8fafc";
|
|
3811
|
+
c.fillRect(0, y, 12, rowH);
|
|
3812
|
+
c.fillStyle = "#94a3b8";
|
|
3813
|
+
c.font = "6px sans-serif";
|
|
3814
|
+
c.fillText(String(ri), 3, y + 9);
|
|
3815
|
+
c.strokeStyle = "#e2e8f0";
|
|
3816
|
+
c.strokeRect(0, y, canvas.width, rowH);
|
|
3817
|
+
c.fillStyle = "#334155";
|
|
3818
|
+
c.font = "6px sans-serif";
|
|
3819
|
+
for (let ci = 0; ci < colHeaders.length; ci++) {
|
|
3820
|
+
const cellRef = `${colHeaders[ci]}${ri}`;
|
|
3821
|
+
const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
|
|
3822
|
+
if (cellVal !== void 0) {
|
|
3823
|
+
c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
|
|
3824
|
+
}
|
|
3825
|
+
}
|
|
3826
|
+
}
|
|
3827
|
+
c.strokeStyle = "#cbd5e1";
|
|
3828
|
+
c.lineWidth = 1;
|
|
3829
|
+
c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
|
|
3830
|
+
}
|
|
3831
|
+
}));
|
|
3832
|
+
},
|
|
3188
3833
|
download: () => {
|
|
3189
3834
|
const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
|
|
3190
3835
|
const url = URL.createObjectURL(blob);
|
|
@@ -3217,7 +3862,39 @@ var CsvPlugin = class {
|
|
|
3217
3862
|
return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
|
|
3218
3863
|
}
|
|
3219
3864
|
getToolbarActions(instance) {
|
|
3220
|
-
|
|
3865
|
+
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3866
|
+
const actions = [];
|
|
3867
|
+
actions.push({
|
|
3868
|
+
id: "thumbnails",
|
|
3869
|
+
icon: "thumbnails",
|
|
3870
|
+
label: "Page Thumbnails",
|
|
3871
|
+
type: "button",
|
|
3872
|
+
group: "navigation",
|
|
3873
|
+
execute: () => instance.toggleThumbnails?.()
|
|
3874
|
+
});
|
|
3875
|
+
if (totalPages > 1) {
|
|
3876
|
+
actions.push({
|
|
3877
|
+
id: "page-nav",
|
|
3878
|
+
icon: "",
|
|
3879
|
+
label: "Page Navigation",
|
|
3880
|
+
type: "page-nav",
|
|
3881
|
+
group: "navigation",
|
|
3882
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
3883
|
+
max: totalPages,
|
|
3884
|
+
execute: (action, page) => {
|
|
3885
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
3886
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
3887
|
+
if (action === "prev") {
|
|
3888
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
3889
|
+
} else if (action === "next") {
|
|
3890
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
3891
|
+
} else if (typeof page === "number") {
|
|
3892
|
+
instance.goToPage?.(page);
|
|
3893
|
+
}
|
|
3894
|
+
}
|
|
3895
|
+
});
|
|
3896
|
+
}
|
|
3897
|
+
actions.push(
|
|
3221
3898
|
{
|
|
3222
3899
|
id: "zoom-out",
|
|
3223
3900
|
icon: "zoom-out",
|
|
@@ -3238,6 +3915,16 @@ var CsvPlugin = class {
|
|
|
3238
3915
|
instance.zoomIn?.();
|
|
3239
3916
|
}
|
|
3240
3917
|
},
|
|
3918
|
+
{
|
|
3919
|
+
id: "fit-page",
|
|
3920
|
+
icon: "fit-page",
|
|
3921
|
+
label: "Fit to View",
|
|
3922
|
+
type: "button",
|
|
3923
|
+
group: "zoom",
|
|
3924
|
+
execute: () => {
|
|
3925
|
+
instance.fitToPage?.();
|
|
3926
|
+
}
|
|
3927
|
+
},
|
|
3241
3928
|
{
|
|
3242
3929
|
id: "download",
|
|
3243
3930
|
icon: "download",
|
|
@@ -3255,10 +3942,21 @@ var CsvPlugin = class {
|
|
|
3255
3942
|
type: "button",
|
|
3256
3943
|
group: "actions",
|
|
3257
3944
|
execute: () => {
|
|
3258
|
-
instance.print?.();
|
|
3945
|
+
instance.print?.();
|
|
3946
|
+
}
|
|
3947
|
+
},
|
|
3948
|
+
{
|
|
3949
|
+
id: "open-window",
|
|
3950
|
+
icon: "open-window",
|
|
3951
|
+
label: "Open in Separate Full Window",
|
|
3952
|
+
type: "button",
|
|
3953
|
+
group: "actions",
|
|
3954
|
+
execute: () => {
|
|
3955
|
+
instance.openInSeparateWindow?.();
|
|
3259
3956
|
}
|
|
3260
3957
|
}
|
|
3261
|
-
|
|
3958
|
+
);
|
|
3959
|
+
return actions;
|
|
3262
3960
|
}
|
|
3263
3961
|
async render(ctx) {
|
|
3264
3962
|
const decoder = new TextDecoder("utf-8");
|
|
@@ -3270,33 +3968,62 @@ var CsvPlugin = class {
|
|
|
3270
3968
|
container.style.padding = "16px";
|
|
3271
3969
|
container.style.boxSizing = "border-box";
|
|
3272
3970
|
container.style.transformOrigin = "top left";
|
|
3971
|
+
const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
|
|
3972
|
+
const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
|
|
3973
|
+
const headerLine = allLines[0] || "";
|
|
3974
|
+
const headerCells = headerLine.split(delimiter);
|
|
3975
|
+
const dataLines = allLines.slice(1);
|
|
3976
|
+
const ROWS_PER_PAGE = 100;
|
|
3977
|
+
const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
|
|
3978
|
+
let currentPage = 1;
|
|
3273
3979
|
const table = document.createElement("table");
|
|
3274
3980
|
table.style.borderCollapse = "collapse";
|
|
3275
3981
|
table.style.width = "100%";
|
|
3276
|
-
table.style.fontFamily = "monospace
|
|
3982
|
+
table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
|
|
3277
3983
|
table.style.fontSize = "13px";
|
|
3278
|
-
const
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
td.style.fontWeight = "bold";
|
|
3292
|
-
}
|
|
3293
|
-
tr.appendChild(td);
|
|
3984
|
+
const renderPage = (pageNum) => {
|
|
3985
|
+
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3986
|
+
table.innerHTML = "";
|
|
3987
|
+
const headerTr = document.createElement("tr");
|
|
3988
|
+
headerCells.forEach((cell) => {
|
|
3989
|
+
const th = document.createElement("th");
|
|
3990
|
+
th.textContent = cell.trim();
|
|
3991
|
+
th.style.border = "1px solid #d0d7de";
|
|
3992
|
+
th.style.padding = "8px 12px";
|
|
3993
|
+
th.style.backgroundColor = "#f6f8fa";
|
|
3994
|
+
th.style.fontWeight = "600";
|
|
3995
|
+
th.style.whiteSpace = "nowrap";
|
|
3996
|
+
headerTr.appendChild(th);
|
|
3294
3997
|
});
|
|
3295
|
-
table.appendChild(
|
|
3296
|
-
|
|
3998
|
+
table.appendChild(headerTr);
|
|
3999
|
+
const start = (currentPage - 1) * ROWS_PER_PAGE;
|
|
4000
|
+
const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
|
|
4001
|
+
const pageRows = dataLines.slice(start, end);
|
|
4002
|
+
pageRows.forEach((row, idx) => {
|
|
4003
|
+
const tr = document.createElement("tr");
|
|
4004
|
+
if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
|
|
4005
|
+
const cells = row.split(delimiter);
|
|
4006
|
+
cells.forEach((cell) => {
|
|
4007
|
+
const td = document.createElement("td");
|
|
4008
|
+
td.textContent = cell.trim();
|
|
4009
|
+
td.style.border = "1px solid #d0d7de";
|
|
4010
|
+
td.style.padding = "6px 12px";
|
|
4011
|
+
td.style.whiteSpace = "nowrap";
|
|
4012
|
+
tr.appendChild(td);
|
|
4013
|
+
});
|
|
4014
|
+
table.appendChild(tr);
|
|
4015
|
+
});
|
|
4016
|
+
container.scrollTop = 0;
|
|
4017
|
+
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4018
|
+
};
|
|
4019
|
+
renderPage(1);
|
|
3297
4020
|
container.appendChild(table);
|
|
3298
4021
|
ctx.container.appendChild(container);
|
|
3299
4022
|
let scale = 1;
|
|
4023
|
+
const applyScale = () => {
|
|
4024
|
+
container.style.transform = `scale(${scale})`;
|
|
4025
|
+
container.style.transformOrigin = "top left";
|
|
4026
|
+
};
|
|
3300
4027
|
const cleanup = () => {
|
|
3301
4028
|
container.remove();
|
|
3302
4029
|
ctx.container.innerHTML = "";
|
|
@@ -3304,22 +4031,86 @@ var CsvPlugin = class {
|
|
|
3304
4031
|
ctx.signal.addEventListener("abort", cleanup);
|
|
3305
4032
|
return {
|
|
3306
4033
|
destroy: cleanup,
|
|
4034
|
+
getPageCount: () => totalPages,
|
|
4035
|
+
getCurrentPage: () => currentPage,
|
|
4036
|
+
goToPage: (page) => renderPage(page),
|
|
4037
|
+
getThumbnails: () => {
|
|
4038
|
+
const thumbnails = [];
|
|
4039
|
+
for (let i = 0; i < totalPages; i++) {
|
|
4040
|
+
const pageIdx = i;
|
|
4041
|
+
const startRow = pageIdx * ROWS_PER_PAGE + 1;
|
|
4042
|
+
const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
|
|
4043
|
+
const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
|
|
4044
|
+
thumbnails.push({
|
|
4045
|
+
index: pageIdx,
|
|
4046
|
+
label,
|
|
4047
|
+
render: async (canvas) => {
|
|
4048
|
+
canvas.width = 140;
|
|
4049
|
+
canvas.height = 100;
|
|
4050
|
+
const c = canvas.getContext("2d");
|
|
4051
|
+
if (!c) return;
|
|
4052
|
+
c.fillStyle = "#ffffff";
|
|
4053
|
+
c.fillRect(0, 0, canvas.width, canvas.height);
|
|
4054
|
+
c.fillStyle = "#2563eb";
|
|
4055
|
+
c.fillRect(0, 0, canvas.width, 16);
|
|
4056
|
+
c.fillStyle = "#ffffff";
|
|
4057
|
+
c.font = "bold 8px sans-serif";
|
|
4058
|
+
c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
|
|
4059
|
+
const startY = 17;
|
|
4060
|
+
const rowH = 13;
|
|
4061
|
+
const colW = 28;
|
|
4062
|
+
const cols = Math.min(4, headerCells.length || 4);
|
|
4063
|
+
c.fillStyle = "#f1f5f9";
|
|
4064
|
+
c.fillRect(0, startY, canvas.width, rowH);
|
|
4065
|
+
c.strokeStyle = "#cbd5e1";
|
|
4066
|
+
c.lineWidth = 0.8;
|
|
4067
|
+
c.strokeRect(0, startY, canvas.width, rowH);
|
|
4068
|
+
c.fillStyle = "#64748b";
|
|
4069
|
+
c.font = "bold 7px sans-serif";
|
|
4070
|
+
for (let ci = 0; ci < cols; ci++) {
|
|
4071
|
+
const hName = headerCells[ci] || `Col ${ci + 1}`;
|
|
4072
|
+
c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
|
|
4073
|
+
}
|
|
4074
|
+
const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
|
|
4075
|
+
for (let ri = 0; ri < rowsToPreview.length; ri++) {
|
|
4076
|
+
const y = startY + (ri + 1) * rowH;
|
|
4077
|
+
if (y > canvas.height - 2) break;
|
|
4078
|
+
c.strokeStyle = "#e2e8f0";
|
|
4079
|
+
c.strokeRect(0, y, canvas.width, rowH);
|
|
4080
|
+
const cVals = rowsToPreview[ri].split(delimiter);
|
|
4081
|
+
c.fillStyle = "#334155";
|
|
4082
|
+
c.font = "6px sans-serif";
|
|
4083
|
+
for (let ci = 0; ci < cols; ci++) {
|
|
4084
|
+
const val = cVals[ci] || "";
|
|
4085
|
+
c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
|
|
4086
|
+
}
|
|
4087
|
+
}
|
|
4088
|
+
c.strokeStyle = "#cbd5e1";
|
|
4089
|
+
c.lineWidth = 1;
|
|
4090
|
+
c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
|
|
4091
|
+
}
|
|
4092
|
+
});
|
|
4093
|
+
}
|
|
4094
|
+
return thumbnails;
|
|
4095
|
+
},
|
|
3307
4096
|
zoomIn: () => {
|
|
3308
4097
|
scale += 0.1;
|
|
3309
|
-
|
|
4098
|
+
applyScale();
|
|
3310
4099
|
},
|
|
3311
4100
|
zoomOut: () => {
|
|
3312
4101
|
scale = Math.max(0.2, scale - 0.1);
|
|
3313
|
-
|
|
4102
|
+
applyScale();
|
|
3314
4103
|
},
|
|
3315
4104
|
getZoom: () => scale,
|
|
3316
4105
|
setZoom: (level) => {
|
|
3317
4106
|
scale = level;
|
|
3318
|
-
|
|
4107
|
+
applyScale();
|
|
3319
4108
|
},
|
|
3320
4109
|
fitToPage: () => {
|
|
3321
|
-
|
|
3322
|
-
|
|
4110
|
+
const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
|
|
4111
|
+
const tW = table.offsetWidth || 600;
|
|
4112
|
+
scale = Math.max(0.35, Math.min(1, availW / tW));
|
|
4113
|
+
applyScale();
|
|
3323
4114
|
},
|
|
3324
4115
|
download: () => {
|
|
3325
4116
|
const blob = new Blob([ctx.buffer], { type: "text/csv" });
|
|
@@ -3383,28 +4174,34 @@ var CodePlugin = class {
|
|
|
3383
4174
|
getToolbarActions(instance) {
|
|
3384
4175
|
const totalPages = instance.getPageCount?.() ?? 1;
|
|
3385
4176
|
const actions = [];
|
|
3386
|
-
|
|
3387
|
-
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
|
|
3404
|
-
|
|
4177
|
+
actions.push({
|
|
4178
|
+
id: "thumbnails",
|
|
4179
|
+
icon: "thumbnails",
|
|
4180
|
+
label: "Page Thumbnails",
|
|
4181
|
+
type: "button",
|
|
4182
|
+
group: "navigation",
|
|
4183
|
+
execute: () => instance.toggleThumbnails?.()
|
|
4184
|
+
});
|
|
4185
|
+
actions.push({
|
|
4186
|
+
id: "page-nav",
|
|
4187
|
+
icon: "",
|
|
4188
|
+
label: "Page Navigation",
|
|
4189
|
+
type: "page-nav",
|
|
4190
|
+
group: "navigation",
|
|
4191
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
4192
|
+
max: totalPages,
|
|
4193
|
+
execute: (action, page) => {
|
|
4194
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
4195
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
4196
|
+
if (action === "prev") {
|
|
4197
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
4198
|
+
} else if (action === "next") {
|
|
4199
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
4200
|
+
} else if (typeof page === "number") {
|
|
4201
|
+
instance.goToPage?.(page);
|
|
3405
4202
|
}
|
|
3406
|
-
}
|
|
3407
|
-
}
|
|
4203
|
+
}
|
|
4204
|
+
});
|
|
3408
4205
|
actions.push(
|
|
3409
4206
|
{
|
|
3410
4207
|
id: "zoom-out",
|
|
@@ -3548,16 +4345,19 @@ var CodePlugin = class {
|
|
|
3548
4345
|
let fontSize = 13;
|
|
3549
4346
|
let rotation = 0;
|
|
3550
4347
|
let zoomLevel = 1;
|
|
4348
|
+
let isUserZoomed = false;
|
|
3551
4349
|
const pre = document.createElement("pre");
|
|
3552
4350
|
const code = document.createElement("code");
|
|
3553
4351
|
if (isTxt) {
|
|
4352
|
+
container.style.overflowX = "hidden";
|
|
4353
|
+
container.style.overflowY = "auto";
|
|
3554
4354
|
container.style.backgroundColor = "#f1f5f9";
|
|
3555
|
-
container.style.padding = "
|
|
4355
|
+
container.style.padding = "16px 8px";
|
|
3556
4356
|
container.style.boxSizing = "border-box";
|
|
3557
4357
|
container.style.display = "flex";
|
|
3558
4358
|
container.style.flexDirection = "column";
|
|
3559
4359
|
container.style.alignItems = "center";
|
|
3560
|
-
pre.style.margin = "0";
|
|
4360
|
+
pre.style.margin = "0 auto";
|
|
3561
4361
|
pre.style.fontFamily = "Consolas, 'Courier New', monospace";
|
|
3562
4362
|
pre.style.fontSize = "13px";
|
|
3563
4363
|
pre.style.color = "#1e293b";
|
|
@@ -3569,10 +4369,12 @@ var CodePlugin = class {
|
|
|
3569
4369
|
pre.style.minHeight = "1056px";
|
|
3570
4370
|
pre.style.padding = "72px 56px";
|
|
3571
4371
|
pre.style.boxSizing = "border-box";
|
|
3572
|
-
pre.style.boxShadow = "0 4px
|
|
4372
|
+
pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
|
|
3573
4373
|
pre.style.borderRadius = "4px";
|
|
3574
4374
|
pre.style.transformOrigin = "top center";
|
|
4375
|
+
pre.style.transition = "transform 0.15s ease";
|
|
3575
4376
|
} else {
|
|
4377
|
+
container.style.overflow = "auto";
|
|
3576
4378
|
container.style.backgroundColor = "#1e1e1e";
|
|
3577
4379
|
container.style.color = "#d4d4d4";
|
|
3578
4380
|
container.style.padding = "16px";
|
|
@@ -3604,42 +4406,9 @@ var CodePlugin = class {
|
|
|
3604
4406
|
renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
|
|
3605
4407
|
pre.appendChild(code);
|
|
3606
4408
|
container.appendChild(pre);
|
|
3607
|
-
let indicator = null;
|
|
3608
|
-
if (totalPages > 1) {
|
|
3609
|
-
indicator = document.createElement("div");
|
|
3610
|
-
indicator.className = "fp-code-page-indicator";
|
|
3611
|
-
indicator.style.position = "sticky";
|
|
3612
|
-
indicator.style.bottom = "16px";
|
|
3613
|
-
if (isTxt) {
|
|
3614
|
-
indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
|
|
3615
|
-
indicator.style.color = "#334155";
|
|
3616
|
-
indicator.style.border = "1px solid #e2e8f0";
|
|
3617
|
-
indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
|
|
3618
|
-
} else {
|
|
3619
|
-
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
3620
|
-
indicator.style.color = "#f8fafc";
|
|
3621
|
-
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
3622
|
-
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
3623
|
-
indicator.style.backdropFilter = "blur(8px)";
|
|
3624
|
-
}
|
|
3625
|
-
indicator.style.fontSize = "12px";
|
|
3626
|
-
indicator.style.fontWeight = "600";
|
|
3627
|
-
indicator.style.padding = "5px 14px";
|
|
3628
|
-
indicator.style.borderRadius = "20px";
|
|
3629
|
-
indicator.style.zIndex = "10";
|
|
3630
|
-
indicator.style.userSelect = "none";
|
|
3631
|
-
indicator.style.pointerEvents = "none";
|
|
3632
|
-
indicator.style.textAlign = "center";
|
|
3633
|
-
indicator.style.width = "fit-content";
|
|
3634
|
-
indicator.style.margin = "16px auto 0";
|
|
3635
|
-
container.appendChild(indicator);
|
|
3636
|
-
}
|
|
3637
4409
|
const showPage = (pageNum) => {
|
|
3638
4410
|
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
3639
4411
|
renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
|
|
3640
|
-
if (indicator) {
|
|
3641
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
3642
|
-
}
|
|
3643
4412
|
container.scrollTop = 0;
|
|
3644
4413
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
3645
4414
|
};
|
|
@@ -3647,36 +4416,90 @@ var CodePlugin = class {
|
|
|
3647
4416
|
showPage(1);
|
|
3648
4417
|
}
|
|
3649
4418
|
ctx.container.appendChild(container);
|
|
3650
|
-
const
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
ctx.container.innerHTML = "";
|
|
4419
|
+
const calculateTxtFit = () => {
|
|
4420
|
+
const availW = Math.max(280, container.clientWidth - 32);
|
|
4421
|
+
return Math.max(0.4, Math.min(3, availW / 816));
|
|
3654
4422
|
};
|
|
3655
|
-
ctx.signal.addEventListener("abort", cleanup);
|
|
3656
4423
|
const updateTransform = () => {
|
|
3657
4424
|
if (isTxt) {
|
|
3658
4425
|
pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
|
|
4426
|
+
const scaledH = 1056 * zoomLevel;
|
|
4427
|
+
const extraH = Math.max(0, scaledH - 1056);
|
|
4428
|
+
pre.style.marginBottom = `${extraH + 32}px`;
|
|
3659
4429
|
} else {
|
|
3660
4430
|
pre.style.transform = `rotate(${rotation}deg)`;
|
|
3661
4431
|
pre.style.fontSize = `${fontSize}px`;
|
|
3662
4432
|
}
|
|
3663
4433
|
};
|
|
4434
|
+
let resizeObserver = null;
|
|
4435
|
+
if (isTxt) {
|
|
4436
|
+
setTimeout(() => {
|
|
4437
|
+
zoomLevel = calculateTxtFit();
|
|
4438
|
+
updateTransform();
|
|
4439
|
+
}, 60);
|
|
4440
|
+
resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
4441
|
+
if (!isUserZoomed) {
|
|
4442
|
+
zoomLevel = calculateTxtFit();
|
|
4443
|
+
updateTransform();
|
|
4444
|
+
}
|
|
4445
|
+
}) : null;
|
|
4446
|
+
resizeObserver?.observe(container);
|
|
4447
|
+
}
|
|
4448
|
+
const cleanup = () => {
|
|
4449
|
+
resizeObserver?.disconnect();
|
|
4450
|
+
container.remove();
|
|
4451
|
+
ctx.container.innerHTML = "";
|
|
4452
|
+
};
|
|
4453
|
+
ctx.signal.addEventListener("abort", cleanup);
|
|
3664
4454
|
return {
|
|
3665
4455
|
destroy: cleanup,
|
|
3666
4456
|
getPageCount: () => totalPages,
|
|
3667
4457
|
getCurrentPage: () => currentPage,
|
|
3668
4458
|
goToPage: (page) => showPage(page),
|
|
4459
|
+
getThumbnails: () => {
|
|
4460
|
+
return rawPages.map((pageText, idx) => ({
|
|
4461
|
+
index: idx,
|
|
4462
|
+
label: `Page ${idx + 1}`,
|
|
4463
|
+
render: async (canvas) => {
|
|
4464
|
+
canvas.width = 140;
|
|
4465
|
+
canvas.height = 180;
|
|
4466
|
+
const c = canvas.getContext("2d");
|
|
4467
|
+
if (!c) return;
|
|
4468
|
+
c.fillStyle = "#ffffff";
|
|
4469
|
+
c.fillRect(0, 0, canvas.width, canvas.height);
|
|
4470
|
+
c.strokeStyle = "#cbd5e1";
|
|
4471
|
+
c.lineWidth = 1;
|
|
4472
|
+
c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
|
|
4473
|
+
c.fillStyle = "#64748b";
|
|
4474
|
+
const lines = (pageText || "").split("\n").slice(0, 24);
|
|
4475
|
+
const lineH = 6;
|
|
4476
|
+
const startY = 14;
|
|
4477
|
+
const startX = 12;
|
|
4478
|
+
const maxW = canvas.width - 24;
|
|
4479
|
+
c.font = "5px monospace";
|
|
4480
|
+
for (let li = 0; li < lines.length; li++) {
|
|
4481
|
+
const l = lines[li].trim();
|
|
4482
|
+
if (!l) continue;
|
|
4483
|
+
const y = startY + li * lineH;
|
|
4484
|
+
if (y > canvas.height - 12) break;
|
|
4485
|
+
c.fillText(l.slice(0, 30), startX, y, maxW);
|
|
4486
|
+
}
|
|
4487
|
+
}
|
|
4488
|
+
}));
|
|
4489
|
+
},
|
|
3669
4490
|
zoomIn: () => {
|
|
4491
|
+
isUserZoomed = true;
|
|
3670
4492
|
if (isTxt) {
|
|
3671
|
-
zoomLevel = Math.min(3, zoomLevel + 0.
|
|
4493
|
+
zoomLevel = Math.min(3.5, zoomLevel + 0.15);
|
|
3672
4494
|
} else {
|
|
3673
4495
|
fontSize = Math.min(32, fontSize + 2);
|
|
3674
4496
|
}
|
|
3675
4497
|
updateTransform();
|
|
3676
4498
|
},
|
|
3677
4499
|
zoomOut: () => {
|
|
4500
|
+
isUserZoomed = true;
|
|
3678
4501
|
if (isTxt) {
|
|
3679
|
-
zoomLevel = Math.max(0.1, zoomLevel - 0.
|
|
4502
|
+
zoomLevel = Math.max(0.1, zoomLevel - 0.15);
|
|
3680
4503
|
} else {
|
|
3681
4504
|
fontSize = Math.max(8, fontSize - 2);
|
|
3682
4505
|
}
|
|
@@ -3684,6 +4507,7 @@ var CodePlugin = class {
|
|
|
3684
4507
|
},
|
|
3685
4508
|
getZoom: () => isTxt ? zoomLevel : fontSize / 13,
|
|
3686
4509
|
setZoom: (level) => {
|
|
4510
|
+
isUserZoomed = true;
|
|
3687
4511
|
if (isTxt) {
|
|
3688
4512
|
zoomLevel = level;
|
|
3689
4513
|
} else {
|
|
@@ -3692,9 +4516,10 @@ var CodePlugin = class {
|
|
|
3692
4516
|
updateTransform();
|
|
3693
4517
|
},
|
|
3694
4518
|
fitToPage: () => {
|
|
4519
|
+
isUserZoomed = false;
|
|
3695
4520
|
fontSize = 13;
|
|
3696
4521
|
rotation = 0;
|
|
3697
|
-
zoomLevel = isTxt ?
|
|
4522
|
+
zoomLevel = isTxt ? calculateTxtFit() : 1;
|
|
3698
4523
|
updateTransform();
|
|
3699
4524
|
},
|
|
3700
4525
|
rotateCW: () => {
|
|
@@ -4575,28 +5400,26 @@ var RtfPlugin = class {
|
|
|
4575
5400
|
getToolbarActions(instance) {
|
|
4576
5401
|
const totalPages = instance.getPageCount?.() ?? 1;
|
|
4577
5402
|
const actions = [];
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
|
|
4583
|
-
|
|
4584
|
-
|
|
4585
|
-
|
|
4586
|
-
|
|
4587
|
-
|
|
4588
|
-
|
|
4589
|
-
|
|
4590
|
-
if (
|
|
4591
|
-
|
|
4592
|
-
|
|
4593
|
-
|
|
4594
|
-
|
|
4595
|
-
instance.goToPage?.(page);
|
|
4596
|
-
}
|
|
5403
|
+
actions.push({
|
|
5404
|
+
id: "page-nav",
|
|
5405
|
+
icon: "",
|
|
5406
|
+
label: "Page Navigation",
|
|
5407
|
+
type: "page-nav",
|
|
5408
|
+
group: "navigation",
|
|
5409
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
5410
|
+
max: totalPages,
|
|
5411
|
+
execute: (action, page) => {
|
|
5412
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
5413
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
5414
|
+
if (action === "prev") {
|
|
5415
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
5416
|
+
} else if (action === "next") {
|
|
5417
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
5418
|
+
} else if (typeof page === "number") {
|
|
5419
|
+
instance.goToPage?.(page);
|
|
4597
5420
|
}
|
|
4598
|
-
}
|
|
4599
|
-
}
|
|
5421
|
+
}
|
|
5422
|
+
});
|
|
4600
5423
|
actions.push(
|
|
4601
5424
|
{
|
|
4602
5425
|
id: "zoom-out",
|
|
@@ -4668,43 +5491,57 @@ var RtfPlugin = class {
|
|
|
4668
5491
|
async render(ctx) {
|
|
4669
5492
|
const wrapper = document.createElement("div");
|
|
4670
5493
|
wrapper.className = "fp-rtf-wrapper";
|
|
4671
|
-
wrapper.style.padding = "
|
|
4672
|
-
wrapper.style.
|
|
5494
|
+
wrapper.style.padding = "0";
|
|
5495
|
+
wrapper.style.width = "816px";
|
|
4673
5496
|
wrapper.style.margin = "0 auto";
|
|
4674
|
-
wrapper.style.
|
|
4675
|
-
wrapper.style.
|
|
4676
|
-
wrapper.style.
|
|
4677
|
-
wrapper.style.
|
|
5497
|
+
wrapper.style.boxSizing = "border-box";
|
|
5498
|
+
wrapper.style.display = "flex";
|
|
5499
|
+
wrapper.style.flexDirection = "column";
|
|
5500
|
+
wrapper.style.alignItems = "center";
|
|
5501
|
+
wrapper.style.backgroundColor = "transparent";
|
|
4678
5502
|
wrapper.style.transformOrigin = "top center";
|
|
4679
|
-
wrapper.style.transition = "transform 0.
|
|
4680
|
-
ctx.container.style.
|
|
4681
|
-
ctx.container.style.
|
|
5503
|
+
wrapper.style.transition = "transform 0.15s ease";
|
|
5504
|
+
ctx.container.style.overflowX = "hidden";
|
|
5505
|
+
ctx.container.style.overflowY = "auto";
|
|
5506
|
+
ctx.container.style.padding = "16px 8px";
|
|
4682
5507
|
ctx.container.style.backgroundColor = "#f1f5f9";
|
|
4683
5508
|
ctx.container.appendChild(wrapper);
|
|
4684
5509
|
let scale = 1;
|
|
4685
5510
|
let rotation = 0;
|
|
4686
5511
|
let pageElements = [];
|
|
4687
|
-
let
|
|
5512
|
+
let isUserZoomed = false;
|
|
5513
|
+
let fitMode = ctx?.options?.fitMode || "width";
|
|
4688
5514
|
const calculateFitScale = (mode = fitMode) => {
|
|
4689
5515
|
const activeEl = pageElements[currentPage - 1] || wrapper;
|
|
4690
|
-
const elW =
|
|
4691
|
-
const singlePageH =
|
|
4692
|
-
const availW = Math.max(280, ctx.container.clientWidth -
|
|
4693
|
-
const availH = Math.max(280, ctx.container.clientHeight -
|
|
5516
|
+
const elW = 816;
|
|
5517
|
+
const singlePageH = activeEl?.offsetHeight || 1056;
|
|
5518
|
+
const availW = Math.max(280, ctx.container.clientWidth - 32);
|
|
5519
|
+
const availH = Math.max(280, ctx.container.clientHeight - 32);
|
|
4694
5520
|
const sW = availW / elW;
|
|
4695
5521
|
const sH = availH / singlePageH;
|
|
4696
5522
|
if (mode === "page") {
|
|
4697
|
-
return Math.max(0.
|
|
5523
|
+
return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
|
|
4698
5524
|
}
|
|
4699
|
-
return Math.max(0.
|
|
5525
|
+
return Math.max(0.4, Math.min(3, sW));
|
|
4700
5526
|
};
|
|
4701
5527
|
const applyTransform = () => {
|
|
4702
5528
|
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
4703
5529
|
wrapper.style.transformOrigin = "top center";
|
|
5530
|
+
const activeEl = pageElements[currentPage - 1];
|
|
5531
|
+
const baseH = activeEl?.offsetHeight || 1056;
|
|
5532
|
+
const scaledH = baseH * scale;
|
|
5533
|
+
const extraH = Math.max(0, scaledH - baseH);
|
|
5534
|
+
wrapper.style.marginBottom = `${extraH + 32}px`;
|
|
4704
5535
|
};
|
|
5536
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
5537
|
+
if (!isUserZoomed) {
|
|
5538
|
+
scale = calculateFitScale(fitMode);
|
|
5539
|
+
applyTransform();
|
|
5540
|
+
}
|
|
5541
|
+
}) : null;
|
|
5542
|
+
resizeObserver?.observe(ctx.container);
|
|
4705
5543
|
setTimeout(() => {
|
|
4706
|
-
|
|
4707
|
-
scale = calculateFitScale("width");
|
|
5544
|
+
scale = calculateFitScale(fitMode);
|
|
4708
5545
|
applyTransform();
|
|
4709
5546
|
}, 60);
|
|
4710
5547
|
try {
|
|
@@ -4714,20 +5551,34 @@ var RtfPlugin = class {
|
|
|
4714
5551
|
const doc = new RTFJS.Document(ctx.buffer, {});
|
|
4715
5552
|
const htmlElements = await doc.render();
|
|
4716
5553
|
const contentNodes = [];
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4721
|
-
|
|
5554
|
+
const extractBlocks = (nodes) => {
|
|
5555
|
+
for (const item of nodes) {
|
|
5556
|
+
if (!item || !(item instanceof HTMLElement)) continue;
|
|
5557
|
+
const tag = item.tagName.toLowerCase();
|
|
5558
|
+
const hasChildBlocks = Array.from(item.children).some((c) => {
|
|
5559
|
+
const ct = c.tagName.toLowerCase();
|
|
5560
|
+
return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
|
|
5561
|
+
});
|
|
5562
|
+
if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
|
|
5563
|
+
extractBlocks(Array.from(item.children));
|
|
5564
|
+
} else {
|
|
5565
|
+
if (tag === "p" || tag === "div") {
|
|
5566
|
+
item.style.display = "block";
|
|
5567
|
+
item.style.marginBottom = "12px";
|
|
5568
|
+
item.style.lineHeight = "1.6";
|
|
5569
|
+
}
|
|
5570
|
+
contentNodes.push(item);
|
|
5571
|
+
}
|
|
4722
5572
|
}
|
|
4723
|
-
}
|
|
5573
|
+
};
|
|
5574
|
+
extractBlocks(htmlElements);
|
|
4724
5575
|
wrapper.innerHTML = "";
|
|
4725
5576
|
contentNodes.forEach((node) => wrapper.appendChild(node));
|
|
4726
5577
|
const childHeights = contentNodes.map((c) => {
|
|
4727
5578
|
const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
|
|
4728
5579
|
const offH = c.offsetHeight || 0;
|
|
4729
5580
|
const textLen = c.textContent?.trim().length || 0;
|
|
4730
|
-
const estH = Math.max(
|
|
5581
|
+
const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
|
|
4731
5582
|
return Math.max(rectH, offH, estH);
|
|
4732
5583
|
});
|
|
4733
5584
|
wrapper.innerHTML = "";
|
|
@@ -4744,6 +5595,7 @@ var RtfPlugin = class {
|
|
|
4744
5595
|
card.style.marginBottom = "24px";
|
|
4745
5596
|
card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4746
5597
|
card.style.lineHeight = "1.6";
|
|
5598
|
+
card.style.color = "#1e293b";
|
|
4747
5599
|
return card;
|
|
4748
5600
|
};
|
|
4749
5601
|
let curCard = createRtfCard();
|
|
@@ -4779,9 +5631,8 @@ var RtfPlugin = class {
|
|
|
4779
5631
|
pageCard.style.padding = "72px 56px";
|
|
4780
5632
|
pageCard.style.width = "816px";
|
|
4781
5633
|
pageCard.style.minHeight = "1056px";
|
|
4782
|
-
pageCard.style.maxWidth = "100%";
|
|
4783
5634
|
pageCard.style.boxSizing = "border-box";
|
|
4784
|
-
pageCard.style.fontFamily = "serif
|
|
5635
|
+
pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
4785
5636
|
pageCard.style.fontSize = "12pt";
|
|
4786
5637
|
pageCard.style.lineHeight = "1.6";
|
|
4787
5638
|
pageCard.style.color = "#1e293b";
|
|
@@ -4794,29 +5645,6 @@ var RtfPlugin = class {
|
|
|
4794
5645
|
}
|
|
4795
5646
|
const totalPages = Math.max(1, pageElements.length);
|
|
4796
5647
|
let currentPage = 1;
|
|
4797
|
-
let indicator = null;
|
|
4798
|
-
if (totalPages > 1) {
|
|
4799
|
-
indicator = document.createElement("div");
|
|
4800
|
-
indicator.className = "fp-rtf-page-indicator";
|
|
4801
|
-
indicator.style.position = "sticky";
|
|
4802
|
-
indicator.style.bottom = "16px";
|
|
4803
|
-
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
4804
|
-
indicator.style.backdropFilter = "blur(8px)";
|
|
4805
|
-
indicator.style.color = "#f8fafc";
|
|
4806
|
-
indicator.style.fontSize = "12px";
|
|
4807
|
-
indicator.style.fontWeight = "600";
|
|
4808
|
-
indicator.style.padding = "5px 14px";
|
|
4809
|
-
indicator.style.borderRadius = "20px";
|
|
4810
|
-
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
4811
|
-
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
4812
|
-
indicator.style.zIndex = "10";
|
|
4813
|
-
indicator.style.userSelect = "none";
|
|
4814
|
-
indicator.style.pointerEvents = "none";
|
|
4815
|
-
indicator.style.textAlign = "center";
|
|
4816
|
-
indicator.style.width = "fit-content";
|
|
4817
|
-
indicator.style.margin = "16px auto 0";
|
|
4818
|
-
ctx.container.appendChild(indicator);
|
|
4819
|
-
}
|
|
4820
5648
|
const showPage = (pageNum) => {
|
|
4821
5649
|
currentPage = Math.max(1, Math.min(totalPages, pageNum));
|
|
4822
5650
|
if (totalPages > 1) {
|
|
@@ -4824,9 +5652,6 @@ var RtfPlugin = class {
|
|
|
4824
5652
|
el.style.display = idx + 1 === currentPage ? "block" : "none";
|
|
4825
5653
|
});
|
|
4826
5654
|
}
|
|
4827
|
-
if (indicator) {
|
|
4828
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
4829
|
-
}
|
|
4830
5655
|
ctx.container.scrollTop = 0;
|
|
4831
5656
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
4832
5657
|
};
|
|
@@ -4834,7 +5659,7 @@ var RtfPlugin = class {
|
|
|
4834
5659
|
showPage(1);
|
|
4835
5660
|
}
|
|
4836
5661
|
const cleanup = () => {
|
|
4837
|
-
|
|
5662
|
+
resizeObserver?.disconnect();
|
|
4838
5663
|
wrapper.remove();
|
|
4839
5664
|
ctx.container.innerHTML = "";
|
|
4840
5665
|
};
|
|
@@ -4845,21 +5670,25 @@ var RtfPlugin = class {
|
|
|
4845
5670
|
getCurrentPage: () => currentPage,
|
|
4846
5671
|
goToPage: (page) => showPage(page),
|
|
4847
5672
|
zoomIn: () => {
|
|
5673
|
+
isUserZoomed = true;
|
|
4848
5674
|
scale += 0.15;
|
|
4849
5675
|
applyTransform();
|
|
4850
5676
|
},
|
|
4851
5677
|
zoomOut: () => {
|
|
5678
|
+
isUserZoomed = true;
|
|
4852
5679
|
scale = Math.max(0.2, scale - 0.15);
|
|
4853
5680
|
applyTransform();
|
|
4854
5681
|
},
|
|
4855
5682
|
getZoom: () => scale,
|
|
4856
5683
|
setZoom: (level) => {
|
|
5684
|
+
isUserZoomed = true;
|
|
4857
5685
|
scale = level;
|
|
4858
5686
|
applyTransform();
|
|
4859
5687
|
},
|
|
4860
5688
|
fitToPage: () => {
|
|
4861
|
-
|
|
4862
|
-
|
|
5689
|
+
isUserZoomed = false;
|
|
5690
|
+
fitMode = "width";
|
|
5691
|
+
scale = calculateFitScale("width");
|
|
4863
5692
|
rotation = 0;
|
|
4864
5693
|
applyTransform();
|
|
4865
5694
|
},
|
|
@@ -5047,40 +5876,36 @@ var OpenDocumentPlugin = class {
|
|
|
5047
5876
|
const isPresentation = instance.isPresentation;
|
|
5048
5877
|
const totalPages = instance.getPageCount?.() ?? 1;
|
|
5049
5878
|
const actions = [];
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
max
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
if (
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
instance.goToPage?.(page);
|
|
5078
|
-
} else if (typeof action === "number") {
|
|
5079
|
-
instance.goToPage?.(action);
|
|
5080
|
-
}
|
|
5879
|
+
actions.push({
|
|
5880
|
+
id: "thumbnails",
|
|
5881
|
+
icon: "thumbnails",
|
|
5882
|
+
label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
|
|
5883
|
+
type: "button",
|
|
5884
|
+
group: "navigation",
|
|
5885
|
+
execute: () => instance.toggleThumbnails?.()
|
|
5886
|
+
});
|
|
5887
|
+
actions.push({
|
|
5888
|
+
id: "page-nav",
|
|
5889
|
+
icon: "",
|
|
5890
|
+
label: isPresentation ? "Slide Navigation" : "Page Navigation",
|
|
5891
|
+
type: "page-nav",
|
|
5892
|
+
group: "navigation",
|
|
5893
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
5894
|
+
max: totalPages,
|
|
5895
|
+
execute: (action, page) => {
|
|
5896
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
5897
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
5898
|
+
if (action === "prev") {
|
|
5899
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
5900
|
+
} else if (action === "next") {
|
|
5901
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
5902
|
+
} else if (typeof page === "number") {
|
|
5903
|
+
instance.goToPage?.(page);
|
|
5904
|
+
} else if (typeof action === "number") {
|
|
5905
|
+
instance.goToPage?.(action);
|
|
5081
5906
|
}
|
|
5082
|
-
}
|
|
5083
|
-
}
|
|
5907
|
+
}
|
|
5908
|
+
});
|
|
5084
5909
|
actions.push(
|
|
5085
5910
|
{
|
|
5086
5911
|
id: "zoom-out",
|
|
@@ -5171,49 +5996,65 @@ var OpenDocumentPlugin = class {
|
|
|
5171
5996
|
container.className = "fp-odf-container";
|
|
5172
5997
|
container.style.width = "100%";
|
|
5173
5998
|
container.style.height = "100%";
|
|
5174
|
-
container.style.
|
|
5175
|
-
container.style.
|
|
5999
|
+
container.style.overflowX = "hidden";
|
|
6000
|
+
container.style.overflowY = "auto";
|
|
6001
|
+
container.style.padding = "16px 8px";
|
|
5176
6002
|
container.style.backgroundColor = "#f1f5f9";
|
|
5177
6003
|
const wrapper = document.createElement("div");
|
|
5178
6004
|
wrapper.className = "fp-odf-wrapper";
|
|
5179
6005
|
wrapper.style.margin = "0 auto";
|
|
6006
|
+
wrapper.style.width = isPresentation ? "960px" : "816px";
|
|
6007
|
+
wrapper.style.boxSizing = "border-box";
|
|
5180
6008
|
wrapper.style.backgroundColor = "#ffffff";
|
|
5181
6009
|
wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
|
|
5182
6010
|
wrapper.style.borderRadius = "4px";
|
|
5183
6011
|
wrapper.style.transformOrigin = "top center";
|
|
5184
|
-
wrapper.style.transition = "transform 0.
|
|
6012
|
+
wrapper.style.transition = "transform 0.15s ease";
|
|
5185
6013
|
container.appendChild(wrapper);
|
|
5186
6014
|
ctx.container.appendChild(container);
|
|
5187
6015
|
let scale = 1;
|
|
5188
6016
|
let rotation = 0;
|
|
5189
|
-
let
|
|
6017
|
+
let isUserZoomed = false;
|
|
6018
|
+
let fitMode = ctx?.options?.fitMode || "width";
|
|
6019
|
+
let currentPage = 1;
|
|
6020
|
+
let totalPages = 1;
|
|
6021
|
+
let slides = [];
|
|
5190
6022
|
const calculateFitScale = (mode = fitMode) => {
|
|
5191
|
-
const
|
|
5192
|
-
const
|
|
5193
|
-
const
|
|
5194
|
-
const
|
|
6023
|
+
const activeSlide = slides[currentPage - 1];
|
|
6024
|
+
const elW = isPresentation ? 960 : 816;
|
|
6025
|
+
const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
|
|
6026
|
+
const availW = Math.max(280, ctx.container.clientWidth - 32);
|
|
6027
|
+
const availH = Math.max(280, ctx.container.clientHeight - 32);
|
|
5195
6028
|
const sW = availW / elW;
|
|
5196
6029
|
const sH = availH / (isPresentation ? 540 : singlePageH);
|
|
5197
6030
|
if (isPresentation) {
|
|
5198
|
-
return Math.min(
|
|
6031
|
+
return Math.min(2.5, Math.min(sW, sH));
|
|
5199
6032
|
}
|
|
5200
6033
|
if (mode === "page") {
|
|
5201
|
-
return Math.max(0.
|
|
6034
|
+
return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
|
|
5202
6035
|
}
|
|
5203
|
-
return Math.max(0.
|
|
6036
|
+
return Math.max(0.4, Math.min(3, sW));
|
|
5204
6037
|
};
|
|
5205
6038
|
const applyTransform = () => {
|
|
5206
6039
|
wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
5207
6040
|
wrapper.style.transformOrigin = "top center";
|
|
6041
|
+
const activeSlide = slides[currentPage - 1];
|
|
6042
|
+
const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
|
|
6043
|
+
const scaledH = baseH * scale;
|
|
6044
|
+
const extraH = Math.max(0, scaledH - baseH);
|
|
6045
|
+
wrapper.style.marginBottom = `${extraH + 32}px`;
|
|
5208
6046
|
};
|
|
6047
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
6048
|
+
if (!isUserZoomed) {
|
|
6049
|
+
scale = calculateFitScale(fitMode);
|
|
6050
|
+
applyTransform();
|
|
6051
|
+
}
|
|
6052
|
+
}) : null;
|
|
6053
|
+
resizeObserver?.observe(ctx.container);
|
|
5209
6054
|
setTimeout(() => {
|
|
5210
|
-
|
|
5211
|
-
scale = calculateFitScale("width");
|
|
6055
|
+
scale = calculateFitScale(fitMode);
|
|
5212
6056
|
applyTransform();
|
|
5213
6057
|
}, 60);
|
|
5214
|
-
let currentPage = 1;
|
|
5215
|
-
let totalPages = 1;
|
|
5216
|
-
let slides = [];
|
|
5217
6058
|
if (isPresentation) {
|
|
5218
6059
|
wrapper.style.maxWidth = "960px";
|
|
5219
6060
|
wrapper.style.aspectRatio = "16 / 9";
|
|
@@ -5314,24 +6155,9 @@ var OpenDocumentPlugin = class {
|
|
|
5314
6155
|
wrapper.appendChild(page);
|
|
5315
6156
|
slides.push(page);
|
|
5316
6157
|
});
|
|
5317
|
-
const pageIndicator = document.createElement("div");
|
|
5318
|
-
pageIndicator.className = "fp-odt-page-indicator";
|
|
5319
|
-
pageIndicator.style.position = "sticky";
|
|
5320
|
-
pageIndicator.style.bottom = "16px";
|
|
5321
|
-
pageIndicator.style.left = "50%";
|
|
5322
|
-
pageIndicator.style.transform = "translateX(-50%)";
|
|
5323
|
-
pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
|
|
5324
|
-
pageIndicator.style.color = "#fff";
|
|
5325
|
-
pageIndicator.style.padding = "6px 12px";
|
|
5326
|
-
pageIndicator.style.borderRadius = "16px";
|
|
5327
|
-
pageIndicator.style.fontSize = "12px";
|
|
5328
|
-
pageIndicator.style.zIndex = "100";
|
|
5329
|
-
pageIndicator.style.display = "inline-block";
|
|
5330
|
-
pageIndicator.style.width = "fit-content";
|
|
5331
|
-
pageIndicator.textContent = `Page 1 of ${totalPages}`;
|
|
5332
|
-
container.appendChild(pageIndicator);
|
|
5333
6158
|
}
|
|
5334
6159
|
const cleanup = () => {
|
|
6160
|
+
resizeObserver?.disconnect();
|
|
5335
6161
|
imageUrls.forEach((url) => URL.revokeObjectURL(url));
|
|
5336
6162
|
container.remove();
|
|
5337
6163
|
ctx.container.innerHTML = "";
|
|
@@ -5343,10 +6169,6 @@ var OpenDocumentPlugin = class {
|
|
|
5343
6169
|
slides.forEach((s, idx) => {
|
|
5344
6170
|
s.style.display = idx === page - 1 ? "block" : "none";
|
|
5345
6171
|
});
|
|
5346
|
-
const indicator = container.querySelector(".fp-odt-page-indicator");
|
|
5347
|
-
if (indicator) {
|
|
5348
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
5349
|
-
}
|
|
5350
6172
|
container.scrollTop = 0;
|
|
5351
6173
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
5352
6174
|
};
|
|
@@ -5354,21 +6176,25 @@ var OpenDocumentPlugin = class {
|
|
|
5354
6176
|
destroy: cleanup,
|
|
5355
6177
|
isPresentation,
|
|
5356
6178
|
zoomIn: () => {
|
|
6179
|
+
isUserZoomed = true;
|
|
5357
6180
|
scale += 0.15;
|
|
5358
6181
|
applyTransform();
|
|
5359
6182
|
},
|
|
5360
6183
|
zoomOut: () => {
|
|
6184
|
+
isUserZoomed = true;
|
|
5361
6185
|
scale = Math.max(0.2, scale - 0.15);
|
|
5362
6186
|
applyTransform();
|
|
5363
6187
|
},
|
|
5364
6188
|
getZoom: () => scale,
|
|
5365
6189
|
setZoom: (level) => {
|
|
6190
|
+
isUserZoomed = true;
|
|
5366
6191
|
scale = level;
|
|
5367
6192
|
applyTransform();
|
|
5368
6193
|
},
|
|
5369
6194
|
fitToPage: () => {
|
|
5370
|
-
|
|
5371
|
-
|
|
6195
|
+
isUserZoomed = false;
|
|
6196
|
+
fitMode = "width";
|
|
6197
|
+
scale = calculateFitScale("width");
|
|
5372
6198
|
rotation = 0;
|
|
5373
6199
|
applyTransform();
|
|
5374
6200
|
},
|
|
@@ -5384,23 +6210,45 @@ var OpenDocumentPlugin = class {
|
|
|
5384
6210
|
getPageCount: () => totalPages,
|
|
5385
6211
|
getCurrentPage: () => currentPage,
|
|
5386
6212
|
getThumbnails: async () => {
|
|
5387
|
-
|
|
5388
|
-
|
|
5389
|
-
|
|
5390
|
-
|
|
5391
|
-
|
|
5392
|
-
|
|
5393
|
-
|
|
5394
|
-
canvas
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
|
|
5398
|
-
|
|
5399
|
-
|
|
5400
|
-
|
|
5401
|
-
|
|
5402
|
-
|
|
5403
|
-
|
|
6213
|
+
const count = totalPages || slides.length || 1;
|
|
6214
|
+
const items = [];
|
|
6215
|
+
for (let idx = 0; idx < count; idx++) {
|
|
6216
|
+
const itemIdx = idx;
|
|
6217
|
+
items.push({
|
|
6218
|
+
index: itemIdx,
|
|
6219
|
+
label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
|
|
6220
|
+
render: async (canvas) => {
|
|
6221
|
+
const ctx2d = canvas.getContext("2d");
|
|
6222
|
+
if (!ctx2d) return;
|
|
6223
|
+
if (isPresentation) {
|
|
6224
|
+
canvas.width = 160;
|
|
6225
|
+
canvas.height = 90;
|
|
6226
|
+
ctx2d.fillStyle = "#f8fafc";
|
|
6227
|
+
ctx2d.fillRect(0, 0, 160, 90);
|
|
6228
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
6229
|
+
ctx2d.lineWidth = 1;
|
|
6230
|
+
ctx2d.strokeRect(0.5, 0.5, 159, 89);
|
|
6231
|
+
ctx2d.fillStyle = "#334155";
|
|
6232
|
+
ctx2d.font = "bold 11px sans-serif";
|
|
6233
|
+
ctx2d.textAlign = "center";
|
|
6234
|
+
ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
|
|
6235
|
+
} else {
|
|
6236
|
+
canvas.width = 140;
|
|
6237
|
+
canvas.height = 180;
|
|
6238
|
+
ctx2d.fillStyle = "#ffffff";
|
|
6239
|
+
ctx2d.fillRect(0, 0, 140, 180);
|
|
6240
|
+
ctx2d.strokeStyle = "#cbd5e1";
|
|
6241
|
+
ctx2d.lineWidth = 1;
|
|
6242
|
+
ctx2d.strokeRect(0.5, 0.5, 139, 179);
|
|
6243
|
+
ctx2d.fillStyle = "#64748b";
|
|
6244
|
+
ctx2d.font = "bold 10px sans-serif";
|
|
6245
|
+
ctx2d.textAlign = "center";
|
|
6246
|
+
ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
|
|
6247
|
+
}
|
|
6248
|
+
}
|
|
6249
|
+
});
|
|
6250
|
+
}
|
|
6251
|
+
return items;
|
|
5404
6252
|
},
|
|
5405
6253
|
download: () => {
|
|
5406
6254
|
const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
|
|
@@ -5505,23 +6353,24 @@ var OpenDocumentPlugin = class {
|
|
|
5505
6353
|
case "h": {
|
|
5506
6354
|
const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
|
|
5507
6355
|
const style = styles.get(node.getAttribute("text:style-name") || "") || "";
|
|
5508
|
-
html += `<h${level} style="${style}; font-family:
|
|
6356
|
+
html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
|
|
5509
6357
|
break;
|
|
5510
6358
|
}
|
|
5511
6359
|
case "p": {
|
|
5512
6360
|
const style = styles.get(node.getAttribute("text:style-name") || "") || "";
|
|
5513
6361
|
const inner = this.renderSpans(node, styles, images);
|
|
5514
|
-
|
|
6362
|
+
const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
|
|
6363
|
+
html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
|
|
5515
6364
|
break;
|
|
5516
6365
|
}
|
|
5517
6366
|
case "list": {
|
|
5518
|
-
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
6367
|
+
html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
|
|
5519
6368
|
Array.from(node.children).forEach((c) => walk(c));
|
|
5520
6369
|
html += "</ul>";
|
|
5521
6370
|
break;
|
|
5522
6371
|
}
|
|
5523
6372
|
case "list-item": {
|
|
5524
|
-
html +=
|
|
6373
|
+
html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
|
|
5525
6374
|
Array.from(node.children).forEach((c) => walk(c));
|
|
5526
6375
|
html += "</li>";
|
|
5527
6376
|
break;
|
|
@@ -5647,28 +6496,26 @@ var DocPlugin = class {
|
|
|
5647
6496
|
getToolbarActions(instance) {
|
|
5648
6497
|
const totalPages = instance.getPageCount?.() ?? 1;
|
|
5649
6498
|
const actions = [];
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
if (
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
instance.goToPage?.(page);
|
|
5668
|
-
}
|
|
6499
|
+
actions.push({
|
|
6500
|
+
id: "page-nav",
|
|
6501
|
+
icon: "",
|
|
6502
|
+
label: "Page Navigation",
|
|
6503
|
+
type: "page-nav",
|
|
6504
|
+
group: "navigation",
|
|
6505
|
+
value: instance.getCurrentPage?.() ?? 1,
|
|
6506
|
+
max: totalPages,
|
|
6507
|
+
execute: (action, page) => {
|
|
6508
|
+
const cur = instance.getCurrentPage?.() ?? 1;
|
|
6509
|
+
const max = instance.getPageCount?.() ?? 1;
|
|
6510
|
+
if (action === "prev") {
|
|
6511
|
+
if (cur > 1) instance.goToPage?.(cur - 1);
|
|
6512
|
+
} else if (action === "next") {
|
|
6513
|
+
if (cur < max) instance.goToPage?.(cur + 1);
|
|
6514
|
+
} else if (typeof page === "number") {
|
|
6515
|
+
instance.goToPage?.(page);
|
|
5669
6516
|
}
|
|
5670
|
-
}
|
|
5671
|
-
}
|
|
6517
|
+
}
|
|
6518
|
+
});
|
|
5672
6519
|
actions.push(
|
|
5673
6520
|
{
|
|
5674
6521
|
id: "zoom-out",
|
|
@@ -5742,8 +6589,9 @@ var DocPlugin = class {
|
|
|
5742
6589
|
container.className = "fp-doc-container";
|
|
5743
6590
|
container.style.width = "100%";
|
|
5744
6591
|
container.style.height = "100%";
|
|
5745
|
-
container.style.
|
|
5746
|
-
container.style.
|
|
6592
|
+
container.style.overflowX = "hidden";
|
|
6593
|
+
container.style.overflowY = "auto";
|
|
6594
|
+
container.style.padding = "16px 8px";
|
|
5747
6595
|
container.style.backgroundColor = "#f1f5f9";
|
|
5748
6596
|
container.style.display = "flex";
|
|
5749
6597
|
container.style.justifyContent = "center";
|
|
@@ -5752,8 +6600,17 @@ var DocPlugin = class {
|
|
|
5752
6600
|
let scale = 1;
|
|
5753
6601
|
let extractedRawText = "";
|
|
5754
6602
|
let isFallback = false;
|
|
6603
|
+
let chartSvg = "";
|
|
5755
6604
|
try {
|
|
5756
6605
|
const cfbf = new CfbfReader(ctx.buffer);
|
|
6606
|
+
try {
|
|
6607
|
+
const pkg = cfbf.readStream("package_stream");
|
|
6608
|
+
if (pkg && pkg.length > 100) {
|
|
6609
|
+
chartSvg = this.parseOdfChartToSvg(pkg);
|
|
6610
|
+
}
|
|
6611
|
+
} catch (chartErr) {
|
|
6612
|
+
console.warn("[DocPlugin] Chart stream parsing info:", chartErr);
|
|
6613
|
+
}
|
|
5757
6614
|
const wordDocStream = cfbf.readStream("WordDocument");
|
|
5758
6615
|
if (!wordDocStream || wordDocStream.length < 512) {
|
|
5759
6616
|
throw new Error("WordDocument stream not found or invalid in CFBF archive");
|
|
@@ -5771,7 +6628,7 @@ var DocPlugin = class {
|
|
|
5771
6628
|
extractedRawText = fallback;
|
|
5772
6629
|
isFallback = true;
|
|
5773
6630
|
}
|
|
5774
|
-
const rawPages = this.splitIntoPages(extractedRawText);
|
|
6631
|
+
const rawPages = this.splitIntoPages(extractedRawText, chartSvg);
|
|
5775
6632
|
const totalPages = Math.max(1, rawPages.length);
|
|
5776
6633
|
let currentPage = 1;
|
|
5777
6634
|
const pageCards = [];
|
|
@@ -5779,16 +6636,15 @@ var DocPlugin = class {
|
|
|
5779
6636
|
const pageCard = document.createElement("div");
|
|
5780
6637
|
pageCard.className = "fp-doc-page-card";
|
|
5781
6638
|
pageCard.style.width = "816px";
|
|
5782
|
-
pageCard.style.
|
|
6639
|
+
pageCard.style.minHeight = "1056px";
|
|
5783
6640
|
pageCard.style.backgroundColor = "#ffffff";
|
|
5784
|
-
pageCard.style.boxShadow = "0
|
|
6641
|
+
pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
|
|
5785
6642
|
pageCard.style.borderRadius = "4px";
|
|
5786
|
-
pageCard.style.padding = "
|
|
6643
|
+
pageCard.style.padding = "72px 56px";
|
|
5787
6644
|
pageCard.style.boxSizing = "border-box";
|
|
5788
|
-
pageCard.style.overflow = "hidden";
|
|
5789
6645
|
pageCard.style.display = i === 0 ? "block" : "none";
|
|
5790
6646
|
pageCard.style.transformOrigin = "top center";
|
|
5791
|
-
pageCard.style.transition = "transform 0.
|
|
6647
|
+
pageCard.style.transition = "transform 0.15s ease";
|
|
5792
6648
|
pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
|
|
5793
6649
|
pageCard.style.color = "#1e293b";
|
|
5794
6650
|
if (isFallback && i === 0) {
|
|
@@ -5805,34 +6661,15 @@ var DocPlugin = class {
|
|
|
5805
6661
|
container.appendChild(pageCard);
|
|
5806
6662
|
pageCards.push(pageCard);
|
|
5807
6663
|
}
|
|
5808
|
-
let indicator = null;
|
|
5809
|
-
if (totalPages > 1) {
|
|
5810
|
-
indicator = document.createElement("div");
|
|
5811
|
-
indicator.className = "fp-doc-page-indicator";
|
|
5812
|
-
indicator.style.position = "fixed";
|
|
5813
|
-
indicator.style.bottom = "16px";
|
|
5814
|
-
indicator.style.left = "50%";
|
|
5815
|
-
indicator.style.transform = "translateX(-50%)";
|
|
5816
|
-
indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
|
|
5817
|
-
indicator.style.backdropFilter = "blur(8px)";
|
|
5818
|
-
indicator.style.color = "#f8fafc";
|
|
5819
|
-
indicator.style.fontSize = "12px";
|
|
5820
|
-
indicator.style.fontWeight = "600";
|
|
5821
|
-
indicator.style.padding = "5px 14px";
|
|
5822
|
-
indicator.style.borderRadius = "20px";
|
|
5823
|
-
indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
|
|
5824
|
-
indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
|
|
5825
|
-
indicator.style.zIndex = "10";
|
|
5826
|
-
indicator.style.userSelect = "none";
|
|
5827
|
-
indicator.style.pointerEvents = "none";
|
|
5828
|
-
indicator.style.textAlign = "center";
|
|
5829
|
-
container.appendChild(indicator);
|
|
5830
|
-
}
|
|
5831
6664
|
let rotation = 0;
|
|
5832
6665
|
const applyTransform = () => {
|
|
5833
6666
|
const activeCard = pageCards[currentPage - 1];
|
|
5834
6667
|
if (activeCard) {
|
|
5835
6668
|
activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
|
|
6669
|
+
const baseH = activeCard.offsetHeight || 1056;
|
|
6670
|
+
const scaledH = baseH * scale;
|
|
6671
|
+
const extraH = Math.max(0, scaledH - baseH);
|
|
6672
|
+
activeCard.style.marginBottom = `${extraH + 32}px`;
|
|
5836
6673
|
}
|
|
5837
6674
|
};
|
|
5838
6675
|
const showPage = (pageNum) => {
|
|
@@ -5845,35 +6682,39 @@ var DocPlugin = class {
|
|
|
5845
6682
|
card.style.display = "none";
|
|
5846
6683
|
}
|
|
5847
6684
|
});
|
|
5848
|
-
if (indicator) {
|
|
5849
|
-
indicator.textContent = `Page ${currentPage} of ${totalPages}`;
|
|
5850
|
-
}
|
|
5851
6685
|
container.scrollTop = 0;
|
|
5852
6686
|
ctx.emit("page-change", { page: currentPage, total: totalPages });
|
|
5853
6687
|
};
|
|
5854
6688
|
if (totalPages > 1) {
|
|
5855
6689
|
showPage(1);
|
|
5856
6690
|
}
|
|
5857
|
-
let fitMode = "width";
|
|
6691
|
+
let fitMode = ctx?.options?.fitMode || "width";
|
|
6692
|
+
let isUserZoomed = false;
|
|
5858
6693
|
const calculateFitScale = (mode = fitMode) => {
|
|
5859
6694
|
const elW = 816;
|
|
5860
6695
|
const elH = 1056;
|
|
5861
|
-
const availW = Math.max(280, ctx.container.clientWidth -
|
|
5862
|
-
const availH = Math.max(280, ctx.container.clientHeight -
|
|
6696
|
+
const availW = Math.max(280, ctx.container.clientWidth - 32);
|
|
6697
|
+
const availH = Math.max(280, ctx.container.clientHeight - 32);
|
|
5863
6698
|
const sW = availW / elW;
|
|
5864
6699
|
const sH = availH / elH;
|
|
5865
6700
|
if (mode === "page") {
|
|
5866
|
-
return Math.max(0.
|
|
6701
|
+
return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
|
|
5867
6702
|
}
|
|
5868
|
-
return Math.max(0.
|
|
6703
|
+
return Math.max(0.4, Math.min(3, sW));
|
|
5869
6704
|
};
|
|
6705
|
+
const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
|
|
6706
|
+
if (!isUserZoomed) {
|
|
6707
|
+
scale = calculateFitScale(fitMode);
|
|
6708
|
+
applyTransform();
|
|
6709
|
+
}
|
|
6710
|
+
}) : null;
|
|
6711
|
+
resizeObserver?.observe(ctx.container);
|
|
5870
6712
|
setTimeout(() => {
|
|
5871
|
-
|
|
5872
|
-
scale = calculateFitScale("width");
|
|
6713
|
+
scale = calculateFitScale(fitMode);
|
|
5873
6714
|
applyTransform();
|
|
5874
6715
|
}, 60);
|
|
5875
6716
|
const cleanup = () => {
|
|
5876
|
-
|
|
6717
|
+
resizeObserver?.disconnect();
|
|
5877
6718
|
container.remove();
|
|
5878
6719
|
ctx.container.innerHTML = "";
|
|
5879
6720
|
};
|
|
@@ -5884,21 +6725,25 @@ var DocPlugin = class {
|
|
|
5884
6725
|
getCurrentPage: () => currentPage,
|
|
5885
6726
|
goToPage: (page) => showPage(page),
|
|
5886
6727
|
zoomIn: () => {
|
|
6728
|
+
isUserZoomed = true;
|
|
5887
6729
|
scale += 0.15;
|
|
5888
6730
|
applyTransform();
|
|
5889
6731
|
},
|
|
5890
6732
|
zoomOut: () => {
|
|
6733
|
+
isUserZoomed = true;
|
|
5891
6734
|
scale = Math.max(0.2, scale - 0.15);
|
|
5892
6735
|
applyTransform();
|
|
5893
6736
|
},
|
|
5894
6737
|
getZoom: () => scale,
|
|
5895
6738
|
setZoom: (level) => {
|
|
6739
|
+
isUserZoomed = true;
|
|
5896
6740
|
scale = level;
|
|
5897
6741
|
applyTransform();
|
|
5898
6742
|
},
|
|
5899
6743
|
fitToPage: () => {
|
|
5900
|
-
|
|
5901
|
-
|
|
6744
|
+
isUserZoomed = false;
|
|
6745
|
+
fitMode = "width";
|
|
6746
|
+
scale = calculateFitScale("width");
|
|
5902
6747
|
rotation = 0;
|
|
5903
6748
|
applyTransform();
|
|
5904
6749
|
},
|
|
@@ -6013,7 +6858,7 @@ var DocPlugin = class {
|
|
|
6013
6858
|
for (const run of [...ansiRuns, ...utf16Runs]) {
|
|
6014
6859
|
const trimmed = run.trim();
|
|
6015
6860
|
if (trimmed.length >= 4 && /[a-zA-Z]/.test(trimmed) && !seen.has(trimmed)) {
|
|
6016
|
-
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)) {
|
|
6861
|
+
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)) {
|
|
6017
6862
|
seen.add(trimmed);
|
|
6018
6863
|
candidateLines.push(trimmed);
|
|
6019
6864
|
}
|
|
@@ -6024,9 +6869,114 @@ var DocPlugin = class {
|
|
|
6024
6869
|
heuristicTextExtraction(buffer) {
|
|
6025
6870
|
return this.extractStringsFromBytes(new Uint8Array(buffer));
|
|
6026
6871
|
}
|
|
6027
|
-
|
|
6872
|
+
/**
|
|
6873
|
+
* Parses an embedded OpenDocument Chart package into a vector SVG bar/column chart
|
|
6874
|
+
*/
|
|
6875
|
+
parseOdfChartToSvg(zipBytes) {
|
|
6876
|
+
try {
|
|
6877
|
+
const unzipped = fflate.unzipSync(zipBytes);
|
|
6878
|
+
const contentXml = unzipped["content.xml"] ? new TextDecoder("utf-8").decode(unzipped["content.xml"]) : "";
|
|
6879
|
+
if (!contentXml) return "";
|
|
6880
|
+
const rowsMatch = contentXml.match(/<table:table-row[\s\S]*?<\/table:table-row>/g) || [];
|
|
6881
|
+
if (rowsMatch.length < 2) return "";
|
|
6882
|
+
const headers = [];
|
|
6883
|
+
const firstRow = rowsMatch[0];
|
|
6884
|
+
const headerCells = firstRow ? firstRow.match(/<text:p>([^<]+)<\/text:p>/g) || [] : [];
|
|
6885
|
+
for (const h of headerCells) {
|
|
6886
|
+
headers.push(h.replace(/<\/?text:p>/g, "").trim());
|
|
6887
|
+
}
|
|
6888
|
+
const categories = [];
|
|
6889
|
+
const seriesValues = headers.map(() => []);
|
|
6890
|
+
for (let r = 1; r < rowsMatch.length; r++) {
|
|
6891
|
+
const rowStr = rowsMatch[r];
|
|
6892
|
+
if (!rowStr) continue;
|
|
6893
|
+
const cells = rowStr.match(/<table:table-cell[\s\S]*?<\/table:table-cell>/g) || [];
|
|
6894
|
+
if (cells.length > 0 && cells[0]) {
|
|
6895
|
+
const catMatch = cells[0].match(/<text:p>([^<]+)<\/text:p>/);
|
|
6896
|
+
categories.push(catMatch ? catMatch[1] : "Row " + r);
|
|
6897
|
+
for (let c = 1; c < cells.length && c - 1 < headers.length; c++) {
|
|
6898
|
+
const cellStr = cells[c];
|
|
6899
|
+
if (!cellStr) continue;
|
|
6900
|
+
const valMatch = cellStr.match(/office:value="([0-9.]+)"/) || cellStr.match(/<text:p>([0-9.]+)<\/text:p>/);
|
|
6901
|
+
const series = seriesValues[c - 1];
|
|
6902
|
+
if (series) {
|
|
6903
|
+
series.push(valMatch ? parseFloat(valMatch[1]) : 0);
|
|
6904
|
+
}
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
}
|
|
6908
|
+
const colors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021"];
|
|
6909
|
+
const colorMatches = contentXml.matchAll(/draw:fill-color="(#[0-9a-fA-F]{6})"/g);
|
|
6910
|
+
let cIdx = 0;
|
|
6911
|
+
for (const cm of colorMatches) {
|
|
6912
|
+
if (cIdx < colors.length) colors[cIdx] = cm[1];
|
|
6913
|
+
cIdx++;
|
|
6914
|
+
}
|
|
6915
|
+
let maxVal = 10;
|
|
6916
|
+
for (const s of seriesValues) {
|
|
6917
|
+
for (const v of s) {
|
|
6918
|
+
if (v > maxVal) maxVal = v;
|
|
6919
|
+
}
|
|
6920
|
+
}
|
|
6921
|
+
maxVal = Math.ceil(maxVal * 1.15);
|
|
6922
|
+
const width = 560;
|
|
6923
|
+
const height = 280;
|
|
6924
|
+
const padLeft = 45;
|
|
6925
|
+
const padRight = 100;
|
|
6926
|
+
const padTop = 20;
|
|
6927
|
+
const padBottom = 40;
|
|
6928
|
+
const chartW = width - padLeft - padRight;
|
|
6929
|
+
const chartH = height - padTop - padBottom;
|
|
6930
|
+
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);">`;
|
|
6931
|
+
for (let step = 0; step <= 4; step++) {
|
|
6932
|
+
const yVal = (maxVal / 4 * step).toFixed(1);
|
|
6933
|
+
const yPos = padTop + chartH - step / 4 * chartH;
|
|
6934
|
+
svg += `<line x1="${padLeft}" y1="${yPos}" x2="${padLeft + chartW}" y2="${yPos}" stroke="#e2e8f0" stroke-dasharray="2,2" />`;
|
|
6935
|
+
svg += `<text x="${padLeft - 8}" y="${yPos + 4}" font-size="11" fill="#64748b" text-anchor="end">${yVal}</text>`;
|
|
6936
|
+
}
|
|
6937
|
+
const numCats = categories.length;
|
|
6938
|
+
const numSeries = headers.length;
|
|
6939
|
+
const groupW = chartW / numCats;
|
|
6940
|
+
const barW = Math.max(8, groupW * 0.7 / numSeries);
|
|
6941
|
+
const groupPad = (groupW - barW * numSeries) / 2;
|
|
6942
|
+
for (let catIdx = 0; catIdx < numCats; catIdx++) {
|
|
6943
|
+
const groupX = padLeft + catIdx * groupW + groupPad;
|
|
6944
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6945
|
+
const val = seriesValues[sIdx][catIdx] || 0;
|
|
6946
|
+
const barH = val / maxVal * chartH;
|
|
6947
|
+
const barX = groupX + sIdx * barW;
|
|
6948
|
+
const barY = padTop + chartH - barH;
|
|
6949
|
+
const col = colors[sIdx % colors.length];
|
|
6950
|
+
svg += `<rect x="${barX}" y="${barY}" width="${barW - 2}" height="${barH}" fill="${col}" rx="2"><title>${headers[sIdx]}: ${val}</title></rect>`;
|
|
6951
|
+
}
|
|
6952
|
+
const catX = padLeft + catIdx * groupW + groupW / 2;
|
|
6953
|
+
svg += `<text x="${catX}" y="${padTop + chartH + 18}" font-size="11" fill="#475569" text-anchor="middle">${categories[catIdx]}</text>`;
|
|
6954
|
+
}
|
|
6955
|
+
let legendY = padTop + 20;
|
|
6956
|
+
for (let sIdx = 0; sIdx < numSeries; sIdx++) {
|
|
6957
|
+
const col = colors[sIdx % colors.length];
|
|
6958
|
+
svg += `<rect x="${padLeft + chartW + 15}" y="${legendY}" width="12" height="12" fill="${col}" rx="2" />`;
|
|
6959
|
+
svg += `<text x="${padLeft + chartW + 32}" y="${legendY + 10}" font-size="11" fill="#334155">${headers[sIdx]}</text>`;
|
|
6960
|
+
legendY += 20;
|
|
6961
|
+
}
|
|
6962
|
+
svg += "</svg>";
|
|
6963
|
+
return svg;
|
|
6964
|
+
} catch (e) {
|
|
6965
|
+
console.warn("[DocPlugin] Error generating chart SVG:", e);
|
|
6966
|
+
return "";
|
|
6967
|
+
}
|
|
6968
|
+
}
|
|
6969
|
+
cleanWordDocFields(text, chartSvg = "") {
|
|
6028
6970
|
if (!text) return "";
|
|
6029
6971
|
let cleaned = text.replace(
|
|
6972
|
+
/\x13\s*EMBED\b[\s\S]*?\x15/gi,
|
|
6973
|
+
() => chartSvg ? `
|
|
6974
|
+
|
|
6975
|
+
${chartSvg}
|
|
6976
|
+
|
|
6977
|
+
` : ""
|
|
6978
|
+
);
|
|
6979
|
+
cleaned = cleaned.replace(
|
|
6030
6980
|
/\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
|
|
6031
6981
|
(_match, url, label) => {
|
|
6032
6982
|
const cleanUrl = url.trim();
|
|
@@ -6034,18 +6984,23 @@ var DocPlugin = class {
|
|
|
6034
6984
|
return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
|
|
6035
6985
|
}
|
|
6036
6986
|
);
|
|
6037
|
-
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g,
|
|
6987
|
+
cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, (_m, res) => {
|
|
6988
|
+
if (/[\x00-\x1F]/.test(res)) return "";
|
|
6989
|
+
return res.trim();
|
|
6990
|
+
});
|
|
6038
6991
|
cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
|
|
6039
6992
|
cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
|
|
6993
|
+
cleaned = cleaned.replace(/[\x00-\x08\x0B-\x0C\x0E-\x1F\x7F]/g, "");
|
|
6994
|
+
cleaned = cleaned.replace(/EMBED\s+LibreOffice\.ChartDocument\.[0-9]+/gi, chartSvg || "");
|
|
6040
6995
|
return cleaned;
|
|
6041
6996
|
}
|
|
6042
|
-
splitIntoPages(text) {
|
|
6997
|
+
splitIntoPages(text, chartSvg = "") {
|
|
6043
6998
|
if (!text) return [""];
|
|
6044
|
-
const cleanedText = this.cleanWordDocFields(text);
|
|
6999
|
+
const cleanedText = this.cleanWordDocFields(text, chartSvg);
|
|
6045
7000
|
const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
|
|
6046
7001
|
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);
|
|
6047
7002
|
if (explicitParts.length === 0) explicitParts.push(normalized);
|
|
6048
|
-
const maxLinesPerPage =
|
|
7003
|
+
const maxLinesPerPage = 34;
|
|
6049
7004
|
const charsPerLine = 80;
|
|
6050
7005
|
const finalPages = [];
|
|
6051
7006
|
for (const part of explicitParts) {
|
|
@@ -6053,8 +7008,8 @@ var DocPlugin = class {
|
|
|
6053
7008
|
let currentLines = [];
|
|
6054
7009
|
let count = 0;
|
|
6055
7010
|
for (const line of lines) {
|
|
6056
|
-
const
|
|
6057
|
-
const vLines = Math.max(1, Math.ceil((
|
|
7011
|
+
const isSvg = line.includes("<svg");
|
|
7012
|
+
const vLines = isSvg ? 12 : Math.max(1, Math.ceil((line.replace(/<[^>]+>/g, "").length || 1) / charsPerLine));
|
|
6058
7013
|
if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
|
|
6059
7014
|
finalPages.push(currentLines.join("\n"));
|
|
6060
7015
|
currentLines = [];
|
|
@@ -6094,9 +7049,44 @@ var DocPlugin = class {
|
|
|
6094
7049
|
tableLines = [];
|
|
6095
7050
|
}
|
|
6096
7051
|
};
|
|
7052
|
+
const sanitizeOptions = {
|
|
7053
|
+
ADD_TAGS: ["a", "svg", "g", "path", "line", "rect", "circle", "text", "title"],
|
|
7054
|
+
ADD_ATTR: [
|
|
7055
|
+
"href",
|
|
7056
|
+
"target",
|
|
7057
|
+
"rel",
|
|
7058
|
+
"style",
|
|
7059
|
+
"viewBox",
|
|
7060
|
+
"width",
|
|
7061
|
+
"height",
|
|
7062
|
+
"x",
|
|
7063
|
+
"y",
|
|
7064
|
+
"x1",
|
|
7065
|
+
"y1",
|
|
7066
|
+
"x2",
|
|
7067
|
+
"y2",
|
|
7068
|
+
"fill",
|
|
7069
|
+
"stroke",
|
|
7070
|
+
"stroke-width",
|
|
7071
|
+
"stroke-dasharray",
|
|
7072
|
+
"rx",
|
|
7073
|
+
"font-size",
|
|
7074
|
+
"text-anchor"
|
|
7075
|
+
]
|
|
7076
|
+
};
|
|
6097
7077
|
let i = 0;
|
|
6098
7078
|
while (i < lines.length) {
|
|
6099
7079
|
let line = lines[i];
|
|
7080
|
+
if (line.includes("<svg")) {
|
|
7081
|
+
if (inList) {
|
|
7082
|
+
html += "</ul>";
|
|
7083
|
+
inList = false;
|
|
7084
|
+
}
|
|
7085
|
+
flushTable();
|
|
7086
|
+
html += line;
|
|
7087
|
+
i++;
|
|
7088
|
+
continue;
|
|
7089
|
+
}
|
|
6100
7090
|
let tabCount = (line.match(/\t/g) || []).length;
|
|
6101
7091
|
if (tabCount > 0) {
|
|
6102
7092
|
let j = i;
|
|
@@ -6130,26 +7120,31 @@ var DocPlugin = class {
|
|
|
6130
7120
|
i++;
|
|
6131
7121
|
continue;
|
|
6132
7122
|
}
|
|
6133
|
-
|
|
6134
|
-
|
|
7123
|
+
if (/^\d{1,2}$/.test(line.trim())) {
|
|
7124
|
+
i++;
|
|
7125
|
+
continue;
|
|
7126
|
+
}
|
|
7127
|
+
const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
|
|
7128
|
+
const isTitleLike = isShortLine && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#") || /^(?:Chapter|Section|Part|\d+\.)\b/i.test(line) || /^[A-Z][a-zA-Z0-9\s#:-]{2,45}$/.test(line));
|
|
7129
|
+
if (isTitleLike) {
|
|
6135
7130
|
if (inList) {
|
|
6136
7131
|
html += "</ul>";
|
|
6137
7132
|
inList = false;
|
|
6138
7133
|
}
|
|
6139
|
-
html += `<h2 style="font-size:
|
|
7134
|
+
html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 18px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
|
|
6140
7135
|
} else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
|
|
6141
7136
|
if (!inList) {
|
|
6142
|
-
html += '<ul style="margin: 8px 0; padding-left: 24px;">';
|
|
7137
|
+
html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
|
|
6143
7138
|
inList = true;
|
|
6144
7139
|
}
|
|
6145
7140
|
const bulletText = line.replace(/^[•\-\*]\s*/, "");
|
|
6146
|
-
html += `<li style="margin: 4px 0; line-height: 1.
|
|
7141
|
+
html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
|
|
6147
7142
|
} else {
|
|
6148
7143
|
if (inList) {
|
|
6149
7144
|
html += "</ul>";
|
|
6150
7145
|
inList = false;
|
|
6151
7146
|
}
|
|
6152
|
-
html += `<p style="line-height: 1.
|
|
7147
|
+
html += `<p style="line-height: 1.5; margin: 0 0 12px 0; font-size: 11pt; color: #1e293b; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
|
|
6153
7148
|
}
|
|
6154
7149
|
i++;
|
|
6155
7150
|
}
|