MikaFil commited on
Commit
47d3c74
·
verified ·
1 Parent(s): 12e3548

Update interface.js

Browse files
Files changed (1) hide show
  1. interface.js +95 -98
interface.js CHANGED
@@ -1,11 +1,9 @@
1
  // interface.js
2
- // ==============================
3
 
4
- // Store a reference to the <script> tag that loaded this file
5
  const currentScriptTag = document.currentScript;
6
 
7
  (async function() {
8
- // 1. Locate the <script> and read data-config
9
  let scriptTag = currentScriptTag;
10
  if (!scriptTag) {
11
  const scripts = document.getElementsByTagName('script');
@@ -26,14 +24,12 @@ const currentScriptTag = document.currentScript;
26
  try {
27
  const response = await fetch(configUrl);
28
  config = await response.json();
29
- } catch (error) {
30
- return;
31
- }
32
  } else {
33
  return;
34
  }
35
 
36
- // 2. If config.css_url is provided, inject a <link> to that CSS
37
  if (config.css_url) {
38
  const linkEl = document.createElement('link');
39
  linkEl.rel = "stylesheet";
@@ -41,24 +37,18 @@ const currentScriptTag = document.currentScript;
41
  document.head.appendChild(linkEl);
42
  }
43
 
44
- // 3. Generate a unique instanceId for this widget
45
  const instanceId = Math.random().toString(36).substr(2, 8);
46
 
47
- // 4. Compute the aspect ratio (padding-bottom %)
48
  let aspectPercent = "100%";
49
  if (config.aspect) {
50
  if (config.aspect.includes(":")) {
51
- const parts = config.aspect.split(":");
52
- const w = parseFloat(parts[0]);
53
- const h = parseFloat(parts[1]);
54
- if (!isNaN(w) && !isNaN(h) && w > 0) {
55
- aspectPercent = (h / w * 100) + "%";
56
- }
57
  } else {
58
  const aspectValue = parseFloat(config.aspect);
59
- if (!isNaN(aspectValue) && aspectValue > 0) {
60
- aspectPercent = (100 / aspectValue) + "%";
61
- }
62
  }
63
  } else {
64
  const parentContainer = scriptTag.parentNode;
@@ -69,7 +59,7 @@ const currentScriptTag = document.currentScript;
69
  }
70
  }
71
 
72
- // 5. Create the widget container
73
  const widgetContainer = document.createElement('div');
74
  widgetContainer.id = 'ply-widget-container-' + instanceId;
75
  widgetContainer.classList.add('ply-widget-container');
@@ -77,29 +67,42 @@ const currentScriptTag = document.currentScript;
77
  widgetContainer.style.paddingBottom = aspectPercent;
78
  widgetContainer.setAttribute('data-original-aspect', aspectPercent);
79
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  const tooltipsButtonHTML = config.tooltips_url
81
- ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>`
82
  : '';
83
 
 
84
  widgetContainer.innerHTML = `
85
  <div id="viewer-container-${instanceId}" class="viewer-container">
86
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
87
  <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
88
  </div>
89
- <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
90
- <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
91
- <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn">
92
  <span class="reset-icon">⟲</span>
93
  </button>
94
  ${tooltipsButtonHTML}
95
  <div id="menu-content-${instanceId}" class="menu-content">
96
- <span id="help-close-${instanceId}" class="help-close">×</span>
97
  <div class="help-text"></div>
98
  </div>
99
  </div>
100
  <div id="tooltip-panel" class="tooltip-panel" style="display: none;">
101
  <div class="tooltip-content">
102
- <span id="tooltip-close" class="tooltip-close">×</span>
103
  <div id="tooltip-text" class="tooltip-text"></div>
104
  <img id="tooltip-image" class="tooltip-image" src="" alt="" style="display: none;" />
105
  </div>
@@ -108,7 +111,7 @@ const currentScriptTag = document.currentScript;
108
 
109
  scriptTag.parentNode.appendChild(widgetContainer);
110
 
111
- // 6. Grab references to new DOM elements
112
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
113
  const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
114
  const helpToggle = document.getElementById('help-toggle-' + instanceId);
@@ -117,53 +120,25 @@ const currentScriptTag = document.currentScript;
117
  const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
118
  const menuContent = document.getElementById('menu-content-' + instanceId);
119
  const helpTextDiv = menuContent.querySelector('.help-text');
 
 
 
 
120
 
121
- // Tooltip panel elements
122
- const tooltipPanel = document.getElementById('tooltip-panel');
123
- const tooltipTextDiv = document.getElementById('tooltip-text');
124
- const tooltipImage = document.getElementById('tooltip-image');
125
- const tooltipCloseBtn = document.getElementById('tooltip-close');
126
-
127
- // 6a. Detect mobile vs. desktop
128
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
129
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
130
 
131
- // Prevent iOS pinch-zoom and double-tap zoom on the container
132
- if (isIOS) {
133
- widgetContainer.style.touchAction = "none";
134
- widgetContainer.addEventListener('touchmove', function(e) {
135
- // Prevent all default browser zooms/scrolls on the widget area
136
- if (e.touches.length > 1) e.preventDefault();
137
- }, { passive: false });
138
- }
139
-
140
- // French help/instructions
141
  const tooltipInstruction = config.tooltips_url
142
  ? '- Cliquez sur ⦿ pour afficher/masquer les tooltips.<br>'
143
  : '';
144
-
145
- if (isMobile) {
146
- helpTextDiv.innerHTML =
147
- '- Pour vous déplacer, glissez deux doigts sur l\'écran.<br>' +
148
- '- Pour orbiter, utilisez un doigt.<br>' +
149
- '- Pour zoomer, pincez avec deux doigts.<br>' +
150
- tooltipInstruction +
151
- '- ⟲ Réinitialise la caméra.<br>' +
152
- '- ⇱ Passe en plein écran.<br>';
153
- } else {
154
- helpTextDiv.innerHTML =
155
- '- orbitez avec le clic droit<br>' +
156
- '- zoomez avec la molette<br>' +
157
- '- déplacez vous avec le clic gauche<br>' +
158
- tooltipInstruction +
159
- '- ⟲ Réinitialise la caméra.<br>' +
160
- '- ⇱ Passe en plein écran.<br>';
161
- }
162
-
163
  menuContent.style.display = 'block';
164
  viewerContainerElem.style.display = 'block';
165
 
166
- // Variable to hold the drag-hide listener reference
167
  let dragHide = null;
168
 
169
  function hideTooltipPanel() {
@@ -173,35 +148,29 @@ const currentScriptTag = document.currentScript;
173
  }
174
  tooltipPanel.style.display = 'none';
175
  }
