MikaFil commited on
Commit
ae97d01
·
verified ·
1 Parent(s): 9d390ba

Update interface.js

Browse files
Files changed (1) hide show
  1. interface.js +137 -182
interface.js CHANGED
@@ -4,45 +4,40 @@
4
  const currentScriptTag = document.currentScript;
5
 
6
  (async function () {
7
- // 1) Retrouver la balise <script> porteuse de data-config
8
  let scriptTag = currentScriptTag;
 
9
  if (!scriptTag) {
10
  const scripts = document.getElementsByTagName('script');
11
  for (let i = 0; i < scripts.length; i++) {
12
- if (scripts[i].src.includes('interface.js') && scripts[i].hasAttribute('data-config')) {
 
 
 
13
  scriptTag = scripts[i];
14
  break;
15
  }
16
  }
17
  if (!scriptTag && scripts.length > 0) {
18
  scriptTag = scripts[scripts.length - 1];
19
-
20
  }
21
  }
22
- if (!scriptTag) return;
23
-
24
-
25
-
26
 
27
-
28
-
29
- // 2) Charger la config
30
  const configUrl = scriptTag.getAttribute('data-config');
31
  let config = {};
 
32
  if (configUrl) {
33
  try {
34
  const response = await fetch(configUrl);
35
  config = await response.json();
36
  } catch (error) {
37
- console.error('[interface.js] Failed to load config:', error);
38
  return;
39
  }
40
  } else {
41
- console.warn('[interface.js] No data-config provided.');
42
  return;
43
  }
44
 
45
- // 3) CSS optionnel
46
  if (config.css_url) {
47
  const linkEl = document.createElement('link');
48
  linkEl.rel = 'stylesheet';
@@ -50,10 +45,10 @@ const currentScriptTag = document.currentScript;
50
  document.head.appendChild(linkEl);
51
  }
52
 
53
- // 4) ID d’instance
54
  const instanceId = Math.random().toString(36).substr(2, 8);
55
 
56
- // 5) Calcul de l’aspect
57
  let aspectPercent = '100%';
58
  if (config.aspect) {
59
  if (config.aspect.includes(':')) {
@@ -61,23 +56,24 @@ const currentScriptTag = document.currentScript;
61
  const w = parseFloat(parts[0]);
62
  const h = parseFloat(parts[1]);
63
  if (!isNaN(w) && !isNaN(h) && w > 0) {
64
- aspectPercent = (h / w * 100) + '%';
65
  }
66
  } else {
67
  const aspectValue = parseFloat(config.aspect);
68
  if (!isNaN(aspectValue) && aspectValue > 0) {
69
- aspectPercent = (100 / aspectValue) + '%';
70
  }
71
  }
72
  } else {
 
73
  const containerWidth = parentContainer.offsetWidth;
74
  const containerHeight = parentContainer.offsetHeight;
75
  if (containerWidth > 0 && containerHeight > 0) {
76
- aspectPercent = (containerHeight / containerWidth * 100) + '%';
77
  }
78
  }
79
 
80
- // 6) Conteneur widget
81
  const widgetContainer = document.createElement('div');
82
  widgetContainer.id = 'ply-widget-container-' + instanceId;
83
  widgetContainer.classList.add('ply-widget-container');
@@ -89,43 +85,37 @@ const currentScriptTag = document.currentScript;
89
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>`
90
  : '';
91
 
92
- // IDs uniques pour le panneau tooltips
93
- const tooltipIds = {
94
- panel: `tooltip-panel-${instanceId}`,
95
- close: `tooltip-close-${instanceId}`,
96
- text: `tooltip-text-${instanceId}`,
97
- image: `tooltip-image-${instanceId}`
98
- };
99
-
100
-
101
  widgetContainer.innerHTML = `
102
  <div id="viewer-container-${instanceId}" class="viewer-container">
103
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
 
104
  </div>
105
  <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
106
  <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
107
- <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn"><span class="reset-icon">⟲</span></button>
108
-
109
-
110
  ${tooltipsButtonHTML}
111
  <div id="menu-content-${instanceId}" class="menu-content">
112
  <span id="help-close-${instanceId}" class="help-close">×</span>
113
  <div class="help-text"></div>
114
  </div>
115
  </div>
116
- <div id="${tooltipIds.panel}" class="tooltip-panel" style="display: none;">
 
117
  <div class="tooltip-content">
118
- <span id="${tooltipIds.close}" class="tooltip-close">×</span>
119
- <div id="${tooltipIds.text}" class="tooltip-text"></div>
120
- <img id="${tooltipIds.image}" class="tooltip-image" src="" alt="" style="display: none;" />
121
  </div>
122
  </div>
123
  `;
124
 
125
-
126
  scriptTag.parentNode.appendChild(widgetContainer);
127
 
128
- // 7) Sélecteurs
129
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
130
  const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
131
  const helpToggle = document.getElementById('help-toggle-' + instanceId);
@@ -135,12 +125,12 @@ const currentScriptTag = document.currentScript;
135
  const menuContent = document.getElementById('menu-content-' + instanceId);
136
  const helpTextDiv = menuContent.querySelector('.help-text');
137
 
138
- // Tooltips (IDs uniques)
139
- const tooltipPanel = document.getElementById(tooltipIds.panel);
140
- const tooltipTextDiv = document.getElementById(tooltipIds.text);
141
- const tooltipImage = document.getElementById(tooltipIds.image);
142
- const tooltipCloseBtn = document.getElementById(tooltipIds.close);
143
 
 
144
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
145
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
146
 
@@ -150,8 +140,8 @@ const currentScriptTag = document.currentScript;
150
 
151
  if (isMobile) {
152
  helpTextDiv.innerHTML =
153
- '- Déplacez vous en glissant deux doigts sur l\'écran.<br>' +
154
- '- Orbitez en glissant un doigt.<br>' +
155
  '- Zoomez en pinçant avec deux doigts.<br>' +
156
  tooltipInstruction +
157
  '- Cliquez sur ⟲ pour réinitialiser la caméra.<br>' +
@@ -166,7 +156,7 @@ const currentScriptTag = document.currentScript;
166
  '- Cliquez sur ⇱ pour passer en plein écran.<br>';
167
  }
168
 
169
- // Menu responsive
170
  function setMenuContentMaxSize() {
171
  if (!isMobile) {
172
  menuContent.style.maxWidth = '';
@@ -178,6 +168,7 @@ const currentScriptTag = document.currentScript;
178
  return;
179
  }
180
  const parent = viewerContainerElem;
 
181
  const vw = parent.offsetWidth;
182
  const vh = parent.offsetHeight;
183
  if (vw && vh) {
@@ -195,17 +186,19 @@ const currentScriptTag = document.currentScript;
195
  }
196
  }
197
  }
 
198
  setMenuContentMaxSize();
199
  window.addEventListener('resize', setMenuContentMaxSize);
200
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
201
  window.addEventListener('orientationchange', setMenuContentMaxSize);
202
 
203
- // Afficher l’aide par défaut
204
  menuContent.style.display = 'block';
205
  viewerContainerElem.style.display = 'block';
206
 
207
- // Fonctions panneau tooltips / aide
208
  let dragHide = null;
 
209
  function hideTooltipPanel() {
210
  if (dragHide) {
211
  viewerContainerElem.removeEventListener('pointermove', dragHide);
@@ -213,99 +206,112 @@ const currentScriptTag = document.currentScript;
213
  }
214
  tooltipPanel.style.display = 'none';
215
  }
 
216
  function hideHelpPanel() {
217
  menuContent.style.display = 'none';
218
  }
219
 
220
- // 8) Charger viewer.js (anti-cache multi-instance via query param)
221
-
222
  let viewerModule;
223
  try {
224
- const viewerUrl = `https://mikafil-viewer-sgos.static.hf.space/viewer.js?i=${instanceId}`;
225
  viewerModule = await import(viewerUrl);
226
  await viewerModule.initializeViewer(config, instanceId);
227
  } catch (err) {
228
- console.error('[interface.js] Failed to init viewer:', err);
229
  return;
230
  }
231
 
232
  const canvasId = 'canvas-' + instanceId;
233
  const canvasEl = document.getElementById(canvasId);
234
 
235
- // Si tooltips non dispo, masquer le bouton
236
  if (tooltipsToggleBtn) {
237
  if (!config.tooltips_url) {
238
  tooltipsToggleBtn.style.display = 'none';
239
  } else {
240
  fetch(config.tooltips_url)
241
- .then(resp => { if (!resp.ok) tooltipsToggleBtn.style.display = 'none'; })
242
- .catch(() => { tooltipsToggleBtn.style.display = 'none'; });
243
-
244
-
245
-
246
-
247
-
248
-
249
-
250
-
251
-
252
-
253
-
254
-
255
-
256
  }
257
-
258
-
259
-
260
-
261
-
262
-
263
-
264
-
265
  }
266
 
267
- // 9) Plein écran
268
-
269
-
270
-
271
-
272
-
273
-
274
-
275
-
276
-
277
-
278
-
279
-
280
-
281
-
282
-
283
-
284
-
285
-
286
-
287
-
288
-
289
-
290
-
291
-
292
-
293
 
 
 
 
 
 
 
294
 
 
 
295
 
 
 
 
296
 
 
 
 
 
 
 
 
 
297
 
 
298
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
 
300
  let isFullscreen = false;
301
  let savedState = null;
302
 
303
  function saveCurrentState() {
304
  if (isFullscreen) return;
305
- const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
 
 
306
  savedState = {
307
  widget: {
308
  position: widgetContainer.style.position,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
309
  if (!savedState) return;
310
  const aspectToUse = savedState.widget.paddingBottom;
311
 
@@ -332,6 +338,8 @@ const currentScriptTag = document.currentScript;
332
 
333
  if (viewerModule.app) {
334
  viewerModule.app.resizeCanvas(
 
 
335
  );
336
  }
337
 
@@ -375,8 +383,8 @@ const currentScriptTag = document.currentScript;
375
  applyFullscreenStyles();
376
  widgetContainer.classList.add('fake-fullscreen');
377
  } else if (widgetContainer.requestFullscreen) {
378
- widgetContainer.requestFullscreen()
379
-
380
  .then(applyFullscreenStyles)
381
  .catch(() => {
382
  applyFullscreenStyles();
@@ -390,7 +398,7 @@ const currentScriptTag = document.currentScript;
390
 
391
  function exitFullscreen() {
392
  if (document.fullscreenElement === widgetContainer && document.exitFullscreen) {
393
- document.exitFullscreen().catch(() => { /* noop */ });
394
  }
395
  widgetContainer.classList.remove('fake-fullscreen');
396
  restoreOriginalStyles();
@@ -415,7 +423,7 @@ const currentScriptTag = document.currentScript;
415
  setMenuContentMaxSize();
416
  });
417
 
418
- // 10) Aide / reset
419
  helpToggle.addEventListener('click', (e) => {
420
  hideTooltipPanel();
421
  e.stopPropagation();
@@ -426,6 +434,7 @@ const currentScriptTag = document.currentScript;
426
  setMenuContentMaxSize();
427
  }
428
  });
 
429
  helpCloseBtn.addEventListener('click', hideHelpPanel);
430
 
431
  resetCameraBtn.addEventListener('click', () => {
@@ -435,7 +444,6 @@ const currentScriptTag = document.currentScript;
435
  }
436
  });
437
 
438
- // 11) Tooltips (bouton)
439
  if (tooltipsToggleBtn) {
440
  let tooltipsVisible = !!config.showTooltipsDefault;
441
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
@@ -443,96 +451,43 @@ const currentScriptTag = document.currentScript;
443
  hideTooltipPanel();
444
  tooltipsVisible = !tooltipsVisible;
445
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
446
-
447
- // Envoi global (compat actuel tooltips.js)
448
- document.dispatchEvent(new CustomEvent('toggle-tooltips', {
449
- detail: { visible: tooltipsVisible }
450
- }));
451
-
452
- // Envoi namespacé (si tu mets à jour tooltips.js pour l'écouter)
453
- document.dispatchEvent(new CustomEvent(`toggle-tooltips-${instanceId}`, {
454
- detail: { visible: tooltipsVisible, instanceId }
455
- }));
456
  });
457
  }
458
 
459
- // 12) Panneau tooltips : fermeture
460
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
461
 
462
- // 13) Gestion évènements "tooltip-selected"
463
- // a) Évènement namespacé (préféré si tooltips.js est mis à jour)
464
- const namespacedEvtName = `tooltip-selected-${instanceId}`;
465
- document.addEventListener(namespacedEvtName, (evt) => {
466
- const { title, description, imgUrl } = (evt.detail || {});
467
- showTooltipPanel(title, description, imgUrl);
468
- });
469
-
470
- // b) Fallback évènement global (tooltips.js actuel)
471
- // Pour éviter les collisions multi-instances, on ne réagit que si
472
- // le pointeur survole VOTRE canvas au moment du clic.
473
- let isPointerOver = false;
474
- if (canvasEl) {
475
- const enter = () => { isPointerOver = true; };
476
- const leave = () => { isPointerOver = false; };
477
- canvasEl.addEventListener('pointerenter', enter);
478
- canvasEl.addEventListener('pointerleave', leave);
479
- canvasEl.addEventListener('mouseenter', enter);
480
- canvasEl.addEventListener('mouseleave', leave);
481
- }
482
- document.addEventListener('tooltip-selected', (evt) => {
483
- if (!isPointerOver) return; // ignorer les autres viewers
484
- const { title, description, imgUrl } = (evt.detail || {});
485
- showTooltipPanel(title, description, imgUrl);
486
- });
487
-
488
- function showTooltipPanel(title, description, imgUrl) {
489
- // Annuler un éventuel handler différé
490
- if (dragHide) {
491
- viewerContainerElem.removeEventListener('pointermove', dragHide);
492
- dragHide = null;
493
- }
494
- tooltipTextDiv.innerHTML = `<strong>${title || ''}</strong><br>${description || ''}`;
495
- tooltipImage.style.display = 'none';
496
- tooltipImage.src = '';
497
- if (imgUrl) {
498
- tooltipImage.onload = () => { tooltipImage.style.display = 'block'; };
499
- tooltipImage.src = imgUrl;
500
- }
501
- tooltipPanel.style.display = 'flex';
502
-
503
- // Cacher si on “traîne” (drag) ou touche
504
- setTimeout(() => {
505
- dragHide = (e) => {
506
- if ((e.pointerType === 'mouse' && e.buttons !== 0) || e.pointerType === 'touch') {
507
- hideTooltipPanel();
508
- }
509
- };
510
- viewerContainerElem.addEventListener('pointermove', dragHide);
511
- }, 100);
512
- }
513
-
514
- // 14) Divers
515
- if (canvasEl) {
516
- canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
517
- }
518
  document.addEventListener('keydown', (e) => {
519
- if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
 
 
520
  });
 
521
  window.addEventListener('resize', () => {
522
  if (viewerModule.app) {
523
  if (isFullscreen) {
524
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
 
 
 
 
 
 
 
525
  setMenuContentMaxSize();
526
  });
527
 
528
- // 15) Init état par défaut
529
  setTimeout(() => {
530
-
531
  saveCurrentState();
532
- // Toggler initial des tooltips
533
- const initVisible = !!config.showTooltipsDefault;
534
- document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: initVisible } }));
535
- document.dispatchEvent(new CustomEvent(`toggle-tooltips-${instanceId}`, { detail: { visible: initVisible, instanceId } }));
536
  setMenuContentMaxSize();
537
  }, 200);
