@bebranded/bb-contents 1.0.43-beta → 1.0.45-beta

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/bb-contents.js +257 -215
  2. package/package.json +1 -1
package/bb-contents.js CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * BeBranded Contents
3
3
  * Contenus additionnels français pour Webflow
4
- * @version 1.0.43-beta
4
+ * @version 1.0.45-beta
5
5
  * @author BeBranded
6
6
  * @license MIT
7
7
  * @website https://www.bebranded.xyz
@@ -17,7 +17,7 @@
17
17
 
18
18
  // Configuration
19
19
  const config = {
20
- version: '1.0.43-beta',
20
+ version: '1.0.45-beta',
21
21
  debug: true, // Activé temporairement pour debug
22
22
  prefix: 'bb-', // utilisé pour générer les sélecteurs (data-bb-*)
23
23
  youtubeEndpoint: null, // URL du worker YouTube (à définir par l'utilisateur)
@@ -25,6 +25,11 @@
25
25
  copied: 'Lien copié !'
26
26
  }
27
27
  };
28
+
29
+ // Détecter la configuration YouTube définie avant le chargement
30
+ if (window.bbContents && window.bbContents.config && window.bbContents.config.youtubeEndpoint) {
31
+ config.youtubeEndpoint = window.bbContents.config.youtubeEndpoint;
32
+ }
28
33
 
29
34
  // Objet principal
30
35
  const bbContents = {
@@ -155,6 +160,24 @@
155
160
  this._initRetryCount = 0;
156
161
  this.init();
157
162
  },
163
+
164
+ // Méthode pour détecter la configuration YouTube définie après le chargement
165
+ checkYouTubeConfig: function() {
166
+ // Vérifier si la configuration a été définie après le chargement
167
+ if (this.config.youtubeEndpoint) {
168
+ console.log('[DEBUG] YouTube endpoint found:', this.config.youtubeEndpoint);
169
+ return true;
170
+ }
171
+
172
+ // Vérifier dans window.bbContents (au cas où)
173
+ if (window.bbContents && window.bbContents.config && window.bbContents.config.youtubeEndpoint) {
174
+ this.config.youtubeEndpoint = window.bbContents.config.youtubeEndpoint;
175
+ console.log('[DEBUG] YouTube endpoint found in window:', this.config.youtubeEndpoint);
176
+ return true;
177
+ }
178
+
179
+ return false;
180
+ },
158
181
 
159
182
  // Observer DOM pour contenu dynamique