176
- function hideHelpPanel() {
177
- menuContent.style.display = 'none';
178
- }
179
 
180
- // 7. Dynamically load viewer.js
181
  let viewerModule;
182
  try {
183
  viewerModule = await import('https://mikafil-viewer-gs.static.hf.space/viewer.js');
184
  await viewerModule.initializeViewer(config, instanceId);
185
- } catch (err) {
186
- return;
187
- }
188
 
189
  const canvasId = 'canvas-' + instanceId;
190
  const canvasEl = document.getElementById(canvasId);
191
 
192
- // Prevent iOS double-tap to zoom on the canvas
193
- if (isIOS && canvasEl) {
194
- let lastTouch = 0;
195
- canvasEl.addEventListener('touchend', function(e) {
196
- const now = new Date().getTime();
197
- if (now - lastTouch <= 400) {
198
- e.preventDefault();
199
- }
200
- lastTouch = now;
201
- }, { passive: false });
202
  }
203
 
204
- // 8. Conditional display of tooltips-toggle button
205
  if (tooltipsToggleBtn) {
206
  if (!config.tooltips_url) {
207
  tooltipsToggleBtn.style.display = 'none';
@@ -212,10 +181,9 @@ const currentScriptTag = document.currentScript;
212
  }
213
  }
214
 
215
- // 9. Fullscreen / state-preservation logic
216
  let isFullscreen = false;
217
  let savedState = null;
218
-
219
  function saveCurrentState() {
220
  if (isFullscreen) return;
221
  const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
@@ -237,7 +205,6 @@ const currentScriptTag = document.currentScript;
237
  }
238
  };
239
  }
240
-
241
  function restoreOriginalStyles() {
242
  if (!savedState) return;
243
  const aspectToUse = savedState.widget.paddingBottom;
@@ -268,10 +235,8 @@ const currentScriptTag = document.currentScript;
268
  viewerContainerElem.clientHeight
269
  );
270
  }
271
-
272
  savedState = null;
273
  }
274
-
275
  function applyFullscreenStyles() {
276
  widgetContainer.style.position = 'fixed';
277
  widgetContainer.style.top = '0';
@@ -297,7 +262,6 @@ const currentScriptTag = document.currentScript;
297
  fullscreenToggle.textContent = '⇲';
298
  isFullscreen = true;
299
  }
