MikaFil commited on
Commit
2a66826
Β·
verified Β·
1 Parent(s): 10da0c1

Update interface.js

Browse files
Files changed (1) hide show
  1. interface.js +247 -191
interface.js CHANGED
@@ -34,14 +34,6 @@ const currentScriptTag = document.currentScript;
34
  return;
35
  }
36
  } else {
37
-
38
-
39
-
40
-
41
-
42
-
43
-
44
-
45
  return;
46
  }
47
 
@@ -53,7 +45,7 @@ const currentScriptTag = document.currentScript;
53
  document.head.appendChild(linkEl);
54
  }
55
 
56
- // 3) ID d’instance
57
  const instanceId = Math.random().toString(36).substr(2, 8);
58
 
59
  // 4) Aspect ratio
@@ -74,7 +66,7 @@ const currentScriptTag = document.currentScript;
74
  }
75
  } else {
76
  const parentContainer = scriptTag.parentNode;
77
- const containerWidth = parentContainer.offsetWidth;
78
  const containerHeight = parentContainer.offsetHeight;
79
  if (containerWidth > 0 && containerHeight > 0) {
80
  aspectPercent = (containerHeight / containerWidth) * 100 + '%';
@@ -93,7 +85,7 @@ const currentScriptTag = document.currentScript;
93
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">β¦Ώ</button>`
94
  : '';
95
 
96
- // HTML du widget (IDs spΓ©cifiques Γ  l’instance)
97
  widgetContainer.innerHTML = `
98
  <div id="viewer-container-${instanceId}" class="viewer-container">
99
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
@@ -110,46 +102,31 @@ const currentScriptTag = document.currentScript;
110
  <div class="help-text"></div>
111
  </div>
112
  </div>
113
-
114
- <div id="tooltip-panel-${instanceId}" class="tooltip-panel" style="display: none;">
115
- <div class="tooltip-content">
116
- <span id="tooltip-close-${instanceId}" class="tooltip-close">Γ—</span>
117
- <div id="tooltip-text-${instanceId}" class="tooltip-text"></div>
118
- <img id="tooltip-image-${instanceId}" class="tooltip-image" src="" alt="" style="display: none;" />
119
- </div>
120
- </div>
121
  `;
122
 
123
  // InsΓ©rer dans le DOM
124
  scriptTag.parentNode.appendChild(widgetContainer);
125
 
126
- // 6) RΓ©fΓ©rences DOM de l’instance
127
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
128
- const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
129
- const helpToggle = document.getElementById('help-toggle-' + instanceId);
130
- const helpCloseBtn = document.getElementById('help-close-' + instanceId);
131
- const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
132
- const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
133
- const menuContent = document.getElementById('menu-content-' + instanceId);
134
- const helpTextDiv = menuContent.querySelector('.help-text');
135
-
136
- const tooltipPanel = document.getElementById('tooltip-panel-' + instanceId);
137
- const tooltipTextDiv = document.getElementById('tooltip-text-' + instanceId);
138
- const tooltipImage = document.getElementById('tooltip-image-' + instanceId);
139
- const tooltipCloseBtn = document.getElementById('tooltip-close-' + instanceId);
140
 
141
  // 7) Aide / textes
142
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
143
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
144
 
145
- const tooltipInstruction = config.tooltips_url
146
- ? '- β¦Ώ : annotations.<br>'
147
- : '';
148
 
149
  if (isMobile) {
150
  helpTextDiv.innerHTML =
151
- "- DΓ©placement avec deux doigts.<br>" +
152
- "- Rotation avec un doigt.<br>" +
153
  '- Zoom en pinΓ§ant avec deux doigts.<br>' +
154
  tooltipInstruction +
155
  '- ⟲ rΓ©initialisation de la camΓ©ra.<br>' +
@@ -164,159 +141,254 @@ const currentScriptTag = document.currentScript;
164
  '- ⇱ plein Γ©cran.<br>';
165
  }
166
 
167
- // 8) Sizing dynamique du panneau d’aide
168
  function setMenuContentMaxSize() {
169
  if (!isMobile) {
170
- menuContent.style.maxWidth = '';
171
  menuContent.style.maxHeight = '';
172
- menuContent.style.width = '';
173
- menuContent.style.height = '';
174
  menuContent.style.overflowY = '';
175
  menuContent.style.overflowX = '';
176
  return;
177
  }
178
- const parent = viewerContainerElem;
179
- if (parent) {
180
- const vw = parent.offsetWidth;
181
- const vh = parent.offsetHeight;
182
- if (vw && vh) {
183
- menuContent.style.maxWidth = Math.round(vw * 0.8) + 'px';
184
- menuContent.style.maxHeight = Math.round(vh * 0.8) + 'px';
185
- menuContent.style.width = '';
186
- menuContent.style.height = '';
187
- menuContent.style.overflowY = 'auto';
188
- menuContent.style.overflowX = 'auto';
189
- } else {
190
- menuContent.style.maxWidth = '80vw';
191
- menuContent.style.maxHeight = '80vh';
192
- menuContent.style.overflowY = 'auto';
193
- menuContent.style.overflowX = 'auto';
194
- }
195
  }
196
  }
197
 
198
-
199
  setMenuContentMaxSize();
200
- window.addEventListener('resize', setMenuContentMaxSize);
201
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
202
  window.addEventListener('orientationchange', setMenuContentMaxSize);
203
 
204
  // 9) Aide visible par dΓ©faut
205
- menuContent.style.display = 'block';
206
  viewerContainerElem.style.display = 'block';
207
 
208
- // 10) Gestion du panneau tooltips
209
- let dragHide = null;
210
-
211
- function hideTooltipPanel() {
212
- if (dragHide) {
213
- viewerContainerElem.removeEventListener('pointermove', dragHide);
214
- dragHide = null;
215
-
216
- }
217
- tooltipPanel.style.display = 'none';
218
- }
219
-
220
- function hideHelpPanel() {
221
- menuContent.style.display = 'none';
222
- }
223
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
  // 11) Charger viewer.js (avec cache-busting par instance)
 
225
  let viewerModule;
226
  try {
227
- const viewerUrl = `https://mikafil-sog-viewer.static.hf.space/viewer.js?inst=${instanceId}`;
228
  viewerModule = await import(viewerUrl);
229
  await viewerModule.initializeViewer(config, instanceId);
230
  } catch (err) {
231
  return;
232
  }
233
 
234
- const canvasId = 'canvas-' + instanceId;
235
- const canvasEl = document.getElementById(canvasId);
236
-
237
  // 12) Bouton tooltips : cacher si URL non valide
238
  if (tooltipsToggleBtn) {
239
  if (!config.tooltips_url) {
240
  tooltipsToggleBtn.style.display = 'none';
241
  } else {
242
  fetch(config.tooltips_url)
243
- .then((resp) => {
244
- if (!resp.ok) tooltipsToggleBtn.style.display = 'none';
245
- })
246
- .catch(() => {
247
- tooltipsToggleBtn.style.display = 'none';
248
- });
249
  }
250
  }
251
 
252
- // 13) Interactions locales / tooltips
253
- if (canvasEl) {
254
- canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
 
 
 
 
 
 
 
 
255
  }
256
 
257
- document.addEventListener('tooltip-selected', (evt) => {
258
- // Toujours afficher le panneau, annuler un hide diffΓ©rΓ© si prΓ©sent
259
- if (dragHide) {
260
- viewerContainerElem.removeEventListener('pointermove', dragHide);
261
- dragHide = null;
262
- }
263
 
264
- const { title, description, imgUrl } = evt.detail || {};
265
- tooltipTextDiv.innerHTML = `<strong>${title || ''}</strong><br>${description || ''}`;
 
266
 
267
- // Forcer un repaint : nettoyer src avant de rΓ©assigner
268
- tooltipImage.style.display = 'none';
269
- tooltipImage.src = '';
 
270
 
 
271
  if (imgUrl) {
272
- tooltipImage.onload = () => {
273
- tooltipImage.style.display = 'block';
274
- };
275
- tooltipImage.src = imgUrl;
 
 
 
 
 
 
 
 
 
276
  } else {
277
- tooltipImage.style.display = 'none';
278
  }
279
 
280
- tooltipPanel.style.display = 'flex';
281
-
282
- // Fermer en cas de drag (après un petit délai pour éviter un flicker)
283
- setTimeout(() => {
284
- dragHide = (e) => {
285
- if (
286
- (e.pointerType === 'mouse' && e.buttons !== 0) ||
287
- e.pointerType === 'touch'
288
- ) {
289
- hideTooltipPanel();
290
- }
291
- };
292
- viewerContainerElem.addEventListener('pointermove', dragHide);
293
- }, 100);
294
  });
295
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
296
  // 14) Fullscreen
 
297
  let isFullscreen = false;
298
- let savedState = null;
299
 
300
  function saveCurrentState() {
301
  if (isFullscreen) return;
302
- const originalAspect =
303
- widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
304
-
305
  savedState = {
306
  widget: {
307
- position: widgetContainer.style.position,
308
- top: widgetContainer.style.top,
309
- left: widgetContainer.style.left,
310
- width: widgetContainer.style.width,
311
- height: widgetContainer.style.height,
312
- maxWidth: widgetContainer.style.maxWidth,
313
- maxHeight: widgetContainer.style.maxHeight,
314
  paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
315
- margin: widgetContainer.style.margin
316
  },
317
  viewer: {
318
  borderRadius: viewerContainerElem.style.borderRadius,
319
- border: viewerContainerElem.style.border
320
  }
321
  };
322
  }
@@ -325,64 +397,58 @@ const currentScriptTag = document.currentScript;
325
  if (!savedState) return;
326
  const aspectToUse = savedState.widget.paddingBottom;
327
 
328
- widgetContainer.style.position = savedState.widget.position || '';
329
- widgetContainer.style.top = savedState.widget.top || '';
330
- widgetContainer.style.left = savedState.widget.left || '';
331
- widgetContainer.style.width = '100%';
332
- widgetContainer.style.height = '0';
333
- widgetContainer.style.maxWidth = savedState.widget.maxWidth || '';
334
- widgetContainer.style.maxHeight = savedState.widget.maxHeight || '';
335
  widgetContainer.style.paddingBottom = aspectToUse;
336
- widgetContainer.style.margin = savedState.widget.margin || '';
337
  widgetContainer.classList.remove('fake-fullscreen');
338
 
339
- viewerContainerElem.style.position = 'absolute';
340
- viewerContainerElem.style.top = '0';
341
- viewerContainerElem.style.left = '0';
342
- viewerContainerElem.style.right = '0';
343
- viewerContainerElem.style.bottom = '0';
344
- viewerContainerElem.style.width = '100%';
345
- viewerContainerElem.style.height = '100%';
346
  viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || '';
347
- viewerContainerElem.style.border = savedState.viewer.border || '';
348
 
349
  if (viewerModule.app) {
350
- viewerModule.app.resizeCanvas(
351
- viewerContainerElem.clientWidth,
352
- viewerContainerElem.clientHeight
353
- );
354
  }
355
 
356
  if (fullscreenToggle) fullscreenToggle.textContent = '⇱';
357
-
358
  savedState = null;
359
  setMenuContentMaxSize();
360
  }
361
 
362
-
363
  function applyFullscreenStyles() {
364
- widgetContainer.style.position = 'fixed';
365
- widgetContainer.style.top = '0';
366
- widgetContainer.style.left = '0';
367
- widgetContainer.style.width = '100vw';
368
- widgetContainer.style.height = '100vh';
369
- widgetContainer.style.maxWidth = '100vw';
370
  widgetContainer.style.maxHeight = '100vh';
371
  widgetContainer.style.paddingBottom = '0';
372
- widgetContainer.style.margin = '0';
373
- widgetContainer.style.border = 'none';
374
  widgetContainer.style.borderRadius = '0';
375
 
376
- viewerContainerElem.style.width = '100%';
377
- viewerContainerElem.style.height = '100%';
378
  viewerContainerElem.style.borderRadius = '0';
379
- viewerContainerElem.style.border = 'none';
380
 
381
  if (viewerModule.app) {
382
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
383
  }
384
 
385
-
386
  if (fullscreenToggle) fullscreenToggle.textContent = '⇲';
387
  isFullscreen = true;
388
  setMenuContentMaxSize();
@@ -420,7 +486,7 @@ const currentScriptTag = document.currentScript;
420
  }
421
 
422
  fullscreenToggle.addEventListener('click', () => {
423
- hideTooltipPanel();
424
  isFullscreen ? exitFullscreen() : enterFullscreen();
425
  });
426
 
@@ -435,9 +501,12 @@ const currentScriptTag = document.currentScript;
435
  setMenuContentMaxSize();
436
  });
