web-mojo 2.1.46

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.
Files changed (91) hide show
  1. package/LICENSE +198 -0
  2. package/README.md +510 -0
  3. package/dist/admin.cjs.js +2 -0
  4. package/dist/admin.cjs.js.map +1 -0
  5. package/dist/admin.css +621 -0
  6. package/dist/admin.es.js +7973 -0
  7. package/dist/admin.es.js.map +1 -0
  8. package/dist/auth.cjs.js +2 -0
  9. package/dist/auth.cjs.js.map +1 -0
  10. package/dist/auth.css +804 -0
  11. package/dist/auth.es.js +2168 -0
  12. package/dist/auth.es.js.map +1 -0
  13. package/dist/charts.cjs.js +2 -0
  14. package/dist/charts.cjs.js.map +1 -0
  15. package/dist/charts.css +1002 -0
  16. package/dist/charts.es.js +16 -0
  17. package/dist/charts.es.js.map +1 -0
  18. package/dist/chunks/ContextMenu-BrHqj0fn.js +80 -0
  19. package/dist/chunks/ContextMenu-BrHqj0fn.js.map +1 -0
  20. package/dist/chunks/ContextMenu-gEcpSz56.js +2 -0
  21. package/dist/chunks/ContextMenu-gEcpSz56.js.map +1 -0
  22. package/dist/chunks/DataView-DPryYpEW.js +2 -0
  23. package/dist/chunks/DataView-DPryYpEW.js.map +1 -0
  24. package/dist/chunks/DataView-DjZQrpba.js +843 -0
  25. package/dist/chunks/DataView-DjZQrpba.js.map +1 -0
  26. package/dist/chunks/Dialog-BsRx4eg3.js +2 -0
  27. package/dist/chunks/Dialog-BsRx4eg3.js.map +1 -0
  28. package/dist/chunks/Dialog-DSlctbon.js +1377 -0
  29. package/dist/chunks/Dialog-DSlctbon.js.map +1 -0
  30. package/dist/chunks/FilePreviewView-BmFHzK5K.js +5868 -0
  31. package/dist/chunks/FilePreviewView-BmFHzK5K.js.map +1 -0
  32. package/dist/chunks/FilePreviewView-DcdRl_ta.js +2 -0
  33. package/dist/chunks/FilePreviewView-DcdRl_ta.js.map +1 -0
  34. package/dist/chunks/FormView-CmBuwKGD.js +2 -0
  35. package/dist/chunks/FormView-CmBuwKGD.js.map +1 -0
  36. package/dist/chunks/FormView-DqUBMPJ9.js +5054 -0
  37. package/dist/chunks/FormView-DqUBMPJ9.js.map +1 -0
  38. package/dist/chunks/MetricsChart-CM4CI6eA.js +2095 -0
  39. package/dist/chunks/MetricsChart-CM4CI6eA.js.map +1 -0
  40. package/dist/chunks/MetricsChart-CPidSMaN.js +2 -0
  41. package/dist/chunks/MetricsChart-CPidSMaN.js.map +1 -0
  42. package/dist/chunks/PDFViewer-BNQlnS83.js +2 -0
  43. package/dist/chunks/PDFViewer-BNQlnS83.js.map +1 -0
  44. package/dist/chunks/PDFViewer-Dyo-Oeyd.js +946 -0
  45. package/dist/chunks/PDFViewer-Dyo-Oeyd.js.map +1 -0
  46. package/dist/chunks/Page-B524zSQs.js +351 -0
  47. package/dist/chunks/Page-B524zSQs.js.map +1 -0
  48. package/dist/chunks/Page-BFgj0pAA.js +2 -0
  49. package/dist/chunks/Page-BFgj0pAA.js.map +1 -0
  50. package/dist/chunks/TokenManager-BXNva8Jk.js +287 -0
  51. package/dist/chunks/TokenManager-BXNva8Jk.js.map +1 -0
  52. package/dist/chunks/TokenManager-Bzn4guFm.js +2 -0
  53. package/dist/chunks/TokenManager-Bzn4guFm.js.map +1 -0
  54. package/dist/chunks/TopNav-D3I3_25f.js +371 -0
  55. package/dist/chunks/TopNav-D3I3_25f.js.map +1 -0
  56. package/dist/chunks/TopNav-MDjL4kV0.js +2 -0
  57. package/dist/chunks/TopNav-MDjL4kV0.js.map +1 -0
  58. package/dist/chunks/User-BalfYTEF.js +3 -0
  59. package/dist/chunks/User-BalfYTEF.js.map +1 -0
  60. package/dist/chunks/User-DwIT-CTQ.js +1937 -0
  61. package/dist/chunks/User-DwIT-CTQ.js.map +1 -0
  62. package/dist/chunks/WebApp-B6mgbNn2.js +4767 -0
  63. package/dist/chunks/WebApp-B6mgbNn2.js.map +1 -0
  64. package/dist/chunks/WebApp-DqDowtkl.js +2 -0
  65. package/dist/chunks/WebApp-DqDowtkl.js.map +1 -0
  66. package/dist/chunks/WebSocketClient-D6i85jl2.js +2 -0
  67. package/dist/chunks/WebSocketClient-D6i85jl2.js.map +1 -0
  68. package/dist/chunks/WebSocketClient-Dvl3AYx1.js +297 -0
  69. package/dist/chunks/WebSocketClient-Dvl3AYx1.js.map +1 -0
  70. package/dist/core.css +1181 -0
  71. package/dist/css/web-mojo.css +17 -0
  72. package/dist/css-manifest.json +6 -0
  73. package/dist/docit.cjs.js +2 -0
  74. package/dist/docit.cjs.js.map +1 -0
  75. package/dist/docit.es.js +959 -0
  76. package/dist/docit.es.js.map +1 -0
  77. package/dist/index.cjs.js +2 -0
  78. package/dist/index.cjs.js.map +1 -0
  79. package/dist/index.es.js +2681 -0
  80. package/dist/index.es.js.map +1 -0
  81. package/dist/lightbox.cjs.js +2 -0
  82. package/dist/lightbox.cjs.js.map +1 -0
  83. package/dist/lightbox.css +606 -0
  84. package/dist/lightbox.es.js +3737 -0
  85. package/dist/lightbox.es.js.map +1 -0
  86. package/dist/loader.es.js +115 -0
  87. package/dist/loader.umd.js +85 -0
  88. package/dist/portal.css +2446 -0
  89. package/dist/table.css +639 -0
  90. package/dist/toast.css +181 -0
  91. package/package.json +179 -0
