@bebranded/bb-contents 1.0.83 → 1.0.84

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 +336 -361
  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.82
4
+ * @version 1.0.84
5
5
  * @author BeBranded
6
6
  * @license MIT
7
7
  * @website https://www.bebranded.xyz
@@ -16,30 +16,27 @@
16
16
 
17
17
  // Protection contre le double chargement
18
18
  if (window.bbContents) {
19
- console.warn('BeBranded Contents est déjà chargé');
20
19
  return;
21
20
  }
22
21
 
23
22
  // Vérifier si la version a déjà été affichée
24
23
  if (window._bbContentsVersionDisplayed) {
25
- console.log('🔄 [BB Contents] Version déjà affichée, réinitialisation...');
26
24
  return;
27
25
  }
28
26
  window._bbContentsVersionDisplayed = true;
29
27
 
30
28
  // Protection supplémentaire contre la double initialisation
31
29
  if (window._bbContentsInitialized) {
32
- console.log('🔄 [BB Contents] Déjà initialisé, réinitialisation...');
33
30
  return;
34
31
  }
35
32
  window._bbContentsInitialized = true;
36
33
 
37
- // Log de démarrage simple
38
- console.log('bb-contents | v1.0.83');
34
+ // Log de démarrage simple (une seule fois)
35
+ console.log('bb-contents | v1.0.84');
39
36
 
40
37
  // Configuration