437
 
438
- // 15) Aide / boutons
 
 
 
439
  helpToggle.addEventListener('click', (e) => {
440
- hideTooltipPanel();
441
  e.stopPropagation();
442
  if (menuContent.style.display === 'block') {
443
  menuContent.style.display = 'none';
@@ -450,32 +519,24 @@ const currentScriptTag = document.currentScript;
450
  helpCloseBtn.addEventListener('click', hideHelpPanel);
451
 
452
  resetCameraBtn.addEventListener('click', () => {
453
- hideTooltipPanel();
454
- if (viewerModule.resetViewerCamera) {
455
- viewerModule.resetViewerCamera();
456
- }
457
  });
458
 
459
  if (tooltipsToggleBtn) {
460
  let tooltipsVisible = !!config.showTooltipsDefault;
461
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
462
  tooltipsToggleBtn.addEventListener('click', () => {
463
- hideTooltipPanel();
464
  tooltipsVisible = !tooltipsVisible;
465
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
466
- document.dispatchEvent(
467
- new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } })
468
- );
469
  });
470
  }
471
 
472
- tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
473
-
474
  // 16) Γ‰chappement / resize
475
  document.addEventListener('keydown', (e) => {
476
- if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) {
477
- exitFullscreen();
478
- }
479
  });