@@ -0,0 +1,946 @@
1
+ import { V as View } from "./WebApp-B6mgbNn2.js";
2
+ import Dialog from "./Dialog-DSlctbon.js";
3
+ class LightboxGallery extends View {
4
+ constructor(options = {}) {
5
+ super({
6
+ ...options,
7
+ className: `lightbox-gallery ${options.className || ""}`,
8
+ tagName: "div"
9
+ });
10
+ const rawImages = Array.isArray(options.images) ? options.images : [options.images || options.src].filter(Boolean);
11
+ this.images = rawImages.map((img) => {
12
+ if (typeof img === "string") {
13
+ return { src: img, alt: "" };
14
+ }
15
+ return { src: img.src, alt: img.alt || "" };
16
+ });
17
+ this.currentIndex = options.startIndex || 0;
18
+ this.showNavigation = options.showNavigation !== false && this.images.length > 1;
19
+ this.showCounter = options.showCounter !== false && this.images.length > 1;
20
+ this.allowKeyboard = options.allowKeyboard !== false;
21
+ this.closeOnBackdrop = options.closeOnBackdrop !== false;
22
+ this.fitToScreen = options.fitToScreen !== false;
23
+ this._keyboardHandler = this.handleKeyboard.bind(this);
24
+ this.updateTemplateProperties();
25
+ }
26
+ updateTemplateProperties() {
27
+ this.currentImage = this.images[this.currentIndex] || { src: "", alt: "" };
28
+ this.currentNumber = this.currentIndex + 1;
29
+ this.total = this.images.length;
30
+ this.isFirst = this.currentIndex === 0;
31
+ this.isLast = this.currentIndex === this.images.length - 1;
32
+ this.imageStyle = this.fitToScreen ? "width: 90vw; max-height: 100%; object-fit: contain; user-select: none; cursor: zoom-in;" : "max-width: none; max-height: none; user-select: none; cursor: zoom-out;";
33
+ this.containerStyle = this.fitToScreen ? "" : "overflow: auto;";
34
+ this.modeIndicator = this.fitToScreen ? "Fit to Screen" : "Original Size";
35
+ }
36
+ async getTemplate() {
37
+ this.images[this.currentIndex];
38
+ this.images.length > 1;
39
+ return `
40
+ <div class="lightbox-overlay position-fixed top-0 start-0 w-100 h-100 d-flex align-items-center justify-content-center"
41
+ style="background: rgba(0,0,0,0.9); z-index: 9999;"
42
+ data-action="backdrop-click">
43
+
44
+ <!-- Close button -->
45
+ <button type="button" class="btn-close btn-close-white position-absolute top-0 end-0 m-4"
46
+ data-action="close"
47
+ style="z-index: 10001;"
48
+ title="Close"></button>
49
+
50
+ <!-- Counter -->
51
+ {{#showCounter}}
52
+ <div class="lightbox-counter position-absolute top-0 start-50 translate-middle-x mt-4 text-white"
53
+ style="z-index: 10001; font-size: 1.1rem;">
54
+ {{currentNumber}} of {{total}}
55
+ </div>
56
+ {{/showCounter}}
57
+
58
+ <!-- Mode Indicator -->
59
+ <div class="lightbox-mode-indicator position-absolute bottom-0 start-50 translate-middle-x mb-4 text-white bg-dark bg-opacity-75 px-3 py-2 rounded"
60
+ style="z-index: 10001; font-size: 0.9rem;">
61
+ {{modeIndicator}} • Click image to toggle
62
+ </div>
63
+
64
+ <!-- Navigation -->
65
+ {{#showNavigation}}
66
+ <button type="button" class="btn btn-light btn-lg position-absolute start-0 top-50 translate-middle-y ms-4"
67
+ data-action="prev"
68
+ style="z-index: 10001;"
69
+ title="Previous"
70
+ {{#isFirst}}disabled{{/isFirst}}>
71
+ <i class="bi bi-chevron-left"></i>
72
+ </button>
73
+
74
+ <button type="button" class="btn btn-light btn-lg position-absolute end-0 top-50 translate-middle-y me-4"
75
+ data-action="next"
76
+ style="z-index: 10001;"
77
+ title="Next"
78
+ {{#isLast}}disabled{{/isLast}}>
79
+ <i class="bi bi-chevron-right"></i>
80
+ </button>
81
+ {{/showNavigation}}
82
+
83
+ <!-- Image container -->
84
+ <div class="lightbox-image-container w-100 h-100 d-flex align-items-center justify-content-center p-5"
85
+ style="{{containerStyle}}">
86
+ {{#currentImage}}
87
+ <img src="{{src}}"
88
+ alt="{{alt}}"
89
+ class="lightbox-image img-fluid"
90
+ style="{{imageStyle}}"
91
+ data-action="image-click">
92
+ {{/currentImage}}
93
+ </div>
94
+
95
+ <!-- Loading spinner -->
96
+ <div class="lightbox-loading position-absolute top-50 start-50 translate-middle text-white"
97
+ style="display: none;">
98
+ <div class="spinner-border" role="status">
99
+ <span class="visually-hidden">Loading...</span>
100
+ </div>
101
+ </div>
102
+ </div>
103
+ `;
104
+ }
105
+ async onAfterRender() {
106
+ document.body.appendChild(this.element);
107
+ document.body.style.overflow = "hidden";
108
+ if (this.allowKeyboard) {
109
+ document.addEventListener("keydown", this._keyboardHandler);
110
+ }
111
+ this.preloadAdjacentImages();
112
+ }
113
+ // Action handlers
114
+ async handleActionClose() {
115
+ this.close();
116
+ }
117
+ async handleActionBackdropClick(e) {
118
+ if (this.closeOnBackdrop && e.target === e.currentTarget) {
119
+ this.close();
120
+ }
121
+ }
122
+ async handleActionPrev() {
123
+ this.showPrevious();
124
+ }
125
+ async handleActionNext() {
126
+ this.showNext();
127
+ }
128
+ async handleActionImageClick() {
129
+ this.toggleImageMode();
130
+ }
131
+ // Navigation methods
132
+ showPrevious() {
133
+ if (this.currentIndex > 0) {
134
+ this.currentIndex--;
135
+ this.updateImage();
136
+ this.preloadAdjacentImages();
137
+ }
138
+ }
139
+ showNext() {
140
+ if (this.currentIndex < this.images.length - 1) {
141
+ this.currentIndex++;
142
+ this.updateImage();
143
+ this.preloadAdjacentImages();
144
+ }
145
+ }
146
+ goToImage(index) {
147
+ if (index >= 0 && index < this.images.length && index !== this.currentIndex) {
148
+ this.currentIndex = index;
149
+ this.updateImage();
150
+ this.preloadAdjacentImages();
151
+ }
152
+ }
153
+ // Update image without full re-render
154
+ async updateImage() {
155
+ const currentImage = this.images[this.currentIndex];
156
+ const imgElement = this.element.querySelector(".lightbox-image");
157
+ const counterElement = this.element.querySelector(".lightbox-counter");
158
+ const prevBtn = this.element.querySelector('[data-action="prev"]');
159
+ const nextBtn = this.element.querySelector('[data-action="next"]');
160
+ if (imgElement) {
161
+ this.showLoading();
162
+ imgElement.src = currentImage.src;
163
+ imgElement.alt = currentImage.alt;
164
+ await this.waitForImageLoad(imgElement);
165
+ this.hideLoading();
166
+ }
167
+ if (counterElement) {
168
+ counterElement.textContent = `${this.currentIndex + 1} of ${this.images.length}`;
169
+ }
170
+ if (prevBtn) {
171
+ prevBtn.disabled = this.currentIndex === 0;
172
+ }
173
+ if (nextBtn) {
174
+ nextBtn.disabled = this.currentIndex === this.images.length - 1;
175
+ }
176
+ this.updateTemplateProperties();
177
+ this.updateImageDisplay();
178
+ const eventBus = this.getApp()?.events;
179
+ if (eventBus) {
180
+ eventBus.emit("lightbox:image-changed", {
181
+ gallery: this,
182
+ index: this.currentIndex,
183
+ image: currentImage
184
+ });
185
+ }
186
+ }
187
+ showLoading() {
188
+ const loading = this.element.querySelector(".lightbox-loading");
189
+ if (loading) {
190
+ loading.style.display = "block";
191
+ }
192
+ }
193
+ hideLoading() {
194
+ const loading = this.element.querySelector(".lightbox-loading");
195
+ if (loading) {
196
+ loading.style.display = "none";
197
+ }
198
+ }
199
+ waitForImageLoad(imgElement) {
200
+ return new Promise((resolve) => {
201
+ if (imgElement.complete) {
202
+ resolve();
203
+ } else {
204
+ imgElement.onload = resolve;
205
+ imgElement.onerror = resolve;
206
+ }
207
+ });
208
+ }
209
+ // Preload adjacent images for smooth navigation
210
+ preloadAdjacentImages() {
211
+ const preloadIndexes = [];
212
+ if (this.currentIndex > 0) {
213
+ preloadIndexes.push(this.currentIndex - 1);
214
+ }
215
+ if (this.currentIndex < this.images.length - 1) {
216
+ preloadIndexes.push(this.currentIndex + 1);
217
+ }
218
+ preloadIndexes.forEach((index) => {
219
+ const image = this.images[index];
220
+ const preloadImg = new Image();
221
+ preloadImg.src = image.src;
222
+ });
223
+ }
224
+ // Keyboard navigation
225
+ handleKeyboard(e) {
226
+ switch (e.key) {
227
+ case "Escape":
228
+ e.preventDefault();
229
+ this.close();
230
+ break;
231
+ case "ArrowLeft":
232
+ e.preventDefault();
233
+ this.showPrevious();
234
+ break;
235
+ case "ArrowRight":
236
+ e.preventDefault();
237
+ this.showNext();
238
+ break;
239
+ case "Home":
240
+ e.preventDefault();
241
+ this.goToImage(0);
242
+ break;
243
+ case "End":
244
+ e.preventDefault();
245
+ this.goToImage(this.images.length - 1);
246
+ break;
247
+ }
248
+ }
249
+ // Toggle between fit-to-screen and original size
250
+ toggleImageMode() {
251
+ this.fitToScreen = !this.fitToScreen;
252
+ this.updateTemplateProperties();
253
+ this.updateImageDisplay();
254
+ const eventBus = this.getApp()?.events;
255
+ if (eventBus) {
256
+ eventBus.emit("lightbox:mode-changed", {
257
+ gallery: this,
258
+ fitToScreen: this.fitToScreen
259
+ });
260
+ }
261
+ }
262
+ // Update image display without full re-render
263
+ updateImageDisplay() {
264
+ const imgElement = this.element.querySelector(".lightbox-image");
265
+ const containerElement = this.element.querySelector(".lightbox-image-container");
266
+ const indicatorElement = this.element.querySelector(".lightbox-mode-indicator");
267
+ if (imgElement) {
268
+ if (this.fitToScreen) {
269
+ imgElement.style.maxWidth = "100%";
270
+ imgElement.style.maxHeight = "100%";
271
+ imgElement.style.objectFit = "contain";
272
+ imgElement.style.cursor = "zoom-in";
273
+ } else {
274
+ imgElement.style.maxWidth = "none";
275
+ imgElement.style.maxHeight = "none";
276
+ imgElement.style.objectFit = "none";
277
+ imgElement.style.cursor = "zoom-out";
278
+ }
279
+ imgElement.style.userSelect = "none";
280
+ }
281
+ if (containerElement) {
282
+ containerElement.style.overflow = this.fitToScreen ? "" : "auto";
283
+ }
284
+ if (indicatorElement) {
285
+ indicatorElement.textContent = `${this.modeIndicator} • Click image to toggle`;
286
+ }
287
+ }
288
+ // Close lightbox
289
+ close() {
290
+ const eventBus = this.getApp()?.events;
291
+ if (eventBus) {
292
+ eventBus.emit("lightbox:closed", { gallery: this });
293
+ }
294
+ this.destroy();
295
+ }
296
+ async onBeforeDestroy() {
297
+ document.body.style.overflow = "";
298
+ if (this.allowKeyboard) {
299
+ document.removeEventListener("keydown", this._keyboardHandler);
300
+ }
301
+ if (this.element.parentNode === document.body) {
302
+ document.body.removeChild(this.element);
303
+ }
304
+ }
305
+ // Static method to show lightbox
306
+ static show(images, options = {}) {
307
+ const lightbox = new LightboxGallery({
308
+ images,
309
+ ...options
310
+ });
311
+ lightbox.render().then(() => {
312
+ lightbox.mount();
313
+ });
314
+ return lightbox;
315
+ }
316
+ }
317
+ window.LightboxGallery = LightboxGallery;
318
+ class PDFViewer extends View {
319
+ constructor(options = {}) {
320
+ super({
321
+ ...options,
322
+ className: `pdf-viewer ${options.className || ""}`,
323
+ tagName: "div"
324
+ });
325
+ this.pdfUrl = options.pdfUrl || options.src || "";
326
+ this.title = options.title || "PDF Document";
327
+ this.pdfDoc = null;
328
+ this.currentPage = 1;
329
+ this.totalPages = 0;
330
+ this.pageRendering = false;
331
+ this.pageNumPending = null;
332
+ this.scale = 1;
333
+ this.minScale = 0.25;
334
+ this.maxScale = 5;
335
+ this.scaleStep = 0.25;
336
+ this.fitMode = "page";
337
+ this.canvas = null;
338
+ this.ctx = null;
339
+ this.showControls = options.showControls !== false;
340
+ this.allowZoom = options.allowZoom !== false;
341
+ this.allowNavigation = options.allowNavigation !== false;
342
+ this.showPageNumbers = options.showPageNumbers !== false;
343
+ this.isLoaded = false;
344
+ this.isLoading = false;
345
+ this.pdfjsWorkerPath = options.pdfjsWorkerPath || "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.worker.min.js";
346
+ this.pdfjsCMapUrl = options.pdfjsCMapUrl || "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/cmaps/";
347
+ this.canvasContainer = null;
348
+ this.controlsElement = null;
349
+ this.statusElement = null;
350
+ this.pageInput = null;
351
+ }
352
+ async getTemplate() {
353
+ return `
354
+ <div class="pdf-viewer-container">
355
+ {{#showControls}}
356
+ <div class="pdf-viewer-toolbar" data-container="toolbar">
357
+ <div class="btn-toolbar" role="toolbar">
358
+ <!-- Navigation Controls -->
359
+ {{#allowNavigation}}
360
+ <div class="btn-group me-2" role="group" aria-label="Navigation">
361
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="first-page" title="First Page">
362
+ <i class="bi bi-chevron-double-left"></i>
363
+ </button>
364
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="prev-page" title="Previous Page">
365
+ <i class="bi bi-chevron-left"></i>
366
+ </button>
367
+
368
+ {{#showPageNumbers}}
369
+ <div class="input-group input-group-sm" style="width: 120px;">
370
+ <input type="number" class="form-control text-center page-input" min="1" value="1" data-change-action="page-input">
371
+ <span class="input-group-text page-total">/ 0</span>
372
+ </div>
373
+ {{/showPageNumbers}}
374
+
375
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="next-page" title="Next Page">
376
+ <i class="bi bi-chevron-right"></i>
377
+ </button>
378
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="last-page" title="Last Page">
379
+ <i class="bi bi-chevron-double-right"></i>
380
+ </button>
381
+ </div>
382
+ {{/allowNavigation}}
383
+
384
+ <!-- Zoom Controls -->
385
+ {{#allowZoom}}
386
+ <div class="btn-group me-2" role="group" aria-label="Zoom">
387
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-out" title="Zoom Out">
388
+ <i class="bi bi-zoom-out"></i>
389
+ </button>
390
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="zoom-in" title="Zoom In">
391
+ <i class="bi bi-zoom-in"></i>
392
+ </button>
393
+
394
+ <div class="btn-group" role="group">
395
+ <button type="button" class="btn btn-outline-secondary btn-sm dropdown-toggle" data-bs-toggle="dropdown" title="Fit">
396
+ <i class="bi bi-arrows-fullscreen"></i>
397
+ </button>
398
+ <ul class="dropdown-menu">
399
+ <li><a class="dropdown-item" href="#" data-action="fit-page">Fit Page</a></li>
400
+ <li><a class="dropdown-item" href="#" data-action="fit-width">Fit Width</a></li>
401
+ <li><a class="dropdown-item" href="#" data-action="actual-size">Actual Size</a></li>
402
+ </ul>
403
+ </div>
404
+ </div>
405
+ {{/allowZoom}}
406
+
407
+ <!-- Utility Controls -->
408
+ <div class="btn-group me-2" role="group" aria-label="Utilities">
409
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="download" title="Download">
410
+ <i class="bi bi-download"></i>
411
+ </button>
412
+ <button type="button" class="btn btn-outline-secondary btn-sm" data-action="print" title="Print">
413
+ <i class="bi bi-printer"></i>
414
+ </button>
415
+ </div>
416
+ </div>
417
+ </div>
418
+ {{/showControls}}
419
+
420
+ <!-- PDF Content Area -->
421
+ <div class="pdf-viewer-content" data-container="content">
422
+ <div class="pdf-canvas-container" data-container="canvasContainer">
423
+ <canvas class="pdf-canvas" data-container="canvas"></canvas>
424
+ </div>
425
+
426
+ <div class="pdf-viewer-overlay">
427
+ <div class="pdf-viewer-loading">
428
+ <div class="spinner-border text-primary" role="status">
429
+ <span class="visually-hidden">Loading PDF...</span>
430
+ </div>
431
+ <div class="mt-2">Loading PDF...</div>
432
+ </div>
433
+ </div>
434
+
435
+ <div class="pdf-viewer-error" style="display: none;">
436
+ <div class="alert alert-danger" role="alert">
437
+ <i class="bi bi-exclamation-triangle"></i>
438
+ <strong>Error:</strong> <span class="error-message">Failed to load PDF</span>
439
+ </div>
440
+ </div>
441
+ </div>
442
+
443
+ <!-- Status Bar -->
444
+ <div class="pdf-viewer-status" data-container="status">
445
+ <small class="text-muted">
446
+ <span class="current-page">0</span> of <span class="total-pages">0</span> pages |
447
+ <span class="zoom-level">100%</span> |
448
+ <span class="document-title">{{title}}</span>
449
+ </small>
450
+ </div>
451
+ </div>
452
+ `;
453
+ }
454
+ get() {
455
+ return {
456
+ pdfUrl: this.pdfUrl,
457
+ title: this.title,
458
+ showControls: this.showControls,
459
+ allowZoom: this.allowZoom,
460
+ allowNavigation: this.allowNavigation,
461
+ showPageNumbers: this.showPageNumbers
462
+ };
463
+ }
464
+ async onAfterRender() {
465
+ this.canvas = this.element.querySelector(".pdf-canvas");
466
+ this.canvasContainer = this.element.querySelector(".pdf-canvas-container");
467
+ this.controlsElement = this.element.querySelector(".pdf-viewer-toolbar");
468
+ this.statusElement = this.element.querySelector(".pdf-viewer-status");
469
+ this.pageInput = this.element.querySelector(".page-input");
470
+ this.overlayElement = this.element.querySelector(".pdf-viewer-overlay");
471
+ this.errorElement = this.element.querySelector(".pdf-viewer-error");
472
+ if (this.canvas) {
473
+ this.ctx = this.canvas.getContext("2d");
474
+ }
475
+ this.setupEssentialEventListeners();
476
+ await this.initializePDFJS();
477
+ if (this.pdfUrl) {
478
+ await this.loadPDF();
479
+ }
480
+ }
481
+ setupEssentialEventListeners() {
482
+ const keydownHandler = (e) => this.handleKeyDown(e);
483
+ document.addEventListener("keydown", keydownHandler);
484
+ let resizeObserver = null;
485
+ if (this.canvasContainer) {
486
+ resizeObserver = new ResizeObserver(() => {
487
+ if (this.fitMode !== "auto") {
488
+ this.applyFitMode();
489
+ }
490
+ });
491
+ resizeObserver.observe(this.canvasContainer);
492
+ }
493
+ this._essentialListeners = [
494
+ { el: document, type: "keydown", fn: keydownHandler }
495
+ ];
496
+ this._resizeObserver = resizeObserver;
497
+ }
498
+ async initializePDFJS() {
499
+ try {
500
+ if (typeof window.pdfjsLib === "undefined") {
501
+ await this.loadPDFJSLibrary();
502
+ }
503
+ window.pdfjsLib.GlobalWorkerOptions.workerSrc = this.pdfjsWorkerPath;
504
+ return true;
505
+ } catch (error) {
506
+ console.error("Failed to initialize PDF.js:", error);
507
+ this.showError("Failed to initialize PDF viewer");
508
+ return false;
509
+ }
510
+ }
511
+ async loadPDFJSLibrary() {
512
+ return new Promise((resolve, reject) => {
513
+ const script = document.createElement("script");
514
+ script.src = "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.0.379/pdf.min.js";
515
+ script.onload = resolve;
516
+ script.onerror = reject;
517
+ document.head.appendChild(script);
518
+ });
519
+ }
520
+ // Action Handlers
521
+ async handleActionFirstPage() {
522
+ await this.goToPage(1);
523
+ }
524
+ async handleActionPrevPage() {
525
+ await this.goToPage(this.currentPage - 1);
526
+ }
527
+ async handleActionNextPage() {
528
+ await this.goToPage(this.currentPage + 1);
529
+ }
530
+ async handleActionLastPage() {
531
+ await this.goToPage(this.totalPages);
532
+ }
533
+ async onChangePageInput(event, element) {
534
+ const pageNumber = parseInt(element.value, 10);
535
+ if (pageNumber >= 1 && pageNumber <= this.totalPages) {
536
+ await this.goToPage(pageNumber);
537
+ } else {
538
+ element.value = this.currentPage;
539
+ }
540
+ }
541
+ async handleActionZoomIn() {
542
+ this.setScale(this.scale + this.scaleStep);
543
+ }
544
+ async handleActionZoomOut() {
545
+ this.setScale(this.scale - this.scaleStep);
546
+ }
547
+ async handleActionFitPage() {
548
+ this.setFitMode("page");
549
+ }
550
+ async handleActionFitWidth() {
551
+ this.setFitMode("width");
552
+ }
553
+ async handleActionActualSize() {
554
+ this.setScale(1);
555
+ this.fitMode = "auto";
556
+ }
557
+ async handleActionDownload() {
558
+ this.downloadPDF();
559
+ }
560
+ async handleActionPrint() {
561
+ window.print();
562
+ }
563
+ // PDF Loading
564
+ async loadPDF() {
565
+ if (!this.pdfUrl || !window.pdfjsLib) {
566
+ this.showError("PDF URL or PDF.js library not available");
567
+ return false;
568
+ }
569
+ this.isLoading = true;
570
+ this.showLoading();
571
+ try {
572
+ const loadingTask = window.pdfjsLib.getDocument({
573
+ url: this.pdfUrl,
574
+ cMapUrl: this.pdfjsCMapUrl,
575
+ cMapPacked: true
576
+ });
577
+ this.pdfDoc = await loadingTask.promise;
578
+ this.totalPages = this.pdfDoc.numPages;
579
+ this.currentPage = 1;
580
+ this.updatePageControls();
581
+ this.updateStatus();
582
+ await this.renderPage(1);
583
+ this.isLoaded = true;
584
+ this.isLoading = false;
585
+ this.element.classList.add("loaded");
586
+ this.hideLoading();
587
+ this.applyFitMode();
588
+ const eventBus = this.getApp()?.events;
589
+ if (eventBus) {
590
+ eventBus.emit("pdfviewer:loaded", {
591
+ viewer: this,
592
+ pdfUrl: this.pdfUrl,
593
+ totalPages: this.totalPages
594
+ });
595
+ }
596
+ return true;
597
+ } catch (error) {
598
+ console.error("Error loading PDF:", error);
599
+ this.isLoading = false;
600
+ this.showError("Failed to load PDF document");
601
+ const eventBus = this.getApp()?.events;
602
+ if (eventBus) {
603
+ eventBus.emit("pdfviewer:error", {
604
+ viewer: this,
605
+ pdfUrl: this.pdfUrl,
606
+ error: error.message
607
+ });
608
+ }
609
+ return false;
610
+ }
611
+ }
612
+ async renderPage(pageNumber) {
613
+ if (this.pageRendering) {
614
+ this.pageNumPending = pageNumber;
615
+ return;
616
+ }
617
+ if (!this.pdfDoc || !this.canvas || !this.ctx) {
618
+ return;
619
+ }
620
+ this.pageRendering = true;
621
+ this.currentPage = pageNumber;
622
+ try {
623
+ const page = await this.pdfDoc.getPage(pageNumber);
624
+ const viewport = page.getViewport({ scale: this.scale });
625
+ this.canvas.height = viewport.height;
626
+ this.canvas.width = viewport.width;
627
+ this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
628
+ const renderContext = {
629
+ canvasContext: this.ctx,
630
+ viewport
631
+ };
632
+ const renderTask = page.render(renderContext);
633
+ await renderTask.promise;
634
+ this.pageRendering = false;
635
+ if (this.pageNumPending !== null) {
636
+ const pendingPage = this.pageNumPending;
637
+ this.pageNumPending = null;
638
+ await this.renderPage(pendingPage);
639
+ }
640
+ this.updatePageControls();
641
+ this.updateStatus();
642
+ const eventBus = this.getApp()?.events;
643
+ if (eventBus) {
644
+ eventBus.emit("pdfviewer:page-changed", {
645
+ viewer: this,
646
+ currentPage: this.currentPage,
647
+ totalPages: this.totalPages
648
+ });
649
+ }
650
+ } catch (error) {
651
+ console.error("Error rendering page:", error);
652
+ this.pageRendering = false;
653
+ this.showError("Failed to render PDF page");
654
+ }
655
+ }
656
+ // Navigation
657
+ async goToPage(pageNumber) {
658
+ if (!this.pdfDoc || pageNumber < 1 || pageNumber > this.totalPages) {
659
+ return;
660
+ }
661
+ await this.renderPage(pageNumber);
662
+ }
663
+ // Zoom and Fit
664
+ setScale(scale) {
665
+ const oldScale = this.scale;
666
+ this.scale = Math.max(this.minScale, Math.min(this.maxScale, scale));
667
+ this.fitMode = "auto";
668
+ if (this.isLoaded) {
669
+ this.renderPage(this.currentPage);
670
+ }
671
+ const eventBus = this.getApp()?.events;
672
+ if (eventBus && oldScale !== this.scale) {
673
+ eventBus.emit("pdfviewer:scale-changed", {
674
+ viewer: this,
675
+ oldScale,
676
+ newScale: this.scale
677
+ });
678
+ }
679
+ }
680
+ setFitMode(mode) {
681
+ const oldMode = this.fitMode;
682
+ this.fitMode = mode;
683
+ this.applyFitMode();
684
+ const eventBus = this.getApp()?.events;
685
+ if (eventBus) {
686
+ eventBus.emit("pdfviewer:fit-mode-changed", {
687
+ viewer: this,
688
+ oldMode,
689
+ newMode: mode
690
+ });
691
+ }
692
+ }
693
+ applyFitMode() {
694
+ if (!this.isLoaded || !this.pdfDoc || !this.canvasContainer) {
695
+ return;
696
+ }
697
+ this.pdfDoc.getPage(this.currentPage).then((page) => {
698
+ const containerRect = this.canvasContainer.getBoundingClientRect();
699
+ const viewport = page.getViewport({ scale: 1 });
700
+ let newScale;
701
+ if (this.fitMode === "page") {
702
+ const scaleX = (containerRect.width - 40) / viewport.width;
703
+ const scaleY = (containerRect.height - 40) / viewport.height;
704
+ newScale = Math.min(scaleX, scaleY);
705
+ } else if (this.fitMode === "width") {
706
+ newScale = (containerRect.width - 40) / viewport.width;
707
+ } else {
708
+ return;
709
+ }
710
+ this.scale = Math.max(this.minScale, Math.min(this.maxScale, newScale));
711
+ this.renderPage(this.currentPage);
712
+ });
713
+ }
714
+ // Event Handlers
715
+ handleKeyDown(e) {
716
+ if (e.target.tagName === "INPUT" && e.target !== this.pageInput) {
717
+ return;
718
+ }
719
+ switch (e.key) {
720
+ case "ArrowLeft":
721
+ case "PageUp":
722
+ e.preventDefault();
723
+ this.goToPage(this.currentPage - 1);
724
+ break;
725
+ case "ArrowRight":
726
+ case "PageDown":
727
+ e.preventDefault();
728
+ this.goToPage(this.currentPage + 1);
729
+ break;
730
+ case "Home":
731
+ e.preventDefault();
732
+ this.goToPage(1);
733
+ break;
734
+ case "End":
735
+ e.preventDefault();
736
+ this.goToPage(this.totalPages);
737
+ break;
738
+ case "+":
739
+ case "=":
740
+ if (e.ctrlKey || e.metaKey) {
741
+ e.preventDefault();
742
+ this.setScale(this.scale + this.scaleStep);
743
+ }
744
+ break;
745
+ case "-":
746
+ if (e.ctrlKey || e.metaKey) {
747
+ e.preventDefault();
748
+ this.setScale(this.scale - this.scaleStep);
749
+ }
750
+ break;
751
+ case "0":
752
+ if (e.ctrlKey || e.metaKey) {
753
+ e.preventDefault();
754
+ this.setFitMode("page");
755
+ }
756
+ break;
757
+ }
758
+ }
759
+ // UI Updates
760
+ updatePageControls() {
761
+ if (this.pageInput) {
762
+ this.pageInput.value = this.currentPage;
763
+ }
764
+ const pageTotalElement = this.element.querySelector(".page-total");
765
+ if (pageTotalElement) {
766
+ pageTotalElement.textContent = `/ ${this.totalPages}`;
767
+ }
768
+ const firstBtn = this.element.querySelector('[data-action="first-page"]');
769
+ const prevBtn = this.element.querySelector('[data-action="prev-page"]');
770
+ const nextBtn = this.element.querySelector('[data-action="next-page"]');
771
+ const lastBtn = this.element.querySelector('[data-action="last-page"]');
772
+ if (firstBtn) firstBtn.disabled = this.currentPage <= 1;
773
+ if (prevBtn) prevBtn.disabled = this.currentPage <= 1;
774
+ if (nextBtn) nextBtn.disabled = this.currentPage >= this.totalPages;
775
+ if (lastBtn) lastBtn.disabled = this.currentPage >= this.totalPages;
776
+ const zoomInBtn = this.element.querySelector('[data-action="zoom-in"]');
777
+ const zoomOutBtn = this.element.querySelector('[data-action="zoom-out"]');
778
+ if (zoomInBtn) zoomInBtn.disabled = this.scale >= this.maxScale;
779
+ if (zoomOutBtn) zoomOutBtn.disabled = this.scale <= this.minScale;
780
+ }
781
+ updateStatus() {
782
+ if (!this.statusElement) return;
783
+ const currentPageElement = this.statusElement.querySelector(".current-page");
784
+ const totalPagesElement = this.statusElement.querySelector(".total-pages");
785
+ const zoomLevelElement = this.statusElement.querySelector(".zoom-level");
786
+ if (currentPageElement) {
787
+ currentPageElement.textContent = this.currentPage;
788
+ }
789
+ if (totalPagesElement) {
790
+ totalPagesElement.textContent = this.totalPages;
791
+ }
792
+ if (zoomLevelElement) {
793
+ zoomLevelElement.textContent = `${Math.round(this.scale * 100)}%`;
794
+ }
795
+ }
796
+ // Utility Methods
797
+ showLoading() {
798
+ if (this.overlayElement) {
799
+ this.overlayElement.style.display = "flex";
800
+ }
801
+ }
802
+ hideLoading() {
803
+ if (this.overlayElement) {
804
+ this.overlayElement.style.display = "none";
805
+ }
806
+ }
807
+ showError(message) {
808
+ this.hideLoading();
809
+ if (this.errorElement) {
810
+ const errorMessageElement = this.errorElement.querySelector(".error-message");
811
+ if (errorMessageElement) {
812
+ errorMessageElement.textContent = message;
813
+ }
814
+ this.errorElement.style.display = "block";
815
+ }
816
+ console.error("PDF Viewer Error:", message);
817
+ }
818
+ downloadPDF() {
819
+ if (!this.pdfUrl) return;
820
+ const link = document.createElement("a");
821
+ link.href = this.pdfUrl;
822
+ link.download = this.title + ".pdf";
823
+ link.target = "_blank";
824
+ link.click();
825
+ }
826
+ // Public API
827
+ setPDF(pdfUrl, title = "") {
828
+ const oldPdfUrl = this.pdfUrl;
829
+ this.pdfUrl = pdfUrl;
830
+ this.title = title || "PDF Document";
831
+ this.isLoaded = false;
832
+ this.element.classList.remove("loaded");
833
+ if (this.pdfDoc) {
834
+ this.pdfDoc.destroy();
835
+ this.pdfDoc = null;
836
+ }
837
+ this.currentPage = 1;
838
+ this.totalPages = 0;
839
+ this.scale = 1;
840
+ if (pdfUrl) {
841
+ this.loadPDF();
842
+ }
843
+ const eventBus = this.getApp()?.events;
844
+ if (eventBus) {
845
+ eventBus.emit("pdfviewer:pdf-changed", {
846
+ viewer: this,
847
+ oldPdfUrl,
848
+ newPdfUrl: pdfUrl
849
+ });
850
+ }
851
+ }
852
+ getCurrentPage() {
853
+ return this.currentPage;
854
+ }
855
+ getTotalPages() {
856
+ return this.totalPages;
857
+ }
858
+ getCurrentScale() {
859
+ return this.scale;
860
+ }
861
+ async onBeforeDestroy() {
862
+ if (this.pdfDoc) {
863
+ this.pdfDoc.destroy();
864
+ this.pdfDoc = null;
865
+ }
866
+ this.pageRendering = false;
867
+ this.pageNumPending = null;
868
+ if (this._essentialListeners) {
869
+ this._essentialListeners.forEach(({ el, type, fn }) => {
870
+ if (el) el.removeEventListener(type, fn);
871
+ });
872
+ this._essentialListeners = null;
873
+ }
874
+ if (this._resizeObserver) {
875
+ this._resizeObserver.disconnect();
876
+ this._resizeObserver = null;
877
+ }
878
+ const eventBus = this.getApp()?.events;
879
+ if (eventBus) {
880
+ eventBus.emit("pdfviewer:destroyed", { viewer: this });
881
+ }
882
+ }
883
+ // Static method to show PDF in a fullscreen dialog
884
+ static async showDialog(pdfUrl, options = {}) {
885
+ const {
886
+ title = "PDF Viewer",
887
+ size = "fullscreen",
888
+ showControls = true,
889
+ allowZoom = true,
890
+ allowNavigation = true,
891
+ showPageNumbers = true,
892
+ ...dialogOptions
893
+ } = options;
894
+ const viewer = new PDFViewer({
895
+ pdfUrl,
896
+ title,
897
+ showControls,
898
+ allowZoom,
899
+ allowNavigation,
900
+ showPageNumbers
901
+ });
902
+ const dialog = new Dialog({
903
+ title,
904
+ body: viewer,
905
+ size,
906
+ centered: true,
907
+ backdrop: "static",
908
+ keyboard: true,
909
+ buttons: [
910
+ {
911
+ text: "Download",
912
+ action: "download",
913
+ class: "btn btn-outline-primary"
914
+ },
915
+ {
916
+ text: "Close",
917
+ action: "close",
918
+ class: "btn btn-secondary",
919
+ dismiss: true
920
+ }
921
+ ],
922
+ ...dialogOptions
923
+ });
924
+ await dialog.render();
925
+ document.body.appendChild(dialog.element);
926
+ await dialog.mount();
927
+ dialog.show();
928
+ return new Promise((resolve) => {
929
+ dialog.on("hidden", () => {
930
+ dialog.destroy();
931
+ resolve(viewer);
932
+ });
933
+ dialog.on("action:download", () => {
934
+ viewer.downloadPDF();
935
+ });
936
+ dialog.on("action:close", () => {
937
+ dialog.hide();
938
+ });
939
+ });
940
+ }
941
+ }
942
+ export {
943
+ LightboxGallery as L,
944
+ PDFViewer as P
945
+ };
946
+ //# sourceMappingURL=PDFViewer-Dyo-Oeyd.js.map