PluginProbe
Yatra – Travel Booking & Tour Operator Software / 3.0.16
Yatra – Travel Booking & Tour Operator Software v3.0.16
3.0.16 3.0.15 3.0.14 3.0.14.1 3.0.14.2 3.0.12 3.0.13 3.0.11 3.0.10 3.0.9 3.0.8 3.0.7 3.0.6 3.0.5 3.0.5.1 3.0.4 3.0.3 3.0.2.9 3.0.2.7 3.0.2.8 3.0.2.6 trunk 1.0.0 2.0.0 2.0.1 All 84 releases
yatra / assets / js / tour-viewer.js

tour-viewer.js in Yatra – Travel Booking & Tour Operator Software 3.0.16, at assets/js/tour-viewer.js

216 lines 7.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Yatra 360° Tour Viewer Module
3 * Reusable 360° virtual tour viewer
4 *
5 * @package Yatra
6 * @version 1.0.0
7 */
8
9 (function() {
10 'use strict';
11
12 // Translation helper. Resolves through wp.i18n.__() when wp-i18n is
13 // enqueued (FrontendAssetsProvider registers it as a dependency for
14 // this script); falls back to identity so the page never breaks
15 // when the i18n runtime is unavailable on older builds.
16 //
17 // IMPORTANT — call sites MUST pass `'yatra'` as the second arg even
18 // though the shim ignores it. `wp i18n make-pot --domain=yatra`
19 // filters extraction to calls whose LITERAL second argument is the
20 // domain, so a bare `__('x')` is silently dropped from the .pot.
21 var __ = (window.wp && window.wp.i18n && window.wp.i18n.__)
22 ? function (text, domain) { return window.wp.i18n.__(text, domain || 'yatra'); }
23 : function (text) { return text; };
24
25 class YatraTourViewer {
26 constructor() {
27 this.modal = null;
28 this.iframe = null;
29 this.currentTour = null;
30 this.init();
31 }
32
33 init() {
34 // Nothing is built here on purpose. The modal markup (and its
35 // empty <h3 class="yatra-tour-viewer-title">) used to be injected
36 // into <body> as soon as this script loaded — on every page,
37 // including ones with no 360° tour. It is now created on the
38 // first view() call, so pages that never open a tour never get
39 // the extra DOM.
40 }
41
42 /**
43 * Build the modal once, on first use.
44 */
45 ensureModal() {
46 if (this.modal) {
47 return;
48 }
49 this.createModal();
50 this.attachEventListeners();
51 }
52
53 createModal() {
54 // Check if modal already exists
55 if (document.getElementById('yatra-tour-viewer-modal')) {
56 this.modal = document.getElementById('yatra-tour-viewer-modal');
57 this.iframe = document.getElementById('yatra-tour-viewer-iframe');
58 return;
59 }
60
61 // Pre-compute translated labels OUT of the template literal so
62 // makepot extracts them reliably. `wp i18n make-pot` doesn't
63 // always traverse into `${ }` interpolations; calls at top-
64 // level statements are always picked up.
65 var closeTourLabel = __('Close Tour', 'yatra');
66 var fullscreenLabel = __('Fullscreen', 'yatra');
67
68 // Create modal HTML
69 const modalHTML = `
70 <div id="yatra-tour-viewer-modal" class="yatra-tour-viewer-modal" style="display: none;">
71 <div class="yatra-tour-viewer-overlay"></div>
72 <div class="yatra-tour-viewer-content">
73 <button type="button" class="yatra-tour-viewer-close" aria-label="${closeTourLabel}">
74 <svg width="24" height="24" fill="none" stroke="currentColor" viewBox="0 0 24 24">
75 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
76 </svg>
77 </button>
78 <div class="yatra-tour-viewer-header">
79 <h3 class="yatra-tour-viewer-title"></h3>
80 <div class="yatra-tour-viewer-controls">
81 <button type="button" class="yatra-tour-viewer-fullscreen" aria-label="${fullscreenLabel}">
82 <svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
83 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 8V4m0 0h4M4 4l5 5m11-1V4m0 0h-4m4 0l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4"/>
84 </svg>
85 </button>
86 </div>
87 </div>
88 <div class="yatra-tour-viewer-wrapper">
89 <iframe id="yatra-tour-viewer-iframe"
90 width="100%"
91 height="100%"
92 frameborder="0"
93 allowfullscreen
94 allow="gyroscope; accelerometer; xr-spatial-tracking">
95 </iframe>
96 </div>
97 </div>
98 </div>
99 `;
100
101 // Append to body
102 document.body.insertAdjacentHTML('beforeend', modalHTML);
103
104 this.modal = document.getElementById('yatra-tour-viewer-modal');
105 this.iframe = document.getElementById('yatra-tour-viewer-iframe');
106 }
107
108 attachEventListeners() {
109 // Close button
110 const closeBtn = this.modal.querySelector('.yatra-tour-viewer-close');
111 if (closeBtn) {
112 closeBtn.addEventListener('click', () => this.close());
113 }
114
115 // Overlay click to close
116 const overlay = this.modal.querySelector('.yatra-tour-viewer-overlay');
117 if (overlay) {
118 overlay.addEventListener('click', () => this.close());
119 }
120
121 // Fullscreen button
122 const fullscreenBtn = this.modal.querySelector('.yatra-tour-viewer-fullscreen');
123 if (fullscreenBtn) {
124 fullscreenBtn.addEventListener('click', () => this.toggleFullscreen());
125 }
126
127 // ESC key to close
128 document.addEventListener('keydown', (e) => {
129 if (e.key === 'Escape' && this.modal.style.display === 'flex') {
130 this.close();
131 }
132 });
133 }
134
135 /**
136 * View a 360° tour
137 * @param {Object} tour - Tour object with url, title, tour_type
138 */
139 view(tour) {
140 if (!tour || !tour.url) {
141 console.error('YatraTourViewer: Invalid tour object');
142 return;
143 }
144
145 this.currentTour = tour;
146 this.ensureModal();
147
148 // Set title
149 const titleElement = this.modal.querySelector('.yatra-tour-viewer-title');
150 if (titleElement) {
151 titleElement.textContent = tour.title || __('360° Virtual Tour', 'yatra');
152 }
153
154 // Set iframe source
155 this.iframe.src = tour.url;
156
157 // Show modal
158 this.open();
159 }
160
161 open() {
162 this.modal.style.display = 'flex';
163 document.body.style.overflow = 'hidden';
164 }
165
166 close() {
167 if (!this.modal) {
168 return; // never opened on this page
169 }
170 this.modal.style.display = 'none';
171 document.body.style.overflow = '';
172
173 // Stop tour by clearing iframe src
174 this.iframe.src = '';
175 this.currentTour = null;
176
177 // Exit fullscreen if active
178 if (document.fullscreenElement) {
179 document.exitFullscreen();
180 }
181 }
182
183 toggleFullscreen() {
184 if (!this.modal) {
185 return;
186 }
187 const wrapper = this.modal.querySelector('.yatra-tour-viewer-wrapper');
188
189 if (!document.fullscreenElement) {
190 if (wrapper.requestFullscreen) {
191 wrapper.requestFullscreen();
192 } else if (wrapper.webkitRequestFullscreen) {
193 wrapper.webkitRequestFullscreen();
194 } else if (wrapper.msRequestFullscreen) {
195 wrapper.msRequestFullscreen();
196 }
197 } else {
198 if (document.exitFullscreen) {
199 document.exitFullscreen();
200 } else if (document.webkitExitFullscreen) {
201 document.webkitExitFullscreen();
202 } else if (document.msExitFullscreen) {
203 document.msExitFullscreen();
204 }
205 }
206 }
207 }
208
209 // Create global instance
210 window.YatraTourViewer = new YatraTourViewer();
211
212 // Log successful initialization
213
214
215 })();
216