480
 
481
  window.addEventListener('resize', () => {
@@ -483,10 +544,7 @@ const currentScriptTag = document.currentScript;
483
  if (isFullscreen) {
484
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
485
  } else {
486
- viewerModule.app.resizeCanvas(
487
- viewerContainerElem.clientWidth,
488
- viewerContainerElem.clientHeight
489
- );
490
  }
491
  }
492
  setMenuContentMaxSize();
@@ -494,12 +552,10 @@ const currentScriptTag = document.currentScript;
494
 
495
  // 17) Init par dΓ©faut
496
  setTimeout(() => {
497
- // Sauvegarder l’état non-fullscreen
498
  saveCurrentState();
499
- // Propager l’état par dΓ©faut des tooltips
500
  document.dispatchEvent(
501
  new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } })
502
  );
503
  setMenuContentMaxSize();
504
  }, 200);
505
- })();
 
34
  return;
35
  }
36
  } else {
 
 
 
 
 
 
 
 
37
  return;
38
  }
39
 
 
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
 
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 + '%';
 
85
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">β¦Ώ</button>`
86
  : '';
87
 
88
+ // HTML du widget (sans panneau tooltip β€” gΓ©rΓ© sΓ©parΓ©ment ci-dessous)
89
  widgetContainer.innerHTML = `
90
  <div id="viewer-container-${instanceId}" class="viewer-container">
91
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
 
102
  <div class="help-text"></div>
103
  </div>
104
  </div>
 
 
 
 
 
 
 
 
105
  `;
