MikaFil commited on
Commit
4eefe5f
·
verified ·
1 Parent(s): f61f85f

Update interface.js

Browse files
Files changed (1) hide show
  1. interface.js +245 -157
interface.js CHANGED
@@ -3,13 +3,17 @@
3
 
4
  const currentScriptTag = document.currentScript;
5
 
6
- (async function() {
7
- // 1. Locate the <script> and read 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
  }
@@ -21,61 +25,104 @@ const currentScriptTag = document.currentScript;
21
 
22
  const configUrl = scriptTag.getAttribute('data-config');
23
  let config = {};
 
24
  if (configUrl) {
25
  try {
26
  const response = await fetch(configUrl);
27
  config = await response.json();
28
  } catch (error) {
29
- console.error("Could not load config: " + error);
30
  return;
31
  }
32
  } else {
33
- console.error("No configUrl found for viewer");
 
 
 
 
 
 
 
34
  return;
35
  }
36
 
37
- // 2. Inject CSS if specified
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
38
  if (config.css_url) {
39
  const linkEl = document.createElement('link');
40
- linkEl.rel = "stylesheet";
41
  linkEl.href = config.css_url;
42
  document.head.appendChild(linkEl);
43
  }
44
 
45
- // 3. Setup unique instanceId for this widget
46
  const instanceId = Math.random().toString(36).substr(2, 8);
47
 
48
- // 4. Calculate aspect ratio (for padding-bottom hack)
49
- let aspectPercent = "100%";
50
  if (config.aspect) {
51
- if (config.aspect.includes(":")) {
52
- const parts = config.aspect.split(":");
53
  const w = parseFloat(parts[0]);
54
  const h = parseFloat(parts[1]);
55
  if (!isNaN(w) && !isNaN(h) && w > 0) {
56
- aspectPercent = (h / w * 100) + "%";
57
  }
58
  } else {
59
  const aspectValue = parseFloat(config.aspect);
60
  if (!isNaN(aspectValue) && aspectValue > 0) {
61
- aspectPercent = (100 / aspectValue) + "%";
62
  }
63
  }
64
  } else {
65
- // fallback: try to use parent container's aspect
66
  const parentContainer = scriptTag.parentNode;
67
- const containerWidth = parentContainer.offsetWidth;
68
  const containerHeight = parentContainer.offsetHeight;
69
  if (containerWidth > 0 && containerHeight > 0) {
70
- aspectPercent = (containerHeight / containerWidth * 100) + "%";
71
  }
72
  }
73
 
74
- // 5. Create the widget container and controls
75
  const widgetContainer = document.createElement('div');
76
  widgetContainer.id = 'ply-widget-container-' + instanceId;
77
  widgetContainer.classList.add('ply-widget-container');
78
- widgetContainer.style.height = "0";
79
  widgetContainer.style.paddingBottom = aspectPercent;
80
  widgetContainer.setAttribute('data-original-aspect', aspectPercent);
81
 
@@ -83,6 +130,7 @@ const currentScriptTag = document.currentScript;
83
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>`
84
  : '';
85
 
 
86
  widgetContainer.innerHTML = `
87
  <div id="viewer-container-${instanceId}" class="viewer-container">
88
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
@@ -99,155 +147,213 @@ const currentScriptTag = document.currentScript;
99
  <div class="help-text"></div>
100
  </div>
101
  </div>
102
- <div id="tooltip-panel" class="tooltip-panel" style="display: none;">
 
103
  <div class="tooltip-content">
104
- <span id="tooltip-close" class="tooltip-close">×</span>
105
- <div id="tooltip-text" class="tooltip-text"></div>
106
- <img id="tooltip-image" class="tooltip-image" src="" alt="" style="display: none;" />
107
  </div>
108
  </div>
109
  `;
110
 
 
111
  scriptTag.parentNode.appendChild(widgetContainer);
112
 
113
- // 6. Grab references for UI controls
114
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
115
- const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
116
- const helpToggle = document.getElementById('help-toggle-' + instanceId);
117
- const helpCloseBtn = document.getElementById('help-close-' + instanceId);
118
- const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
119
- const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
120
- const menuContent = document.getElementById('menu-content-' + instanceId);
121
- const helpTextDiv = menuContent.querySelector('.help-text');
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
- const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
 
 
128
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
129
 
130
  const tooltipInstruction = config.tooltips_url
131
- ? '- Cliquez sur ⦿ pour afficher/masquer les tooltips.<br>'
132
  : '';
133
 
134
  if (isMobile) {
135
- helpTextDiv.innerHTML =
136
- '- Déplacez vous en glissant deux doigts sur l\'écran.<br>' +
137
- '- Orbitez en glissant un doigt.<br>' +
138
- '- Zoomez en pinçant avec deux doigts.<br>' +
139
  tooltipInstruction +
140
- '- Cliquez sur pour réinitialiser la caméra.<br>' +
141
- '- Cliquez sur pour passer en plein écran.<br>';
142
  } else {
143
- helpTextDiv.innerHTML =
144
- '- Orbitez avec le clic droit<br>' +
145
- '- Zoomez avec la molette<br>' +
146
- '- Déplacez vous avec le clic gauche<br>' +
147
  tooltipInstruction +
148
- '- Cliquez sur pour réinitialiser la caméra.<br>' +
149
- '- Cliquez sur pour passer en plein écran.<br>';
150
  }
151
 
152
- // --- DYNAMIC MENU SIZING ---
153
  function setMenuContentMaxSize() {
154
  if (!isMobile) {
155
- menuContent.style.maxWidth = "";
156
- menuContent.style.maxHeight = "";
157
- menuContent.style.width = "";
158
- menuContent.style.height = "";
159
- menuContent.style.overflowY = "";
160
- menuContent.style.overflowX = "";
161
  return;
162
  }
163
- let parent = viewerContainerElem;
164
  if (parent) {
165
  const vw = parent.offsetWidth;
166
  const vh = parent.offsetHeight;
167
  if (vw && vh) {
168
- menuContent.style.maxWidth = Math.round(vw * 0.8) + "px";
169
- menuContent.style.maxHeight = Math.round(vh * 0.8) + "px";
170
- menuContent.style.width = ""; // Let it shrink if smaller
171
- menuContent.style.height = "";
172
- menuContent.style.overflowY = "auto";
173
- menuContent.style.overflowX = "auto";
174
  } else {
175
- menuContent.style.maxWidth = "80vw";
176
- menuContent.style.maxHeight = "80vh";
177
- menuContent.style.overflowY = "auto";
178
- menuContent.style.overflowX = "auto";
179
  }
180
  }
181
  }
 
 
182
  setMenuContentMaxSize();
183
  window.addEventListener('resize', setMenuContentMaxSize);
184
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
185
  window.addEventListener('orientationchange', setMenuContentMaxSize);
186
 
187
- // --- HELP PANEL DEFAULT VISIBILITY ---
188
  menuContent.style.display = 'block';
189
-
190
  viewerContainerElem.style.display = 'block';
191
 
 
192
  let dragHide = null;
193
 
194
  function hideTooltipPanel() {
195
  if (dragHide) {
196
  viewerContainerElem.removeEventListener('pointermove', dragHide);
197
  dragHide = null;
 
198
  }
199
  tooltipPanel.style.display = 'none';
200
  }
 
201
  function hideHelpPanel() {
202
  menuContent.style.display = 'none';
203
  }
204
 
205
- // 7. Dynamically load viewer.js (modern ES2024 import, always fresh load)
206
  let viewerModule;
207
  try {
208
- viewerModule = await import('https://mikafil-viewer-sgos.static.hf.space/viewer.js?' + Date.now());
 
209
  await viewerModule.initializeViewer(config, instanceId);
210
  } catch (err) {
211
- console.error("Could not load viewer.js: " + err);
212
  return;
213
  }
214
 
215
- // 8. Canvas reference (for tooltips and resizing)
216
  const canvasId = 'canvas-' + instanceId;
217
  const canvasEl = document.getElementById(canvasId);
218
 
219
- // 9. Tooltips toggle if any
220
  if (tooltipsToggleBtn) {
221
  if (!config.tooltips_url) {
222
  tooltipsToggleBtn.style.display = 'none';
223
  } else {
224
  fetch(config.tooltips_url)
225
- .then(resp => { if (!resp.ok) tooltipsToggleBtn.style.display = 'none'; })
226
- .catch(() => { tooltipsToggleBtn.style.display = 'none'; });
 
 
 
 
227
  }
228
  }
229
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
230
  let isFullscreen = false;
231
  let savedState = null;
232
 
233
  function saveCurrentState() {
234
  if (isFullscreen) return;
235
- const originalAspect = widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
 
 
236
  savedState = {
237
  widget: {
238
  position: widgetContainer.style.position,
239
- top: widgetContainer.style.top,
240
- left: widgetContainer.style.left,
241
- width: widgetContainer.style.width,
242
- height: widgetContainer.style.height,
243
  maxWidth: widgetContainer.style.maxWidth,
244
- maxHeight:widgetContainer.style.maxHeight,
245
  paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
246
- margin: widgetContainer.style.margin,
247
  },
248
  viewer: {
249
  borderRadius: viewerContainerElem.style.borderRadius,
250
- border: viewerContainerElem.style.border,
251
  }
252
  };
253
  }
@@ -255,26 +361,27 @@ const currentScriptTag = document.currentScript;
255
  function restoreOriginalStyles() {
256
  if (!savedState) return;
257
  const aspectToUse = savedState.widget.paddingBottom;
258
- widgetContainer.style.position = savedState.widget.position || "";
259
- widgetContainer.style.top = savedState.widget.top || "";
260
- widgetContainer.style.left = savedState.widget.left || "";
261
- widgetContainer.style.width = "100%";
262
- widgetContainer.style.height = "0";
263
- widgetContainer.style.maxWidth = savedState.widget.maxWidth || "";
264
- widgetContainer.style.maxHeight = savedState.widget.maxHeight || "";
265
- widgetContainer.style.paddingBottom= aspectToUse;
266
- widgetContainer.style.margin = savedState.widget.margin || "";
 
267
  widgetContainer.classList.remove('fake-fullscreen');
268
 
269
- viewerContainerElem.style.position = "absolute";
270
- viewerContainerElem.style.top = "0";
271
- viewerContainerElem.style.left = "0";
272
- viewerContainerElem.style.right = "0";
273
- viewerContainerElem.style.bottom = "0";
274
- viewerContainerElem.style.width = "100%";
275
- viewerContainerElem.style.height = "100%";
276
- viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || "";
277
- viewerContainerElem.style.border = savedState.viewer.border || "";
278
 
279
  if (viewerModule.app) {
280
  viewerModule.app.resizeCanvas(
@@ -289,28 +396,30 @@ const currentScriptTag = document.currentScript;
289
  setMenuContentMaxSize();
290
  }
291
 
 
292
  function applyFullscreenStyles() {
293
- widgetContainer.style.position = 'fixed';
294
- widgetContainer.style.top = '0';
295
- widgetContainer.style.left = '0';
296
- widgetContainer.style.width = '100vw';
297
- widgetContainer.style.height = '100vh';
298
- widgetContainer.style.maxWidth = '100vw';
299
- widgetContainer.style.maxHeight = '100vh';
300
  widgetContainer.style.paddingBottom = '0';
301
- widgetContainer.style.margin = '0';
302
- widgetContainer.style.border = 'none';
303
- widgetContainer.style.borderRadius = '0';
304
 
305
- viewerContainerElem.style.width = '100%';
306
- viewerContainerElem.style.height = '100%';
307
- viewerContainerElem.style.borderRadius= '0';
308
- viewerContainerElem.style.border = 'none';
309
 
310
  if (viewerModule.app) {
311
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
312
  }
313
 
 
314
  if (fullscreenToggle) fullscreenToggle.textContent = '⇲';
315
  isFullscreen = true;
316
  setMenuContentMaxSize();
@@ -318,11 +427,13 @@ const currentScriptTag = document.currentScript;
318
 
319
  function enterFullscreen() {
320
  if (!savedState) saveCurrentState();
 
321
  if (isIOS) {
322
  applyFullscreenStyles();
323
  widgetContainer.classList.add('fake-fullscreen');
324
  } else if (widgetContainer.requestFullscreen) {
325
- widgetContainer.requestFullscreen()
 
326
  .then(applyFullscreenStyles)
327
  .catch(() => {
328
  applyFullscreenStyles();
@@ -361,10 +472,10 @@ const currentScriptTag = document.currentScript;
361
  setMenuContentMaxSize();
362
  });
363
 
 
364
  helpToggle.addEventListener('click', (e) => {
365
  hideTooltipPanel();
366
  e.stopPropagation();
367
- // Toggle menu panel
368
  if (menuContent.style.display === 'block') {
369
  menuContent.style.display = 'none';
370
  } else {
@@ -372,6 +483,7 @@ const currentScriptTag = document.currentScript;
372
  setMenuContentMaxSize();
373
  }
374
  });
 
375
  helpCloseBtn.addEventListener('click', hideHelpPanel);
376
 
377
  resetCameraBtn.addEventListener('click', () => {
@@ -388,49 +500,21 @@ const currentScriptTag = document.currentScript;
388
  hideTooltipPanel();
389
  tooltipsVisible = !tooltipsVisible;
390
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
391
- document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } }));
 
 
392
  });
393
  }
394
 
395
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
396
 
397
- document.addEventListener('tooltip-selected', (evt) => {
398
- // Always show panel, cancel hide first
399
- if (dragHide) {
400
- viewerContainerElem.removeEventListener('pointermove', dragHide);
401
- dragHide = null;
402
- }
403
- const { title, description, imgUrl } = evt.detail;
404
- tooltipTextDiv.innerHTML = `<strong>${title}</strong><br>${description}`;
405
- // Force a repaint: clear src before setting, for repeated images
406
- tooltipImage.style.display = 'none';
407
- tooltipImage.src = '';
408
- if (imgUrl) {
409
- tooltipImage.onload = () => {
410
- tooltipImage.style.display = 'block';
411
- };
412
- tooltipImage.src = imgUrl;
413
- } else {
414
- tooltipImage.style.display = 'none';
415
  }
416
- tooltipPanel.style.display = 'flex';
417
- // --- DELAYED pointermove handler ---
418
- setTimeout(() => {
419
- dragHide = (e) => {
420
- if ((e.pointerType === 'mouse' && e.buttons !== 0) || e.pointerType === 'touch') {
421
- hideTooltipPanel();
422
- }
423
- };
424
- viewerContainerElem.addEventListener('pointermove', dragHide);
425
- }, 100);
426
  });
427
 
428
- if (canvasEl) {
429
- canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
430
- }
431
- document.addEventListener('keydown', (e) => {
432
- if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) exitFullscreen();
433
- });
434
  window.addEventListener('resize', () => {
435
  if (viewerModule.app) {
436
  if (isFullscreen) {
@@ -445,10 +529,14 @@ const currentScriptTag = document.currentScript;
445
  setMenuContentMaxSize();
446
  });
447
 
 
448
  setTimeout(() => {
 
449
  saveCurrentState();
450
- document.dispatchEvent(new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } }));
 
 
 
451
  setMenuContentMaxSize();
452
  }, 200);
453
-
454
- })();
 
3
 
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
  }
 
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
+
38
+
39
+
40
+
41
+
42
+
43
+
44
+
45
  return;
46
  }
47
 
48
+
49
+
50
+
51
+
52
+
53
+
54
+
55
+
56
+
57
+
58
+
59
+
60
+
61
+
62
+
63
+
64
+
65
+
66
+
67
+
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+ // 2) CSS optionnelle
86
  if (config.css_url) {
87
  const linkEl = document.createElement('link');
88
+ linkEl.rel = 'stylesheet';
89
  linkEl.href = config.css_url;
90
  document.head.appendChild(linkEl);
91
  }
92
 
93
+ // 3) ID d’instance
94
  const instanceId = Math.random().toString(36).substr(2, 8);
95
 
96
+ // 4) Aspect ratio
97
+ let aspectPercent = '100%';
98
  if (config.aspect) {
99
+ if (config.aspect.includes(':')) {
100
+ const parts = config.aspect.split(':');
101
  const w = parseFloat(parts[0]);
102
  const h = parseFloat(parts[1]);
103
  if (!isNaN(w) && !isNaN(h) && w > 0) {
104
+ aspectPercent = (h / w) * 100 + '%';
105
  }
106
  } else {
107
  const aspectValue = parseFloat(config.aspect);
108
  if (!isNaN(aspectValue) && aspectValue > 0) {
109
+ aspectPercent = 100 / aspectValue + '%';
110
  }
111
  }
112
  } else {
 
113
  const parentContainer = scriptTag.parentNode;
114
+ const containerWidth = parentContainer.offsetWidth;
115
  const containerHeight = parentContainer.offsetHeight;
116
  if (containerWidth > 0 && containerHeight > 0) {
117
+ aspectPercent = (containerHeight / containerWidth) * 100 + '%';
118
  }
119
  }
120
 
121
+ // 5) Conteneur widget
122
  const widgetContainer = document.createElement('div');
123
  widgetContainer.id = 'ply-widget-container-' + instanceId;
124
  widgetContainer.classList.add('ply-widget-container');
125
+ widgetContainer.style.height = '0';
126
  widgetContainer.style.paddingBottom = aspectPercent;
127
  widgetContainer.setAttribute('data-original-aspect', aspectPercent);
128
 
 
130
  ? `<button id="tooltips-toggle-${instanceId}" class="widget-button tooltips-toggle">⦿</button>`
131
  : '';
132
 
133
+ // HTML du widget (IDs spécifiques à l’instance)
134
  widgetContainer.innerHTML = `
135
  <div id="viewer-container-${instanceId}" class="viewer-container">
136
  <div id="progress-dialog-${instanceId}" class="progress-dialog">
 
147
  <div class="help-text"></div>
148
  </div>
149
  </div>
150
+
151
+ <div id="tooltip-panel-${instanceId}" class="tooltip-panel" style="display: none;">
152
  <div class="tooltip-content">
153
+ <span id="tooltip-close-${instanceId}" class="tooltip-close">×</span>
154
+ <div id="tooltip-text-${instanceId}" class="tooltip-text"></div>
155
+ <img id="tooltip-image-${instanceId}" class="tooltip-image" src="" alt="" style="display: none;" />
156
  </div>
157
  </div>
158
  `;
159
 
160
+ // Insérer dans le DOM
161
  scriptTag.parentNode.appendChild(widgetContainer);
162
 
163
+ // 6) Références DOM de l’instance
164
  const viewerContainerElem = document.getElementById('viewer-container-' + instanceId);
165
+ const fullscreenToggle = document.getElementById('fullscreen-toggle-' + instanceId);
166
+ const helpToggle = document.getElementById('help-toggle-' + instanceId);
167
+ const helpCloseBtn = document.getElementById('help-close-' + instanceId);
168
+ const resetCameraBtn = document.getElementById('reset-camera-btn-' + instanceId);
169
+ const tooltipsToggleBtn = document.getElementById('tooltips-toggle-' + instanceId);
170
+ const menuContent = document.getElementById('menu-content-' + instanceId);
171
+ const helpTextDiv = menuContent.querySelector('.help-text');
172
+
173
+ const tooltipPanel = document.getElementById('tooltip-panel-' + instanceId);
174
+ const tooltipTextDiv = document.getElementById('tooltip-text-' + instanceId);
175
+ const tooltipImage = document.getElementById('tooltip-image-' + instanceId);
176
+ const tooltipCloseBtn = document.getElementById('tooltip-close-' + instanceId);
177
+
178
+ // 7) Aide / textes
179
+ const isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) && !window.MSStream;
180
  const isMobile = isIOS || /Android/i.test(navigator.userAgent);
181
 
182
  const tooltipInstruction = config.tooltips_url
183
+ ? '- ⦿ : annotations.<br>'
184
  : '';
185
 
186
  if (isMobile) {
187
+ helpTextDiv.innerHTML =
188
+ "- Déplacement avec deux doigts.<br>" +
189
+ "- Rotation avec un doigt.<br>" +
190
+ '- Zoom en pinçant avec deux doigts.<br>' +
191
  tooltipInstruction +
192
+ '- ⟲ réinitialisation de la caméra.<br>' +
193
+ '- ⇱ plein écran.<br>';
194
  } else {
195
+ helpTextDiv.innerHTML =
196
+ '- Rotation avec le clic droit ou maj + ←↑↓→<br>' +
197
+ '- Zoom avec la molette ou ctrl + ↑↓<br>' +
198
+ '- Déplacement avec le clic gauche ou ←↑↓→<br>' +
199
  tooltipInstruction +
200
+ '- ⟲ réinitialisation de la caméra.<br>' +
201
+ '- ⇱ plein écran.<br>';
202
  }
203
 
204
+ // 8) Sizing dynamique du panneau d’aide
205
  function setMenuContentMaxSize() {
206
  if (!isMobile) {
207
+ menuContent.style.maxWidth = '';
208
+ menuContent.style.maxHeight = '';
209
+ menuContent.style.width = '';
210
+ menuContent.style.height = '';
211
+ menuContent.style.overflowY = '';
212
+ menuContent.style.overflowX = '';
213
  return;
214
  }
215
+ const parent = viewerContainerElem;
216
  if (parent) {
217
  const vw = parent.offsetWidth;
218
  const vh = parent.offsetHeight;
219
  if (vw && vh) {
220
+ menuContent.style.maxWidth = Math.round(vw * 0.8) + 'px';
221
+ menuContent.style.maxHeight = Math.round(vh * 0.8) + 'px';
222
+ menuContent.style.width = '';
223
+ menuContent.style.height = '';
224
+ menuContent.style.overflowY = 'auto';
225
+ menuContent.style.overflowX = 'auto';
226
  } else {
227
+ menuContent.style.maxWidth = '80vw';
228
+ menuContent.style.maxHeight = '80vh';
229
+ menuContent.style.overflowY = 'auto';
230
+ menuContent.style.overflowX = 'auto';
231
  }
232
  }
233
  }
234
+
235
+
236
  setMenuContentMaxSize();
237
  window.addEventListener('resize', setMenuContentMaxSize);
238
  document.addEventListener('fullscreenchange', setMenuContentMaxSize);
239
  window.addEventListener('orientationchange', setMenuContentMaxSize);
240
 
241
+ // 9) Aide visible par défaut
242
  menuContent.style.display = 'block';
 
243
  viewerContainerElem.style.display = 'block';
244
 
245
+ // 10) Gestion du panneau tooltips
246
  let dragHide = null;
247
 
248
  function hideTooltipPanel() {
249
  if (dragHide) {
250
  viewerContainerElem.removeEventListener('pointermove', dragHide);
251
  dragHide = null;
252
+
253
  }
254
  tooltipPanel.style.display = 'none';
255
  }
256
+
257
  function hideHelpPanel() {
258
  menuContent.style.display = 'none';
259
  }
260
 
261
+ // 11) Charger viewer.js (avec cache-busting par instance)
262
  let viewerModule;
263
  try {
264
+ const viewerUrl = `https://mikafil-viewer-sgos.static.hf.space/viewer.js?inst=${instanceId}`;
265
+ viewerModule = await import(viewerUrl);
266
  await viewerModule.initializeViewer(config, instanceId);
267
  } catch (err) {
 
268
  return;
269
  }
