Anne-Charlotte commited on
Commit
c2e3b2a
·
verified ·
1 Parent(s): c52aa4d

Create script.js

Browse files
Files changed (1) hide show
  1. script.js +404 -0
script.js ADDED
@@ -0,0 +1,404 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Assembly Steps Data
2
+ const YOUTUBE_VIDEO_ID = "_r0cHySFbeY";
3
+
4
+ const stepsData = [
5
+ { timestamp: "00:00:00", title: "Apply the foot pads." },
6
+ { timestamp: "00:01:14", title: "Install the USB extension cable." },
7
+ { timestamp: "00:02:55", title: "Mount the body PCB." },
8
+ { timestamp: "00:04:30", title: "Connect the cables to the body PCB." },
9
+ { timestamp: "00:05:05", title: "Insert the ball bearing." },
10
+ { timestamp: "00:05:44", title: "Fit the body down assembly to the foot assembly." },
11
+ { timestamp: "00:06:14", title: "Mount the base foot motor to the body turning unit." },
12
+ { timestamp: "00:07:14", title: "Position the body turning assembly on the body down assembly." },
13
+ { timestamp: "00:08:25", title: "Secure the body turning assembly to the body down assembly." },
14
+ { timestamp: "00:10:15", title: "Screw the Stewart main plate in place." },
15
+ { timestamp: "00:13:10", title: "Connect the base foot motor." },
16
+ { timestamp: "00:14:13", title: "Screw the link rods onto the motor arms." },
17
+ { timestamp: "00:18:05", title: "Connect Motors 1 and 2." },
18
+ { timestamp: "00:18:44", title: "Connect Motors 2 and 3." },
19
+ { timestamp: "00:19:31", title: "Connect Motors 4 and 5." },
20
+ { timestamp: "00:20:19", title: "Connect Motors 5 and 6." },
21
+ { timestamp: "00:20:53", title: "Insert all motors into the Stewart main plate." },
22
+ { timestamp: "00:22:55", title: "Clip the motor cables into the body down assembly." },
23
+ { timestamp: "00:24:46", title: "Screw the speaker into the tricap." },
24
+ { timestamp: "00:25:39", title: "Position the tricap." },
25
+ { timestamp: "00:27:01", title: "Connect Motors 3 and 4." },
26
+ { timestamp: "00:27:44", title: "Route the cables." },
27
+ { timestamp: "00:28:04", title: "Secure the tricap with screws." },
28
+ { timestamp: "00:30:47", title: "Screw the bottom head onto the link rods." },
29
+ { timestamp: "00:35:17", title: "Route the cables through the bottom head." },
30
+ { timestamp: "00:35:49", title: "Route the cables through the head PCB." },
31
+ { timestamp: "00:36:10", title: "Screw the head PCB in place." },
32
+ { timestamp: "00:37:38", title: "Position the top shell." },
33
+ { timestamp: "00:38:42", title: "Screw the top shell in place." },
34
+ { timestamp: "00:41:08", title: "Place the lenses in the glasses holder." },
35
+ { timestamp: "00:41:38", title: "Insert the fisheye lenses into the caps." },
36
+ { timestamp: "00:42:14", title: "Snap the fisheye lenses into the glasses holder." },
37
+ { timestamp: "00:43:07", title: "Position the Arducam camera." },
38
+ { timestamp: "00:43:35", title: "Screw the Arducam camera in place." },
39
+ { timestamp: "00:44:08", title: "Screw the glasses assembly onto the front head shell." },
40
+ { timestamp: "00:45:51", title: "Plug in USB-C." },
41
+ { timestamp: "00:46:06", title: "Attach the cases to the antenna motors." },
42
+ { timestamp: "00:46:46", title: "Mount the motor assembly to the back head shell." },
43
+ { timestamp: "00:50:11", title: "Connect the antenna motors." },
44
+ { timestamp: "00:51:13", title: "Slide the back-head assembly onto the Reachy Mini body." },
45
+ { timestamp: "00:51:45", title: "Screw the back head in place." },
46
+ { timestamp: "00:53:35", title: "Attach the cable holder." },
47
+ { timestamp: "00:54:29", title: "Connect the speaker and motor cables." },
48
+ { timestamp: "00:55:22", title: "Connect the power and USB extension cables." },
49
+ { timestamp: "00:55:48", title: "Connect the flexible printed cable to the top head PCB." },
50
+ { timestamp: "00:56:23", title: "Slide the top-head assembly onto the back head." },
51
+ { timestamp: "00:56:31", title: "Connect the flexible printed cable to the head PCB." }
52
+ ];
53
+
54
+ // Parse timestamp to seconds
55
+ function parseTimestamp(ts) {
56
+ const parts = ts.split(':').map(Number);
57
+ return parts[0] * 3600 + parts[1] * 60 + parts[2];
58
+ }
59
+
60
+ // Create assembly steps with parsed timestamps
61
+ const assemblySteps = stepsData.map((step, index) => ({
62
+ id: index + 1,
63
+ title: step.title,
64
+ timestamp: step.timestamp,
65
+ timestampSeconds: parseTimestamp(step.timestamp)
66
+ }));
67
+
68
+ const TOTAL_STEPS = assemblySteps.length;
69
+
70
+ // Available step images
71
+ const availableImages = {
72
+ 1: 'assets/step1.jpg',
73
+ 2: 'assets/step2.jpg',
74
+ 3: 'assets/step3bis.jpg',
75
+ 4: 'assets/step4.jpg',
76
+ 5: 'assets/step5.jpg',
77
+ 6: 'assets/step6.jpg',
78
+ 7: 'assets/step7.jpg',
79
+ 8: 'assets/step8.jpg',
80
+ 9: 'assets/step9.jpg',
81
+ 10: 'assets/step10.jpg',
82
+ 11: 'assets/step11.jpg',
83
+ 12: 'assets/step12.jpg',
84
+ 13: 'assets/step13.jpg',
85
+ 14: 'assets/step14.jpg',
86
+ 15: 'assets/step15.jpg',
87
+ 16: 'assets/step16.jpg',
88
+ 17: 'assets/step17.jpg',
89
+ 18: 'assets/step18.jpg',
90
+ 19: 'assets/step19.jpg',
91
+ 20: 'assets/step20.jpg',
92
+ 21: 'assets/step21.jpg',
93
+ 22: 'assets/step22.jpg',
94
+ 23: 'assets/step23.jpg',
95
+ 24: 'assets/step24.jpg',
96
+ 25: 'assets/step25.jpg',
97
+ 26: 'assets/step26.jpg',
98
+ 27: 'assets/step27.jpg',
99
+ 28: 'assets/step28.jpg',
100
+ 29: 'assets/step29.jpg',
101
+ 30: 'assets/step30.jpg',
102
+ 31: 'assets/step31.jpg',
103
+ 32: 'assets/step32.jpg',
104
+ 33: 'assets/step33.jpg',
105
+ 34: 'assets/step34.jpg',
106
+ 35: 'assets/step35.jpg',
107
+ 36: 'assets/step36.jpg',
108
+ 37: 'assets/step37.jpg',
109
+ 38: 'assets/step38.jpg',
110
+ 39: 'assets/step39.jpg',
111
+ 40: 'assets/step40.jpg',
112
+ 41: 'assets/step41.jpg',
113
+ 42: 'assets/step42.jpg',
114
+ 43: 'assets/step43.jpg',
115
+ 44: 'assets/step44.jpg',
116
+ 45: 'assets/step45.jpg',
117
+ 46: 'assets/step46.jpg',
118
+ 47: 'assets/step47.jpg',
119
+ 48: 'assets/step48.jpg',
120
+ 49: 'assets/step49.jpg',
121
+ 50: 'assets/step50.jpg',
122
+ 51: 'assets/step51.jpg',
123
+ };
124
+
125
+ function getStepImage(stepId) {
126
+ return availableImages[stepId] || null;
127
+ }
128
+
129
+ // App State
130
+ let currentStep = 1;
131
+ let isFullscreen = false;
132
+ let scale = 1;
133
+ let position = { x: 0, y: 0 };
134
+ let isDragging = false;
135
+ let dragStart = { x: 0, y: 0 };
136
+
137
+ // DOM Elements
138
+ const stepCounterText = document.getElementById('step-counter-text');
139
+ const stepImage = document.getElementById('step-image');
140
+ const placeholder = document.getElementById('placeholder');
141
+ const placeholderNumber = document.getElementById('placeholder-number');
142
+ const imageWrapper = document.getElementById('image-wrapper');
143
+ const youtubeIframeDesktop = document.getElementById('youtube-iframe-desktop');
144
+ const youtubeIframeMobile = document.getElementById('youtube-iframe-mobile');
145
+ const prevBtn = document.getElementById('prev-btn');
146
+ const nextBtn = document.getElementById('next-btn');
147
+ const stepIndicators = document.getElementById('step-indicators');
148
+ const progressBar = document.getElementById('progress-bar');
149
+ const fullscreenBtn = document.getElementById('fullscreen-btn');
150
+ const fullscreenModal = document.getElementById('fullscreen-modal');
151
+ const closeFullscreenBtn = document.getElementById('close-fullscreen-btn');
152
+ const fullscreenStepTitle = document.getElementById('fullscreen-step-title');
153
+ const fullscreenImage = document.getElementById('fullscreen-image');
154
+ const fullscreenPlaceholder = document.getElementById('fullscreen-placeholder');
155
+ const fullscreenPlaceholderNumber = document.getElementById('fullscreen-placeholder-number');
156
+ const fullscreenImageContainer = document.getElementById('fullscreen-image-container');
157
+ const fullscreenYoutubeIframeDesktop = document.getElementById('fullscreen-youtube-iframe-desktop');
158
+ const fullscreenYoutubeIframeMobile = document.getElementById('fullscreen-youtube-iframe-mobile');
159
+ const fullscreenPrevBtn = document.getElementById('fullscreen-prev-btn');
160
+ const fullscreenNextBtn = document.getElementById('fullscreen-next-btn');
161
+ const fullscreenStepIndicators = document.getElementById('fullscreen-step-indicators');
162
+ const fullscreenProgressBar = document.getElementById('fullscreen-progress-bar');
163
+ const zoomInBtn = document.getElementById('zoom-in-btn');
164
+ const zoomOutBtn = document.getElementById('zoom-out-btn');
165
+ const zoomLevel = document.getElementById('zoom-level');
166
+
167
+ // Update YouTube embed
168
+ function updateYouTubeEmbed(timestampSeconds) {
169
+ const embedUrl = `https://www.youtube.com/embed/${YOUTUBE_VIDEO_ID}?start=${timestampSeconds}&rel=0&autoplay=1&mute=1`;
170
+ youtubeIframeDesktop.src = embedUrl;
171
+ youtubeIframeMobile.src = embedUrl;
172
+ fullscreenYoutubeIframeDesktop.src = embedUrl;
173
+ fullscreenYoutubeIframeMobile.src = embedUrl;
174
+ }
175
+
176
+ // Render step indicators
177
+ function renderStepIndicators(containerId, currentStep, onClick) {
178
+ const container = document.getElementById(containerId);
179
+ container.innerHTML = '';
180
+
181
+ const groupStart = Math.floor((currentStep - 1) / 10) * 10 + 1;
182
+
183
+ for (let i = 0; i < 10; i++) {
184
+ const stepNum = groupStart + i;
185
+ if (stepNum > TOTAL_STEPS) break;
186
+
187
+ const isActive = stepNum === currentStep;
188
+ const button = document.createElement('button');
189
+ button.className = `step-indicator ${isActive ? 'step-indicator-active' : 'step-indicator-inactive'}`;
190
+ button.setAttribute('aria-label', `Go to step ${stepNum}`);
191
+ button.addEventListener('click', () => onClick(stepNum));
192
+ container.appendChild(button);
193
+ }
194
+ }
195
+
196
+ // Update UI
197
+ function updateUI() {
198
+ const step = assemblySteps[currentStep - 1];
199
+ const imageSrc = getStepImage(step.id);
200
+
201
+ // Update step counter
202
+ stepCounterText.textContent = `Step ${step.id}/${TOTAL_STEPS}`;
203
+
204
+ // Update image
205
+ if (imageSrc) {
206
+ stepImage.src = imageSrc;
207
+ stepImage.alt = `Assembly step ${step.id}`;
208
+ stepImage.classList.remove('hidden');
209
+ placeholder.classList.add('hidden');
210
+ } else {
211
+ stepImage.classList.add('hidden');
212
+ placeholder.classList.remove('hidden');
213
+ placeholderNumber.textContent = step.id;
214
+ }
215
+
216
+ // Update YouTube embed
217
+ updateYouTubeEmbed(step.timestampSeconds);
218
+
219
+ // Update buttons
220
+ prevBtn.disabled = currentStep === 1;
221
+ nextBtn.disabled = currentStep === TOTAL_STEPS;
222
+
223
+ // Update step indicators
224
+ renderStepIndicators('step-indicators', currentStep, goToStep);
225
+
226
+ // Update progress bar
227
+ const progress = (currentStep / TOTAL_STEPS) * 100;
228
+ progressBar.style.width = `${progress}%`;
229
+
230
+ // Update fullscreen UI
231
+ updateFullscreenUI();
232
+ }
233
+
234
+ // Update fullscreen UI
235
+ function updateFullscreenUI() {
236
+ const step = assemblySteps[currentStep - 1];
237
+ const imageSrc = getStepImage(step.id);
238
+
239
+ fullscreenStepTitle.textContent = `Step ${step.id}/${TOTAL_STEPS} - ${step.title}`;
240
+
241
+ if (imageSrc) {
242
+ fullscreenImage.src = imageSrc;
243
+ fullscreenImage.alt = `Step ${step.id}`;
244
+ fullscreenImage.classList.remove('hidden');
245
+ fullscreenPlaceholder.classList.add('hidden');
246
+ } else {
247
+ fullscreenImage.classList.add('hidden');
248
+ fullscreenPlaceholder.classList.remove('hidden');
249
+ fullscreenPlaceholderNumber.textContent = step.id;
250
+ }
251
+
252
+ fullscreenPrevBtn.disabled = currentStep === 1;
253
+ fullscreenNextBtn.disabled = currentStep === TOTAL_STEPS;
254
+
255
+ renderStepIndicators('fullscreen-step-indicators', currentStep, goToStepFullscreen);
256
+
257
+ const progress = (currentStep / TOTAL_STEPS) * 100;
258
+ fullscreenProgressBar.style.width = `${progress}%`;
259
+
260
+ updateZoomDisplay();
261
+ }
262
+
263
+ // Navigation functions
264
+ function goToPrevious() {
265
+ if (currentStep > 1) {
266
+ currentStep--;
267
+ updateUI();
268
+ }
269
+ }
270
+
271
+ function goToNext() {
272
+ if (currentStep < TOTAL_STEPS) {
273
+ currentStep++;
274
+ updateUI();
275
+ }
276
+ }
277
+
278
+ function goToStep(step) {
279
+ if (step >= 1 && step <= TOTAL_STEPS) {
280
+ currentStep = step;
281
+ updateUI();
282
+ }
283
+ }
284
+
285
+ function goToStepFullscreen(step) {
286
+ resetZoom();
287
+ goToStep(step);
288
+ }
289
+
290
+ function goToPreviousFullscreen() {
291
+ resetZoom();
292
+ goToPrevious();
293
+ }
294
+
295
+ function goToNextFullscreen() {
296
+ resetZoom();
297
+ goToNext();
298
+ }
299
+
300
+ // Fullscreen functions
301
+ function openFullscreen() {
302
+ isFullscreen = true;
303
+ fullscreenModal.classList.remove('hidden');
304
+ document.body.style.overflow = 'hidden';
305
+ updateFullscreenUI();
306
+ }
307
+
308
+ function closeFullscreen() {
309
+ isFullscreen = false;
310
+ fullscreenModal.classList.add('hidden');
311
+ document.body.style.overflow = '';
312
+ resetZoom();
313
+ }
314
+
315
+ // Zoom functions
316
+ function zoomIn() {
317
+ scale = Math.min(scale + 0.5, 4);
318
+ updateZoomDisplay();
319
+ }
320
+
321
+ function zoomOut() {
322
+ scale = Math.max(scale - 0.5, 0.5);
323
+ updateZoomDisplay();
324
+ }
325
+
326
+ function resetZoom() {
327
+ scale = 1;
328
+ position = { x: 0, y: 0 };
329
+ updateZoomDisplay();
330
+ }
331
+
332
+ function updateZoomDisplay() {
333
+ zoomLevel.textContent = `${Math.round(scale * 100)}%`;
334
+ fullscreenImage.style.transform = `translate(${position.x}px, ${position.y}px) scale(${scale})`;
335
+ }
336
+
337
+ // Drag functions for fullscreen image
338
+ function handleMouseDown(e) {
339
+ if (scale > 1) {
340
+ isDragging = true;
341
+ dragStart = {
342
+ x: e.clientX - position.x,
343
+ y: e.clientY - position.y
344
+ };
345
+ fullscreenImageContainer.style.cursor = 'grabbing';
346
+ }
347
+ }
348
+
349
+ function handleMouseMove(e) {
350
+ if (isDragging && scale > 1) {
351
+ position = {
352
+ x: e.clientX - dragStart.x,
353
+ y: e.clientY - dragStart.y
354
+ };
355
+ updateZoomDisplay();
356
+ }
357
+ }
358
+
359
+ function handleMouseUp() {
360
+ isDragging = false;
361
+ fullscreenImageContainer.style.cursor = 'grab';
362
+ }
363
+
364
+ function handleWheel(e) {
365
+ e.preventDefault();
366
+ const delta = e.deltaY > 0 ? -0.2 : 0.2;
367
+ scale = Math.max(0.5, Math.min(4, scale + delta));
368
+ updateZoomDisplay();
369
+ }
370
+
371
+ // Event Listeners
372
+ prevBtn.addEventListener('click', goToPrevious);
373
+ nextBtn.addEventListener('click', goToNext);
374
+ fullscreenBtn.addEventListener('click', openFullscreen);
375
+ stepImage.addEventListener('click', openFullscreen);
376
+ closeFullscreenBtn.addEventListener('click', closeFullscreen);
377
+ fullscreenPrevBtn.addEventListener('click', goToPreviousFullscreen);
378
+ fullscreenNextBtn.addEventListener('click', goToNextFullscreen);
379
+ zoomInBtn.addEventListener('click', zoomIn);
380
+ zoomOutBtn.addEventListener('click', zoomOut);
381
+
382
+ fullscreenImageContainer.addEventListener('mousedown', handleMouseDown);
383
+ fullscreenImageContainer.addEventListener('mousemove', handleMouseMove);
384
+ fullscreenImageContainer.addEventListener('mouseup', handleMouseUp);
385
+ fullscreenImageContainer.addEventListener('mouseleave', handleMouseUp);
386
+ fullscreenImageContainer.addEventListener('wheel', handleWheel, { passive: false });
387
+
388
+ // Keyboard navigation
389
+ document.addEventListener('keydown', (e) => {
390
+ if (isFullscreen) {
391
+ if (e.key === 'Escape') closeFullscreen();
392
+ if (e.key === '+' || e.key === '=') zoomIn();
393
+ if (e.key === '-') zoomOut();
394
+ if (e.key === '0') resetZoom();
395
+ if (e.key === 'ArrowLeft' && currentStep > 1) goToPreviousFullscreen();
396
+ if (e.key === 'ArrowRight' && currentStep < TOTAL_STEPS) goToNextFullscreen();
397
+ } else {
398
+ if (e.key === 'ArrowRight') goToNext();
399
+ if (e.key === 'ArrowLeft') goToPrevious();
400
+ }
401
+ });
402
+
403
+ // Initialize
404
+ updateUI();