106
 
107
  // InsΓ©rer dans le DOM
108
  scriptTag.parentNode.appendChild(widgetContainer);
109
 
110
+ // 6) RΓ©fΓ©rences DOM de l'instance
111
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
112
+ const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
113
+ const helpToggle = document.getElementById('help-toggle-' + instanceId);
114
+ const helpCloseBtn = document.getElementById('help-close-' + instanceId);
115
+ const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
116
+ const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
117
+ const menuContent = document.getElementById('menu-content-' + instanceId);
118
+ const helpTextDiv = menuContent.querySelector('.help-text');
 
 
 
 
 
119
 
120
  // 7) Aide / textes
121
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
122
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
123
 
124
+ const tooltipInstruction = config.tooltips_url ? '- β¦Ώ : annotations.<br>' : '';
 
 
125
 
126
  if (isMobile) {
127
  helpTextDiv.innerHTML =
128
+ '- DΓ©placement avec deux doigts.<br>' +
129
+ '- Rotation avec un doigt.<br>' +
130
  '- Zoom en pinΓ§ant avec deux doigts.<br>' +
131
  tooltipInstruction +
132
  '- ⟲ rΓ©initialisation de la camΓ©ra.<br>' +
 
141
  '- ⇱ plein Γ©cran.<br>';
142
  }
