@bebranded/bb-contents 1.0.84-beta → 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 +329 -329
  2. package/package.json +1 -1
package/bb-contents.js CHANGED
@@ -589,259 +589,259 @@
589
589
 
590
590
  // Module Share (Partage Social)
591
591
  share: {
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);
598
- },
599
- facebook: function(data) {
600
- return 'https://facebook.com/sharer/sharer.php?u=' +
601
- encodeURIComponent(data.url);
602
- },
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);
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
- }
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);
627
598
  },
628
-
629
- // Détection
630
- detect: function(scope) {
631
- const s = scope || document;
632
- return s.querySelector(bbContents._attrSelector('share')) !== null;
599
+ facebook: function(data) {
600
+ return 'https://facebook.com/sharer/sharer.php?u=' +
601
+ encodeURIComponent(data.url);
633
602
  },
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'));
640
-
641
- elements.forEach(function(element) {
642
- // Vérifier si déjà traité
643
- if (element.bbProcessed) return;
644
- element.bbProcessed = true;
645
-
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');
650
-
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
- };
656
-
657
- // Gestionnaire de clic
658
- element.addEventListener('click', function(e) {
659
- e.preventDefault();
660
- bbContents.modules.share.share(network, data, element);
661
- });
662
-
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);
673
- }
674
- });
675
- }
676
-
677
- element.style.cursor = 'pointer';
678
- });
679
-
680
- 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);
681
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'));
682
640
 
683
- // Fonction de partage
684
- share: function(network, data, element) {
685
- const networkFunc = this.networks[network];
686
-
687
- if (!networkFunc) {
688
- return;
689
- }
690
-
691
- const shareUrl = networkFunc(data);
641
+ elements.forEach(function(element) {
642
+ // Vérifier si déjà traité
643
+ if (element.bbProcessed) return;
644
+ element.bbProcessed = true;
692
645
 
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
- }
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');
700
650
 
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;
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
+ };
713
656
 
714
- window.open(
715
- shareUrl,
716
- 'bbshare',
717
- 'width=' + width + ',height=' + height + ',left=' + left + ',top=' + top + ',noopener,noreferrer'
718
- );
657
+ // Gestionnaire de clic
658
+ element.addEventListener('click', function(e) {
659
+ e.preventDefault();
660
+ bbContents.modules.share.share(network, data, element);
661
+ });
719
662
 
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é !'));
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);
731
673
  }
732
- }).catch(function() {
733
- bbContents.modules.share.fallbackCopy(text, element, isSilent);
734
674
  });
735
- } else {
736
- // Fallback pour environnements sans Clipboard API
737
- this.fallbackCopy(text, element, isSilent);
738
675
  }
739
- },
676
+
677
+ element.style.cursor = 'pointer';
678
+ });
740
679
 
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
- },
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];
754
686
 
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);
774
- }
775
- },
687
+ if (!networkFunc) {
688
+ return;
689
+ }
776
690
 
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);
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);
787
774
  }
788
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
+ },
789
789
 
790
790
  // Module Current Year (Année courante)
791
791
  currentYear: {
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'));
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'));
800
800
 
801
- const year = String(new Date().getFullYear());
802
- elements.forEach(function(element) {
803
- if (element.bbProcessed) return;
804
- element.bbProcessed = true;
801
+ const year = String(new Date().getFullYear());
802
+ elements.forEach(function(element) {
803
+ if (element.bbProcessed) return;
804
+ element.bbProcessed = true;
805
805
 
806
806
  const customFormat = bbContents._getAttr(element, 'bb-current-year-format');
807
807
  const prefix = bbContents._getAttr(element, 'bb-current-year-prefix');
808
808
  const suffix = bbContents._getAttr(element, 'bb-current-year-suffix');
809
809
 
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
- });
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
+ });
818
818
 
819
- bbContents.utils.log('Module CurrentYear initialisé:', elements.length, 'éléments');
820
- }
819
+ bbContents.utils.log('Module CurrentYear initialisé:', elements.length, 'éléments');
820
+ }
821
821
  },
822
822
 
823
823
  // Module Reading Time (Temps de lecture)