270
 
 
271
  const canvasId = 'canvas-' + instanceId;
272
  const canvasEl = document.getElementById(canvasId);
273
 
274
+ // 12) Bouton tooltips : cacher si URL non valide
275
  if (tooltipsToggleBtn) {
276
  if (!config.tooltips_url) {
277
  tooltipsToggleBtn.style.display = 'none';
278
  } else {
279
  fetch(config.tooltips_url)
280
+ .then((resp) => {
281
+ if (!resp.ok) tooltipsToggleBtn.style.display = 'none';
282
+ })
283
+ .catch(() => {
284
+ tooltipsToggleBtn.style.display = 'none';
285
+ });
286
  }
287
  }
288
 
289
+ // 13) Interactions locales / tooltips
290
+ if (canvasEl) {
291
+ canvasEl.addEventListener('wheel', hideTooltipPanel, { passive: true });
292
+ }
293
+
294
+ document.addEventListener('tooltip-selected', (evt) => {
295
+ // Toujours afficher le panneau, annuler un hide différé si présent
296
+ if (dragHide) {
297
+ viewerContainerElem.removeEventListener('pointermove', dragHide);
298
+ dragHide = null;
299
+ }
300
+
301
+ const { title, description, imgUrl } = evt.detail || {};
302
+ tooltipTextDiv.innerHTML = `<strong>${title || ''}</strong><br>${description || ''}`;
303
+
304
+ // Forcer un repaint : nettoyer src avant de réassigner
305
+ tooltipImage.style.display = 'none';
306
+ tooltipImage.src = '';
307
+
308
+ if (imgUrl) {
309
+ tooltipImage.onload = () => {
310
+ tooltipImage.style.display = 'block';
311
+ };
312
+ tooltipImage.src = imgUrl;
313
+ } else {
314
+ tooltipImage.style.display = 'none';
315
+ }
316
+
317
+ tooltipPanel.style.display = 'flex';
318
+
319
+ // Fermer en cas de drag (après un petit délai pour éviter un flicker)
320
+ setTimeout(() => {
321
+ dragHide = (e) => {
322
+ if (
323
+ (e.pointerType === 'mouse' && e.buttons !== 0) ||
324
+ e.pointerType === 'touch'
325
+ ) {
326
+ hideTooltipPanel();
327
+ }
328
+ };
329
+ viewerContainerElem.addEventListener('pointermove', dragHide);
330
+ }, 100);
331
+ });
332
+
333
+ // 14) Fullscreen
334
  let isFullscreen = false;