143
 
144
+ // 8) Sizing dynamique du panneau d'aide
145
  function setMenuContentMaxSize() {
146
  if (!isMobile) {
147
+ menuContent.style.maxWidth = '';
148
  menuContent.style.maxHeight = '';
149
+ menuContent.style.width = '';
150
+ menuContent.style.height = '';
151
  menuContent.style.overflowY = '';
152
  menuContent.style.overflowX = '';
153
  return;
154
  }
155
+ const vw = viewerContainerElem.offsetWidth;
156
+ const vh = viewerContainerElem.offsetHeight;
157
+ if (vw && vh) {
158
+ menuContent.style.maxWidth = Math.round(vw * 0.8) + 'px';
159
+ menuContent.style.maxHeight = Math.round(vh * 0.8) + 'px';
160
+ menuContent.style.width = '';
161
+ menuContent.style.height = '';
162
+ menuContent.style.overflowY = 'auto';
163
+ menuContent.style.overflowX = 'auto';
164
+ } else {
165
+ menuContent.style.maxWidth = '80vw';
166
+ menuContent.style.maxHeight = '80vh';
167
+ menuContent.style.overflowY = 'auto';
168
+ menuContent.style.overflowX = 'auto';
 
 
 
169
  }
170
  }
171
 
 
172
  setMenuContentMaxSize();
173
+ window.addEventListener('resize', setMenuContentMaxSize);
174
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
175
  window.addEventListener('orientationchange', setMenuContentMaxSize);
176
 
177
  // 9) Aide visible par dΓ©faut
178
+ menuContent.style.display = 'block';
179
  viewerContainerElem.style.display = 'block';
180
 
181
+ // ─────────────────────────────────────────────────────────────────────────────
182
+ // 10) Overlay d'annotation (style viewer B β€” dark theme, lightbox, lien externe)
183
+ // Appended dans widgetContainer β†’ fonctionne en fullscreen natif, fake et normal.
184
+ // ─────────────────────────────────────────────────────────────────────────────
185
+
186
+ // β€” Overlay principal β€”
187
+ const annOverlay = document.createElement('div');
188
+ annOverlay.className = 'ann-overlay';
189
+
190
+ // β€” Modal tooltip β€”
191
+ const annTooltip = document.createElement('div');
192
+ annTooltip.className = 'ann-tooltip';
193
+
194
+ const annCloseBtn = document.createElement('button');
195
+ annCloseBtn.className = 'ann-close-btn-base ann-close-btn';
196
+ annCloseBtn.textContent = 'Γ—';
197
+ annCloseBtn.setAttribute('aria-label', 'Fermer');
198
+
199
+ const annImgWrap = document.createElement('div');
200
+ annImgWrap.className = 'ann-img-wrap';
201
+ const annImg = document.createElement('img');
202
+ annImg.alt = '';
203
+ annImgWrap.appendChild(annImg);
204
+
205
+ const annScroll = document.createElement('div');
206
+ annScroll.className = 'ann-scroll';
207
+ const annTitle = document.createElement('div');
208
+ annTitle.className = 'ann-title';
209
+ const annBody = document.createElement('div');
210
+ annBody.className = 'ann-body';
211
+ annScroll.appendChild(annTitle);
212
+ annScroll.appendChild(annBody);
213
+
214
+ const annFooter = document.createElement('div');
215
+ annFooter.className = 'ann-footer';
216
+ const annLinkBtn = document.createElement('button');
217
+ annLinkBtn.className = 'ann-link-btn';
218
+ annFooter.appendChild(annLinkBtn);
219
+
220
+ annTooltip.appendChild(annCloseBtn);
221
+ annTooltip.appendChild(annImgWrap);
222
+ annTooltip.appendChild(annScroll);
223
+ annTooltip.appendChild(annFooter);
224
+ annOverlay.appendChild(annTooltip);
225
+
226
+ // β€” Lightbox β€”
227
+ const annLightbox = document.createElement('div');
228
+ annLightbox.className = 'ann-lightbox';
229
+
230
+ const annLbWrap = document.createElement('div');
231
+ annLbWrap.className = 'ann-lb-content-wrap';
232
+ const annLbImg = document.createElement('img');
233
+ annLbImg.alt = '';
234
+ const annLbClose = document.createElement('button');
235
+ annLbClose.className = 'ann-close-btn-base ann-lb-close';
236
+ annLbClose.textContent = 'Γ—';
237
+ annLbClose.setAttribute('aria-label', 'Fermer');
238
+ annLbWrap.appendChild(annLbImg);
239
+ annLbWrap.appendChild(annLbClose);
240
+ annLightbox.appendChild(annLbWrap);
241
+
242
+ // Injection dans widgetContainer (fonctionne en fullscreen natif)
243
+ widgetContainer.appendChild(annOverlay);
244
+ widgetContainer.appendChild(annLightbox);
245
+
246
+ // ─────────────────────────────────────────────────────────────────────────────
247
  // 11) Charger viewer.js (avec cache-busting par instance)