824
824
  readingTime: {
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'));
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'));
833
833
 
834
- elements.forEach(function(element) {
835
- if (element.bbProcessed) return;
836
- element.bbProcessed = true;
834
+ elements.forEach(function(element) {
835
+ if (element.bbProcessed) return;
836
+ element.bbProcessed = true;
837
837
 
838
838
  const targetSelector = bbContents._getAttr(element, 'bb-reading-time-target');
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';
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';
842
842
 
843
- const wordsPerMinute = Number(speedAttr) > 0 ? Number(speedAttr) : 230;
844
- 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;
845
845
 
846
846
  // Validation des valeurs
847
847
  if (isNaN(wordsPerMinute) || wordsPerMinute <= 0) {
@@ -851,125 +851,125 @@
851
851
  bbContents.utils.log('Temps par image invalide, utilisation de la valeur par défaut (12)');
852
852
  }
853
853
 
854
- let sourceNode = element;
855
- if (targetSelector) {
856
- const found = document.querySelector(targetSelector);
857
- if (found) sourceNode = found;
858
- }
854
+ let sourceNode = element;
855
+ if (targetSelector) {
856
+ const found = document.querySelector(targetSelector);
857
+ if (found) sourceNode = found;
858
+ }
859
859
 
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);
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);
870
870
 
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;
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;
873
873
 
874
- const output = format.replace('{minutes}', String(minutes));
875
- element.textContent = output;
876
- });
874
+ const output = format.replace('{minutes}', String(minutes));
875
+ element.textContent = output;
876
+ });
877
877
 
878
- bbContents.utils.log('Module ReadingTime initialisé:', elements.length, 'éléments');
879
- }
878
+ bbContents.utils.log('Module ReadingTime initialisé:', elements.length, 'éléments');
879
+ }
880
880
  },
881
881
 
882
882
  // Module Favicon (Favicon Dynamique)
883
883
  favicon: {
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
- },
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;
891
896
 
892
- // Initialisation
893
- init: function(root) {
894
- const scope = root || document;
895
- if (scope.closest && scope.closest('[data-bb-disable]')) return;
896
-
897
- // Chercher les éléments avec bb-favicon ou bb-favicon-dark
897
+ // Chercher les éléments avec bb-favicon ou bb-favicon-dark
898
898
  const elements = scope.querySelectorAll(bbContents._attrSelector('favicon') + ', ' + bbContents._attrSelector('favicon-dark'));
899
- if (elements.length === 0) return;
900
-
901
- // Sauvegarder le favicon original
902
- const existingLink = document.querySelector("link[rel*='icon']");
903
- if (existingLink) {
904
- this.originalFavicon = existingLink.href;
905
- }
906
-
907
- // Collecter les URLs depuis tous les éléments
908
- let faviconUrl = null;
909
- let darkUrl = null;
910
-
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');
914
-
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);
925
- }
926
- },
899
+ if (elements.length === 0) return;
927
900
 
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
- },
901
+ // Sauvegarder le favicon original
902
+ const existingLink = document.querySelector("link[rel*='icon']");
903
+ if (existingLink) {
904
+ this.originalFavicon = existingLink.href;
905
+ }
939
906
 
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
- },
907
+ // Collecter les URLs depuis tous les éléments
908
+ let faviconUrl = null;
909
+ let darkUrl = null;
951
910
 
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();
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');
963
914
 
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
- }
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);
971
925
  }
972
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
+ },
973
973
 
974
974
  // Module YouTube Feed
975
975
  youtube: {
@@ -1022,7 +1022,7 @@
1022
1022
  }
1023
1023
  },
1024
1024
 
1025
- detect: function(scope) {
1025
+ detect: function(scope) {
1026
1026
  return scope.querySelector('[bb-youtube-channel]') !== null;
1027
1027
  },
1028
1028
 
@@ -1091,7 +1091,7 @@
1091
1091
  } else {
1092
1092
  // Timeout après 5 secondes
1093
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>';
1094
- return;
1094
+ return;
1095
1095
  }
1096
1096
  }
1097
1097
 
@@ -1110,9 +1110,9 @@
1110
1110
 
1111
1111
  if (!template) {
1112
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>';
1113
- return;
1114
- }
1115
-
1113
+ return;
1114
+ }
1115
+
1116
1116
  // Cacher le template original
1117
1117
  template.style.display = 'none';
1118
1118
 
@@ -1139,7 +1139,7 @@
1139
1139
  const newCachedData = this.cache.get(cacheKey);
1140
1140
  if (newCachedData && newCachedData.value) {
1141
1141
  this.generateYouTubeFeed(container, template, newCachedData.value, allowShorts, language);
1142
- } else {
1142
+ } else {
1143
1143
  container.innerHTML = '<div style="padding: 20px; text-align: center; color: #6b7280;">Erreur de chargement</div>';
1144
1144
  }
1145
1145
  } else {
@@ -1284,7 +1284,7 @@
1284
1284
  if (bbContents.config.debug) {
1285
1285
  // Thumbnail optimisée
1286
1286
  }
1287
- } else {
1287
+ } else {
1288
1288
  // Aucune thumbnail disponible
1289
1289
  }
1290
1290
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bebranded/bb-contents",
3
- "version": "1.0.84-beta",
3
+ "version": "1.0.84",
4
4
  "description": "Contenus additionnels français pour Webflow",
5
5
  "main": "bb-contents.js",
6
6
  "scripts": {