335
  let savedState = null;
336
 
337
  function saveCurrentState() {
338
  if (isFullscreen) return;
339
+ const originalAspect =
340
+ widgetContainer.getAttribute('data-original-aspect') || aspectPercent;
341
+
342
  savedState = {
343
  widget: {
344
  position: widgetContainer.style.position,
345
+ top: widgetContainer.style.top,
346
+ left: widgetContainer.style.left,
347
+ width: widgetContainer.style.width,
348
+ height: widgetContainer.style.height,
349
  maxWidth: widgetContainer.style.maxWidth,
350
+ maxHeight: widgetContainer.style.maxHeight,
351
  paddingBottom: widgetContainer.style.paddingBottom || originalAspect,
352
+ margin: widgetContainer.style.margin
353
  },
354
  viewer: {
355
  borderRadius: viewerContainerElem.style.borderRadius,
356
+ border: viewerContainerElem.style.border
357
  }
358
  };
359
  }
 
361
  function restoreOriginalStyles() {
362
  if (!savedState) return;
363
  const aspectToUse = savedState.widget.paddingBottom;
364
+
365
+ widgetContainer.style.position = savedState.widget.position || '';
366
+ widgetContainer.style.top = savedState.widget.top || '';
367
+ widgetContainer.style.left = savedState.widget.left || '';
368
+ widgetContainer.style.width = '100%';
369
+ widgetContainer.style.height = '0';
370
+ widgetContainer.style.maxWidth = savedState.widget.maxWidth || '';
371
+ widgetContainer.style.maxHeight = savedState.widget.maxHeight || '';
372
+ widgetContainer.style.paddingBottom = aspectToUse;
373
+ widgetContainer.style.margin = savedState.widget.margin || '';
374
  widgetContainer.classList.remove('fake-fullscreen');
375
 
376
+ viewerContainerElem.style.position = 'absolute';
377
+ viewerContainerElem.style.top = '0';
378
+ viewerContainerElem.style.left = '0';
379
+ viewerContainerElem.style.right = '0';
380
+ viewerContainerElem.style.bottom = '0';
381
+ viewerContainerElem.style.width = '100%';
382
+ viewerContainerElem.style.height = '100%';
383
+ viewerContainerElem.style.borderRadius = savedState.viewer.borderRadius || '';
384
+ viewerContainerElem.style.border = savedState.viewer.border || '';
385
 
386
  if (viewerModule.app) {
387
  viewerModule.app.resizeCanvas(
 
396
  setMenuContentMaxSize();
397
  }
398
 
399
+
400
  function applyFullscreenStyles() {
401
+ widgetContainer.style.position = 'fixed';
402
+ widgetContainer.style.top = '0';
403
+ widgetContainer.style.left = '0';
404
+ widgetContainer.style.width = '100vw';
405
+ widgetContainer.style.height = '100vh';
406
+ widgetContainer.style.maxWidth = '100vw';
407
+ widgetContainer.style.maxHeight = '100vh';
408
  widgetContainer.style.paddingBottom = '0';
409
+ widgetContainer.style.margin = '0';
410
+ widgetContainer.style.border = 'none';
411
+ widgetContainer.style.borderRadius = '0';
412
 
413
+ viewerContainerElem.style.width = '100%';
414
+ viewerContainerElem.style.height = '100%';
415
+ viewerContainerElem.style.borderRadius = '0';
416
+ viewerContainerElem.style.border = 'none';
417
 
418
  if (viewerModule.app) {
419
  viewerModule.app.resizeCanvas(window.innerWidth, window.innerHeight);
420
  }
421
 
422
+
423
  if (fullscreenToggle) fullscreenToggle.textContent = '⇲';
424
  isFullscreen = true;
425
  setMenuContentMaxSize();
 
427
 
428
  function enterFullscreen() {
429
  if (!savedState) saveCurrentState();
430
+
431
  if (isIOS) {
432
  applyFullscreenStyles();
433
  widgetContainer.classList.add('fake-fullscreen');
434
  } else if (widgetContainer.requestFullscreen) {
435
+ widgetContainer
436
+ .requestFullscreen()
437
  .then(applyFullscreenStyles)
438
  .catch(() => {
439
  applyFullscreenStyles();
 
472
  setMenuContentMaxSize();
473
  });
474
 
475
+ // 15) Aide / boutons
476
  helpToggle.addEventListener('click', (e) => {
477
  hideTooltipPanel();
478
  e.stopPropagation();
 
479
  if (menuContent.style.display === 'block') {
480
  menuContent.style.display = 'none';
481
  } else {
 
483
  setMenuContentMaxSize();
484
  }
485
  });
486
+
487
  helpCloseBtn.addEventListener('click', hideHelpPanel);
488
 
489
  resetCameraBtn.addEventListener('click', () => {
 
500
  hideTooltipPanel();
501
  tooltipsVisible = !tooltipsVisible;
502
  tooltipsToggleBtn.style.opacity = tooltipsVisible ? '1' : '0.5';
503
+ document.dispatchEvent(
504
+ new CustomEvent('toggle-tooltips', { detail: { visible: tooltipsVisible } })
505
+ );
506
  });
507
  }
508
 
509
  tooltipCloseBtn.addEventListener('click', hideTooltipPanel);
510
 
511
+ // 16) Échappement / resize
512
+ document.addEventListener('keydown', (e) => {
513
+ if ((e.key === 'Escape' || e.key === 'Esc') && isFullscreen) {
514
+ exitFullscreen();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
515
  }
 
 
 
 
 
 
 
 
 
 
516
  });
517
 
 
 
 
 
 
 
518
  window.addEventListener('resize', () => {
519
  if (viewerModule.app) {
520
  if (isFullscreen) {
 
529
  setMenuContentMaxSize();
530
  });
531
 
532
+ // 17) Init par défaut
533
  setTimeout(() => {
534
+ // Sauvegarder l’état non-fullscreen
535
  saveCurrentState();
536
+ // Propager l’état par défaut des tooltips
537
+ document.dispatchEvent(
538
+ new CustomEvent('toggle-tooltips', { detail: { visible: !!config.showTooltipsDefault } })
539
+ );
540
  setMenuContentMaxSize();
541
  }, 200);
542
+ })();