248
+ // ─────────────────────────────────────────────────────────────────────────────
249
  let viewerModule;
250
  try {
251
+ const viewerUrl = `https://mikafil-viewer-sgos.static.hf.space/viewer.js?inst=${instanceId}`;
252
  viewerModule = await import(viewerUrl);
253
  await viewerModule.initializeViewer(config, instanceId);
254
  } catch (err) {
255
  return;
256
  }
257
 
 
 
 
258
  // 12) Bouton tooltips : cacher si URL non valide
259
  if (tooltipsToggleBtn) {
260
  if (!config.tooltips_url) {
261
  tooltipsToggleBtn.style.display = 'none';
262
  } else {
263
  fetch(config.tooltips_url)
264
+ .then((resp) => { if (!resp.ok) tooltipsToggleBtn.style.display = 'none'; })
265
+ .catch(() => { tooltipsToggleBtn.style.display = 'none'; });
 
 
 
 
266
  }
267
  }
268
 
269
+ // ─────────────────────────────────────────────────────────────────────────────
270
+ // 13) Gestion de l'annotation (tooltip-selected β†’ overlay)
271
+ // ─────────────────────────────────────────────────────────────────────────────
272
+
273
+ let currentLinkUrl = '';
274
+ let annIsOpen = false;
275
+
276
+ function hideAnnotation() {
277
+ annIsOpen = false;
278
+ annOverlay.classList.remove('ann-open');
279
+ annLightbox.classList.remove('ann-lb-open');
280
  }
281
 
282
+ function hideHelpPanel() {
283
+ menuContent.style.display = 'none';
284
+ }
 
 
 
285
 
286
+ // Affichage de l'annotation
287
+ document.addEventListener('tooltip-selected', (evt) => {
288
+ const { title, description, imgUrl, linkUrl, linkText } = evt.detail || {};
289
 
290
+ annTitle.textContent = title || '';
291
+ annBody.textContent = description || '';
292
+ annScroll.scrollTop = 0;
293
+ currentLinkUrl = linkUrl || '';
294
 
295
+ // Image
296
  if (imgUrl) {
297
+ annImg.src = imgUrl;
298
+ annLbImg.src = imgUrl;
299
+ annImgWrap.style.display = '';
300
+ } else {
301
+ annImg.src = '';
302
+ annLbImg.src = '';
303
+ annImgWrap.style.display = 'none';
304
+ }
305
+
306
+ // Bouton lien
307
+ if (currentLinkUrl) {
308
+ annLinkBtn.textContent = linkText || 'En savoir plus';
309
+ annFooter.style.display = 'flex';
310
  } else {
311
+ annFooter.style.display = 'none';
312
  }
313
 
314
+ annIsOpen = true;
315
+ annOverlay.classList.add('ann-open');
 
 
 
 
 
 
 
 
 
 
 
 
316
  });
317
 
