@files-preview-app/preview-file 1.2.9 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/angular.cjs CHANGED
@@ -18,6 +18,7 @@ var OBJLoader_js = require('three/examples/jsm/loaders/OBJLoader.js');
18
18
  var OrbitControls_js = require('three/examples/jsm/controls/OrbitControls.js');
19
19
  var RTFJS = require('rtf.js/dist/RTFJS.bundle.js');
20
20
 
21
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
21
22
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
22
23
 
23
24
  function _interopNamespace(e) {
@@ -417,16 +418,21 @@ async function sourceToArrayBuffer(source, signal) {
417
418
  metadata.mimeType = source.type || void 0;
418
419
  metadata.extension = extractExtension(source.name);
419
420
  buffer = await source.arrayBuffer();
420
- } else if (source instanceof Blob) {
421
+ } else if (source instanceof Blob || source && typeof source.arrayBuffer === "function" && typeof source.size === "number") {
421
422
  metadata.size = source.size;
422
423
  metadata.mimeType = source.type || void 0;
424
+ if (source.name) {
425
+ metadata.name = source.name;
426
+ metadata.extension = extractExtension(source.name);
427
+ }
423
428
  buffer = await source.arrayBuffer();
424
- } else if (source instanceof ArrayBuffer) {
429
+ } else if (source instanceof ArrayBuffer || Object.prototype.toString.call(source) === "[object ArrayBuffer]" || source && typeof source.byteLength === "number" && typeof source.slice === "function") {
425
430
  buffer = source;
426
- } else if (source instanceof Uint8Array) {
427
- buffer = source.buffer.slice(
428
- source.byteOffset,
429
- source.byteOffset + source.byteLength
431
+ } else if (source instanceof Uint8Array || ArrayBuffer.isView(source)) {
432
+ const view = source;
433
+ buffer = view.buffer.slice(
434
+ view.byteOffset,
435
+ view.byteOffset + view.byteLength
430
436
  );
431
437
  } else {
432
438
  throw new Error("Unsupported file source type");
@@ -511,6 +517,45 @@ function createElement(tag, attrs, ...children) {
511
517
  }
512
518
  return el;
513
519
  }
520
+ var DB_NAME = "PreviewFileTransferDB";
521
+ var DB_STORE = "transfers";
522
+ function openDB() {
523
+ return new Promise((resolve, reject) => {
524
+ if (typeof indexedDB === "undefined") {
525
+ return reject(new Error("IndexedDB is not available"));
526
+ }
527
+ const req = indexedDB.open(DB_NAME, 1);
528
+ req.onupgradeneeded = () => {
529
+ const db = req.result;
530
+ if (!db.objectStoreNames.contains(DB_STORE)) {
531
+ db.createObjectStore(DB_STORE, { keyPath: "id" });
532
+ }
533
+ };
534
+ req.onsuccess = () => resolve(req.result);
535
+ req.onerror = () => reject(req.error);
536
+ });
537
+ }
538
+ async function saveTransferPayload(id, payload) {
539
+ if (typeof window !== "undefined") {
540
+ try {
541
+ window[id] = payload;
542
+ window.__lastTransfer = payload;
543
+ } catch {
544
+ }
545
+ }
546
+ try {
547
+ const db = await openDB();
548
+ return new Promise((resolve, reject) => {
549
+ const tx = db.transaction(DB_STORE, "readwrite");
550
+ const store = tx.objectStore(DB_STORE);
551
+ store.put({ id, ...payload, timestamp: Date.now() });
552
+ tx.oncomplete = () => resolve();
553
+ tx.onerror = () => reject(tx.error);
554
+ });
555
+ } catch (e) {
556
+ console.warn("[saveTransferPayload] IndexedDB store warning:", e);
557
+ }
558
+ }
514
559
  var ICON_ZOOM_IN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
515
560
  var ICON_ZOOM_OUT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
516
561
  var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="8" y1="12" x2="16" y2="12"></line><polyline points="11 9 8 12 11 15"></polyline><polyline points="13 9 16 12 13 15"></polyline></svg>`;
@@ -530,6 +575,7 @@ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" str
530
575
  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>`;
531
576
  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>`;
532
577
  var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
578
+ 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>`;
533
579
  var ICON_MAP = {
534
580
  "zoom-in": ICON_ZOOM_IN,
535
581
  "zoom-out": ICON_ZOOM_OUT,
@@ -556,7 +602,9 @@ var ICON_MAP = {
556
602
  "forward-10": ICON_FAST_FORWARD,
557
603
  "rewind": ICON_REWIND,
558
604
  "replay-10": ICON_REWIND,
559
- "speed": ICON_SPEED
605
+ "speed": ICON_SPEED,
606
+ "open-window": ICON_EXTERNAL_WINDOW,
607
+ "external-window": ICON_EXTERNAL_WINDOW
560
608
  };
561
609
  var ToolbarController = class {
562
610
  el;
@@ -773,7 +821,7 @@ var ThumbnailPanel = class {
773
821
  }
774
822
  }
775
823
  };
776
- var FilePreviewViewer = class {
824
+ var FilePreviewViewer = class _FilePreviewViewer {
777
825
  plugins = [];
778
826
  activeInstance = null;
779
827
  abortController = null;
@@ -810,6 +858,7 @@ var FilePreviewViewer = class {
810
858
  * Preview a file in the given container element.
811
859
  */
812
860
  async preview(container, source, options = {}) {
861
+ this.currentOptions = options;
813
862
  this.abort();
814
863
  this.abortController = new AbortController();
815
864
  const { signal } = this.abortController;
@@ -819,7 +868,10 @@ var FilePreviewViewer = class {
819
868
  this.showLoading();
820
869
  try {
821
870
  const { buffer, metadata } = await sourceToArrayBuffer(source, signal);
822
- this.currentBuffer = buffer;
871
+ if (options.metadata) {
872
+ Object.assign(metadata, options.metadata);
873
+ }
874
+ this.currentBuffer = buffer.slice(0);
823
875
  this.currentMetadata = metadata;
824
876
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
825
877
  const fileInfo = { metadata, buffer };
@@ -849,6 +901,7 @@ var FilePreviewViewer = class {
849
901
  }
850
902
  });
851
903
  this.activeInstance = instance;
904
+ instance.openInSeparateWindow = () => this.openInSeparateWindow();
852
905
  this.hideLoading();
853
906
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
854
907
  if (options.showToolbar !== false && this.toolbar) {
@@ -858,26 +911,16 @@ var FilePreviewViewer = class {
858
911
  actions.push({
859
912
  id: "fullscreen",
860
913
  icon: "fullscreen",
861
- label: "Toggle Fullscreen",
914
+ label: "Fullscreen",
862
915
  type: "button",
863
916
  group: "view",
864
- execute: async () => {
917
+ execute: () => {
865
918
  try {
866
- const isNativeFs = !!document.fullscreenElement;
867
- const isCssFs = this.wrapperEl?.classList.contains("fp-fullscreen-active");
868
- if (!isNativeFs && !isCssFs) {
869
- if (this.wrapperEl?.requestFullscreen) {
870
- await this.wrapperEl.requestFullscreen().catch(() => {
871
- this.wrapperEl?.classList.add("fp-fullscreen-active");
872
- });
873
- } else {
874
- this.wrapperEl?.classList.add("fp-fullscreen-active");
875
- }
919
+ if (!document.fullscreenElement) {
920
+ this.wrapperEl?.requestFullscreen?.();
921
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
876
922
  } else {
877
- if (document.fullscreenElement) {
878
- await document.exitFullscreen().catch(() => {
879
- });
880
- }
923
+ document.exitFullscreen?.();
881
924
  this.wrapperEl?.classList.remove("fp-fullscreen-active");
882
925
  }
883
926
  } catch {
@@ -889,6 +932,28 @@ var FilePreviewViewer = class {
889
932
  }
890
933
  });
891
934
  }
935
+ const openWinAction = actions.find((a) => a.id === "open-window");
936
+ if (openWinAction) {
937
+ if (options?._isSeparateWindow) {
938
+ const idx = actions.indexOf(openWinAction);
939
+ if (idx !== -1) actions.splice(idx, 1);
940
+ } else {
941
+ openWinAction.execute = () => {
942
+ this.openInSeparateWindow();
943
+ };
944
+ }
945
+ } else if (!options?._isSeparateWindow) {
946
+ actions.push({
947
+ id: "open-window",
948
+ icon: "open-window",
949
+ label: "Open in Separate Full Window",
950
+ type: "button",
951
+ group: "actions",
952
+ execute: () => {
953
+ this.openInSeparateWindow();
954
+ }
955
+ });
956
+ }
892
957
  this.toolbar.update(actions);
893
958
  this.toolbar.show();
894
959
  }
@@ -921,6 +986,104 @@ var FilePreviewViewer = class {
921
986
  throw error;
922
987
  }
923
988
  }
989
+ /**
990
+ * Opens the current file preview in a separate full browser window.
991
+ */
992
+ openInSeparateWindow() {
993
+ if (!this.currentBuffer) {
994
+ console.warn("[FilePreviewViewer] No active file buffer to open in separate window");
995
+ return null;
996
+ }
997
+ if (this.currentOptions.onOpenSeparateWindow) {
998
+ return this.currentOptions.onOpenSeparateWindow({
999
+ buffer: this.currentBuffer,
1000
+ metadata: this.currentMetadata || { name: "Document" },
1001
+ options: this.currentOptions
1002
+ });
1003
+ }
1004
+ const transferId = "fp_win_" + Date.now() + "_" + Math.random().toString(36).slice(2, 8);
1005
+ let clonedBuffer;
1006
+ try {
1007
+ clonedBuffer = this.currentBuffer.slice(0);
1008
+ } catch {
1009
+ clonedBuffer = this.currentBuffer;
1010
+ }
1011
+ const payload = {
1012
+ buffer: clonedBuffer,
1013
+ metadata: this.currentMetadata ? { ...this.currentMetadata } : void 0,
1014
+ options: { ...this.currentOptions, _isSeparateWindow: true }
1015
+ };
1016
+ if (typeof window !== "undefined") {
1017
+ try {
1018
+ window[transferId] = payload;
1019
+ window.__lastTransfer = payload;
1020
+ } catch {
1021
+ }
1022
+ }
1023
+ saveTransferPayload(transferId, payload).catch((err) => {
1024
+ console.warn("[FilePreviewViewer] Transfer payload save warning:", err);
1025
+ });
1026
+ let targetUrl = null;
1027
+ if (this.currentOptions.standaloneViewerUrl) {
1028
+ const u = new URL(this.currentOptions.standaloneViewerUrl, window.location.href);
1029
+ u.searchParams.set("mode", "fullscreen");
1030
+ u.searchParams.set("transferId", transferId);
1031
+ targetUrl = u.toString();
1032
+ } else if (typeof window !== "undefined" && window.location?.href && !window.location.href.startsWith("about:")) {
1033
+ const u = new URL(window.location.href);
1034
+ u.searchParams.set("mode", "fullscreen");
1035
+ u.searchParams.set("transferId", transferId);
1036
+ targetUrl = u.toString();
1037
+ }
1038
+ if (targetUrl) {
1039
+ const newWin2 = window.open(targetUrl, "_blank");
1040
+ if (!newWin2) {
1041
+ alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1042
+ return null;
1043
+ }
1044
+ return newWin2;
1045
+ }
1046
+ const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1047
+ const newWin = window.open("", "_blank");
1048
+ if (!newWin) {
1049
+ alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1050
+ return null;
1051
+ }
1052
+ newWin.document.title = title;
1053
+ newWin.document.body.style.margin = "0";
1054
+ newWin.document.body.style.padding = "0";
1055
+ newWin.document.body.style.width = "100vw";
1056
+ newWin.document.body.style.height = "100vh";
1057
+ newWin.document.body.style.overflow = "hidden";
1058
+ newWin.document.body.style.backgroundColor = "#f8fafc";
1059
+ const headNodes = document.querySelectorAll('link[rel="stylesheet"], style');
1060
+ headNodes.forEach((node) => {
1061
+ newWin.document.head.appendChild(node.cloneNode(true));
1062
+ });
1063
+ const root = newWin.document.createElement("div");
1064
+ root.id = "full-window-preview-root";
1065
+ root.style.width = "100%";
1066
+ root.style.height = "100%";
1067
+ root.style.overflow = "hidden";
1068
+ newWin.document.body.appendChild(root);
1069
+ const separateViewer = new _FilePreviewViewer();
1070
+ for (const plugin of this.plugins) {
1071
+ separateViewer.registerPlugin(plugin);
1072
+ }
1073
+ separateViewer.preview(root, this.currentBuffer.slice(0), {
1074
+ ...this.currentOptions,
1075
+ showToolbar: true,
1076
+ toolbarPosition: "top",
1077
+ metadata: this.currentMetadata || void 0,
1078
+ _isSeparateWindow: true
1079
+ }).catch((err) => {
1080
+ console.error("[FilePreviewViewer] Error rendering in separate window:", err);
1081
+ });
1082
+ newWin.addEventListener("beforeunload", () => {
1083
+ separateViewer.destroy();
1084
+ });
1085
+ return newWin;
1086
+ }
924
1087
  /**
925
1088
  * Subscribe to viewer events.
926
1089
  */
@@ -1326,8 +1489,20 @@ var CfbfReader = class {
1326
1489
  }
1327
1490
  };
1328
1491
  if (typeof window !== "undefined" && pdfjsLib__namespace.GlobalWorkerOptions) {
1329
- if (!pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1330
- pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/build/pdf.worker.min.mjs`;
1492
+ if (!pdfjsLib__namespace.GlobalWorkerOptions.workerPort && !pdfjsLib__namespace.GlobalWorkerOptions.workerSrc) {
1493
+ const customWorker = window.__PDF_WORKER_SRC__;
1494
+ if (customWorker) {
1495
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = customWorker;
1496
+ } else {
1497
+ try {
1498
+ pdfjsLib__namespace.GlobalWorkerOptions.workerPort = new Worker(
1499
+ new URL("pdfjs-dist/build/pdf.worker.min.mjs", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('angular.cjs', document.baseURI).href))),
1500
+ { type: "module" }
1501
+ );
1502
+ } catch {
1503
+ pdfjsLib__namespace.GlobalWorkerOptions.workerSrc = "./pdf.worker.min.mjs";
1504
+ }
1505
+ }
1331
1506
  }
1332
1507
  }
1333
1508
  var PdfPlugin = class {
@@ -1421,6 +1596,14 @@ var PdfPlugin = class {
1421
1596
  type: "button",
1422
1597
  group: "actions",
1423
1598
  execute: () => instance.print?.()
1599
+ },
1600
+ {
1601
+ id: "open-window",
1602
+ icon: "open-window",
1603
+ label: "Open in Separate Full Window",
1604
+ type: "button",
1605
+ group: "actions",
1606
+ execute: () => instance.openInSeparateWindow?.()
1424
1607
  }
1425
1608
  ];
1426
1609
  }
@@ -1470,18 +1653,21 @@ var PdfPlugin = class {
1470
1653
  indicator.style.pointerEvents = "none";
1471
1654
  container.appendChild(indicator);
1472
1655
  ctx.container.appendChild(container);
1656
+ const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1657
+ const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
1473
1658
  const loadingTask = pdfjsLib__namespace.getDocument({
1474
- data: new Uint8Array(ctx.buffer),
1475
- cMapUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/cmaps/`,
1659
+ data: new Uint8Array(ctx.buffer.slice(0)),
1660
+ cMapUrl: cmapsUrl,
1476
1661
  cMapPacked: true,
1477
- standardFontDataUrl: `https://unpkg.com/pdfjs-dist@${pdfjsLib__namespace.version || "4.10.38"}/standard_fonts/`
1662
+ standardFontDataUrl: standardFontsUrl,
1663
+ verbosity: 0
1478
1664
  });
1479
1665
  const pdfDoc = await loadingTask.promise;
1480
1666
  const totalPages = Math.max(1, pdfDoc.numPages);
1481
1667
  let currentPage = 1;
1482
1668
  let zoomScale = 1;
1483
1669
  let rotation = 0;
1484
- let fitMode = "width";
1670
+ let fitMode = "page";
1485
1671
  let currentRenderTask = null;
1486
1672
  const renderPage = async (pageNum) => {
1487
1673
  if (currentRenderTask) {
@@ -1501,15 +1687,15 @@ var PdfPlugin = class {
1501
1687
  const containerWidth = container.clientWidth || 900;
1502
1688
  const containerHeight = container.clientHeight || 700;
1503
1689
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1504
- const availWidth = Math.max(280, containerWidth - 48);
1505
- const availHeight = Math.max(280, containerHeight - 88);
1690
+ const availWidth = Math.max(320, containerWidth - 48);
1691
+ const availHeight = Math.max(550, containerHeight - 88);
1506
1692
  const scaleW = availWidth / unscaledVp.width;
1507
1693
  const scaleH = availHeight / unscaledVp.height;
1508
1694
  let fitScale;
1509
1695
  if (fitMode === "page") {
1510
- fitScale = Math.max(0.5, Math.min(scaleW, scaleH));
1696
+ fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
1511
1697
  } else {
1512
- fitScale = Math.max(0.65, Math.min(1.15, scaleW));
1698
+ fitScale = Math.max(0.65, Math.min(1.25, scaleW));
1513
1699
  }
1514
1700
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1515
1701
  const pixelRatio = window.devicePixelRatio || 1;
@@ -1535,7 +1721,7 @@ var PdfPlugin = class {
1535
1721
  currentRenderTask = null;
1536
1722
  }
1537
1723
  };
1538
- await renderPage(1);
1724
+ renderPage(1);
1539
1725
  let resizeTimer = null;
1540
1726
  const resizeObserver = new ResizeObserver(() => {
1541
1727
  if (resizeTimer) clearTimeout(resizeTimer);
@@ -1579,7 +1765,7 @@ var PdfPlugin = class {
1579
1765
  renderPage(currentPage);
1580
1766
  },
1581
1767
  fitToPage: () => {
1582
- fitMode = fitMode === "width" ? "page" : "width";
1768
+ fitMode = fitMode === "page" ? "width" : "page";
1583
1769
  zoomScale = 1;
1584
1770
  rotation = 0;
1585
1771
  renderPage(currentPage);
@@ -2056,6 +2242,14 @@ var DocxPlugin = class {
2056
2242
  type: "button",
2057
2243
  group: "actions",
2058
2244
  execute: () => instance.print?.()
2245
+ },
2246
+ {
2247
+ id: "open-window",
2248
+ icon: "open-window",
2249
+ label: "Open in Separate Full Window",
2250
+ type: "button",
2251
+ group: "actions",
2252
+ execute: () => instance.openInSeparateWindow?.()
2059
2253
  }
2060
2254
  );
2061
2255
  return actions;
@@ -2115,6 +2309,31 @@ var DocxPlugin = class {
2115
2309
  }
2116
2310
  if (wrapper.children.length > 0 && (wrapper.textContent?.trim().length ?? 0) > 0) {
2117
2311
  renderedSuccessfully = true;
2312
+ try {
2313
+ const unzipped = fflate.unzipSync(new Uint8Array(ctx.buffer));
2314
+ const chartKeys = Object.keys(unzipped).filter((k) => k.replace(/^[./\\]+/, "").toLowerCase().startsWith("word/charts/chart") && k.endsWith(".xml")).sort();
2315
+ if (chartKeys.length > 0) {
2316
+ const allDivs = Array.from(wrapper.querySelectorAll("div"));
2317
+ const emptyContainers = allDivs.filter((div) => {
2318
+ const st = div.getAttribute("style") || "";
2319
+ return st.includes("width:") && st.includes("height:") && div.children.length === 0 && (div.textContent?.trim().length ?? 0) === 0;
2320
+ });
2321
+ chartKeys.forEach((cKey, idx) => {
2322
+ const target = emptyContainers[idx];
2323
+ if (target) {
2324
+ const xmlStr = fflate.strFromU8(unzipped[cKey]);
2325
+ const svg = this.parseAndRenderChartSvg(xmlStr);
2326
+ if (svg) {
2327
+ target.innerHTML = svg;
2328
+ target.style.display = "block";
2329
+ target.style.margin = "12px auto";
2330
+ }
2331
+ }
2332
+ });
2333
+ }
2334
+ } catch (chartErr) {
2335
+ console.warn("[DocxPlugin] Non-critical error rendering DrawingML charts:", chartErr);
2336
+ }
2118
2337
  }
2119
2338
  } catch (err) {
2120
2339
  console.warn("[DocxPlugin] docx-preview failed, triggering native fallback:", err);
@@ -2146,64 +2365,74 @@ var DocxPlugin = class {
2146
2365
  }
2147
2366
  let sections = Array.from(wrapper.querySelectorAll("section.docx"));
2148
2367
  const cards = Array.from(wrapper.querySelectorAll(".fp-docx-page-card"));
2149
- if (sections.length === 1 && cards.length === 0) {
2150
- const singleSec = sections[0];
2151
- const contentContainer = singleSec.querySelector("article") || singleSec;
2152
- const children = Array.from(contentContainer.children);
2153
- const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
2154
- const secH = singleSec.offsetHeight || singleSec.scrollHeight;
2155
- if (secH > pageH * 1.25 && children.length > 1) {
2156
- const childHeights = children.map((c) => {
2157
- const rectH = c.getBoundingClientRect().height;
2158
- const offH = c.offsetHeight;
2159
- const textLen = c.textContent?.trim().length || 0;
2160
- const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
2161
- return Math.max(rectH, offH, estH);
2162
- });
2163
- const parent = singleSec.parentElement || wrapper;
2164
- const newSections = [singleSec];
2165
- const headerEl = singleSec.querySelector("header");
2166
- const footerEl = singleSec.querySelector("footer");
2167
- contentContainer.innerHTML = "";
2168
- singleSec.style.minHeight = `${pageH}px`;
2169
- singleSec.style.boxSizing = "border-box";
2170
- let curContent = contentContainer;
2171
- let curH = 0;
2172
- const maxH = pageH - 140;
2173
- for (let i = 0; i < children.length; i++) {
2174
- const child = children[i];
2175
- const chH = childHeights[i];
2176
- curContent.appendChild(child);
2177
- curH += chH;
2178
- if (curH >= maxH && i < children.length - 1) {
2179
- const nextSec = document.createElement("section");
2180
- nextSec.className = singleSec.className;
2181
- nextSec.style.cssText = singleSec.style.cssText;
2182
- nextSec.style.minHeight = `${pageH}px`;
2183
- nextSec.style.boxSizing = "border-box";
2184
- nextSec.style.backgroundColor = "#ffffff";
2185
- nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2186
- nextSec.style.borderRadius = "4px";
2187
- nextSec.style.marginBottom = "24px";
2188
- if (headerEl) {
2189
- nextSec.appendChild(headerEl.cloneNode(true));
2190
- }
2191
- const nextArticle = document.createElement("article");
2192
- if (contentContainer.tagName.toLowerCase() === "article") {
2193
- nextArticle.style.cssText = contentContainer.style.cssText;
2194
- }
2195
- nextSec.appendChild(nextArticle);
2196
- if (footerEl) {
2197
- nextSec.appendChild(footerEl.cloneNode(true));
2368
+ if (sections.length > 0 && cards.length === 0) {
2369
+ const finalSections = [];
2370
+ for (const singleSec of sections) {
2371
+ const contentContainer = singleSec.querySelector("article") || singleSec;
2372
+ const children = Array.from(contentContainer.children);
2373
+ const pageH = singleSec.offsetHeight > 1300 ? 1122 : Math.max(1056, singleSec.offsetHeight);
2374
+ const secH = singleSec.scrollHeight || singleSec.offsetHeight;
2375
+ if (secH > pageH * 1.25 && children.length > 1) {
2376
+ const childHeights = children.map((c) => {
2377
+ const rectH = c.getBoundingClientRect().height;
2378
+ const offH = c.offsetHeight;
2379
+ const textLen = c.textContent?.trim().length || 0;
2380
+ const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
2381
+ return Math.max(rectH, offH, estH);
2382
+ });
2383
+ const parent = singleSec.parentElement || wrapper;
2384
+ const headerEl = singleSec.querySelector("header");
2385
+ const footerEl = singleSec.querySelector("footer");
2386
+ contentContainer.innerHTML = "";
2387
+ singleSec.style.minHeight = `${pageH}px`;
2388
+ singleSec.style.boxSizing = "border-box";
2389
+ let curContent = contentContainer;
2390
+ let curSec = singleSec;
2391
+ let curH = 0;
2392
+ const maxH = pageH - 140;
2393
+ finalSections.push(singleSec);
2394
+ for (let i = 0; i < children.length; i++) {
2395
+ const child = children[i];
2396
+ const chH = childHeights[i];
2397
+ curContent.appendChild(child);
2398
+ curH += chH;
2399
+ if (curH >= maxH && i < children.length - 1) {
2400
+ const nextSec = document.createElement("section");
2401
+ nextSec.className = singleSec.className;
2402
+ nextSec.style.cssText = singleSec.style.cssText;
2403
+ nextSec.style.minHeight = `${pageH}px`;
2404
+ nextSec.style.boxSizing = "border-box";
2405
+ nextSec.style.backgroundColor = "#ffffff";
2406
+ nextSec.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
2407
+ nextSec.style.borderRadius = "4px";
2408
+ nextSec.style.marginBottom = "24px";
2409
+ if (headerEl) {
2410
+ nextSec.appendChild(headerEl.cloneNode(true));
2411
+ }
2412
+ const nextArticle = document.createElement("article");
2413
+ if (contentContainer.tagName.toLowerCase() === "article") {
2414
+ nextArticle.style.cssText = contentContainer.style.cssText;
2415
+ }
2416
+ nextSec.appendChild(nextArticle);
2417
+ if (footerEl) {
2418
+ nextSec.appendChild(footerEl.cloneNode(true));
2419
+ }
2420
+ if (curSec.nextSibling) {
2421
+ parent.insertBefore(nextSec, curSec.nextSibling);
2422
+ } else {
2423
+ parent.appendChild(nextSec);
2424
+ }
2425
+ finalSections.push(nextSec);
2426
+ curSec = nextSec;
2427
+ curContent = nextArticle;
2428
+ curH = 0;
2198
2429
  }
2199
- parent.appendChild(nextSec);
2200
- newSections.push(nextSec);
2201
- curContent = nextArticle;
2202
- curH = 0;
2203
2430
  }
2431
+ } else {
2432
+ finalSections.push(singleSec);
2204
2433
  }
2205
- sections = newSections;
2206
2434
  }
2435
+ sections = finalSections;
2207
2436
  }
2208
2437
  const pageElements = sections.length > 0 ? sections : cards;
2209
2438
  const totalPages = Math.max(1, pageElements.length);
@@ -2595,6 +2824,88 @@ var DocxPlugin = class {
2595
2824
  }
2596
2825
  return result;
2597
2826
  }
2827
+ parseAndRenderChartSvg(xmlStr, width = 500, height = 260) {
2828
+ const catMatches = [...xmlStr.matchAll(/<c:cat>[\s\S]*?<c:strCache>([\s\S]*?)<\/c:strCache>/g)];
2829
+ let categories = [];
2830
+ if (catMatches.length > 0) {
2831
+ categories = [...catMatches[0][1].matchAll(/<c:v>([^<]+)<\/c:v>/g)].map((m) => m[1]);
2832
+ }
2833
+ if (categories.length === 0) {
2834
+ categories = ["Category 1", "Category 2", "Category 3", "Category 4"];
2835
+ }
2836
+ const defaultColors = ["#004586", "#ff420e", "#ffd320", "#579d1c", "#7e0021", "#83caff"];
2837
+ const sers = [...xmlStr.matchAll(/<c:ser>([\s\S]*?)<\/c:ser>/g)];
2838
+ const series = [];
2839
+ sers.forEach((s, sIdx) => {
2840
+ const titleMatch = s[1].match(/<c:tx>[\s\S]*?<c:v>([^<]+)<\/c:v>/);
2841
+ const title = titleMatch ? titleMatch[1] : `Series ${sIdx + 1}`;
2842
+ const clrMatch = s[1].match(/<a:srgbClr\s+val="([^"]+)"/);
2843
+ const color = clrMatch ? "#" + clrMatch[1] : defaultColors[sIdx % defaultColors.length];
2844
+ const valMatch = s[1].match(/<c:val>[\s\S]*?<c:numCache>([\s\S]*?)<\/c:numCache>/);
2845
+ let values = [];
2846
+ if (valMatch) {
2847
+ values = [...valMatch[1].matchAll(/<c:pt\s+idx="(\d+)">\s*<c:v>([^<]+)<\/c:v>/g)].sort((a, b) => parseInt(a[1], 10) - parseInt(b[1], 10)).map((m) => parseFloat(m[2]) || 0);
2848
+ }
2849
+ series.push({ title, color, values });
2850
+ });
2851
+ if (series.length === 0) return "";
2852
+ let maxVal = 10;
2853
+ series.forEach((s) => s.values.forEach((v) => {
2854
+ if (v > maxVal) maxVal = v;
2855
+ }));
2856
+ maxVal = Math.ceil(maxVal * 1.15);
2857
+ if (maxVal % 2 !== 0) maxVal++;
2858
+ const padLeft = 45;
2859
+ const padBottom = 55;
2860
+ const padTop = 20;
2861
+ const padRight = 20;
2862
+ const plotW = width - padLeft - padRight;
2863
+ const plotH = height - padTop - padBottom;
2864
+ const yTicks = 5;
2865
+ let gridLines = "";
2866
+ for (let i = 0; i <= yTicks; i++) {
2867
+ const val = maxVal / yTicks * i;
2868
+ const y = padTop + plotH - val / maxVal * plotH;
2869
+ gridLines += `<line x1="${padLeft}" y1="${y}" x2="${padLeft + plotW}" y2="${y}" stroke="#e2e8f0" stroke-width="1" />`;
2870
+ gridLines += `<text x="${padLeft - 8}" y="${y + 4}" text-anchor="end" font-size="11" fill="#64748b" font-family="Calibri, sans-serif">${Math.round(val)}</text>`;
2871
+ }
2872
+ const numCats = categories.length;
2873
+ const numSers = series.length;
2874
+ const groupW = plotW / numCats;
2875
+ const barW = Math.max(8, Math.min(28, groupW * 0.7 / numSers));
2876
+ const groupPad = (groupW - barW * numSers) / 2;
2877
+ let bars = "";
2878
+ let catLabels = "";
2879
+ for (let c = 0; c < numCats; c++) {
2880
+ const catX = padLeft + c * groupW;
2881
+ catLabels += `<text x="${catX + groupW / 2}" y="${padTop + plotH + 18}" text-anchor="middle" font-size="11" fill="#334155" font-family="Calibri, sans-serif">${categories[c]}</text>`;
2882
+ for (let s = 0; s < numSers; s++) {
2883
+ const val = series[s].values[c] ?? 0;
2884
+ const bH = Math.max(0, val / maxVal * plotH);
2885
+ const bX = catX + groupPad + s * barW;
2886
+ const bY = padTop + plotH - bH;
2887
+ bars += `<rect x="${bX}" y="${bY}" width="${barW - 2}" height="${bH}" fill="${series[s].color}" rx="1" />`;
2888
+ }
2889
+ }
2890
+ let legend = "";
2891
+ const legY = height - 12;
2892
+ let legX = padLeft + (plotW - numSers * 100) / 2;
2893
+ series.forEach((s) => {
2894
+ legend += `<rect x="${legX}" y="${legY - 9}" width="10" height="10" fill="${s.color}" rx="2" />`;
2895
+ legend += `<text x="${legX + 15}" y="${legY}" font-size="11" fill="#475569" font-family="Calibri, sans-serif">${s.title}</text>`;
2896
+ legX += 95;
2897
+ });
2898
+ return `
2899
+ <svg viewBox="0 0 ${width} ${height}" width="100%" height="100%" style="background:#ffffff; border-radius:4px; overflow:visible;" xmlns="http://www.w3.org/2000/svg">
2900
+ ${gridLines}
2901
+ <line x1="${padLeft}" y1="${padTop + plotH}" x2="${padLeft + plotW}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
2902
+ <line x1="${padLeft}" y1="${padTop}" x2="${padLeft}" y2="${padTop + plotH}" stroke="#94a3b8" stroke-width="1.5" />
2903
+ ${bars}
2904
+ ${catLabels}
2905
+ ${legend}
2906
+ </svg>
2907
+ `.trim();
2908
+ }
2598
2909
  };
2599
2910
  function docxPlugin() {
2600
2911
  return new DocxPlugin();
@@ -3161,6 +3472,16 @@ var CodePlugin = class {
3161
3472
  execute: () => {
3162
3473
  instance.print?.();
3163
3474
  }
3475
+ },
3476
+ {
3477
+ id: "open-window",
3478
+ icon: "open-window",
3479
+ label: "Open in Separate Full Window",
3480
+ type: "button",
3481
+ group: "actions",
3482
+ execute: () => {
3483
+ instance.openInSeparateWindow?.();
3484
+ }
3164
3485
  }
3165
3486
  );
3166
3487
  return actions;
@@ -4329,6 +4650,14 @@ var RtfPlugin = class {
4329
4650
  type: "button",
4330
4651
  group: "actions",
4331
4652
  execute: () => instance.print?.()
4653
+ },
4654
+ {
4655
+ id: "open-window",
4656
+ icon: "open-window",
4657
+ label: "Open in Separate Full Window",
4658
+ type: "button",
4659
+ group: "actions",
4660
+ execute: () => instance.openInSeparateWindow?.()
4332
4661
  }
4333
4662
  );
4334
4663
  return actions;
@@ -4381,59 +4710,54 @@ var RtfPlugin = class {
4381
4710
  }
4382
4711
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
4383
4712
  const htmlElements = await doc.render();
4384
- if (htmlElements.length === 1) {
4385
- const singleEl = htmlElements[0];
4386
- wrapper.appendChild(singleEl);
4387
- const children = Array.from(singleEl.children);
4388
- const secH = singleEl.offsetHeight || singleEl.scrollHeight;
4389
- if (secH > 1300 && children.length > 1) {
4390
- const childHeights = children.map((c) => {
4391
- const rectH = c.getBoundingClientRect().height;
4392
- const offH = c.offsetHeight;
4393
- const textLen = c.textContent?.trim().length || 0;
4394
- const estH = Math.max(24, Math.ceil(textLen / 80) * 22 + 16);
4395
- return Math.max(rectH, offH, estH);
4396
- });
4397
- wrapper.innerHTML = "";
4398
- const createRtfCard = () => {
4399
- const card = document.createElement("div");
4400
- card.className = "fp-rtf-page-card";
4401
- card.style.backgroundColor = "#ffffff";
4402
- card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
4403
- card.style.borderRadius = "4px";
4404
- card.style.padding = "72px 56px";
4405
- card.style.width = "816px";
4406
- card.style.minHeight = "1056px";
4407
- card.style.boxSizing = "border-box";
4408
- card.style.marginBottom = "24px";
4409
- return card;
4410
- };
4411
- let curCard = createRtfCard();
4412
- wrapper.appendChild(curCard);
4413
- pageElements = [curCard];
4414
- let curH = 0;
4415
- const maxH = 920;
4416
- for (let i = 0; i < children.length; i++) {
4417
- const child = children[i];
4418
- const chH = childHeights[i];
4419
- curCard.appendChild(child);
4420
- curH += chH;
4421
- if (curH >= maxH && i < children.length - 1) {
4422
- curCard = createRtfCard();
4423
- wrapper.appendChild(curCard);
4424
- pageElements.push(curCard);
4425
- curH = 0;
4426
- }
4427
- }
4713
+ const contentNodes = [];
4714
+ for (const item of htmlElements) {
4715
+ if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4716
+ contentNodes.push(...Array.from(item.children));
4428
4717
  } else {
4429
- pageElements = [singleEl];
4718
+ contentNodes.push(item);
4430
4719
  }
4431
- } else {
4432
- pageElements = htmlElements;
4433
- for (let i = 0; i < htmlElements.length; i++) {
4434
- const el = htmlElements[i];
4435
- el.style.display = i === 0 ? "block" : "none";
4436
- wrapper.appendChild(el);
4720
+ }
4721
+ wrapper.innerHTML = "";
4722
+ contentNodes.forEach((node) => wrapper.appendChild(node));
4723
+ const childHeights = contentNodes.map((c) => {
4724
+ const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4725
+ const offH = c.offsetHeight || 0;
4726
+ const textLen = c.textContent?.trim().length || 0;
4727
+ const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
4728
+ return Math.max(rectH, offH, estH);
4729
+ });
4730
+ wrapper.innerHTML = "";
4731
+ const createRtfCard = () => {
4732
+ const card = document.createElement("div");
4733
+ card.className = "fp-rtf-page-card";
4734
+ card.style.backgroundColor = "#ffffff";
4735
+ card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
4736
+ card.style.borderRadius = "4px";
4737
+ card.style.padding = "72px 56px";
4738
+ card.style.width = "816px";
4739
+ card.style.minHeight = "1056px";
4740
+ card.style.boxSizing = "border-box";
4741
+ card.style.marginBottom = "24px";
4742
+ card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4743
+ card.style.lineHeight = "1.6";
4744
+ return card;
4745
+ };
4746
+ let curCard = createRtfCard();
4747
+ wrapper.appendChild(curCard);
4748
+ pageElements = [curCard];
4749
+ let curH = 0;
4750
+ const maxH = 912;
4751
+ for (let i = 0; i < contentNodes.length; i++) {
4752
+ const child = contentNodes[i];
4753
+ const chH = childHeights[i];
4754
+ curCard.appendChild(child);
4755
+ curH += chH;
4756
+ if (curH >= maxH && i < contentNodes.length - 1) {
4757
+ curCard = createRtfCard();
4758
+ wrapper.appendChild(curCard);
4759
+ pageElements.push(curCard);
4760
+ curH = 0;
4437
4761
  }
4438
4762
  }
4439
4763
  } catch (err) {
@@ -4802,6 +5126,14 @@ var OpenDocumentPlugin = class {
4802
5126
  type: "button",
4803
5127
  group: "actions",
4804
5128
  execute: () => instance.print?.()
5129
+ },
5130
+ {
5131
+ id: "open-window",
5132
+ icon: "open-window",
5133
+ label: "Open in Separate Full Window",
5134
+ type: "button",
5135
+ group: "actions",
5136
+ execute: () => instance.openInSeparateWindow?.()
4805
5137
  }
4806
5138
  );
4807
5139
  return actions;
@@ -5390,6 +5722,14 @@ var DocPlugin = class {
5390
5722
  type: "button",
5391
5723
  group: "actions",
5392
5724
  execute: () => instance.print?.()
5725
+ },
5726
+ {
5727
+ id: "open-window",
5728
+ icon: "open-window",
5729
+ label: "Open in Separate Full Window",
5730
+ type: "button",
5731
+ group: "actions",
5732
+ execute: () => instance.openInSeparateWindow?.()
5393
5733
  }
5394
5734
  );
5395
5735
  return actions;
@@ -5681,9 +6021,25 @@ var DocPlugin = class {
5681
6021
  heuristicTextExtraction(buffer) {
5682
6022
  return this.extractStringsFromBytes(new Uint8Array(buffer));
5683
6023
  }
6024
+ cleanWordDocFields(text) {
6025
+ if (!text) return "";
6026
+ let cleaned = text.replace(
6027
+ /\x13\s*HYPERLINK\s*"?([^"\x14]+)"?\s*\x14([\s\S]*?)\x15/gi,
6028
+ (_match, url, label) => {
6029
+ const cleanUrl = url.trim();
6030
+ const cleanLabel = label.trim() || cleanUrl;
6031
+ return `<a href="${cleanUrl}" target="_blank" rel="noopener noreferrer" style="color: #2563eb; text-decoration: underline;">${cleanLabel}</a>`;
6032
+ }
6033
+ );
6034
+ cleaned = cleaned.replace(/\x13[^\x14\x15]*\x14([^\x15]*)\x15/g, "$1");
6035
+ cleaned = cleaned.replace(/\x13[^\x15]*\x15/g, "");
6036
+ cleaned = cleaned.replace(/[\x13\x14\x15]/g, "");
6037
+ return cleaned;
6038
+ }
5684
6039
  splitIntoPages(text) {
5685
6040
  if (!text) return [""];
5686
- const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
6041
+ const cleanedText = this.cleanWordDocFields(text);
6042
+ const normalized = cleanedText.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x0B/g, "\n").replace(/\x07\n/g, "\n").replace(/\x07/g, " ");
5687
6043
  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);
5688
6044
  if (explicitParts.length === 0) explicitParts.push(normalized);
5689
6045
  const maxLinesPerPage = 32;
@@ -5694,7 +6050,8 @@ var DocPlugin = class {
5694
6050
  let currentLines = [];
5695
6051
  let count = 0;
5696
6052
  for (const line of lines) {
5697
- const vLines = Math.max(1, Math.ceil((line.length || 1) / charsPerLine));
6053
+ const plainLine = line.replace(/<[^>]+>/g, "");
6054
+ const vLines = Math.max(1, Math.ceil((plainLine.length || 1) / charsPerLine));
5698
6055
  if (count + vLines > maxLinesPerPage && currentLines.length > 0) {
5699
6056
  finalPages.push(currentLines.join("\n"));
5700
6057
  currentLines = [];
@@ -5770,25 +6127,26 @@ var DocPlugin = class {
5770
6127
  i++;
5771
6128
  continue;
5772
6129
  }
6130
+ const sanitizeOptions = { ADD_TAGS: ["a"], ADD_ATTR: ["href", "target", "rel", "style"] };
5773
6131
  if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
5774
6132
  if (inList) {
5775
6133
  html += "</ul>";
5776
6134
  inList = false;
5777
6135
  }
5778
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line)}</h2>`;
6136
+ html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</h2>`;
5779
6137
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
5780
6138
  if (!inList) {
5781
6139
  html += '<ul style="margin: 8px 0; padding-left: 24px;">';
5782
6140
  inList = true;
5783
6141
  }
5784
6142
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
5785
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText)}</li>`;
6143
+ html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
5786
6144
  } else {
5787
6145
  if (inList) {
5788
6146
  html += "</ul>";
5789
6147
  inList = false;
5790
6148
  }
5791
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line)}</p>`;
6149
+ html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
5792
6150
  }
5793
6151
  i++;
5794
6152
  }