41
38
  const config = {
42
- version: '1.0.83',
39
+ version: '1.0.84',
43
40
  debug: false, // Debug désactivé pour rendu propre
44
41
  prefix: 'bb-', // utilisé pour générer les sélecteurs (data-bb-*)
45
42
  youtubeEndpoint: null, // URL du worker YouTube (à définir par l'utilisateur)
@@ -112,9 +109,7 @@
112
109
 
113
110
  // Initialisation
114
111
  init: function() {
115
- // Console simple et épurée
116
- console.log('bb-contents | v' + this.config.version);
117
-
112
+ // Initialisation silencieuse
118
113
  this.utils.log('Initialisation v' + this.config.version);
119
114
 
120
115
  // Debug environnement supprimé pour console propre
@@ -263,7 +258,7 @@
263
258
  if (scope.closest && scope.closest('[data-bb-disable]')) return;
264
259
  const elements = scope.querySelectorAll(bbContents._attrSelector('marquee'));
265
260
 
266
- console.log('🔍 [MARQUEE] Éléments trouvés:', elements.length);
261
+ // Éléments marquee détectés
267
262
 
268
263
  // Traitement simple et parallèle de tous les marquees
269
264
  elements.forEach((element, index) => {
@@ -356,7 +351,6 @@
356
351
 
357
352
 
358
353
  if (contentSize === 0) {
359
- console.log('⚠️ [MARQUEE] Contenu vide, retry dans 200ms');
360
354
  setTimeout(() => this.initAnimation(element, scrollContainer, mainBlock, options), 200);
361
355
  return;
362
356
  }
@@ -416,12 +410,9 @@
416
410
  waitTimeout += 100;
417
411
 
418
412
  if (imagesLoaded >= totalImages || imagesLoaded === 0 || waitTimeout >= maxWaitTime) {
419
- console.log(`✅ [MARQUEE] Safari - Images chargées: ${imagesLoaded}/${totalImages} (timeout: ${waitTimeout}ms)`);
420
-
421
413
  // Attendre plus longtemps sur mobile pour le rendu visuel
422
414
  const renderDelay = isMobile ? 1000 : 200;
423
415
  setTimeout(() => {
424
- console.log(`🖼️ [MARQUEE] Safari - Attente rendu visuel des images...`);
425
416
  startSafariAnimation();
426
417
  }, renderDelay);
427
418
  } else {
@@ -434,7 +425,6 @@
434
425
  const startSafariAnimation = () => {
435
426
  // Forcer le chargement des images restantes si timeout
436
427
  if (waitTimeout >= maxWaitTime && imagesLoaded < totalImages) {
437
- console.log(`⚠️ [MARQUEE] Safari - Timeout atteint, forcer chargement images restantes`);
438
428
  images.forEach(img => {
439
429
  if (img.dataset.src && !img.src) {
440
430
  img.src = img.dataset.src;
@@ -450,11 +440,9 @@
450
440
  imagesWithSize++;
451
441
  }
452
442
  });
453
- console.log(`🖼️ [MARQUEE] Safari - Images avec taille visible: ${imagesWithSize}/${totalImages}`);
454
443
 
455
444
  // Recalculer la taille après chargement des images
456
445
  const newContentSize = isVertical ? mainBlock.offsetHeight : mainBlock.offsetWidth;
457
- console.log(`🔍 [MARQUEE] Safari - Nouvelle taille après images: ${newContentSize}px`);
458
446
 
459
447
  let finalContentSize = newContentSize > contentSize ? newContentSize : contentSize;
460
448
 
@@ -467,7 +455,6 @@
467
455
  if (finalContentSize < 200) {
468
456
  // Valeurs par défaut plus généreuses sur mobile
469
457
  finalContentSize = isVertical ? (isMobile ? 600 : 400) : (isMobile ? 1000 : 800);
470
- console.log(`🔍 [MARQUEE] Safari - Utilisation valeur par défaut mobile: ${finalContentSize}px`);
471
458
  }
472
459
  }
473
460
 
@@ -496,8 +483,6 @@
496
483
  ? `translate3d(0, ${currentPosition}px, 0)`
497
484
  : `translate3d(${currentPosition}px, 0, 0)`;
498
485
  scrollContainer.style.transform = initialTransform;
499
-
500
- console.log(`🔍 [MARQUEE] Safari - Position initiale: ${currentPosition}px, transform: ${initialTransform}`);
501
486
 
502
487
  // Fonction d'animation Safari avec debug des resets
503
488
  let frameCount = 0;
@@ -508,21 +493,16 @@
508
493
  if (direction === (isVertical ? 'bottom' : 'right')) {
509
494
  currentPosition += step;
510
495
  if (currentPosition >= 0) {
511
- console.log(`🔄 [MARQUEE] Safari RESET bottom/right: ${currentPosition} → ${-(finalContentSize + gapSize)}`);
512
496
  currentPosition = -(finalContentSize + gapSize);
513
497
  }
514
498
  } else {
515
499
  currentPosition -= step;
516
500
  if (currentPosition <= -(2 * (finalContentSize + gapSize))) {
517
- console.log(`🔄 [MARQUEE] Safari RESET top/left: ${currentPosition} → ${-(finalContentSize + gapSize)}`);
518
501
  currentPosition = -(finalContentSize + gapSize);
519
502
  }
520
503
  }
521
504
 
522
- // Log toutes les 60 frames (1 seconde)
523
- if (frameCount % 60 === 0) {
524
- console.log(`📍 [MARQUEE] Safari position: ${currentPosition}px (frame ${frameCount})`);
525
- }
505
+ // Animation continue
526
506
 
527
507
  // ARRONDI pour éviter les erreurs de précision JavaScript
528
508
  currentPosition = Math.round(currentPosition * 100) / 100;
@@ -539,7 +519,6 @@
539
519
  // Démarrer l'animation avec un petit délai pour Safari
540
520
  setTimeout(() => {
541
521
  animate();
542
- console.log('✅ [MARQUEE] Animation Safari démarrée avec JavaScript optimisé');
543
522
  }, 50);
544
523
 
545
524
  // Pause au survol pour Safari
@@ -599,7 +578,6 @@
599
578
 
600
579
  // Démarrer l'animation
601
580
  animate();
602
- console.log('✅ [MARQUEE] Animation standard démarrée');
603
581
 
604
582
  // Pause au survol
605
583
  if (pauseOnHover === 'true') {
@@ -611,261 +589,259 @@
611
589
 
612
590
  // Module Share (Partage Social)
613
591
  share: {
614
- // Configuration des réseaux
615
- networks: {
616
- twitter: function(data) {
617
- return 'https://twitter.com/intent/tweet?url=' +
618
- encodeURIComponent(data.url) +
619
- '&text=' + encodeURIComponent(data.text);
620
- },
621
- facebook: function(data) {
622
- return 'https://facebook.com/sharer/sharer.php?u=' +
623
- encodeURIComponent(data.url);
624
- },
625
- linkedin: function(data) {
626
- // LinkedIn - URL de partage officielle (2024+)
627
- return 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(data.url);
628
- },
629
- whatsapp: function(data) {
630
- return 'https://wa.me/?text=' +
631
- encodeURIComponent(data.text + ' ' + data.url);
632
- },
633
- telegram: function(data) {
634
- return 'https://t.me/share/url?url=' +
635
- encodeURIComponent(data.url) +
636
- '&text=' + encodeURIComponent(data.text);
637
- },
638
- email: function(data) {
639
- return 'mailto:?subject=' +
640
- encodeURIComponent(data.text) +
641
- '&body=' + encodeURIComponent(data.text + ' ' + data.url);
642
- },
643
- copy: function(data) {
644
- return 'copy:' + data.url;
645
- },
646
- native: function(data) {
647
- return 'native:' + JSON.stringify(data);
648
- }
592
+ // Configuration des réseaux
593
+ networks: {
594
+ twitter: function(data) {
595
+ return 'https://twitter.com/intent/tweet?url=' +
596
+ encodeURIComponent(data.url) +
597
+ '&text=' + encodeURIComponent(data.text);
649
598
  },
650
-
651
- // Détection
652
- detect: function(scope) {
653
- const s = scope || document;
654
- return s.querySelector(bbContents._attrSelector('share')) !== null;
599
+ facebook: function(data) {
600
+ return 'https://facebook.com/sharer/sharer.php?u=' +
601
+ encodeURIComponent(data.url);
655
602
  },
656
-
657
- // Initialisation
658
- init: function(root) {
659
- const scope = root || document;
660
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
661
- const elements = scope.querySelectorAll(bbContents._attrSelector('share'));
662
-
663
- elements.forEach(function(element) {
664
- // Vérifier si déjà traité
665
- if (element.bbProcessed) return;
666
- element.bbProcessed = true;
667
-
668
- // Récupérer les données
669
- const network = bbContents._getAttr(element, 'bb-share');
670
- const customUrl = bbContents._getAttr(element, 'bb-url');
671
- const customText = bbContents._getAttr(element, 'bb-text');
672
-
673
- // Valeurs par défaut sécurisées
674
- const data = {
675
- url: bbContents.utils.isValidUrl(customUrl) ? customUrl : window.location.href,
676
- text: bbContents.utils.sanitize(customText || document.title || 'Découvrez ce site')
677
- };
678
-
679
- // Gestionnaire de clic
680
- element.addEventListener('click', function(e) {
681
- e.preventDefault();
682
- bbContents.modules.share.share(network, data, element);
683
- });
684
-
685
- // Accessibilité
686
- if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
687
- element.setAttribute('role', 'button');
688
- element.setAttribute('tabindex', '0');
689
-
690
- // Support clavier
691
- element.addEventListener('keydown', function(e) {
692
- if (e.key === 'Enter' || e.key === ' ') {
693
- e.preventDefault();
694
- bbContents.modules.share.share(network, data, element);
695
- }
696
- });
697
- }
698
-
699
- element.style.cursor = 'pointer';
700
- });
701
-
702
- bbContents.utils.log('Module Share initialisé:', elements.length, 'éléments');
603
+ linkedin: function(data) {
604
+ // LinkedIn - URL de partage officielle (2024+)
605
+ return 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(data.url);
606
+ },
607
+ whatsapp: function(data) {
608
+ return 'https://wa.me/?text=' +
609
+ encodeURIComponent(data.text + ' ' + data.url);
610
+ },
611
+ telegram: function(data) {
612
+ return 'https://t.me/share/url?url=' +
613
+ encodeURIComponent(data.url) +
614
+ '&text=' + encodeURIComponent(data.text);
703
615
  },
616
+ email: function(data) {
617
+ return 'mailto:?subject=' +
618
+ encodeURIComponent(data.text) +
619
+ '&body=' + encodeURIComponent(data.text + ' ' + data.url);
620
+ },
621
+ copy: function(data) {
622
+ return 'copy:' + data.url;
623
+ },
624
+ native: function(data) {
625
+ return 'native:' + JSON.stringify(data);
626
+ }
627
+ },
628
+
629
+ // Détection
630
+ detect: function(scope) {
631
+ const s = scope || document;
632
+ return s.querySelector(bbContents._attrSelector('share')) !== null;
633
+ },
634
+
635
+ // Initialisation
636
+ init: function(root) {
637
+ const scope = root || document;
638
+ if (scope.closest && scope.closest('[data-bb-disable]')) return;
639
+ const elements = scope.querySelectorAll(bbContents._attrSelector('share'));
704
640
 
705
- // Fonction de partage
706
- share: function(network, data, element) {
707
- const networkFunc = this.networks[network];
708
-
709
- if (!networkFunc) {
710
- console.error('[BB Contents] Réseau non supporté:', network);
711
- return;
712
- }
713
-
714
- const shareUrl = networkFunc(data);
715
-
716
- // Cas spécial : copier le lien
717
- if (shareUrl.startsWith('copy:')) {
718
- const url = shareUrl.substring(5);
719
- // Copie silencieuse (pas de feedback visuel)
720
- this.copyToClipboard(url, element, true);
721
- return;
722
- }
641
+ elements.forEach(function(element) {
642
+ // Vérifier si déjà traité
643
+ if (element.bbProcessed) return;
644
+ element.bbProcessed = true;
723
645
 
724
- // Cas spécial : partage natif (Web Share API)
725
- if (shareUrl.startsWith('native:')) {
726
- const shareData = JSON.parse(shareUrl.substring(7));
727
- this.nativeShare(shareData, element);
728
- return;
729
- }
646
+ // Récupérer les données
647
+ const network = bbContents._getAttr(element, 'bb-share');
648
+ const customUrl = bbContents._getAttr(element, 'bb-url');
649
+ const customText = bbContents._getAttr(element, 'bb-text');
730
650
 
731
- // Ouvrir popup de partage
732
- const width = 600;
733
- const height = 400;
734
- const left = (window.innerWidth - width) / 2;
735
- const top = (window.innerHeight - height) / 2;
651
+ // Valeurs par défaut sécurisées
652
+ const data = {
653
+ url: bbContents.utils.isValidUrl(customUrl) ? customUrl : window.location.href,
654
+ text: bbContents.utils.sanitize(customText || document.title || 'Découvrez ce site')
655
+ };
736
656
 
737
- window.open(
738
- shareUrl,
739
- 'bbshare',
740
- 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',noopener,noreferrer'
741
- );
657
+ // Gestionnaire de clic
658
+ element.addEventListener('click', function(e) {
659
+ e.preventDefault();
660
+ bbContents.modules.share.share(network, data, element);
661
+ });
742
662
 
743
- bbContents.utils.log('Partage sur', network, data);
744
- },
745
-
746
- // Copier dans le presse-papier
747
- copyToClipboard: function(text, element, silent) {
748
- const isSilent = !!silent;
749
- // Méthode moderne
750
- if (navigator.clipboard && navigator.clipboard.writeText) {
751
- navigator.clipboard.writeText(text).then(function() {
752
- if (!isSilent) {
753
- bbContents.modules.share.showFeedback(element, '✓ ' + (bbContents.config.i18n.copied || 'Lien copié !'));
663
+ // Accessibilité
664
+ if (element.tagName !== 'BUTTON' && element.tagName !== 'A') {
665
+ element.setAttribute('role', 'button');
666
+ element.setAttribute('tabindex', '0');
667
+
668
+ // Support clavier
669
+ element.addEventListener('keydown', function(e) {
670
+ if (e.key === 'Enter' || e.key === ' ') {
671
+ e.preventDefault();
672
+ bbContents.modules.share.share(network, data, element);
754
673
  }
755
- }).catch(function() {
756
- bbContents.modules.share.fallbackCopy(text, element, isSilent);
757
674
  });
758
- } else {
759
- // Fallback pour environnements sans Clipboard API
760
- this.fallbackCopy(text, element, isSilent);
761
675
  }
762
- },
676
+
677
+ element.style.cursor = 'pointer';
678
+ });
763
679
 
764
- // Fallback copie
765
- fallbackCopy: function(text, element, silent) {
766
- const isSilent = !!silent;
767
- // Pas de UI si silencieux (exigence produit)
768
- if (isSilent) return;
769
- try {
770
- // Afficher un prompt natif pour permettre à l'utilisateur de copier manuellement
771
- // (solution universelle sans execCommand)
772
- window.prompt('Copiez le lien ci-dessous (Ctrl/Cmd+C) :', text);
773
- } catch (err) {
774
- // Dernier recours: ne rien faire
775
- }
776
- },
680
+ bbContents.utils.log('Module Share initialisé:', elements.length, 'éléments');
681
+ },
682
+
683
+ // Fonction de partage
684
+ share: function(network, data, element) {
685
+ const networkFunc = this.networks[network];
777
686
 
778
- // Partage natif (Web Share API)
779
- nativeShare: function(data, element) {
780
- // Vérifier si Web Share API est disponible
781
- if (navigator.share) {
782
- navigator.share({
783
- title: data.text,
784
- url: data.url
785
- }).then(function() {
786
- bbContents.utils.log('Partage natif réussi');
787
- }).catch(function(error) {
788
- if (error.name !== 'AbortError') {
789
- console.error('[BB Contents] Erreur partage natif:', error);
790
- // Fallback vers copie si échec
791
- bbContents.modules.share.copyToClipboard(data.url, element, false);
792
- }
793
- });
794
- } else {
795
- // Fallback si Web Share API non disponible
796
- bbContents.utils.log('Web Share API non disponible, fallback vers copie');
797
- this.copyToClipboard(data.url, element, false);
798
- }
799
- },
687
+ if (!networkFunc) {
688
+ return;
689
+ }
800
690
 
801
- // Feedback visuel
802
- showFeedback: function(element, message) {
803
- const originalText = element.textContent;
804
- element.textContent = message;
805
- element.style.pointerEvents = 'none';
806
-
807
- setTimeout(function() {
808
- element.textContent = originalText;
809
- element.style.pointerEvents = '';
810
- }, 2000);
691
+ const shareUrl = networkFunc(data);
692
+
693
+ // Cas spécial : copier le lien
694
+ if (shareUrl.startsWith('copy:')) {
695
+ const url = shareUrl.substring(5);
696
+ // Copie silencieuse (pas de feedback visuel)
697
+ this.copyToClipboard(url, element, true);
698
+ return;
699
+ }
700
+
701
+ // Cas spécial : partage natif (Web Share API)
702
+ if (shareUrl.startsWith('native:')) {
703
+ const shareData = JSON.parse(shareUrl.substring(7));
704
+ this.nativeShare(shareData, element);
705
+ return;
706
+ }
707
+
708
+ // Ouvrir popup de partage
709
+ const width = 600;
710
+ const height = 400;
711
+ const left = (window.innerWidth - width) / 2;
712
+ const top = (window.innerHeight - height) / 2;
713
+
714
+ window.open(
715
+ shareUrl,
716
+ 'bbshare',
717
+ 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',noopener,noreferrer'
718
+ );
719
+
720
+ bbContents.utils.log('Partage sur', network, data);
721
+ },
722
+
723
+ // Copier dans le presse-papier
724
+ copyToClipboard: function(text, element, silent) {
725
+ const isSilent = !!silent;
726
+ // Méthode moderne
727
+ if (navigator.clipboard && navigator.clipboard.writeText) {
728
+ navigator.clipboard.writeText(text).then(function() {
729
+ if (!isSilent) {
730
+ bbContents.modules.share.showFeedback(element, '✓ ' + (bbContents.config.i18n.copied || 'Lien copié !'));
731
+ }
732
+ }).catch(function() {
733
+ bbContents.modules.share.fallbackCopy(text, element, isSilent);
734
+ });
735
+ } else {
736
+ // Fallback pour environnements sans Clipboard API
737
+ this.fallbackCopy(text, element, isSilent);
738
+ }
739
+ },
740
+
741
+ // Fallback copie
742
+ fallbackCopy: function(text, element, silent) {
743
+ const isSilent = !!silent;
744
+ // Pas de UI si silencieux (exigence produit)
745
+ if (isSilent) return;
746
+ try {
747
+ // Afficher un prompt natif pour permettre à l'utilisateur de copier manuellement
748
+ // (solution universelle sans execCommand)
749
+ window.prompt('Copiez le lien ci-dessous (Ctrl/Cmd+C) :', text);
750
+ } catch (err) {
751
+ // Dernier recours: ne rien faire
752
+ }
753
+ },
754
+
755
+ // Partage natif (Web Share API)
756
+ nativeShare: function(data, element) {
757
+ // Vérifier si Web Share API est disponible
758
+ if (navigator.share) {
759
+ navigator.share({
760
+ title: data.text,
761
+ url: data.url
762
+ }).then(function() {
763
+ bbContents.utils.log('Partage natif réussi');
764
+ }).catch(function(error) {
765
+ if (error.name !== 'AbortError') {
766
+ // Fallback vers copie si échec
767
+ bbContents.modules.share.copyToClipboard(data.url, element, false);
768
+ }
769
+ });
770
+ } else {
771
+ // Fallback si Web Share API non disponible
772
+ bbContents.utils.log('Web Share API non disponible, fallback vers copie');
773
+ this.copyToClipboard(data.url, element, false);
811
774
  }
812
775
  },
776
+
777
+ // Feedback visuel
778
+ showFeedback: function(element, message) {
779
+ const originalText = element.textContent;
780
+ element.textContent = message;
781
+ element.style.pointerEvents = 'none';
782
+
783
+ setTimeout(function() {
784
+ element.textContent = originalText;
785
+ element.style.pointerEvents = '';
786
+ }, 2000);
787
+ }
788
+ },
813
789
 
814
790
  // Module Current Year (Année courante)
815
791
  currentYear: {
816
- detect: function(scope) {
817
- const s = scope || document;
818
- return s.querySelector(bbContents._attrSelector('current-year')) !== null;
819
- },
820
- init: function(root) {
821
- const scope = root || document;
822
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
823
- const elements = scope.querySelectorAll(bbContents._attrSelector('current-year'));
792
+ detect: function(scope) {
793
+ const s = scope || document;
794
+ return s.querySelector(bbContents._attrSelector('current-year')) !== null;
795
+ },
796
+ init: function(root) {
797
+ const scope = root || document;
798
+ if (scope.closest && scope.closest('[data-bb-disable]')) return;
799
+ const elements = scope.querySelectorAll(bbContents._attrSelector('current-year'));
824
800
 
825
- const year = String(new Date().getFullYear());
826
- elements.forEach(function(element) {
827
- if (element.bbProcessed) return;
828
- element.bbProcessed = true;
801
+ const year = String(new Date().getFullYear());
802
+ elements.forEach(function(element) {
803
+ if (element.bbProcessed) return;
804
+ element.bbProcessed = true;
829
805
 
830
806
  const customFormat = bbContents._getAttr(element, 'bb-current-year-format');
831
807
  const prefix = bbContents._getAttr(element, 'bb-current-year-prefix');
832
808
  const suffix = bbContents._getAttr(element, 'bb-current-year-suffix');
833
809
 
834
- if (customFormat && customFormat.includes('{year}')) {
835
- element.textContent = customFormat.replace('{year}', year);
836
- } else if (prefix || suffix) {
837
- element.textContent = prefix + year + suffix;
838
- } else {
839
- element.textContent = year;
840
- }
841
- });
810
+ if (customFormat && customFormat.includes('{year}')) {
811
+ element.textContent = customFormat.replace('{year}', year);
812
+ } else if (prefix || suffix) {
813
+ element.textContent = prefix + year + suffix;
814
+ } else {
815
+ element.textContent = year;
816
+ }
817
+ });
842
818
 
843
- bbContents.utils.log('Module CurrentYear initialisé:', elements.length, 'éléments');
844
- }
819
+ bbContents.utils.log('Module CurrentYear initialisé:', elements.length, 'éléments');
820
+ }
845
821
  },
846
822
 
847
823
  // Module Reading Time (Temps de lecture)
848
824
  readingTime: {
849
- detect: function(scope) {
850
- const s = scope || document;
851
- return s.querySelector(bbContents._attrSelector('reading-time')) !== null;
852
- },
853
- init: function(root) {
854
- const scope = root || document;
855
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
856
- const elements = scope.querySelectorAll(bbContents._attrSelector('reading-time'));
825
+ detect: function(scope) {
826
+ const s = scope || document;
827
+ return s.querySelector(bbContents._attrSelector('reading-time')) !== null;
828
+ },
829
+ init: function(root) {
830
+ const scope = root || document;
831
+ if (scope.closest && scope.closest('[data-bb-disable]')) return;
832
+ const elements = scope.querySelectorAll(bbContents._attrSelector('reading-time'));
857
833
 
858
- elements.forEach(function(element) {
859
- if (element.bbProcessed) return;
860
- element.bbProcessed = true;
834
+ elements.forEach(function(element) {
835
+ if (element.bbProcessed) return;
836
+ element.bbProcessed = true;
861
837
 
862
838
  const targetSelector = bbContents._getAttr(element, 'bb-reading-time-target');
863
- const speedAttr = bbContents._getAttr(element, 'bb-reading-time-speed');
864
- const imageSpeedAttr = bbContents._getAttr(element, 'bb-reading-time-image-speed');
865
- const format = bbContents._getAttr(element, 'bb-reading-time-format') || '{minutes} min';
839
+ const speedAttr = bbContents._getAttr(element, 'bb-reading-time-speed');
840
+ const imageSpeedAttr = bbContents._getAttr(element, 'bb-reading-time-image-speed');
841
+ const format = bbContents._getAttr(element, 'bb-reading-time-format') || '{minutes} min';
866
842
 
867
- const wordsPerMinute = Number(speedAttr) > 0 ? Number(speedAttr) : 230;
868
- const secondsPerImage = Number(imageSpeedAttr) > 0 ? Number(imageSpeedAttr) : 12;
843
+ const wordsPerMinute = Number(speedAttr) > 0 ? Number(speedAttr) : 230;
844
+ const secondsPerImage = Number(imageSpeedAttr) > 0 ? Number(imageSpeedAttr) : 12;
869
845
 
870
846
  // Validation des valeurs
871
847
  if (isNaN(wordsPerMinute) || wordsPerMinute <= 0) {
@@ -875,125 +851,125 @@
875
851
  bbContents.utils.log('Temps par image invalide, utilisation de la valeur par défaut (12)');
876
852
  }
877
853
 
878
- let sourceNode = element;
879
- if (targetSelector) {
880
- const found = document.querySelector(targetSelector);
881
- if (found) sourceNode = found;
882
- }
854
+ let sourceNode = element;
855
+ if (targetSelector) {
856
+ const found = document.querySelector(targetSelector);
857
+ if (found) sourceNode = found;
858
+ }
883
859
 
884
- const text = (sourceNode.textContent || '').trim();
885
- const wordCount = text ? (text.match(/\b\w+\b/g) || []).length : 0;
886
-
887
- // Compter les images dans le contenu ciblé
888
- const images = sourceNode.querySelectorAll('img');
889
- const imageCount = images.length;
890
- const imageTimeInMinutes = (imageCount * secondsPerImage) / 60;
891
-
892
- let minutesFloat = (wordCount / wordsPerMinute) + imageTimeInMinutes;
893
- let minutes = Math.ceil(minutesFloat);
860
+ const text = (sourceNode.textContent || '').trim();
861
+ const wordCount = text ? (text.match(/\b\w+\b/g) || []).length : 0;
862
+
863
+ // Compter les images dans le contenu ciblé
864
+ const images = sourceNode.querySelectorAll('img');
865
+ const imageCount = images.length;
866
+ const imageTimeInMinutes = (imageCount * secondsPerImage) / 60;
867
+
868
+ let minutesFloat = (wordCount / wordsPerMinute) + imageTimeInMinutes;
869
+ let minutes = Math.ceil(minutesFloat);
894
870
 
895
- if ((wordCount > 0 || imageCount > 0) && minutes < 1) minutes = 1; // affichage minimal 1 min si contenu non vide
896
- if (wordCount === 0 && imageCount === 0) minutes = 0;
871
+ if ((wordCount > 0 || imageCount > 0) && minutes < 1) minutes = 1; // affichage minimal 1 min si contenu non vide
872
+ if (wordCount === 0 && imageCount === 0) minutes = 0;
897
873
 
898
- const output = format.replace('{minutes}', String(minutes));
899
- element.textContent = output;
900
- });
874
+ const output = format.replace('{minutes}', String(minutes));
875
+ element.textContent = output;
876
+ });
901
877
 
902
- bbContents.utils.log('Module ReadingTime initialisé:', elements.length, 'éléments');
903
- }
878
+ bbContents.utils.log('Module ReadingTime initialisé:', elements.length, 'éléments');
879
+ }
904
880
  },
905
881
 
906
882
  // Module Favicon (Favicon Dynamique)
907
883
  favicon: {
908
- originalFavicon: null,
909
-
910
- // Détection
911
- detect: function(scope) {
912
- const s = scope || document;
913
- return s.querySelector(bbContents._attrSelector('favicon')) !== null;
914
- },
884
+ originalFavicon: null,
885
+
886
+ // Détection
887
+ detect: function(scope) {
888
+ const s = scope || document;
889
+ return s.querySelector(bbContents._attrSelector('favicon')) !== null;
890
+ },
891
+
892
+ // Initialisation
893
+ init: function(root) {
894
+ const scope = root || document;
895
+ if (scope.closest && scope.closest('[data-bb-disable]')) return;
915
896
 
916
- // Initialisation
917
- init: function(root) {
918
- const scope = root || document;
919
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
920
-
921
- // Chercher les éléments avec bb-favicon ou bb-favicon-dark
897
+ // Chercher les éléments avec bb-favicon ou bb-favicon-dark
922
898
  const elements = scope.querySelectorAll(bbContents._attrSelector('favicon') + ', ' + bbContents._attrSelector('favicon-dark'));
923
- if (elements.length === 0) return;
924
-
925
- // Sauvegarder le favicon original
926
- const existingLink = document.querySelector("link[rel*='icon']");
927
- if (existingLink) {
928
- this.originalFavicon = existingLink.href;
929
- }
930
-
931
- // Collecter les URLs depuis tous les éléments
932
- let faviconUrl = null;
933
- let darkUrl = null;
934
-
935
- elements.forEach(function(element) {
936
- const light = bbContents._getAttr(element, 'bb-favicon') || bbContents._getAttr(element, 'favicon');
937
- const dark = bbContents._getAttr(element, 'bb-favicon-dark') || bbContents._getAttr(element, 'favicon-dark');
938
-
939
- if (light) faviconUrl = light;
940
- if (dark) darkUrl = dark;
941
- });
942
-
943
- // Appliquer la logique
944
- if (faviconUrl && darkUrl) {
945
- this.setupDarkMode(faviconUrl, darkUrl);
946
- } else if (faviconUrl) {
947
- this.setFavicon(faviconUrl);
948
- bbContents.utils.log('Favicon changé:', faviconUrl);
949
- }
950
- },
899
+ if (elements.length === 0) return;
951
900
 
952
- // Helper: Récupérer ou créer un élément favicon
953
- getFaviconElement: function() {
954
- let favicon = document.querySelector('link[rel="icon"]') ||
955
- document.querySelector('link[rel="shortcut icon"]');
956
- if (!favicon) {
957
- favicon = document.createElement('link');
958
- favicon.rel = 'icon';
959
- document.head.appendChild(favicon);
960
- }
961
- return favicon;
962
- },
901
+ // Sauvegarder le favicon original
902
+ const existingLink = document.querySelector("link[rel*='icon']");
903
+ if (existingLink) {
904
+ this.originalFavicon = existingLink.href;
905
+ }
963
906
 
964
- // Changer le favicon
965
- setFavicon: function(url) {
966
- if (!url) return;
967
-
968
- // Ajouter un timestamp pour forcer le rafraîchissement du cache
969
- const cacheBuster = '?v=' + Date.now();
970
- const urlWithCacheBuster = url + cacheBuster;
971
-
972
- const favicon = this.getFaviconElement();
973
- favicon.href = urlWithCacheBuster;
974
- },
907
+ // Collecter les URLs depuis tous les éléments
908
+ let faviconUrl = null;
909
+ let darkUrl = null;
975
910
 
976
- // Support dark mode (méthode simplifiée et directe)
977
- setupDarkMode: function(lightUrl, darkUrl) {
978
- // Fonction pour mettre à jour le favicon selon le mode sombre
979
- const updateFavicon = function(e) {
980
- const darkModeOn = e ? e.matches : window.matchMedia('(prefers-color-scheme: dark)').matches;
981
- const selectedUrl = darkModeOn ? darkUrl : lightUrl;
982
- bbContents.modules.favicon.setFavicon(selectedUrl);
983
- };
984
-
985
- // Initialiser le favicon au chargement de la page
986
- updateFavicon();
911
+ elements.forEach(function(element) {
912
+ const light = bbContents._getAttr(element, 'bb-favicon') || bbContents._getAttr(element, 'favicon');
913
+ const dark = bbContents._getAttr(element, 'bb-favicon-dark') || bbContents._getAttr(element, 'favicon-dark');
987
914
 
988
- // Écouter les changements du mode sombre
989
- const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
990
- if (typeof darkModeMediaQuery.addEventListener === 'function') {
991
- darkModeMediaQuery.addEventListener('change', updateFavicon);
992
- } else if (typeof darkModeMediaQuery.addListener === 'function') {
993
- darkModeMediaQuery.addListener(updateFavicon);
994
- }
915
+ if (light) faviconUrl = light;
916
+ if (dark) darkUrl = dark;
917
+ });
918
+
919
+ // Appliquer la logique
920
+ if (faviconUrl && darkUrl) {
921
+ this.setupDarkMode(faviconUrl, darkUrl);
922
+ } else if (faviconUrl) {
923
+ this.setFavicon(faviconUrl);
924
+ bbContents.utils.log('Favicon changé:', faviconUrl);
995
925
  }
996
926
  },
927
+
928
+ // Helper: Récupérer ou créer un élément favicon
929
+ getFaviconElement: function() {
930
+ let favicon = document.querySelector('link[rel="icon"]') ||
931
+ document.querySelector('link[rel="shortcut icon"]');
932
+ if (!favicon) {
933
+ favicon = document.createElement('link');
934
+ favicon.rel = 'icon';
935
+ document.head.appendChild(favicon);
936
+ }
937
+ return favicon;
938
+ },
939
+
940
+ // Changer le favicon
941
+ setFavicon: function(url) {
942
+ if (!url) return;
943
+
944
+ // Ajouter un timestamp pour forcer le rafraîchissement du cache
945
+ const cacheBuster = '?v=' + Date.now();
946
+ const urlWithCacheBuster = url + cacheBuster;
947
+
948
+ const favicon = this.getFaviconElement();
949
+ favicon.href = urlWithCacheBuster;
950
+ },
951
+
952
+ // Support dark mode (méthode simplifiée et directe)
953
+ setupDarkMode: function(lightUrl, darkUrl) {
954
+ // Fonction pour mettre à jour le favicon selon le mode sombre
955
+ const updateFavicon = function(e) {
956
+ const darkModeOn = e ? e.matches : window.matchMedia('(prefers-color-scheme: dark)').matches;
957
+ const selectedUrl = darkModeOn ? darkUrl : lightUrl;
958
+ bbContents.modules.favicon.setFavicon(selectedUrl);
959
+ };
960
+
961
+ // Initialiser le favicon au chargement de la page
962
+ updateFavicon();
963
+
964
+ // Écouter les changements du mode sombre
965
+ const darkModeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
966
+ if (typeof darkModeMediaQuery.addEventListener === 'function') {
967
+ darkModeMediaQuery.addEventListener('change', updateFavicon);
968
+ } else if (typeof darkModeMediaQuery.addListener === 'function') {
969
+ darkModeMediaQuery.addListener(updateFavicon);
970
+ }
971
+ }
972
+ },
997
973
 
998
974
  // Module YouTube Feed
999
975
  youtube: {
@@ -1046,7 +1022,7 @@
1046
1022
  }
1047
1023
  },
1048
1024
 
1049
- detect: function(scope) {
1025
+ detect: function(scope) {
1050
1026
  return scope.querySelector('[bb-youtube-channel]') !== null;
1051
1027
  },
1052
1028
 
@@ -1115,7 +1091,7 @@
1115
1091
  } else {
1116
1092
  // Timeout après 5 secondes
1117
1093
  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>';
1118
- return;
1094
+ return;
1119
1095
  }
1120
1096
  }
1121
1097
 
@@ -1134,9 +1110,9 @@
1134
1110
 
1135
1111
  if (!template) {
1136
1112
  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>';
1137
- return;
1138
- }
1139
-
1113
+ return;
1114
+ }
1115
+
1140
1116
  // Cacher le template original
1141
1117
  template.style.display = 'none';
1142
1118
 
@@ -1163,7 +1139,7 @@
1163
1139
  const newCachedData = this.cache.get(cacheKey);
1164
1140
  if (newCachedData && newCachedData.value) {
1165
1141
  this.generateYouTubeFeed(container, template, newCachedData.value, allowShorts, language);
1166
- } else {
1142
+ } else {
1167
1143
  container.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Erreur de chargement</div>';
1168
1144
  }
1169
1145
  } else {
@@ -1203,7 +1179,6 @@
1203
1179
  window[loadingKey] = false;
1204
1180
  })
1205
1181
  .catch(error => {
1206
- console.error('Erreur API YouTube:', error);
1207
1182
  // Erreur dans le module youtube
1208
1183
 
1209
1184
  // Libérer le verrou en cas d'erreur
@@ -1309,7 +1284,7 @@
1309
1284
  if (bbContents.config.debug) {
1310
1285
  // Thumbnail optimisée
1311
1286
  }
1312
- } else {
1287
+ } else {
1313
1288
  // Aucune thumbnail disponible
1314
1289
  }
1315
1290
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bebranded/bb-contents",
3
- "version": "1.0.83",
3
+ "version": "1.0.84",
4
4
  "description": "Contenus additionnels français pour Webflow",
5
5
  "main": "bb-contents.js",
6
6
  "scripts": {