318
+ // Fermeture annotation
319
+ annCloseBtn.addEventListener('click', (e) => { e.stopPropagation(); hideAnnotation(); });
320
+ annOverlay.addEventListener('click', (e) => { if (e.target === annOverlay) hideAnnotation(); });
321
+
322
+ // Lien externe
323
+ annLinkBtn.addEventListener('click', (e) => {
324
+ e.stopPropagation();
325
+ if (currentLinkUrl) window.open(currentLinkUrl, '_blank');
326
+ });
327
+
328
+ // Clic sur image β†’ lightbox
329
+ annImgWrap.addEventListener('click', (e) => {
330
+ e.stopPropagation();
331
+ if (annImg.src) annLightbox.classList.add('ann-lb-open');
332
+ });
333
+
334
+ // Fermeture lightbox
335
+ annLbClose.addEventListener('click', (e) => {
336
+ e.stopPropagation();
337
+ annLightbox.classList.remove('ann-lb-open');
338
+ });
339
+ annLightbox.addEventListener('click', (e) => {
340
+ if (e.target === annLightbox || e.target === annLbWrap) {
341
+ annLightbox.classList.remove('ann-lb-open');
342
+ }
343
+ });
344
+
345
+ // Bloqueurs d'input camΓ©ra quand l'overlay est ouvert
346
+ // (l'overlay couvre le canvas β†’ les clics/pointeurs sont dΓ©jΓ  interceptΓ©s ;
347
+ // on bloque uniquement wheel/touch qui peuvent atteindre PlayCanvas en capture)
348
+ const blockForCamera = (eventName, allowScrollOnBody) => {
349
+ widgetContainer.addEventListener(eventName, (e) => {
350
+ if (!annIsOpen) return;
351
+ const inTooltip = annTooltip.contains(e.target);
352
+ const lbOpen = annLightbox.classList.contains('ann-lb-open');
353
+ const inLightbox = lbOpen && annLightbox.contains(e.target);
354
+ if (inTooltip || inLightbox) {
355
+ e.stopImmediatePropagation();
356
+ // Sur ann-scroll : autoriser le scroll natif du navigateur
357
+ if (allowScrollOnBody && annScroll.contains(e.target)) return;
358
+ e.preventDefault();
359
+ }
360
+ }, { capture: true, passive: false });
361
+ };
362
+
363
+ blockForCamera('wheel', true);
364
+ blockForCamera('touchstart', false);
365
+ blockForCamera('touchmove', true);
366
+ blockForCamera('touchend', false);
367
+
368
+ // ─────────────────────────────────────────────────────────────────────────────
369
  // 14) Fullscreen
370
+ // ─────────────────────────────────────────────────────────────────────────────
371
  let isFullscreen = false;
372
+ let savedState = null;
373
 
374
  function saveCurrentState() {
375
  if (isFullscreen) return;
376
+ const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
 
 
377
  savedState = {
378
  widget: {
379
+ position: widgetContainer.style.position,
380
+ top: widgetContainer.style.top,
381
+ left: widgetContainer.style.left,
382
+ width: widgetContainer.style.width,
383
+ height: widgetContainer.style.height,
384
+ maxWidth: widgetContainer.style.maxWidth,
385
+ maxHeight: widgetContainer.style.maxHeight,
386
  paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
387
+ margin: widgetContainer.style.margin
388
  },
389
  viewer: {
390
  borderRadius: viewerContainerElem.style.borderRadius,
391
+ border: viewerContainerElem.style.border
392
  }
393
  };
394
  }
 
397
  if (!savedState) return;
398
  const aspectToUse = savedState.widget.paddingBottom;
399
 
400
+ widgetContainer.style.position = savedState.widget.position || '';
401
+ widgetContainer.style.top = savedState.widget.top || '';
402
+ widgetContainer.style.left = savedState.widget.left || '';
403
+ widgetContainer.style.width = '100%';
404
+ widgetContainer.style.height = '0';
405
+ widgetContainer.style.maxWidth = savedState.widget.maxWidth || '';
406
+ widgetContainer.style.maxHeight = savedState.widget.maxHeight || '';
407
  widgetContainer.style.paddingBottom = aspectToUse;
408
+ widgetContainer.style.margin = savedState.widget.margin || '';
409
  widgetContainer.classList.remove('fake-fullscreen');
