zoomora 1.0.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.
@@ -0,0 +1,1146 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
8
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
9
+ var __spreadValues = (a, b) => {
10
+ for (var prop in b || (b = {}))
11
+ if (__hasOwnProp.call(b, prop))
12
+ __defNormalProp(a, prop, b[prop]);
13
+ if (__getOwnPropSymbols)
14
+ for (var prop of __getOwnPropSymbols(b)) {
15
+ if (__propIsEnum.call(b, prop))
16
+ __defNormalProp(a, prop, b[prop]);
17
+ }
18
+ return a;
19
+ };
20
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
21
+ var __commonJS = (cb, mod) => function __require() {
22
+ return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
23
+ };
24
+ var require_zoomora_es = __commonJS({
25
+ "zoomora.es.js"(exports, module) {
26
+ /*!
27
+ * Zoomora Lightbox Plugin v1.0.0
28
+ * A modern, responsive lightbox plugin with zoom, fullscreen, and gallery features
29
+ *
30
+ * Copyright (c) 2025 Faruk Ahmed (FrontTheme)
31
+ * Licensed under MIT License
32
+ */
33
+ (function(global, factory) {
34
+ if (typeof module === "object" && typeof module.exports === "object") {
35
+ module.exports = factory();
36
+ } else if (typeof define === "function" && define.amd) {
37
+ define(factory);
38
+ } else {
39
+ global.Zoomora = factory();
40
+ }
41
+ })(typeof window !== "undefined" ? window : void 0, function() {
42
+ class Zoomora {
43
+ /**
44
+ * Default configuration options
45
+ * @type {Object}
46
+ */
47
+ static get defaults() {
48
+ return {
49
+ selector: "[data-zoomora]",
50
+ showCounter: true,
51
+ showThumbnails: true,
52
+ showFullscreen: true,
53
+ showZoom: true,
54
+ transition: "fade",
55
+ // 'fade' or 'slide'
56
+ useHref: false,
57
+ // Use href instead of data-src
58
+ maxZoomScale: 3,
59
+ // Maximum zoom scale
60
+ zoomStep: 0.1,
61
+ // Zoom step for scroll wheel
62
+ animationDuration: 300,
63
+ // Animation duration in ms
64
+ onOpen: null,
65
+ // Callback when lightbox opens
66
+ onClose: null,
67
+ // Callback when lightbox closes
68
+ onNext: null,
69
+ // Callback when navigating to next item
70
+ onPrev: null,
71
+ // Callback when navigating to previous item
72
+ // Auto-hide controls
73
+ showAutoHideToggle: true,
74
+ // Show auto-hide toggle button
75
+ autoHideDelay: 3e3,
76
+ // Delay in ms before hiding controls
77
+ autoHideEnabled: false
78
+ // Default state of auto-hide
79
+ };
80
+ }
81
+ /**
82
+ * Constructor
83
+ * @param {Object} options - Configuration options
84
+ */
85
+ constructor(options = {}) {
86
+ this.options = __spreadValues(__spreadValues({}, Zoomora.defaults), options);
87
+ this.currentIndex = 0;
88
+ this.items = [];
89
+ this.isZoomed = false;
90
+ this.isFullscreen = false;
91
+ this.isDragging = false;
92
+ this.thumbnailsVisible = true;
93
+ this.startX = 0;
94
+ this.startY = 0;
95
+ this.currentX = 0;
96
+ this.currentY = 0;
97
+ this.currentScale = 1;
98
+ this.isInitialized = false;
99
+ this.eventListeners = /* @__PURE__ */ new Map();
100
+ this.autoHideEnabled = false;
101
+ this.autoHideTimeout = null;
102
+ this.autoHideDelay = 3e3;
103
+ this.isControlsVisible = true;
104
+ this.idleTimer = null;
105
+ this.boundMethods = {
106
+ handleDocumentClick: this.handleDocumentClick.bind(this),
107
+ handleKeydown: this.handleKeydown.bind(this),
108
+ handleFullscreenChange: this.handleFullscreenChange.bind(this),
109
+ handleZoomoraClick: this.handleZoomoraClick.bind(this),
110
+ handleContentClick: this.handleContentClick.bind(this),
111
+ handleMouseDown: this.handleMouseDown.bind(this),
112
+ handleMouseMove: this.handleMouseMove.bind(this),
113
+ handleMouseUp: this.handleMouseUp.bind(this),
114
+ handleTouchStart: this.handleTouchStart.bind(this),
115
+ handleTouchMove: this.handleTouchMove.bind(this),
116
+ handleTouchEnd: this.handleTouchEnd.bind(this)
117
+ };
118
+ this.init();
119
+ }
120
+ /**
121
+ * Initialize the plugin
122
+ */
123
+ init() {
124
+ if (this.isInitialized) {
125
+ console.warn("Zoomora: Plugin already initialized");
126
+ return;
127
+ }
128
+ try {
129
+ this.createLightbox();
130
+ this.bindEvents();
131
+ this.isInitialized = true;
132
+ } catch (error) {
133
+ console.error("Zoomora: Initialization failed", error);
134
+ }
135
+ }
136
+ /**
137
+ * Static method to bind to anchor tags with href
138
+ * @param {string} selector - CSS selector
139
+ * @param {Object} options - Configuration options
140
+ * @returns {Zoomora} - New Zoomora instance
141
+ */
142
+ static bind(selector, options = {}) {
143
+ const enhancedOptions = __spreadProps(__spreadValues({}, options), {
144
+ selector,
145
+ useHref: true
146
+ });
147
+ return new Zoomora(enhancedOptions);
148
+ }
149
+ /**
150
+ * Create lightbox HTML structure
151
+ */
152
+ createLightbox() {
153
+ if (document.getElementById("zoomoraLightbox")) {
154
+ console.warn("Zoomora: Lightbox already exists in DOM");
155
+ return;
156
+ }
157
+ const zoomoraHTML = `
158
+ <div class="zoomora" id="zoomoraLightbox">
159
+ <div class="zoomora-container">
160
+ <div class="zoomora-header" id="zoomoraHeader">
161
+ <div class="zoomora-counter" id="zoomoraCounter">1 / 1</div>
162
+ <div class="zoomora-controls">
163
+ <button class="zoomora-btn" id="zoomoraThumbnailBtn" title="Toggle Thumbnails">
164
+ <i class="zoomorai-thumbnails"></i>
165
+ </button>
166
+ <button class="zoomora-btn" id="zoomoraAutoHideBtn" title="Toggle Auto-Hide Controls">
167
+ <i class="zoomorai-controls-off"></i>
168
+ </button>
169
+ <button class="zoomora-btn" id="zoomoraZoomBtn" title="Zoom">
170
+ <i class="zoomorai-zoom"></i>
171
+ </button>
172
+ <button class="zoomora-btn" id="zoomoraFullscreenBtn" title="Fullscreen">
173
+ <i class="zoomorai-expand"></i>
174
+ </button>
175
+ <button class="zoomora-btn" id="zoomoraCollapseBtn" title="Exit Fullscreen" style="display: none;">
176
+ <i class="zoomorai-collapse"></i>
177
+ </button>
178
+ <button class="zoomora-btn" id="zoomoraCloseBtn" title="Close">
179
+ <i class="zoomorai-close"></i>
180
+ </button>
181
+ </div>
182
+ </div>
183
+
184
+ <div class="zoomora-content" id="zoomoraContent">
185
+ <div class="zoomora-loading" id="zoomoraLoading"></div>
186
+ <div class="zoomora-slides-container" id="zoomoraSlidesContainer"></div>
187
+ </div>
188
+
189
+ <button class="zoomora-nav prev" id="zoomoraPrevBtn">
190
+ <i class="zoomorai-arrow-left"></i>
191
+ </button>
192
+ <button class="zoomora-nav next" id="zoomoraNextBtn">
193
+ <i class="zoomorai-arrow-right"></i>
194
+ </button>
195
+
196
+ <div class="zoomora-caption" id="zoomoraCaption"></div>
197
+ <div class="zoomora-thumbnails" id="zoomoraThumbs"></div>
198
+ </div>
199
+ </div>
200
+ `;
201
+ document.body.insertAdjacentHTML("beforeend", zoomoraHTML);
202
+ this.zoomora = document.getElementById("zoomoraLightbox");
203
+ this.header = document.getElementById("zoomoraHeader");
204
+ this.content = document.getElementById("zoomoraContent");
205
+ this.slidesContainer = document.getElementById("zoomoraSlidesContainer");
206
+ this.counter = document.getElementById("zoomoraCounter");
207
+ this.thumbs = document.getElementById("zoomoraThumbs");
208
+ this.loading = document.getElementById("zoomoraLoading");
209
+ this.caption = document.getElementById("zoomoraCaption");
210
+ this.prevBtn = document.getElementById("zoomoraPrevBtn");
211
+ this.nextBtn = document.getElementById("zoomoraNextBtn");
212
+ if (!this.zoomora || !this.content || !this.counter || !this.thumbs || !this.loading || !this.caption) {
213
+ throw new Error("Failed to create required DOM elements");
214
+ }
215
+ }
216
+ /**
217
+ * Bind event listeners
218
+ */
219
+ bindEvents() {
220
+ this.addEventListener(document, "click", this.boundMethods.handleDocumentClick);
221
+ this.addEventListener(document.getElementById("zoomoraCloseBtn"), "click", () => this.close());
222
+ this.addEventListener(document.getElementById("zoomoraPrevBtn"), "click", () => this.prev());
223
+ this.addEventListener(document.getElementById("zoomoraNextBtn"), "click", () => this.next());
224
+ this.addEventListener(document.getElementById("zoomoraZoomBtn"), "click", () => this.toggleZoom());
225
+ this.addEventListener(document.getElementById("zoomoraFullscreenBtn"), "click", () => this.toggleFullscreen());
226
+ this.addEventListener(document.getElementById("zoomoraCollapseBtn"), "click", () => this.exitFullscreen());
227
+ this.addEventListener(document.getElementById("zoomoraThumbnailBtn"), "click", () => this.toggleThumbnails());
228
+ this.addEventListener(document, "fullscreenchange", this.boundMethods.handleFullscreenChange);
229
+ this.addEventListener(document, "webkitfullscreenchange", this.boundMethods.handleFullscreenChange);
230
+ this.addEventListener(document, "mozfullscreenchange", this.boundMethods.handleFullscreenChange);
231
+ this.addEventListener(document, "MSFullscreenChange", this.boundMethods.handleFullscreenChange);
232
+ this.addEventListener(document, "keydown", this.boundMethods.handleKeydown);
233
+ this.addEventListener(this.zoomora, "click", this.boundMethods.handleZoomoraClick);
234
+ this.addEventListener(this.content, "click", this.boundMethods.handleContentClick);
235
+ this.addEventListener(this.content, "mousedown", this.boundMethods.handleMouseDown);
236
+ this.addEventListener(this.content, "mousemove", this.boundMethods.handleMouseMove);
237
+ this.addEventListener(this.content, "mouseup", this.boundMethods.handleMouseUp);
238
+ this.addEventListener(this.content, "mouseleave", this.boundMethods.handleMouseUp);
239
+ this.addEventListener(this.content, "touchstart", this.boundMethods.handleTouchStart, { passive: false });
240
+ this.addEventListener(this.content, "touchmove", this.boundMethods.handleTouchMove, { passive: false });
241
+ this.addEventListener(this.content, "touchend", this.boundMethods.handleTouchEnd);
242
+ this.addEventListener(document.getElementById("zoomoraAutoHideBtn"), "click", () => this.toggleAutoHide());
243
+ this.addEventListener(this.zoomora, "mousemove", () => this.handleUserActivity());
244
+ this.addEventListener(this.zoomora, "touchstart", () => this.handleUserActivity());
245
+ this.addEventListener(this.zoomora, "keydown", () => this.handleUserActivity());
246
+ }
247
+ /**
248
+ * Add event listener with cleanup tracking
249
+ * @param {Element} element - DOM element
250
+ * @param {string} event - Event type
251
+ * @param {Function} handler - Event handler
252
+ * @param {Object} options - Event options
253
+ */
254
+ addEventListener(element, event, handler, options = {}) {
255
+ if (!element) return;
256
+ element.addEventListener(event, handler, options);
257
+ const key = `${element.constructor.name}_${event}`;
258
+ if (!this.eventListeners.has(key)) {
259
+ this.eventListeners.set(key, []);
260
+ }
261
+ this.eventListeners.get(key).push({ element, event, handler, options });
262
+ }
263
+ /**
264
+ * Handle document click events
265
+ * @param {Event} e - Click event
266
+ */
267
+ handleDocumentClick(e) {
268
+ const target = e.target.closest(this.options.selector);
269
+ if (target) {
270
+ e.preventDefault();
271
+ this.open(target);
272
+ }
273
+ }
274
+ /**
275
+ * Handle keyboard events
276
+ * @param {KeyboardEvent} e - Keyboard event
277
+ */
278
+ handleKeydown(e) {
279
+ if (!this.zoomora.classList.contains("active")) return;
280
+ switch (e.key) {
281
+ case "Escape":
282
+ if (this.isFullscreen) {
283
+ this.exitFullscreen();
284
+ } else {
285
+ this.close();
286
+ }
287
+ break;
288
+ case "ArrowLeft":
289
+ this.prev();
290
+ break;
291
+ case "ArrowRight":
292
+ this.next();
293
+ break;
294
+ case "z":
295
+ case "Z":
296
+ this.toggleZoom();
297
+ break;
298
+ case "f":
299
+ case "F":
300
+ this.toggleFullscreen();
301
+ break;
302
+ case "t":
303
+ case "T":
304
+ this.toggleThumbnails();
305
+ break;
306
+ }
307
+ }
308
+ /**
309
+ * Handle fullscreen change events
310
+ */
311
+ handleFullscreenChange() {
312
+ const isInFullscreen = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
313
+ if (!isInFullscreen && this.isFullscreen) {
314
+ this.isFullscreen = false;
315
+ this.zoomora.classList.remove("fullscreen");
316
+ this.updateButtons();
317
+ }
318
+ }
319
+ /**
320
+ * Handle zoomora container clicks
321
+ * @param {Event} e - Click event
322
+ */
323
+ handleZoomoraClick(e) {
324
+ if (e.target === this.zoomora) {
325
+ this.close();
326
+ }
327
+ }
328
+ /**
329
+ * Handle content clicks
330
+ * @param {Event} e - Click event
331
+ */
332
+ handleContentClick(e) {
333
+ const media = e.target.closest(".zoomora-media");
334
+ if (media && !media.classList.contains("no-zoom") && !this.isDragging) {
335
+ this.toggleZoom();
336
+ }
337
+ }
338
+ /**
339
+ * Handle mouse down events
340
+ * @param {MouseEvent} e - Mouse event
341
+ */
342
+ handleMouseDown(e) {
343
+ this.startDrag(e);
344
+ }
345
+ /**
346
+ * Handle mouse move events
347
+ * @param {MouseEvent} e - Mouse event
348
+ */
349
+ handleMouseMove(e) {
350
+ this.drag(e);
351
+ }
352
+ /**
353
+ * Handle mouse up events
354
+ */
355
+ handleMouseUp() {
356
+ this.endDrag();
357
+ }
358
+ /**
359
+ * Handle touch start events
360
+ * @param {TouchEvent} e - Touch event
361
+ */
362
+ handleTouchStart(e) {
363
+ this.startDrag(e);
364
+ }
365
+ /**
366
+ * Handle touch move events
367
+ * @param {TouchEvent} e - Touch event
368
+ */
369
+ handleTouchMove(e) {
370
+ this.drag(e);
371
+ }
372
+ /**
373
+ * Handle touch end events
374
+ */
375
+ handleTouchEnd() {
376
+ this.endDrag();
377
+ }
378
+ /**
379
+ * Get image source from element
380
+ * @param {Element} item - DOM element
381
+ * @returns {string} - Image source URL
382
+ */
383
+ getImageSource(item) {
384
+ const src = item.dataset.src || item.src || item.getAttribute("src");
385
+ const type = item.dataset.type || "image";
386
+ if (type === "video" && src) {
387
+ return this.convertToEmbedUrl(src);
388
+ }
389
+ return src;
390
+ }
391
+ /**
392
+ * Convert YouTube URLs to embed format
393
+ * @param url
394
+ * @returns {*|string}
395
+ */
396
+ convertToEmbedUrl(url) {
397
+ if (url.includes("youtube.com/embed/")) {
398
+ return url;
399
+ }
400
+ let videoId = null;
401
+ let startTime = null;
402
+ if (url.includes("youtube.com/watch")) {
403
+ const urlParams = new URLSearchParams(url.split("?")[1]);
404
+ videoId = urlParams.get("v");
405
+ const t = urlParams.get("t");
406
+ if (t) {
407
+ startTime = t.replace("s", "");
408
+ }
409
+ } else if (url.includes("youtu.be/")) {
410
+ const parts = url.split("/");
411
+ const videoAndParams = parts[parts.length - 1];
412
+ if (videoAndParams.includes("?")) {
413
+ videoId = videoAndParams.split("?")[0];
414
+ const urlParams = new URLSearchParams(videoAndParams.split("?")[1]);
415
+ const t = urlParams.get("t");
416
+ if (t) startTime = t.replace("s", "");
417
+ } else {
418
+ videoId = videoAndParams;
419
+ }
420
+ }
421
+ if (videoId) {
422
+ let embedUrl = `https://www.youtube.com/embed/${videoId}`;
423
+ const params = new URLSearchParams();
424
+ if (startTime) {
425
+ params.append("start", startTime);
426
+ }
427
+ params.append("rel", "0");
428
+ params.append("modestbranding", "1");
429
+ if (params.toString()) {
430
+ embedUrl += "?" + params.toString();
431
+ }
432
+ return embedUrl;
433
+ }
434
+ return url;
435
+ }
436
+ /**
437
+ * Get thumbnail source from element
438
+ * @param {Element} item - DOM element
439
+ * @returns {string} - Thumbnail source URL
440
+ */
441
+ getThumbnailSource(item) {
442
+ const type = item.dataset.type || "image";
443
+ if (type === "video") {
444
+ return item.src || item.getAttribute("src");
445
+ }
446
+ return item.dataset.src || item.src || item.getAttribute("src");
447
+ }
448
+ /**
449
+ * Get group identifier from element
450
+ * @param {Element} element - DOM element
451
+ * @returns {string} - Group identifier
452
+ */
453
+ getGroup(element) {
454
+ if (this.options.useHref) {
455
+ return element.dataset.lightbox || element.dataset.gallery || "default";
456
+ }
457
+ return element.dataset.zoomora || "default";
458
+ }
459
+ /**
460
+ * Open lightbox with specified element
461
+ * @param {Element} element - DOM element to open
462
+ */
463
+ open(element) {
464
+ if (!element) return;
465
+ const group = this.getGroup(element);
466
+ let groupSelector;
467
+ if (this.options.useHref) {
468
+ if (element.dataset.lightbox) {
469
+ groupSelector = `[data-lightbox="${group}"]`;
470
+ } else if (element.dataset.gallery) {
471
+ groupSelector = `[data-gallery="${group}"]`;
472
+ } else {
473
+ groupSelector = this.options.selector;
474
+ }
475
+ } else {
476
+ groupSelector = `[data-zoomora="${group}"]`;
477
+ }
478
+ this.items = Array.from(document.querySelectorAll(groupSelector));
479
+ this.currentIndex = Math.max(0, this.items.indexOf(element));
480
+ this.isZoomed = false;
481
+ this.isFullscreen = false;
482
+ this.thumbnailsVisible = true;
483
+ this.currentX = 0;
484
+ this.currentY = 0;
485
+ this.currentScale = 1;
486
+ this.zoomora.classList.add("active");
487
+ document.body.style.overflow = "hidden";
488
+ this.loadContent();
489
+ this.updateThumbnails();
490
+ this.updateCounter();
491
+ this.updateNavigation();
492
+ this.updateCaption();
493
+ this.updateButtons();
494
+ if (this.autoHideEnabled) {
495
+ this.startAutoHideTimer();
496
+ } else {
497
+ this.showControls();
498
+ }
499
+ if (typeof this.options.onOpen === "function") {
500
+ this.options.onOpen(element, this.currentIndex);
501
+ }
502
+ }
503
+ /**
504
+ * Close lightbox
505
+ */
506
+ close() {
507
+ if (this.isFullscreen) {
508
+ this.exitFullscreen();
509
+ }
510
+ this.zoomora.classList.remove("active");
511
+ document.body.style.overflow = "";
512
+ this.isZoomed = false;
513
+ this.isFullscreen = false;
514
+ this.currentX = 0;
515
+ this.currentY = 0;
516
+ this.currentScale = 1;
517
+ const media = this.content.querySelector(".zoomora-media");
518
+ if (media) {
519
+ media.classList.remove("zoomed");
520
+ media.style.transform = "";
521
+ media.style.cursor = "";
522
+ }
523
+ this.zoomora.classList.remove("fullscreen");
524
+ this.updateButtons();
525
+ this.stopAutoHideTimer();
526
+ this.showControls();
527
+ if (typeof this.options.onClose === "function") {
528
+ this.options.onClose(this.currentIndex);
529
+ }
530
+ }
531
+ /**
532
+ * Navigate to previous item
533
+ */
534
+ prev() {
535
+ if (this.currentIndex > 0) {
536
+ this.currentIndex--;
537
+ this.loadContent();
538
+ this.updateCounter();
539
+ this.updateThumbnails();
540
+ this.updateNavigation();
541
+ this.updateCaption();
542
+ if (typeof this.options.onPrev === "function") {
543
+ this.options.onPrev(this.currentIndex);
544
+ }
545
+ }
546
+ }
547
+ /**
548
+ * Navigate to next item
549
+ */
550
+ next() {
551
+ if (this.currentIndex < this.items.length - 1) {
552
+ this.currentIndex++;
553
+ this.loadContent();
554
+ this.updateCounter();
555
+ this.updateThumbnails();
556
+ this.updateNavigation();
557
+ this.updateCaption();
558
+ if (typeof this.options.onNext === "function") {
559
+ this.options.onNext(this.currentIndex);
560
+ }
561
+ }
562
+ }
563
+ /**
564
+ * Load content for current item
565
+ */
566
+ loadContent() {
567
+ if (!this.items[this.currentIndex]) return;
568
+ const item = this.items[this.currentIndex];
569
+ const src = this.getImageSource(item);
570
+ const type = item.dataset.type || "image";
571
+ if (!src) {
572
+ console.error("Zoomora: No source found for item");
573
+ return;
574
+ }
575
+ this.loading.style.display = "block";
576
+ if (this.slidesContainer) {
577
+ this.slidesContainer.innerHTML = "";
578
+ }
579
+ setTimeout(() => {
580
+ this.renderContent(src, type, item);
581
+ }, 100);
582
+ }
583
+ /**
584
+ * Render content based on type
585
+ * @param {string} src - Source URL
586
+ * @param {string} type - Content type
587
+ * @param {Element} item - Original item element
588
+ */
589
+ renderContent(src, type, item) {
590
+ const slideContainer = document.createElement("div");
591
+ slideContainer.className = "zoomora-slide-container";
592
+ const slide = document.createElement("div");
593
+ slide.className = `zoomora-slide ${this.options.transition === "slide" ? "slide-effect" : "fade-effect"}`;
594
+ slide.style.opacity = "0";
595
+ slide.style.transform = this.options.transition === "slide" ? "translateX(100%)" : "scale(0.8)";
596
+ let mediaElement;
597
+ try {
598
+ switch (type) {
599
+ case "video":
600
+ if (src.endsWith(".mp4") || src.endsWith(".webm") || src.endsWith(".ogg")) {
601
+ const videoContainer = document.createElement("div");
602
+ videoContainer.className = "zoomora-video-container";
603
+ mediaElement = document.createElement("video");
604
+ mediaElement.src = src;
605
+ mediaElement.className = "zoomora-video";
606
+ mediaElement.setAttribute("preload", "metadata");
607
+ const posterSrc = item.src || item.getAttribute("src");
608
+ if (posterSrc) {
609
+ mediaElement.setAttribute("poster", posterSrc);
610
+ }
611
+ const playButton = document.createElement("div");
612
+ playButton.className = "zoomora-play-button";
613
+ playButton.innerHTML = `<svg viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`;
614
+ playButton.addEventListener("click", () => {
615
+ mediaElement.play();
616
+ playButton.style.display = "none";
617
+ mediaElement.setAttribute("controls", "");
618
+ });
619
+ mediaElement.addEventListener("ended", () => {
620
+ playButton.style.display = "flex";
621
+ });
622
+ videoContainer.appendChild(mediaElement);
623
+ videoContainer.appendChild(playButton);
624
+ slideContainer.appendChild(videoContainer);
625
+ } else {
626
+ mediaElement = document.createElement("iframe");
627
+ mediaElement.src = src;
628
+ mediaElement.className = "zoomora-video";
629
+ mediaElement.setAttribute("allowfullscreen", "");
630
+ mediaElement.setAttribute("frameborder", "0");
631
+ slideContainer.appendChild(mediaElement);
632
+ }
633
+ break;
634
+ case "iframe":
635
+ mediaElement = document.createElement("iframe");
636
+ mediaElement.src = src;
637
+ mediaElement.className = "zoomora-iframe";
638
+ mediaElement.setAttribute("frameborder", "0");
639
+ slideContainer.appendChild(mediaElement);
640
+ break;
641
+ default:
642
+ mediaElement = document.createElement("img");
643
+ mediaElement.src = src;
644
+ mediaElement.className = "zoomora-media";
645
+ mediaElement.alt = item.alt || item.title || "";
646
+ mediaElement.onload = () => {
647
+ this.loading.style.display = "none";
648
+ this.checkZoomability(mediaElement);
649
+ this.animateSlideIn(slide);
650
+ };
651
+ mediaElement.onerror = () => {
652
+ this.loading.style.display = "none";
653
+ console.error("Zoomora: Failed to load image:", src);
654
+ this.animateSlideIn(slide);
655
+ };
656
+ slideContainer.appendChild(mediaElement);
657
+ }
658
+ slide.appendChild(slideContainer);
659
+ this.slidesContainer.innerHTML = "";
660
+ this.slidesContainer.appendChild(slide);
661
+ if (type !== "image") {
662
+ this.loading.style.display = "none";
663
+ this.animateSlideIn(slide);
664
+ }
665
+ } catch (error) {
666
+ console.error("Zoomora: Error rendering content:", error);
667
+ this.loading.style.display = "none";
668
+ }
669
+ }
670
+ // Animation helper
671
+ animateSlideIn(slide) {
672
+ slide.offsetHeight;
673
+ slide.classList.add("active");
674
+ if (this.options.transition === "slide") {
675
+ slide.style.transition = `transform ${this.options.animationDuration}ms ease, opacity ${this.options.animationDuration}ms ease`;
676
+ slide.style.transform = "translateX(0)";
677
+ slide.style.opacity = "1";
678
+ } else {
679
+ slide.style.transition = `transform ${this.options.animationDuration}ms ease, opacity ${this.options.animationDuration}ms ease`;
680
+ slide.style.transform = "scale(1)";
681
+ slide.style.opacity = "1";
682
+ }
683
+ }
684
+ // Toggle auto-hide controls
685
+ toggleAutoHide() {
686
+ this.autoHideEnabled = !this.autoHideEnabled;
687
+ const autoHideBtn = document.getElementById("zoomoraAutoHideBtn");
688
+ if (this.autoHideEnabled) {
689
+ autoHideBtn.classList.add("active");
690
+ autoHideBtn.title = "Disable Auto-Hide Controls";
691
+ this.startAutoHideTimer();
692
+ } else {
693
+ autoHideBtn.classList.remove("active");
694
+ autoHideBtn.title = "Enable Auto-Hide Controls";
695
+ this.stopAutoHideTimer();
696
+ this.showControls();
697
+ }
698
+ }
699
+ // Handle user activity
700
+ handleUserActivity() {
701
+ if (!this.autoHideEnabled) return;
702
+ this.showControls();
703
+ this.startAutoHideTimer();
704
+ }
705
+ // Start auto-hide timer
706
+ startAutoHideTimer() {
707
+ this.stopAutoHideTimer();
708
+ this.autoHideTimeout = setTimeout(() => {
709
+ this.hideControls();
710
+ }, this.options.autoHideDelay);
711
+ }
712
+ // Stop auto-hide timer
713
+ stopAutoHideTimer() {
714
+ if (this.autoHideTimeout) {
715
+ clearTimeout(this.autoHideTimeout);
716
+ this.autoHideTimeout = null;
717
+ }
718
+ }
719
+ // Show controls
720
+ showControls() {
721
+ if (this.isControlsVisible) return;
722
+ this.isControlsVisible = true;
723
+ this.zoomora.classList.remove("controls-hidden");
724
+ if (this.header) {
725
+ this.header.style.transform = "translateY(0)";
726
+ this.header.style.opacity = "1";
727
+ }
728
+ if (this.caption) {
729
+ this.caption.style.transform = "translateX(-50%) translateY(0)";
730
+ this.caption.style.opacity = "1";
731
+ }
732
+ if (this.thumbs) {
733
+ this.thumbs.style.transform = "translateX(-50%) translateY(0)";
734
+ this.thumbs.style.opacity = "1";
735
+ }
736
+ if (this.prevBtn) {
737
+ this.prevBtn.style.transform = "translateX(0)";
738
+ this.prevBtn.style.opacity = "1";
739
+ }
740
+ if (this.nextBtn) {
741
+ this.nextBtn.style.transform = "translateX(0)";
742
+ this.nextBtn.style.opacity = "1";
743
+ }
744
+ }
745
+ // Hide controls
746
+ hideControls() {
747
+ if (!this.isControlsVisible) return;
748
+ this.isControlsVisible = false;
749
+ this.zoomora.classList.add("controls-hidden");
750
+ if (this.header) {
751
+ this.header.style.transform = "translateY(-100%)";
752
+ this.header.style.opacity = "0";
753
+ }
754
+ if (this.caption) {
755
+ this.caption.style.transform = "translateX(-50%) translateY(100%)";
756
+ this.caption.style.opacity = "0";
757
+ }
758
+ if (this.thumbs) {
759
+ this.thumbs.style.transform = "translateX(-50%) translateY(100%)";
760
+ this.thumbs.style.opacity = "0";
761
+ }
762
+ if (this.prevBtn) {
763
+ this.prevBtn.style.transform = "translateX(-100%)";
764
+ this.prevBtn.style.opacity = "0";
765
+ }
766
+ if (this.nextBtn) {
767
+ this.nextBtn.style.transform = "translateX(100%)";
768
+ this.nextBtn.style.opacity = "0";
769
+ }
770
+ }
771
+ /**
772
+ * Check if image can be zoomed
773
+ * @param {HTMLImageElement} img - Image element
774
+ */
775
+ checkZoomability(img) {
776
+ if (!img || !img.naturalWidth || !img.naturalHeight) return;
777
+ const imgRect = img.getBoundingClientRect();
778
+ const renderedWidth = imgRect.width;
779
+ const renderedHeight = imgRect.height;
780
+ const naturalWidth = img.naturalWidth;
781
+ const naturalHeight = img.naturalHeight;
782
+ const isScaledDown = naturalWidth > renderedWidth || naturalHeight > renderedHeight;
783
+ const zoomRatioX = naturalWidth / renderedWidth;
784
+ const zoomRatioY = naturalHeight / renderedHeight;
785
+ const maxZoomRatio = Math.max(zoomRatioX, zoomRatioY);
786
+ img.dataset.zoomRatio = maxZoomRatio;
787
+ img.dataset.naturalWidth = naturalWidth;
788
+ img.dataset.naturalHeight = naturalHeight;
789
+ img.dataset.renderedWidth = renderedWidth;
790
+ img.dataset.renderedHeight = renderedHeight;
791
+ if (isScaledDown && maxZoomRatio > 1.1) {
792
+ img.classList.remove("no-zoom");
793
+ } else {
794
+ img.classList.add("no-zoom");
795
+ }
796
+ }
797
+ /**
798
+ * Update counter display
799
+ */
800
+ updateCounter() {
801
+ if (this.options.showCounter && this.counter) {
802
+ this.counter.textContent = `${this.currentIndex + 1} / ${this.items.length}`;
803
+ }
804
+ }
805
+ /**
806
+ * Update caption display
807
+ */
808
+ updateCaption() {
809
+ if (!this.caption || !this.items[this.currentIndex]) return;
810
+ const item = this.items[this.currentIndex];
811
+ const caption = item.dataset.caption || item.title || "";
812
+ if (caption) {
813
+ this.caption.textContent = caption;
814
+ this.caption.style.transform = "translateX(-50%) translateY(0)";
815
+ this.caption.style.opacity = "1";
816
+ this.caption.style.pointerEvents = "auto";
817
+ this.caption.style.transition = "opacity 0.3s ease, transform 0.3s ease";
818
+ } else {
819
+ this.caption.style.transform = "translateX(-50%) translateY(100%)";
820
+ this.caption.style.opacity = "0";
821
+ this.caption.style.pointerEvents = "none";
822
+ this.caption.style.transition = "opacity 0.3s ease, transform 0.3s ease";
823
+ }
824
+ }
825
+ /**
826
+ * Update thumbnails display
827
+ */
828
+ updateThumbnails() {
829
+ if (!this.options.showThumbnails || !this.thumbs) return;
830
+ if (this.items.length === 1) {
831
+ this.caption.classList.add("single-item-thumbnail");
832
+ this.thumbs.style.display = "none";
833
+ return;
834
+ }
835
+ this.caption.classList.remove("single-item-thumbnail");
836
+ this.thumbs.style.display = "flex";
837
+ this.thumbs.innerHTML = "";
838
+ this.items.forEach((item, index) => {
839
+ const thumb = document.createElement("div");
840
+ thumb.className = "zoomora-thumb";
841
+ if (index === this.currentIndex) thumb.classList.add("active");
842
+ const img = document.createElement("img");
843
+ const thumbSrc = this.getThumbnailSource(item);
844
+ if (thumbSrc) {
845
+ img.src = thumbSrc;
846
+ img.alt = item.alt || item.title || "";
847
+ img.onerror = () => {
848
+ thumb.style.display = "none";
849
+ };
850
+ }
851
+ thumb.appendChild(img);
852
+ thumb.addEventListener("click", () => {
853
+ this.currentIndex = index;
854
+ this.loadContent();
855
+ this.updateCounter();
856
+ this.updateThumbnails();
857
+ this.updateNavigation();
858
+ this.updateCaption();
859
+ });
860
+ this.thumbs.appendChild(thumb);
861
+ });
862
+ }
863
+ /**
864
+ * Update navigation buttons
865
+ */
866
+ updateNavigation() {
867
+ const prevBtn = document.getElementById("zoomoraPrevBtn");
868
+ const nextBtn = document.getElementById("zoomoraNextBtn");
869
+ if (prevBtn) {
870
+ prevBtn.style.display = this.currentIndex > 0 ? "flex" : "none";
871
+ }
872
+ if (nextBtn) {
873
+ nextBtn.style.display = this.currentIndex < this.items.length - 1 ? "flex" : "none";
874
+ }
875
+ }
876
+ /**
877
+ * Update control buttons
878
+ */
879
+ updateButtons() {
880
+ const fullscreenBtn = document.getElementById("zoomoraFullscreenBtn");
881
+ const collapseBtn = document.getElementById("zoomoraCollapseBtn");
882
+ if (fullscreenBtn && collapseBtn) {
883
+ if (this.isFullscreen) {
884
+ fullscreenBtn.style.display = "none";
885
+ collapseBtn.style.display = "flex";
886
+ } else {
887
+ fullscreenBtn.style.display = "flex";
888
+ collapseBtn.style.display = "none";
889
+ }
890
+ }
891
+ }
892
+ /**
893
+ * Toggle zoom state
894
+ */
895
+ toggleZoom() {
896
+ const media = this.content.querySelector(".zoomora-media");
897
+ if (!media || media.tagName !== "IMG" || media.classList.contains("no-zoom")) return;
898
+ this.isZoomed = !this.isZoomed;
899
+ media.classList.toggle("zoomed", this.isZoomed);
900
+ if (this.isZoomed) {
901
+ const zoomRatio = parseFloat(media.dataset.zoomRatio) || 2;
902
+ let zoomScale = Math.min(zoomRatio, this.options.maxZoomScale);
903
+ if (zoomRatio > 2.5) {
904
+ zoomScale = Math.min(zoomRatio * 0.7, 2.5);
905
+ }
906
+ media.style.transform = `scale(${zoomScale})`;
907
+ media.style.cursor = "grab";
908
+ media.dataset.currentZoomScale = zoomScale;
909
+ this.currentScale = zoomScale;
910
+ } else {
911
+ media.style.transform = "";
912
+ media.style.cursor = "";
913
+ this.currentX = 0;
914
+ this.currentY = 0;
915
+ this.currentScale = 1;
916
+ }
917
+ }
918
+ /**
919
+ * Toggle fullscreen mode
920
+ */
921
+ toggleFullscreen() {
922
+ if (!this.isFullscreen) {
923
+ this.enterFullscreen();
924
+ } else {
925
+ this.exitFullscreen();
926
+ }
927
+ }
928
+ /**
929
+ * Enter fullscreen mode
930
+ */
931
+ enterFullscreen() {
932
+ const element = this.zoomora;
933
+ const requestFullscreen = element.requestFullscreen || element.webkitRequestFullscreen || element.mozRequestFullScreen || element.msRequestFullscreen;
934
+ if (requestFullscreen) {
935
+ requestFullscreen.call(element).then(() => {
936
+ this.isFullscreen = true;
937
+ this.zoomora.classList.add("fullscreen");
938
+ this.updateButtons();
939
+ }).catch((err) => {
940
+ console.warn("Zoomora: Could not enter fullscreen:", err);
941
+ this.fallbackFullscreen();
942
+ });
943
+ } else {
944
+ this.fallbackFullscreen();
945
+ }
946
+ }
947
+ /**
948
+ * Exit fullscreen mode
949
+ */
950
+ exitFullscreen() {
951
+ const isInFullscreen = document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
952
+ if (isInFullscreen) {
953
+ const exitFullscreen = document.exitFullscreen || document.webkitExitFullscreen || document.mozCancelFullScreen || document.msExitFullscreen;
954
+ if (exitFullscreen) {
955
+ exitFullscreen.call(document).then(() => {
956
+ this.isFullscreen = false;
957
+ this.zoomora.classList.remove("fullscreen");
958
+ this.updateButtons();
959
+ }).catch((err) => {
960
+ console.warn("Zoomora: Could not exit fullscreen:", err);
961
+ this.fallbackExitFullscreen();
962
+ });
963
+ } else {
964
+ this.fallbackExitFullscreen();
965
+ }
966
+ } else {
967
+ this.fallbackExitFullscreen();
968
+ }
969
+ }
970
+ /**
971
+ * Fallback fullscreen using CSS
972
+ */
973
+ fallbackFullscreen() {
974
+ this.isFullscreen = true;
975
+ this.zoomora.classList.add("fullscreen");
976
+ this.updateButtons();
977
+ }
978
+ /**
979
+ * Exit CSS fullscreen
980
+ */
981
+ fallbackExitFullscreen() {
982
+ this.isFullscreen = false;
983
+ this.zoomora.classList.remove("fullscreen");
984
+ this.updateButtons();
985
+ }
986
+ /**
987
+ * Toggle thumbnails visibility
988
+ */
989
+ toggleThumbnails() {
990
+ if (this.items.length === 1) return;
991
+ this.thumbnailsVisible = !this.thumbnailsVisible;
992
+ if (this.thumbs) {
993
+ this.thumbs.classList.toggle("thumbs-hidden", !this.thumbnailsVisible);
994
+ if (!this.caption || !this.items[this.currentIndex]) return;
995
+ const item = this.items[this.currentIndex];
996
+ const caption = item.dataset.caption || item.title || "";
997
+ if (caption && this.thumbs.classList.contains("thumbs-hidden")) {
998
+ this.caption.style.bottom = "20px";
999
+ this.caption.style.transition = "bottom 0.3s ease, transform 0.3s ease";
1000
+ } else {
1001
+ this.caption.style.bottom = "120px";
1002
+ }
1003
+ }
1004
+ }
1005
+ /**
1006
+ * Start drag operation
1007
+ * @param {Event} e - Mouse or touch event
1008
+ */
1009
+ startDrag(e) {
1010
+ const media = this.content.querySelector(".zoomora-media");
1011
+ if (!media || !this.isZoomed) return;
1012
+ this.isDragging = true;
1013
+ media.style.cursor = "grabbing";
1014
+ this.startX = e.clientX || e.touches && e.touches[0].clientX || 0;
1015
+ this.startY = e.clientY || e.touches && e.touches[0].clientY || 0;
1016
+ const transform = media.style.transform;
1017
+ const scaleMatch = transform.match(/scale\(([^)]+)\)/);
1018
+ const translateMatch = transform.match(/translate\(([^,]+),\s*([^)]+)\)/);
1019
+ this.currentScale = scaleMatch ? parseFloat(scaleMatch[1]) : 1;
1020
+ this.currentX = translateMatch ? parseFloat(translateMatch[1]) : 0;
1021
+ this.currentY = translateMatch ? parseFloat(translateMatch[2]) : 0;
1022
+ e.preventDefault();
1023
+ }
1024
+ /**
1025
+ * Handle drag movement
1026
+ * @param {Event} e - Mouse or touch event
1027
+ */
1028
+ drag(e) {
1029
+ if (!this.isDragging) return;
1030
+ e.preventDefault();
1031
+ const clientX = e.clientX || e.touches && e.touches[0].clientX || 0;
1032
+ const clientY = e.clientY || e.touches && e.touches[0].clientY || 0;
1033
+ const deltaX = clientX - this.startX;
1034
+ const deltaY = clientY - this.startY;
1035
+ const newX = this.currentX + deltaX;
1036
+ const newY = this.currentY + deltaY;
1037
+ const media = this.content.querySelector(".zoomora-media");
1038
+ if (!media) return;
1039
+ const mediaRect = media.getBoundingClientRect();
1040
+ const scale = this.currentScale;
1041
+ const maxX = mediaRect.width * (scale - 1) / 2;
1042
+ const maxY = mediaRect.height * (scale - 1) / 2;
1043
+ const constrainedX = Math.max(-maxX, Math.min(maxX, newX));
1044
+ const constrainedY = Math.max(-maxY, Math.min(maxY, newY));
1045
+ media.style.transform = `scale(${scale}) translate(${constrainedX}px, ${constrainedY}px)`;
1046
+ }
1047
+ /**
1048
+ * End drag operation
1049
+ */
1050
+ endDrag() {
1051
+ if (!this.isDragging) return;
1052
+ this.isDragging = false;
1053
+ const media = this.content.querySelector(".zoomora-media");
1054
+ if (media && this.isZoomed) {
1055
+ media.style.cursor = "grab";
1056
+ }
1057
+ }
1058
+ /**
1059
+ * Go to specific item by index
1060
+ * @param {number} index - Item index
1061
+ */
1062
+ goTo(index) {
1063
+ if (index < 0 || index >= this.items.length) return;
1064
+ this.currentIndex = index;
1065
+ this.loadContent();
1066
+ this.updateCounter();
1067
+ this.updateThumbnails();
1068
+ this.updateNavigation();
1069
+ this.updateCaption();
1070
+ }
1071
+ /**
1072
+ * Get current item index
1073
+ * @returns {number} - Current index
1074
+ */
1075
+ getCurrentIndex() {
1076
+ return this.currentIndex;
1077
+ }
1078
+ /**
1079
+ * Get total number of items
1080
+ * @returns {number} - Total items
1081
+ */
1082
+ getTotalItems() {
1083
+ return this.items.length;
1084
+ }
1085
+ /**
1086
+ * Check if lightbox is open
1087
+ * @returns {boolean} - True if open
1088
+ */
1089
+ isOpen() {
1090
+ return this.zoomora && this.zoomora.classList.contains("active");
1091
+ }
1092
+ /**
1093
+ * Update options
1094
+ * @param {Object} newOptions - New options to merge
1095
+ */
1096
+ updateOptions(newOptions) {
1097
+ this.options = __spreadValues(__spreadValues({}, this.options), newOptions);
1098
+ }
1099
+ /**
1100
+ * Refresh the lightbox (re-scan for items)
1101
+ */
1102
+ refresh() {
1103
+ if (this.isOpen()) {
1104
+ const currentItem = this.items[this.currentIndex];
1105
+ if (currentItem) {
1106
+ this.open(currentItem);
1107
+ }
1108
+ }
1109
+ }
1110
+ /**
1111
+ * Destroy the lightbox instance
1112
+ */
1113
+ destroy() {
1114
+ if (!this.isInitialized) return;
1115
+ if (this.isOpen()) {
1116
+ this.close();
1117
+ }
1118
+ this.stopAutoHideTimer();
1119
+ this.eventListeners.forEach((listeners, key) => {
1120
+ listeners.forEach(({ element, event, handler, options }) => {
1121
+ if (element && element.removeEventListener) {
1122
+ element.removeEventListener(event, handler, options);
1123
+ }
1124
+ });
1125
+ });
1126
+ this.eventListeners.clear();
1127
+ if (this.zoomora && this.zoomora.parentNode) {
1128
+ this.zoomora.parentNode.removeChild(this.zoomora);
1129
+ }
1130
+ this.isInitialized = false;
1131
+ this.items = [];
1132
+ this.currentIndex = 0;
1133
+ this.zoomora = null;
1134
+ this.content = null;
1135
+ this.counter = null;
1136
+ this.thumbs = null;
1137
+ this.loading = null;
1138
+ this.caption = null;
1139
+ }
1140
+ }
1141
+ return Zoomora;
1142
+ });
1143
+ }
1144
+ });
1145
+ export default require_zoomora_es();
1146
+ //# sourceMappingURL=zoomora.es.js.map