300
-
301
  function enterFullscreen() {
302
  if (!savedState) saveCurrentState();
303
  if (isIOS) {
@@ -315,7 +279,6 @@ const currentScriptTag = document.currentScript;
315
  widgetContainer.classList.add('fake-fullscreen');
316
  }
317
  }
318
-
319
  function exitFullscreen() {
320
  if (document.fullscreenElement === widgetContainer && document.exitFullscreen) {
321
  document.exitFullscreen().catch(() => {});
@@ -325,7 +288,7 @@ const currentScriptTag = document.currentScript;
325
  isFullscreen = false;
326
  }
327
 
328
- // 10. Hook up event listeners
329
  fullscreenToggle.addEventListener('click', () => {
330
  hideTooltipPanel();
331
  isFullscreen ? exitFullscreen() : enterFullscreen();
@@ -361,7 +324,6 @@ const currentScriptTag = document.currentScript;
361
  document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } }));
362
  });
363
  }
364
-
365
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
366
 
367
  document.addEventListener('tooltip-selected', (evt) => {
@@ -383,16 +345,15 @@ const currentScriptTag = document.currentScript;
383
  });
384
 
385
  if (canvasEl) {
386
- // Prevent pinch-zoom and double-tap zoom on iOS (extra guard)
387
- if (isIOS) {
388
- canvasEl.style.touchAction = "none";
389
- canvasEl.addEventListener('gesturestart', e => e.preventDefault());
390
- canvasEl.addEventListener('gesturechange', e => e.preventDefault());
391
- canvasEl.addEventListener('gestureend', e => e.preventDefault());
392
- }
393
  canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
394
- }
395
 
 
 
 
 
 
 
 
396
  document.addEventListener('keydown', (e) => {
397
  if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
398
  });
@@ -409,9 +370,45 @@ const currentScriptTag = document.currentScript;
409
  }
410
  });
411
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
412
  setTimeout(() => {
413
  saveCurrentState();
414
  document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } }));
415
  }, 200);
416
 
 
 
 
 
 
 
 
 
 
 
 
 
417
  })();
 
1
  // interface.js
 
2
 
 
3
  const currentScriptTag = document.currentScript;
4
 