410
 
411
+ viewerContainerElem.style.position = 'absolute';
412
+ viewerContainerElem.style.top = '0';
413
+ viewerContainerElem.style.left = '0';
414
+ viewerContainerElem.style.right = '0';
415
+ viewerContainerElem.style.bottom = '0';
416
+ viewerContainerElem.style.width = '100%';
417
+ viewerContainerElem.style.height = '100%';
418
  viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || '';
419
+ viewerContainerElem.style.border = savedState.viewer.border || '';
420
 
421
  if (viewerModule.app) {
422
+ viewerModule.app.resizeCanvas(viewerContainerElem.clientWidth, viewerContainerElem.clientHeight);
 
 
 
423
  }
424
 
425
  if (fullscreenToggle) fullscreenToggle.textContent = '⇱';
 
426
  savedState = null;
427
  setMenuContentMaxSize();
428
  }
429
 
 
430
  function applyFullscreenStyles() {
431
+ widgetContainer.style.position = 'fixed';
432
+ widgetContainer.style.top = '0';
433
+ widgetContainer.style.left = '0';
434
+ widgetContainer.style.width = '100vw';
435
+ widgetContainer.style.height = '100vh';
436
+ widgetContainer.style.maxWidth = '100vw';
437
  widgetContainer.style.maxHeight = '100vh';
438
  widgetContainer.style.paddingBottom = '0';
439
+ widgetContainer.style.margin = '0';
440
+ widgetContainer.style.border = 'none';
441
  widgetContainer.style.borderRadius = '0';
442
 
443
+ viewerContainerElem.style.width = '100%';
444
+ viewerContainerElem.style.height = '100%';
445
  viewerContainerElem.style.borderRadius = '0';
446
+ viewerContainerElem.style.border = 'none';
447
 
448
  if (viewerModule.app) {
449
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
450
  }
451
 
 
452
  if (fullscreenToggle) fullscreenToggle.textContent = '⇲';
453
  isFullscreen = true;
454
  setMenuContentMaxSize();
 
486
  }
487
 
488
  fullscreenToggle.addEventListener('click', () => {
489
+ hideAnnotation();
490
  isFullscreen ? exitFullscreen() : enterFullscreen();
491
  });
492
 
 
501
  setMenuContentMaxSize();
502
  });
503
 
504
+ // ─────────────────────────────────────────────────────────────────────────────
505
+ // 15) Boutons d'interface
506
+ // ─────────────────────────────────────────────────────────────────────────────
507
+
508
  helpToggle.addEventListener('click', (e) => {
509
+ hideAnnotation();
510
  e.stopPropagation();
511
  if (menuContent.style.display === 'block') {
512
  menuContent.style.display = 'none';
 
519
  helpCloseBtn.addEventListener('click', hideHelpPanel);
520
 
521
  resetCameraBtn.addEventListener('click', () => {
522
+ hideAnnotation();
523
+ if (viewerModule.resetViewerCamera) viewerModule.resetViewerCamera();
 
 
524
  });
525
 
526
  if (tooltipsToggleBtn) {
527
  let tooltipsVisible = !!config.showTooltipsDefault;
528
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
529
  tooltipsToggleBtn.addEventListener('click', () => {
530
+ hideAnnotation();
531
  tooltipsVisible = !tooltipsVisible;
532
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
533
+ document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } }));
 
 
534
  });
535
  }
536
 
 
 
537
  // 16) Γ‰chappement / resize
538
  document.addEventListener('keydown', (e) => {
539
+ if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
 
 
540
  });
541
 
542
  window.addEventListener('resize', () => {
 
544
  if (isFullscreen) {
545
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
546
  } else {
547
+ viewerModule.app.resizeCanvas(viewerContainerElem.clientWidth, viewerContainerElem.clientHeight);
 
 
 
548
  }
549
  }
550
  setMenuContentMaxSize();
 
552
 
553
  // 17) Init par dΓ©faut
554
  setTimeout(() => {
 
555
  saveCurrentState();
 
556
  document.dispatchEvent(
557
  new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } })
558
  );
559
  setMenuContentMaxSize();
560
  }, 200);
561
+ })();