538
- })();
 
4
  const currentScriptTag = document.currentScript;
5
 
6
  (async function () {
7
+ // 1) Localiser la balise <script> et lire data-config
8
  let scriptTag = currentScriptTag;
9
+
10
  if (!scriptTag) {
11
  const scripts = document.getElementsByTagName('script');
12
  for (let i = 0; i < scripts.length; i++) {
13
+ if (
14
+ scripts[i].src.includes('interface.js') &&
15
+ scripts[i].hasAttribute('data-config')
16
+ ) {
17
  scriptTag = scripts[i];
18
  break;
19
  }
20
  }
21
  if (!scriptTag && scripts.length > 0) {
22
  scriptTag = scripts[scripts.length - 1];
 
23
  }
24
  }
 
 
 
 
25
 
 
 
 
26
  const configUrl = scriptTag.getAttribute('data-config');
27
  let config = {};
28
+
29
  if (configUrl) {
30
  try {
31
  const response = await fetch(configUrl);
32
  config = await response.json();
33
  } catch (error) {
 
34
  return;
35
  }
36
  } else {
 
37
  return;
38
  }
39
 
40
+ // 2) CSS optionnelle
41
  if (config.css_url) {
42
  const linkEl = document.createElement('link');
43
  linkEl.rel = 'stylesheet';
 
45
  document.head.appendChild(linkEl);
46
  }
47
 
48
+ // 3) ID d’instance
49
  const instanceId = Math.random().toString(36).substr(2, 8);
50
 
51
+ // 4) Aspect ratio
52
  let aspectPercent = '100%';
53
  if (config.aspect) {
54
  if (config.aspect.includes(':')) {
 
56
  const w = parseFloat(parts[0]);
57
  const h = parseFloat(parts[1]);
58
  if (!isNaN(w) && !isNaN(h) && w > 0) {
59
+ aspectPercent = (h / w) * 100 + '%';
60
  }
61
  } else {
62
  const aspectValue = parseFloat(config.aspect);
63
  if (!isNaN(aspectValue) && aspectValue > 0) {
64
+ aspectPercent = 100 / aspectValue + '%';
65
  }
66
  }
67
  } else {
68
+ const parentContainer = scriptTag.parentNode;
69
  const containerWidth = parentContainer.offsetWidth;
70
  const containerHeight = parentContainer.offsetHeight;
71
  if (containerWidth > 0 && containerHeight > 0) {
72
+ aspectPercent = (containerHeight / containerWidth) * 100 + '%';
73
  }
74
  }
75
 
76
+ // 5) Conteneur widget
77
  const widgetContainer = document.createElement('div');
78
  widgetContainer.id = 'ply-widget-container-' + instanceId;
79
  widgetContainer.classList.add('ply-widget-container');
 
85
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>`
86
  : '';
87
 
88
+ // HTML du widget (IDs spécifiques à l’instance)
 
 
 
 
 
 
 
 
89
  widgetContainer.innerHTML = `
90
  <div id="viewer-container-${instanceId}" class="viewer-container">
91
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
92
+ <progress id="progress-indicator-${instanceId}" max="100" value="0"></progress>
93
  </div>
94
  <button id="fullscreen-toggle-${instanceId}" class="widget-button fullscreen-toggle">⇱</button>
95
  <button id="help-toggle-${instanceId}" class="widget-button help-toggle">?</button>
96
+ <button id="reset-camera-btn-${instanceId}" class="widget-button reset-camera-btn">
97
+ <span class="reset-icon">⟲</span>
98
+ </button>
99
  ${tooltipsButtonHTML}
100
  <div id="menu-content-${instanceId}" class="menu-content">
101
  <span id="help-close-${instanceId}" class="help-close">×</span>
102
  <div class="help-text"></div>
103
  </div>
104
  </div>
105
+
106
+ <div id="tooltip-panel-${instanceId}" class="tooltip-panel" style="display: none;">
107
  <div class="tooltip-content">
108
+ <span id="tooltip-close-${instanceId}" class="tooltip-close">×</span>
109
+ <div id="tooltip-text-${instanceId}" class="tooltip-text"></div>
110
+ <img id="tooltip-image-${instanceId}" class="tooltip-image" src="" alt="" style="display: none;" />
111
  </div>
112
  </div>
113
  `;
114
 
115
+ // Insérer dans le DOM
116
  scriptTag.parentNode.appendChild(widgetContainer);
117
 
118
+ // 6) Références DOM de l’instance
119
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
120
  const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
121
  const helpToggle = document.getElementById('help-toggle-' + instanceId);
 
125
  const menuContent = document.getElementById('menu-content-' + instanceId);
126
  const helpTextDiv = menuContent.querySelector('.help-text');
127
 
128
+ const tooltipPanel = document.getElementById('tooltip-panel-' + instanceId);
129
+ const tooltipTextDiv = document.getElementById('tooltip-text-' + instanceId);
130
+ const tooltipImage = document.getElementById('tooltip-image-' + instanceId);
131
+ const tooltipCloseBtn = document.getElementById('tooltip-close-' + instanceId);
 
132
 
133
+ // 7) Aide / textes
134
  const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
135
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
136
 
 
140
 
141
  if (isMobile) {
142
  helpTextDiv.innerHTML =
143
+ "- Déplacez vous en glissant deux doigts sur l'écran.<br>" +
144
+ "- Orbitez en glissant un doigt.<br>" +
145
  '- Zoomez en pinçant avec deux doigts.<br>' +
146
  tooltipInstruction +
147
  '- Cliquez sur ⟲ pour réinitialiser la caméra.<br>' +
 
156
  '- Cliquez sur ⇱ pour passer en plein écran.<br>';
157
  }
158
 
159
+ // 8) Sizing dynamique du panneau d’aide
160
  function setMenuContentMaxSize() {
161
  if (!isMobile) {
162
  menuContent.style.maxWidth = '';
 
168
  return;
169
  }
170
  const parent = viewerContainerElem;
171
+ if (parent) {
172
  const vw = parent.offsetWidth;
173
  const vh = parent.offsetHeight;
174
  if (vw && vh) {
 
186
  }
187
  }
188
  }
189
+
190
  setMenuContentMaxSize();
191
  window.addEventListener('resize', setMenuContentMaxSize);
192
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
193
  window.addEventListener('orientationchange', setMenuContentMaxSize);
194
 
195
+ // 9) Aide visible par défaut
196
  menuContent.style.display = 'block';
197
  viewerContainerElem.style.display = 'block';
198
 
199
+ // 10) Gestion du panneau tooltips
200
  let dragHide = null;
201
+
202
  function hideTooltipPanel() {
203
  if (dragHide) {
204
  viewerContainerElem.removeEventListener('pointermove', dragHide);
 
206
  }
207
  tooltipPanel.style.display = 'none';
208
  }
209
+
210
  function hideHelpPanel() {
211
  menuContent.style.display = 'none';
212
  }
213
 
214
+ // 11) Charger viewer.js (avec cache-busting par instance)
 
215
  let viewerModule;
216
  try {
217
+ const viewerUrl = `https://mikafil-viewer-sgos.static.hf.space/viewer.js?inst=${instanceId}`;
218
  viewerModule = await import(viewerUrl);
219
  await viewerModule.initializeViewer(config, instanceId);
220
  } catch (err) {
 
221
  return;
222
  }
223
 
224
  const canvasId = 'canvas-' + instanceId;
225
  const canvasEl = document.getElementById(canvasId);
226
 
227
+ // 12) Bouton tooltips : cacher si URL non valide
228
  if (tooltipsToggleBtn) {
229
  if (!config.tooltips_url) {
230
  tooltipsToggleBtn.style.display = 'none';
231
  } else {
232
  fetch(config.tooltips_url)
233
+ .then((resp) => {
234
+ if (!resp.ok) tooltipsToggleBtn.style.display = 'none';
235
+ })
236
+ .catch(() => {
237
+ tooltipsToggleBtn.style.display = 'none';
238
+ });
 
 
 
 
 
 
 
 
 
239
  }
 
 
 
 
 
 
 
 
240
  }
241
 
242
+ // 13) Interactions locales / tooltips
243
+ if (canvasEl) {
244
+ canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
245
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
246
 
247
+ document.addEventListener('tooltip-selected', (evt) => {
248
+ // Toujours afficher le panneau, annuler un hide différé si présent
249
+ if (dragHide) {
250
+ viewerContainerElem.removeEventListener('pointermove', dragHide);
251
+ dragHide = null;
252
+ }
253
 
254
+ const { title, description, imgUrl } = evt.detail || {};
255
+ tooltipTextDiv.innerHTML = `<strong>${title || ''}</strong><br>${description || ''}`;
256
 
257
+ // Forcer un repaint : nettoyer src avant de réassigner
258
+ tooltipImage.style.display = 'none';
259
+ tooltipImage.src = '';
260
 
261
+ if (imgUrl) {
262
+ tooltipImage.onload = () => {
263
+ tooltipImage.style.display = 'block';
264
+ };
265
+ tooltipImage.src = imgUrl;
266
+ } else {
267
+ tooltipImage.style.display = 'none';
268
+ }
269
 
270
+ tooltipPanel.style.display = 'flex';
271
 
272
+ // Fermer en cas de drag (après un petit délai pour éviter un flicker)
273
+ setTimeout(() => {
274
+ dragHide = (e) => {
275
+ if (
276
+ (e.pointerType === 'mouse' && e.buttons !== 0) ||
277
+ e.pointerType === 'touch'
278
+ ) {
279
+ hideTooltipPanel();
280
+ }
281
+ };
282
+ viewerContainerElem.addEventListener('pointermove', dragHide);
283
+ }, 100);
284
+ });
285
 
286
+ // 14) Fullscreen
287
  let isFullscreen = false;
288
  let savedState = null;
289
 
290
  function saveCurrentState() {
291
  if (isFullscreen) return;
292
+ const originalAspect =
293
+ widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
294
+
295
  savedState = {
296
  widget: {
297
  position: widgetContainer.style.position,
298
+ top: widgetContainer.style.top,
299
+ left: widgetContainer.style.left,
300
+ width: widgetContainer.style.width,
301
+ height: widgetContainer.style.height,
302
+ maxWidth: widgetContainer.style.maxWidth,
303
+ maxHeight: widgetContainer.style.maxHeight,
304
+ paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
305
+ margin: widgetContainer.style.margin
306
+ },
307
+ viewer: {
308
+ borderRadius: viewerContainerElem.style.borderRadius,
309
+ border: viewerContainerElem.style.border
310
+ }
311
+ };
312
+ }
313
+
314
+ function restoreOriginalStyles() {
315
  if (!savedState) return;
316
  const aspectToUse = savedState.widget.paddingBottom;
317
 
 
338
 
339
  if (viewerModule.app) {
340
  viewerModule.app.resizeCanvas(
341
+ viewerContainerElem.clientWidth,
342
+ viewerContainerElem.clientHeight
343
  );
344
  }
345
 
 
383
  applyFullscreenStyles();
384
  widgetContainer.classList.add('fake-fullscreen');
385
  } else if (widgetContainer.requestFullscreen) {
386
+ widgetContainer
387
+ .requestFullscreen()
388
  .then(applyFullscreenStyles)
389
  .catch(() => {
390
  applyFullscreenStyles();
 
398
 
399
  function exitFullscreen() {
400
  if (document.fullscreenElement === widgetContainer && document.exitFullscreen) {
401
+ document.exitFullscreen().catch(() => {});
402
  }
403
  widgetContainer.classList.remove('fake-fullscreen');
404
  restoreOriginalStyles();
 
423
  setMenuContentMaxSize();
424
  });
425
 
426
+ // 15) Aide / boutons
427
  helpToggle.addEventListener('click', (e) => {
428
  hideTooltipPanel();
429
  e.stopPropagation();
 
434
  setMenuContentMaxSize();
435
  }
436
  });
437
+
438
  helpCloseBtn.addEventListener('click', hideHelpPanel);
439
 
440
  resetCameraBtn.addEventListener('click', () => {
 
444
  }
445
  });
446
 
 
447
  if (tooltipsToggleBtn) {
448
  let tooltipsVisible = !!config.showTooltipsDefault;
449
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
 
451
  hideTooltipPanel();
452
  tooltipsVisible = !tooltipsVisible;
453
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
454
+ document.dispatchEvent(
455
+ new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } })
456
+ );
 
 
 
 
 
 
 
457
  });
458
  }
459
 
 
460
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
461
 
462
+ // 16) Échappement / resize
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
463
  document.addEventListener('keydown', (e) => {
464
+ if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) {
465
+ exitFullscreen();
466
+ }
467
  });
468
+
469
  window.addEventListener('resize', () => {
470
  if (viewerModule.app) {
471
  if (isFullscreen) {
472
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
473
+ } else {
474
+ viewerModule.app.resizeCanvas(
475
+ viewerContainerElem.clientWidth,
476
+ viewerContainerElem.clientHeight
477
+ );
478
+ }
479
+ }
480
  setMenuContentMaxSize();
481
  });
482
 
483
+ // 17) Init par défaut
484
  setTimeout(() => {
485
+ // Sauvegarder l’état non-fullscreen
486
  saveCurrentState();
487
+ // Propager l’état par défaut des tooltips
488
+ document.dispatchEvent(
489
+ new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } })
490
+ );
491
  setMenuContentMaxSize();
492
  }, 200);
493
+ })();