5
  (async function() {
6
+ // 1. Locate script tag and read data-config
7
  let scriptTag = currentScriptTag;
8
  if (!scriptTag) {
9
  const scripts = document.getElementsByTagName('script');
 
24
  try {
25
  const response = await fetch(configUrl);
26
  config = await response.json();
27
+ } catch (error) { return; }
 
 
28
  } else {
29
  return;
30
  }
31
 
32
+ // 2. CSS injection
33
  if (config.css_url) {
34
  const linkEl = document.createElement('link');
35
  linkEl.rel = "stylesheet";
 
37
  document.head.appendChild(linkEl);
38
  }
39
 
40
+ // 3. Unique instanceId
41
  const instanceId = Math.random().toString(36).substr(2, 8);
42
 
43
+ // 4. Aspect ratio calc
44
  let aspectPercent = "100%";
45
  if (config.aspect) {
46
  if (config.aspect.includes(":")) {
47
+ const [w, h] = config.aspect.split(":").map(Number);
48
+ if (w > 0 && h > 0) aspectPercent = (h / w * 100) + "%";
 
 
 
 
49
  } else {
50
  const aspectValue = parseFloat(config.aspect);
51
+ if (!isNaN(aspectValue) && aspectValue > 0) aspectPercent = (100 / aspectValue) + "%";
 
 
52
  }
53
  } else {
54
  const parentContainer = scriptTag.parentNode;
 
59
  }
60
  }
61
 
62
+ // 5. Widget container
63
  const widgetContainer = document.createElement('div');
64
  widgetContainer.id = 'ply-widget-container-' + instanceId;
65
  widgetContainer.classList.add('ply-widget-container');
 
67
  widgetContainer.style.paddingBottom = aspectPercent;
68
  widgetContainer.setAttribute('data-original-aspect', aspectPercent);
69
 
70
+ // 5a. Ensure touch-action disables iOS Safari pinch/zoom/scroll on viewer
71
+ widgetContainer.style.touchAction = 'none';
72
+
73
+ // 5b. Set iOS meta viewport to prevent zoom
74
+ if (!document.querySelector('meta[name="viewport"]')) {
75
+ const meta = document.createElement('meta');
76
+ meta.name = "viewport";
77
+ meta.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0";
78
+ document.head.appendChild(meta);
79
+ }
80
+
81
+ // Tooltips toggle button (if configured)
82
  const tooltipsButtonHTML = config.tooltips_url
83
+ ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle" tabindex="0" aria-label="Afficher/masquer tooltips">⦿</button>`
84
  : '';
85
 
86
+ // Viewer HTML
87
  widgetContainer.innerHTML = `
88
  <div id="viewer-container-${instanceId}" class="viewer-container">
89
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
90
  <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
91
  </div>
92
+ <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle" tabindex="0" aria-label="Plein écran">⇱</button>
93
+ <button id="help-toggle-${instanceId}" class="widget-button help-toggle" tabindex="0" aria-label="Aide">?</button>
94
+ <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn" tabindex="0" aria-label="Réinitialiser caméra">
95
  <span class="reset-icon">⟲</span>
96
  </button>
97
  ${tooltipsButtonHTML}
98
  <div id="menu-content-${instanceId}" class="menu-content">
99
+ <span id="help-close-${instanceId}" class="help-close" tabindex="0" aria-label="Fermer">×</span>
100
  <div class="help-text"></div>
101
  </div>
102
  </div>
103
  <div id="tooltip-panel" class="tooltip-panel" style="display: none;">
104
  <div class="tooltip-content">
105
+ <span id="tooltip-close" class="tooltip-close" tabindex="0" aria-label="Fermer">×</span>
106
  <div id="tooltip-text" class="tooltip-text"></div>
107
  <img id="tooltip-image" class="tooltip-image" src="" alt="" style="display: none;" />
108
  </div>
 
111
 
112
  scriptTag.parentNode.appendChild(widgetContainer);
113
 
114
+ // 6. DOM references
115
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
116
  const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
117
  const helpToggle = document.getElementById('help-toggle-' + instanceId);
 
120
  const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
121
  const menuContent = document.getElementById('menu-content-' + instanceId);
122
  const helpTextDiv = menuContent.querySelector('.help-text');
123
+ const tooltipPanel = document.getElementById('tooltip-panel');
124
+ const tooltipTextDiv = document.getElementById('tooltip-text');
125
+ const tooltipImage = document.getElementById('tooltip-image');
126
+ const tooltipCloseBtn = document.getElementById('tooltip-close');
127
 
128
+ // Platform detection
 
 
 
 
 
 
129
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
130
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
131
 
132
+ // Help text
 
 
 
 
 
 
 
 
 
133
  const tooltipInstruction = config.tooltips_url
134
  ? '- Cliquez sur ⦿ pour afficher/masquer les tooltips.<br>'
135
  : '';
136
+ helpTextDiv.innerHTML = isMobile
137
+ ? '- Pour vous déplacer, glissez deux doigts sur l\'écran.<br>- Pour orbiter, utilisez un doigt.<br>- Pour zoomer, pincez avec deux doigts.<br>' + tooltipInstruction + '- ⟲ Réinitialise la caméra.<br>- ⇱ Passe en plein écran.<br>'
138
+ : '- orbitez avec le clic droit<br>- zoomez avec la molette<br>- déplacez vous avec le clic gauche<br>' + tooltipInstruction + '- ⟲ Réinitialise la caméra.<br>- ⇱ Passe en plein écran.<br>';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
  menuContent.style.display = 'block';
140
  viewerContainerElem.style.display = 'block';
141
 
 
142
  let dragHide = null;
143
 
144
  function hideTooltipPanel() {
 
148
  }
149
  tooltipPanel.style.display = 'none';
150
  }
151
+ function hideHelpPanel() { menuContent.style.display = 'none'; }
 
 
152
 
153
+ // 7. Load viewer.js
154
  let viewerModule;
155
  try {
156
  viewerModule = await import('https://mikafil-viewer-gs.static.hf.space/viewer.js');
157
  await viewerModule.initializeViewer(config, instanceId);
158
+ } catch (err) { return; }
 
 
159
 
160
  const canvasId = 'canvas-' + instanceId;
161
  const canvasEl = document.getElementById(canvasId);
162
 
163
+ // 7b. Fix for iOS: always use correct canvas style and prevent double-scaling
164
+ if (canvasEl) {
165
+ canvasEl.style.touchAction = 'none';
166
+ canvasEl.style.width = '100%';
167
+ canvasEl.style.height = '100%';
168
+ canvasEl.style.display = 'block';
169
+ // Remove any tab focus from canvas to prevent scroll-zoom
170
+ canvasEl.setAttribute('tabindex', '-1');
 
 
171
  }
172
 
173
+ // 8. Tooltips button conditional display
174
  if (tooltipsToggleBtn) {
175
  if (!config.tooltips_url) {
176
  tooltipsToggleBtn.style.display = 'none';
 
181
  }
182
  }
183
 
184
+ // 9. Fullscreen/state logic
185
  let isFullscreen = false;
186
  let savedState = null;
 
187
  function saveCurrentState() {
188
  if (isFullscreen) return;
189
  const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
 
205
  }
206
  };
207
  }
 
208
  function restoreOriginalStyles() {
209
  if (!savedState) return;
210
  const aspectToUse = savedState.widget.paddingBottom;
 
235
  viewerContainerElem.clientHeight
236
  );
237
  }
 
238
  savedState = null;
239
  }
 
240
  function applyFullscreenStyles() {
241
  widgetContainer.style.position = 'fixed';
242
  widgetContainer.style.top = '0';
 
262
  fullscreenToggle.textContent = '⇲';
263
  isFullscreen = true;
264
  }
 
265
  function enterFullscreen() {
266
  if (!savedState) saveCurrentState();
267
  if (isIOS) {
 
279
  widgetContainer.classList.add('fake-fullscreen');
280
  }
281
  }
 
282
  function exitFullscreen() {
283
  if (document.fullscreenElement === widgetContainer && document.exitFullscreen) {
284
  document.exitFullscreen().catch(() => {});
 
288
  isFullscreen = false;
289
  }
290
 
291
+ // 10. Event listeners
292
  fullscreenToggle.addEventListener('click', () => {
293
  hideTooltipPanel();
294
  isFullscreen ? exitFullscreen() : enterFullscreen();
 
324
  document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } }));
325
  });
326
  }
 
327
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
328
 
329
  document.addEventListener('tooltip-selected', (evt) => {
 
345
  });
346
 
347
  if (canvasEl) {
 
 
 
 
 
 
 
348
  canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
 
349
 
350
+ // iOS pinch zoom prevention
351
+ canvasEl.addEventListener('touchmove', function(e) {
352
+ if (e.touches.length > 1) {
353
+ e.preventDefault();
354
+ }
355
+ }, { passive: false });
356
+ }
357
  document.addEventListener('keydown', (e) => {
358
  if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
359
  });
 
370
  }
371
  });
372
 
373
+ // iOS: unlock WebGL/audio on first user gesture (if needed)
374
+ if (isIOS && canvasEl) {
375
+ let firstUserAction = false;
376
+ function unlockWebGLAndAudio() {
377
+ if (!firstUserAction) {
378
+ if (viewerModule.app && viewerModule.app.graphicsDevice && viewerModule.app.graphicsDevice.canvas) {
379
+ // iOS hack: force focus and dummy audio play
380
+ viewerModule.app.graphicsDevice.canvas.focus && viewerModule.app.graphicsDevice.canvas.focus();
381
+ try {
382
+ const ctx = new AudioContext();
383
+ const buffer = ctx.createBuffer(1, 1, 22050);
384
+ const src = ctx.createBufferSource();
385
+ src.buffer = buffer;
386
+ src.connect(ctx.destination);
387
+ src.start(0);
388
+ } catch {}
389
+ }
390
+ firstUserAction = true;
391
+ }
392
+ }
393
+ canvasEl.addEventListener('touchstart', unlockWebGLAndAudio, { once: true });
394
+ canvasEl.addEventListener('mousedown', unlockWebGLAndAudio, { once: true });
395
+ }
396
+
397
  setTimeout(() => {
398
  saveCurrentState();
399
  document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } }));
400
  }, 200);
401
 
402
+ // Ensure ResizeObserver always triggers a correct canvas resize (iOS)
403
+ if (canvasEl && window.ResizeObserver) {
404
+ const resizeObs = new ResizeObserver(() => {
405
+ if (viewerModule.app) {
406
+ viewerModule.app.resizeCanvas(
407
+ viewerContainerElem.clientWidth,
408
+ viewerContainerElem.clientHeight
409
+ );
410
+ }
411
+ });
412
+ resizeObs.observe(viewerContainerElem);
413
+ }
414
  })();