160
183
  setupObserver: function() {
@@ -205,32 +228,32 @@
205
228
  bbContents.modules = {
206
229
  // Module Marquee - Version live 1.0.41-beta avec modules parasites supprimés
207
230
  marquee: {
208
- detect: function(scope) {
209
- const s = scope || document;
231
+ detect: function(scope) {
232
+ const s = scope || document;
210
233
  return s.querySelector(bbContents._attrSelector('marquee')) !== null;
211
234
  },
212
235
 
213
236
  // Nouvelle méthode pour vérifier les éléments échoués
214
237
  checkFailed: function(scope) {
215
- const s = scope || document;
238
+ const s = scope || document;
216
239
  const failedElements = s.querySelectorAll('[bb-marquee]:not([data-bb-marquee-processed])');
217
240
  return failedElements.length > 0;
218
- },
219
-
220
- init: function(root) {
221
- const scope = root || document;
222
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
223
- const elements = scope.querySelectorAll(bbContents._attrSelector('marquee'));
241
+ },
242
+
243
+ init: function(root) {
244
+ const scope = root || document;
245
+ if (scope.closest && scope.closest('[data-bb-disable]')) return;
246
+ const elements = scope.querySelectorAll(bbContents._attrSelector('marquee'));
224
247
 
225
- elements.forEach(function(element) {
248
+ elements.forEach(function(element) {
226
249
  // Vérifier si l'élément a déjà été traité par un autre module
227
250
  if (element.bbProcessed || element.hasAttribute('data-bb-youtube-processed')) {
228
251
  // Élément marquee déjà traité par un autre module, ignoré
229
252
  return;
230
253
  }
231
- element.bbProcessed = true;
254
+ element.bbProcessed = true;
232
255
 
233
- // Récupérer les options
256
+ // Récupérer les options
234
257
  const speed = bbContents._getAttr(element, 'bb-marquee-speed') || '100';
235
258
  const direction = bbContents._getAttr(element, 'bb-marquee-direction') || 'left';
236
259
  const pauseOnHover = bbContents._getAttr(element, 'bb-marquee-pause');
@@ -239,67 +262,67 @@
239
262
  const height = bbContents._getAttr(element, 'bb-marquee-height') || '300';
240
263
  const minHeight = bbContents._getAttr(element, 'bb-marquee-min-height');
241
264
 
242
- // Sauvegarder le contenu original
243
- const originalHTML = element.innerHTML;
244
-
245
- // Créer le conteneur principal
246
- const mainContainer = document.createElement('div');
247
- const isVertical = orientation === 'vertical';
265
+ // Sauvegarder le contenu original
266
+ const originalHTML = element.innerHTML;
267
+
268
+ // Créer le conteneur principal
269
+ const mainContainer = document.createElement('div');
270
+ const isVertical = orientation === 'vertical';
248
271
  const useAutoHeight = isVertical && height === 'auto';
249
272
 
250
- mainContainer.style.cssText = `
251
- position: relative;
252
- width: 100%;
273
+ mainContainer.style.cssText = `
274
+ position: relative;
275
+ width: 100%;
253
276
  height: ${isVertical ? (height === 'auto' ? 'auto' : height + 'px') : 'auto'};
254
- overflow: hidden;
255
- min-height: ${isVertical ? '100px' : '50px'};
277
+ overflow: hidden;
278
+ min-height: ${isVertical ? '100px' : '50px'};
256
279
  ${minHeight ? `min-height: ${minHeight};` : ''}
257
- `;
280
+ `;
258
281
 
259
- // Créer le conteneur de défilement
260
- const scrollContainer = document.createElement('div');
261
- scrollContainer.style.cssText = `
282
+ // Créer le conteneur de défilement
283
+ const scrollContainer = document.createElement('div');
284
+ scrollContainer.style.cssText = `
262
285
  ${useAutoHeight ? 'position: relative;' : 'position: absolute;'}
263
- will-change: transform;
286
+ will-change: transform;
264
287
  ${useAutoHeight ? '' : 'height: 100%; top: 0px; left: 0px;'}
265
- display: flex;
266
- ${isVertical ? 'flex-direction: column;' : ''}
267
- align-items: center;
268
- gap: ${gap}px;
269
- ${isVertical ? '' : 'white-space: nowrap;'}
270
- flex-shrink: 0;
288
+ display: flex;
289
+ ${isVertical ? 'flex-direction: column;' : ''}
290
+ align-items: center;
291
+ gap: ${gap}px;
292
+ ${isVertical ? '' : 'white-space: nowrap;'}
293
+ flex-shrink: 0;
271
294
  transition: transform 0.1s ease-out;
272
- `;
295
+ `;
273
296
 
274
- // Créer le bloc de contenu principal
275
- const mainBlock = document.createElement('div');
276
- mainBlock.innerHTML = originalHTML;
277
- mainBlock.style.cssText = `
278
- display: flex;
279
- ${isVertical ? 'flex-direction: column;' : ''}
280
- align-items: center;
281
- gap: ${gap}px;
282
- ${isVertical ? '' : 'white-space: nowrap;'}
283
- flex-shrink: 0;
284
- ${isVertical ? 'min-height: 100px;' : ''}
285
- `;
297
+ // Créer le bloc de contenu principal
298
+ const mainBlock = document.createElement('div');
299
+ mainBlock.innerHTML = originalHTML;
300
+ mainBlock.style.cssText = `
301
+ display: flex;
302
+ ${isVertical ? 'flex-direction: column;' : ''}
303
+ align-items: center;
304
+ gap: ${gap}px;
305
+ ${isVertical ? '' : 'white-space: nowrap;'}
306
+ flex-shrink: 0;
307
+ ${isVertical ? 'min-height: 100px;' : ''}
308
+ `;
309
+
310
+ // Créer plusieurs répétitions pour un défilement continu
311
+ const repeatBlock1 = mainBlock.cloneNode(true);
312
+ const repeatBlock2 = mainBlock.cloneNode(true);
313
+ const repeatBlock3 = mainBlock.cloneNode(true);
314
+
315
+ // Assembler la structure
316
+ scrollContainer.appendChild(mainBlock);
317
+ scrollContainer.appendChild(repeatBlock1);
318
+ scrollContainer.appendChild(repeatBlock2);
319
+ scrollContainer.appendChild(repeatBlock3);
320
+ mainContainer.appendChild(scrollContainer);
321
+
322
+ // Vider et remplacer le contenu original
323
+ element.innerHTML = '';
324
+ element.appendChild(mainContainer);
286
325
 
287
- // Créer plusieurs répétitions pour un défilement continu
288
- const repeatBlock1 = mainBlock.cloneNode(true);
289
- const repeatBlock2 = mainBlock.cloneNode(true);
290
- const repeatBlock3 = mainBlock.cloneNode(true);
291
-
292
- // Assembler la structure
293
- scrollContainer.appendChild(mainBlock);
294
- scrollContainer.appendChild(repeatBlock1);
295
- scrollContainer.appendChild(repeatBlock2);
296
- scrollContainer.appendChild(repeatBlock3);
297
- mainContainer.appendChild(scrollContainer);
298
-
299
- // Vider et remplacer le contenu original
300
- element.innerHTML = '';
301
- element.appendChild(mainContainer);
302
-
303
326
  // Marquer l'élément comme traité par le module marquee
304
327
  element.setAttribute('data-bb-marquee-processed', 'true');
305
328
 
@@ -310,7 +333,7 @@
310
333
  const imagesLoaded = Array.from(images).every(img => img.complete && img.naturalHeight > 0);
311
334
 
312
335
  // Attendre que le contenu soit dans le DOM et que les images soient chargées
313
- requestAnimationFrame(() => {
336
+ requestAnimationFrame(() => {
314
337
  // Calcul plus robuste des dimensions
315
338
  const rect = mainBlock.getBoundingClientRect();
316
339
  const contentWidth = rect.width || mainBlock.offsetWidth;
@@ -342,24 +365,24 @@
342
365
  return;
343
366
  } else {
344
367
  // Échec d'initialisation après plusieurs tentatives
345
- return;
368
+ return;
346
369
  }
347
- }
348
-
349
- if (isVertical) {
350
- // Animation JavaScript pour le vertical
370
+ }
371
+
372
+ if (isVertical) {
373
+ // Animation JavaScript pour le vertical
351
374
  const contentSize = finalHeight;
352
- const totalSize = contentSize * 4 + parseInt(gap) * 3; // 4 copies au lieu de 3
375
+ const totalSize = contentSize * 4 + parseInt(gap) * 3; // 4 copies au lieu de 3
353
376
 
354
377
  // Ajuster la hauteur du scrollContainer seulement si pas en mode auto
355
378
  if (!useAutoHeight) {
356
- scrollContainer.style.height = totalSize + 'px';
379
+ scrollContainer.style.height = totalSize + 'px';
357
380
  }
358
-
359
- let currentPosition = direction === 'bottom' ? -contentSize - parseInt(gap) : 0;
381
+
382
+ let currentPosition = direction === 'bottom' ? -contentSize - parseInt(gap) : 0;
360
383
  const baseStep = (parseFloat(speed) * 2) / 60; // Vitesse de base
361
384
  let currentStep = baseStep;
362
- let isPaused = false;
385
+ let isPaused = false;
363
386
  let animationId = null;
364
387
  let lastTime = 0;
365
388
 
@@ -383,15 +406,15 @@
383
406
 
384
407
  scrollContainer.style.transform = `translate3d(0px, ${currentPosition}px, 0px)`;
385
408
  animationId = requestAnimationFrame(animate);
386
- };
387
-
388
- // Démarrer l'animation
409
+ };
410
+
411
+ // Démarrer l'animation
389
412
  animationId = requestAnimationFrame(animate);
390
-
413
+
391
414
  // Marquee vertical créé avec animation JS
392
-
415
+
393
416
  // Pause au survol avec transition fluide CSS + JS
394
- if (pauseOnHover === 'true') {
417
+ if (pauseOnHover === 'true') {
395
418
  // Transition fluide avec easing naturel
396
419
  const transitionSpeed = (targetSpeed, duration = 300) => {
397
420
  const startSpeed = currentStep;
@@ -422,19 +445,19 @@
422
445
  requestAnimationFrame(animateTransition);
423
446
  };
424
447
 
425
- element.addEventListener('mouseenter', function() {
448
+ element.addEventListener('mouseenter', function() {
426
449
  transitionSpeed(0); // Ralentir jusqu'à 0
427
- });
428
- element.addEventListener('mouseleave', function() {
450
+ });
451
+ element.addEventListener('mouseleave', function() {
429
452
  transitionSpeed(baseStep); // Revenir à la vitesse normale
430
- });
431
- }
432
- } else {
453
+ });
454
+ }
455
+ } else {
433
456
  // Animation JavaScript pour l'horizontal (comme le vertical pour éviter les saccades)
434
457
  const contentSize = finalWidth;
435
458
  const totalSize = contentSize * 4 + parseInt(gap) * 3;
436
- scrollContainer.style.width = totalSize + 'px';
437
-
459
+ scrollContainer.style.width = totalSize + 'px';
460
+
438
461
  let currentPosition = direction === 'right' ? -contentSize - parseInt(gap) : 0;
439
462
  const baseStep = (parseFloat(speed) * 0.5) / 60; // Vitesse de base
440
463
  let currentStep = baseStep;
@@ -448,12 +471,12 @@
448
471
  const deltaTime = currentTime - lastTime;
449
472
  lastTime = currentTime;
450
473
 
451
- if (direction === 'right') {
474
+ if (direction === 'right') {
452
475
  currentPosition += currentStep * (deltaTime / 16.67); // Normaliser à 60fps
453
476
  if (currentPosition >= 0) {
454
477
  currentPosition = -contentSize - parseInt(gap);
455
478
  }
456
- } else {
479
+ } else {
457
480
  currentPosition -= currentStep * (deltaTime / 16.67);
458
481
  if (currentPosition <= -contentSize - parseInt(gap)) {
459
482
  currentPosition = 0;
@@ -470,7 +493,7 @@
470
493
  // Marquee horizontal créé avec animation JS
471
494
 
472
495
  // Pause au survol avec transition fluide CSS + JS
473
- if (pauseOnHover === 'true') {
496
+ if (pauseOnHover === 'true') {
474
497
  // Transition fluide avec easing naturel
475
498
  const transitionSpeed = (targetSpeed, duration = 300) => {
476
499
  const startSpeed = currentStep;
@@ -501,17 +524,17 @@
501
524
  requestAnimationFrame(animateTransition);
502
525
  };
503
526
 
504
- element.addEventListener('mouseenter', function() {
527
+ element.addEventListener('mouseenter', function() {
505
528
  transitionSpeed(0); // Ralentir jusqu'à 0
506
- });
507
- element.addEventListener('mouseleave', function() {
529
+ });
530
+ element.addEventListener('mouseleave', function() {
508
531
  transitionSpeed(baseStep); // Revenir à la vitesse normale
509
- });
510
- }
532
+ });
511
533
  }
512
- });
513
- };
514
-
534
+ }
535
+ });
536
+ };
537
+
515
538
  // Démarrer l'initialisation avec délai adaptatif - Option 1: Attendre que tout soit prêt
516
539
  let initDelay = isVertical ? 500 : 200; // Délais plus longs par défaut
517
540
  if (bbContents._performanceBoostDetected) {
@@ -613,126 +636,135 @@
613
636
  }
614
637
  element.bbProcessed = true;
615
638
 
616
- const channelId = bbContents._getAttr(element, 'bb-youtube-channel');
617
- const videoCount = bbContents._getAttr(element, 'bb-youtube-video-count') || '10';
618
- const allowShorts = bbContents._getAttr(element, 'bb-youtube-allow-shorts') === 'true';
619
- const language = bbContents._getAttr(element, 'bb-youtube-language') || 'fr';
620
-
621
- // Vérifier la configuration au moment de l'initialisation
622
- const endpoint = bbContents.config.youtubeEndpoint;
623
-
624
- console.log('[DEBUG] YouTube config:', {channelId, videoCount, allowShorts, language, endpoint});
625
-
626
- if (!channelId) {
627
- // Erreur: bb-youtube-channel manquant
628
- return;
629
- }
639
+ // Utiliser la nouvelle fonction initElement
640
+ this.initElement(element);
641
+ });
642
+ },
643
+
644
+ // Fonction pour initialiser un seul élément YouTube
645
+ initElement: function(element) {
646
+ // Vérifier si c'est un bot - pas d'appel API
647
+ if (this.isBot()) {
648
+ return;
649
+ }
650
+
651
+ const channelId = bbContents._getAttr(element, 'bb-youtube-channel');
652
+ const videoCount = bbContents._getAttr(element, 'bb-youtube-video-count') || '10';
653
+ const allowShorts = bbContents._getAttr(element, 'bb-youtube-allow-shorts') === 'true';
654
+ const language = bbContents._getAttr(element, 'bb-youtube-language') || 'fr';
655
+
656
+ // Vérifier la configuration au moment de l'initialisation
657
+ const endpoint = bbContents.checkYouTubeConfig() ? bbContents.config.youtubeEndpoint : null;
658
+
659
+ console.log('[DEBUG] YouTube element config:', {channelId, videoCount, allowShorts, language, endpoint});
660
+
661
+ if (!channelId) {
662
+ return;
663
+ }
664
+
665
+ if (!endpoint) {
666
+ // Attendre que la configuration soit définie (max 5 secondes)
667
+ const retryCount = element.getAttribute('data-youtube-retry-count') || '0';
668
+ const retries = parseInt(retryCount);
630
669
 
631
- if (!endpoint) {
632
- // Attendre que la configuration soit définie (max 5 secondes)
633
- const retryCount = element.getAttribute('data-youtube-retry-count') || '0';
634
- const retries = parseInt(retryCount);
670
+ if (retries < 50) { // 50 * 100ms = 5 secondes max
671
+ console.log('[DEBUG] YouTube endpoint not configured yet, waiting... (attempt', retries + 1, ')');
672
+ element.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Configuration YouTube en cours...</div>';
673
+ element.setAttribute('data-youtube-retry-count', (retries + 1).toString());
635
674
 
636
- if (retries < 50) { // 50 * 100ms = 5 secondes max
637
- console.log('[DEBUG] YouTube endpoint not configured yet, waiting... (attempt', retries + 1, ')');
638
- element.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Configuration YouTube en cours...</div>';
639
- element.setAttribute('data-youtube-retry-count', (retries + 1).toString());
640
-
641
- // Réessayer dans 100ms
642
- setTimeout(() => {
643
- this.init(scope);
644
- }, 100);
645
- return;
646
- } else {
647
- // Timeout après 5 secondes
648
- console.log('[DEBUG] YouTube endpoint configuration timeout');
649
- element.innerHTML = '<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Configuration YouTube manquante</strong><br>Ajoutez dans le &lt;head&gt; :<br><code style="display: block; background: #f3f4f6; padding: 10px; margin: 10px 0; border-radius: 4px; font-family: monospace;">&lt;script&gt;<br>bbContents.config.youtubeEndpoint = \'votre-worker-url\';<br>&lt;/script&gt;</code></div>';
650
- return;
651
- }
652
- }
653
-
654
- // Chercher le template pour une vidéo (directement dans l'élément ou dans un conteneur)
655
- let template = element.querySelector('[bb-youtube-item]');
656
- let container = element;
657
-
658
- // Si pas de template direct, chercher dans un conteneur
659
- if (!template) {
660
- const containerElement = element.querySelector('[bb-youtube-container]');
661
- if (containerElement) {
662
- container = containerElement;
663
- template = containerElement.querySelector('[bb-youtube-item]');
664
- }
665
- }
666
-
667
- if (!template) {
668
- // Erreur: élément [bb-youtube-item] manquant
669
- element.innerHTML = '<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Template manquant</strong><br>Ajoutez un élément avec l\'attribut bb-youtube-item</div>';
675
+ // Réessayer dans 100ms
676
+ setTimeout(() => {
677
+ this.initElement(element);
678
+ }, 100);
670
679
  return;
671
- }
672
-
673
- // Cacher le template original
674
- template.style.display = 'none';
675
-
676
- // Marquer l'élément comme traité par le module YouTube
677
- element.setAttribute('data-bb-youtube-processed', 'true');
678
-
679
- // Vérifier le cache d'abord
680
- const cacheKey = `youtube_${channelId}_${videoCount}_${allowShorts}_${language}`;
681
- const cachedData = this.cache.get(cacheKey);
682
-
683
- if (cachedData) {
684
- // Données YouTube récupérées du cache (économie API)
685
- this.generateYouTubeFeed(container, template, cachedData.value, allowShorts, language);
680
+ } else {
681
+ // Timeout après 5 secondes
682
+ console.log('[DEBUG] YouTube endpoint configuration timeout');
683
+ element.innerHTML = '<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Configuration YouTube manquante</strong><br>Ajoutez dans le &lt;head&gt; :<br><code style="display: block; background: #f3f4f6; padding: 10px; margin: 10px 0; border-radius: 4px; font-family: monospace;">&lt;script&gt;<br>bbContents.config.youtubeEndpoint = \'votre-worker-url\';<br>&lt;/script&gt;</code></div>';
686
684
  return;
687
685
  }
688
-
689
- // Afficher un loader
690
- container.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Chargement des vidéos YouTube...</div>';
691
-
692
- // Appeler l'API via le Worker
693
- console.log('[DEBUG] Fetching YouTube data from:', `${endpoint}?channelId=${channelId}&maxResults=${videoCount}&allowShorts=${allowShorts}`);
694
- fetch(`${endpoint}?channelId=${channelId}&maxResults=${videoCount}&allowShorts=${allowShorts}`)
695
- .then(response => {
696
- console.log('[DEBUG] YouTube API response status:', response.status);
697
- if (!response.ok) {
698
- throw new Error(`HTTP ${response.status}`);
699
- }
700
- return response.json();
701
- })
702
- .then(data => {
703
- console.log('[DEBUG] YouTube API data received:', data);
704
- if (data.error) {
705
- throw new Error(data.error.message || 'Erreur API YouTube');
706
- }
707
-
708
- // Sauvegarder en cache pour 24h
709
- this.cache.set(cacheKey, data);
710
- // Données YouTube mises en cache pour 24h (économie API)
711
-
712
- this.generateYouTubeFeed(container, template, data, allowShorts, language);
713
- })
714
- .catch(error => {
715
- console.error('[DEBUG] YouTube API error:', error);
716
- // Erreur dans le module youtube
717
-
718
- // En cas d'erreur, essayer de récupérer du cache même expiré
719
- const expiredCache = localStorage.getItem(cacheKey);
720
- if (expiredCache) {
721
- try {
722
- const cachedData = JSON.parse(expiredCache);
723
- console.log('[DEBUG] Using expired cache:', cachedData);
724
- // Utilisation du cache expiré en cas d'erreur API
725
- this.generateYouTubeFeed(container, template, cachedData.value, allowShorts, language);
726
- return;
727
- } catch (e) {
728
- console.error('[DEBUG] Cache parsing error:', e);
729
- // Ignorer les erreurs de parsing
730
- }
686
+ }
687
+
688
+ // Chercher le template pour une vidéo (directement dans l'élément ou dans un conteneur)
689
+ let template = element.querySelector('[bb-youtube-item]');
690
+ let container = element;
691
+
692
+ // Si pas de template direct, chercher dans un conteneur
693
+ if (!template) {
694
+ const containerElement = element.querySelector('[bb-youtube-container]');
695
+ if (containerElement) {
696
+ container = containerElement;
697
+ template = containerElement.querySelector('[bb-youtube-item]');
698
+ }
699
+ }
700
+
701
+ if (!template) {
702
+ element.innerHTML = '<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Template manquant</strong><br>Ajoutez un élément avec l\'attribut bb-youtube-item</div>';
703
+ return;
704
+ }
705
+
706
+ // Cacher le template original
707
+ template.style.display = 'none';
708
+
709
+ // Marquer l'élément comme traité par le module YouTube
710
+ element.setAttribute('data-bb-youtube-processed', 'true');
711
+
712
+ // Vérifier le cache d'abord
713
+ const cacheKey = `youtube_${channelId}_${videoCount}_${allowShorts}_${language}`;
714
+ const cachedData = this.cache.get(cacheKey);
715
+
716
+ if (cachedData) {
717
+ // Données YouTube récupérées du cache (économie API)
718
+ this.generateYouTubeFeed(container, template, cachedData.value, allowShorts, language);
719
+ return;
720
+ }
721
+
722
+ // Afficher un loader
723
+ container.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Chargement des vidéos YouTube...</div>';
724
+
725
+ // Appeler l'API via le Worker
726
+ console.log('[DEBUG] Fetching YouTube data from:', `${endpoint}?channelId=${channelId}&maxResults=${videoCount}&allowShorts=${allowShorts}`);
727
+ fetch(`${endpoint}?channelId=${channelId}&maxResults=${videoCount}&allowShorts=${allowShorts}`)
728
+ .then(response => {
729
+ console.log('[DEBUG] YouTube API response status:', response.status);
730
+ if (!response.ok) {
731
+ throw new Error(`HTTP ${response.status}`);
732
+ }
733
+ return response.json();
734
+ })
735
+ .then(data => {
736
+ console.log('[DEBUG] YouTube API data received:', data);
737
+ if (data.error) {
738
+ throw new Error(data.error.message || 'Erreur API YouTube');
739
+ }
740
+
741
+ // Sauvegarder en cache pour 24h
742
+ this.cache.set(cacheKey, data);
743
+ // Données YouTube mises en cache pour 24h (économie API)
744
+
745
+ this.generateYouTubeFeed(container, template, data, allowShorts, language);
746
+ })
747
+ .catch(error => {
748
+ console.error('[DEBUG] YouTube API error:', error);
749
+ // Erreur dans le module youtube
750
+
751
+ // En cas d'erreur, essayer de récupérer du cache même expiré
752
+ const expiredCache = localStorage.getItem(cacheKey);
753
+ if (expiredCache) {
754
+ try {
755
+ const cachedData = JSON.parse(expiredCache);
756
+ console.log('[DEBUG] Using expired cache:', cachedData);
757
+ // Utilisation du cache expiré en cas d'erreur API
758
+ this.generateYouTubeFeed(container, template, cachedData.value, allowShorts, language);
759
+ return;
760
+ } catch (e) {
761
+ console.error('[DEBUG] Cache parsing error:', e);
762
+ // Ignorer les erreurs de parsing
731
763
  }
732
-
733
- container.innerHTML = `<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Erreur de chargement</strong><br>${error.message}</div>`;
734
- });
735
- });
764
+ }
765
+
766
+ container.innerHTML = `<div style="padding: 20px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; color: #dc2626;"><strong>Erreur de chargement</strong><br>${error.message}</div>`;
767
+ });
736
768
  },
737
769
 
738
770
  generateYouTubeFeed: function(container, template, data, allowShorts, language = 'fr') {
@@ -944,6 +976,16 @@
944
976
 
945
977
  // Exposer globalement
946
978
  window.bbContents = bbContents;
979
+
980
+ // Méthode globale pour configurer YouTube après le chargement
981
+ window.configureYouTube = function(endpoint) {
982
+ if (bbContents) {
983
+ bbContents.config.youtubeEndpoint = endpoint;
984
+ console.log('[DEBUG] YouTube endpoint configured globally:', endpoint);
985
+ // Réinitialiser les modules YouTube
986
+ bbContents.reinit();
987
+ }
988
+ };
947
989
 
948
990
  // Initialisation automatique avec délai pour éviter le blocage
949
991
  function initBBContents() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bebranded/bb-contents",
3
- "version": "1.0.43-beta",
3
+ "version": "1.0.45-beta",
4
4
  "description": "Contenus additionnels français pour Webflow",
5
5
  "main": "bb-contents.js",
6
6
  "scripts": {