@bebranded/bb-contents 1.0.114 → 1.0.116
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.
- package/bb-contents.js +134 -39
- 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.
|
|
4
|
+
* @version 1.0.116
|
|
5
5
|
* @author BeBranded
|
|
6
6
|
* @license MIT
|
|
7
7
|
* @website https://www.bebranded.xyz
|
|
@@ -32,11 +32,11 @@
|
|
|
32
32
|
window._bbContentsInitialized = true;
|
|
33
33
|
|
|
34
34
|
// Log de démarrage simple (une seule fois)
|
|
35
|
-
console.log('bb-contents | v1.0.
|
|
35
|
+
console.log('bb-contents | v1.0.116');
|
|
36
36
|
|
|
37
37
|
// Configuration
|
|
38
38
|
const config = {
|
|
39
|
-
version: '1.0.
|
|
39
|
+
version: '1.0.116',
|
|
40
40
|
debug: false, // Debug désactivé pour rendu propre
|
|
41
41
|
prefix: 'bb-', // utilisé pour générer les sélecteurs (data-bb-*)
|
|
42
42
|
youtubeEndpoint: null, // URL du worker YouTube (à définir par l'utilisateur)
|
|
@@ -390,6 +390,10 @@
|
|
|
390
390
|
let imagesLoaded = 0;
|
|
391
391
|
const totalImages = images.length;
|
|
392
392
|
|
|
393
|
+
// DÉCLARER isMobile et isSafari AVANT leur utilisation dans img.onload
|
|
394
|
+
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
395
|
+
// Détecter spécifiquement Safari (pas Chrome mobile)
|
|
396
|
+
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) || /iPhone|iPad|iPod/.test(navigator.userAgent);
|
|
393
397
|
|
|
394
398
|
// OPTIMISATION: Charger les images et appliquer les styles SVG AVANT le clonage
|
|
395
399
|
// pour éviter les reflows qui causent la saccade de l'animation
|
|
@@ -488,11 +492,6 @@
|
|
|
488
492
|
};
|
|
489
493
|
});
|
|
490
494
|
|
|
491
|
-
// SOLUTION SAFARI MOBILE SIMPLE : Attendre plus longtemps
|
|
492
|
-
const isMobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
|
493
|
-
// Détecter spécifiquement Safari (pas Chrome mobile)
|
|
494
|
-
const isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent) || /iPhone|iPad|iPod/.test(navigator.userAgent);
|
|
495
|
-
|
|
496
495
|
// Timeout plus long sur mobile pour laisser le temps aux images de se charger
|
|
497
496
|
const maxWaitTime = isMobile ? 5000 : 3000; // 5 secondes sur mobile
|
|
498
497
|
let waitTimeout = 0;
|
|
@@ -531,7 +530,9 @@
|
|
|
531
530
|
}
|
|
532
531
|
} else if (waitTimeout >= maxWaitTime) {
|
|
533
532
|
// Timeout atteint : forcer le démarrage mais c'est un fallback
|
|
534
|
-
|
|
533
|
+
if (bbContents.config.debug) {
|
|
534
|
+
console.warn('[MARQUEE] Timeout atteint, certaines images peuvent ne pas être chargées');
|
|
535
|
+
}
|
|
535
536
|
const renderDelay = isSafari && isMobile ? 1500 : (isMobile ? 1000 : 200);
|
|
536
537
|
setTimeout(() => {
|
|
537
538
|
startSafariAnimation();
|
|
@@ -559,14 +560,6 @@
|
|
|
559
560
|
// Cela évite les reflows qui causaient la saccade de l'animation
|
|
560
561
|
// Les copies héritent automatiquement des styles des images originales
|
|
561
562
|
|
|
562
|
-
// Vérifier que les images ont une taille visible
|
|
563
|
-
let imagesWithSize = 0;
|
|
564
|
-
images.forEach(img => {
|
|
565
|
-
if (img.offsetWidth > 0 && img.offsetHeight > 0) {
|
|
566
|
-
imagesWithSize++;
|
|
567
|
-
}
|
|
568
|
-
});
|
|
569
|
-
|
|
570
563
|
// Recalculer la taille après chargement des images
|
|
571
564
|
const newContentSize = isVertical ? mainBlock.offsetHeight : mainBlock.offsetWidth;
|
|
572
565
|
|
|
@@ -623,12 +616,9 @@
|
|
|
623
616
|
}
|
|
624
617
|
|
|
625
618
|
// Fonction d'animation Safari optimisée
|
|
626
|
-
let frameCount = 0;
|
|
627
619
|
let lastTime = performance.now();
|
|
628
620
|
const animate = (currentTime) => {
|
|
629
621
|
if (!isPaused) {
|
|
630
|
-
frameCount++;
|
|
631
|
-
|
|
632
622
|
// OPTIMISATION SAFARI MOBILE : Utiliser le temps réel pour une animation plus fluide
|
|
633
623
|
const deltaTime = isSafari && isMobile ? (currentTime - lastTime) / 16.67 : 1;
|
|
634
624
|
lastTime = currentTime;
|
|
@@ -975,31 +965,146 @@
|
|
|
975
965
|
const s = scope || document;
|
|
976
966
|
return s.querySelector(bbContents._attrSelector('reading-time')) !== null;
|
|
977
967
|
},
|
|
968
|
+
|
|
969
|
+
// Fonction pour extraire le texte et les images depuis une URL
|
|
970
|
+
fetchContentFromUrl: function(url, targetSelector) {
|
|
971
|
+
return fetch(url)
|
|
972
|
+
.then(function(response) {
|
|
973
|
+
if (!response.ok) {
|
|
974
|
+
throw new Error('HTTP ' + response.status);
|
|
975
|
+
}
|
|
976
|
+
return response.text();
|
|
977
|
+
})
|
|
978
|
+
.then(function(html) {
|
|
979
|
+
// Parser le HTML pour extraire le contenu principal
|
|
980
|
+
const parser = new DOMParser();
|
|
981
|
+
const doc = parser.parseFromString(html, 'text/html');
|
|
982
|
+
|
|
983
|
+
let contentNode = null;
|
|
984
|
+
|
|
985
|
+
// Priorité 1 : Utiliser le targetSelector si fourni
|
|
986
|
+
if (targetSelector) {
|
|
987
|
+
contentNode = doc.querySelector(targetSelector);
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
// Priorité 2 : Si aucun targetSelector ou rien trouvé, utiliser les sélecteurs génériques
|
|
991
|
+
if (!contentNode) {
|
|
992
|
+
const contentSelectors = [
|
|
993
|
+
'article',
|
|
994
|
+
'[role="article"]',
|
|
995
|
+
'.blog-post-content',
|
|
996
|
+
'.post-content',
|
|
997
|
+
'.article-content',
|
|
998
|
+
'.content',
|
|
999
|
+
'main article',
|
|
1000
|
+
'main .w-dyn-bind-empty', // Webflow CMS content
|
|
1001
|
+
'main .w-richtext' // Webflow rich text
|
|
1002
|
+
];
|
|
1003
|
+
|
|
1004
|
+
for (let i = 0; i < contentSelectors.length; i++) {
|
|
1005
|
+
contentNode = doc.querySelector(contentSelectors[i]);
|
|
1006
|
+
if (contentNode) break;
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// Fallback final : utiliser le body
|
|
1011
|
+
if (!contentNode) {
|
|
1012
|
+
contentNode = doc.body;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
if (!contentNode) {
|
|
1016
|
+
return { text: '', images: [] };
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
// Extraire le texte et les images
|
|
1020
|
+
const text = contentNode.textContent.trim();
|
|
1021
|
+
const images = contentNode.querySelectorAll('img');
|
|
1022
|
+
|
|
1023
|
+
return { text: text, images: images };
|
|
1024
|
+
});
|
|
1025
|
+
},
|
|
1026
|
+
|
|
1027
|
+
// Fonction pour calculer le temps de lecture
|
|
1028
|
+
calculateReadingTime: function(text, images, wordsPerMinute, secondsPerImage) {
|
|
1029
|
+
const wordCount = text ? (text.match(/\b\w+\b/g) || []).length : 0;
|
|
1030
|
+
const imageCount = images ? images.length : 0;
|
|
1031
|
+
const imageTimeInMinutes = (imageCount * secondsPerImage) / 60;
|
|
1032
|
+
|
|
1033
|
+
let minutesFloat = (wordCount / wordsPerMinute) + imageTimeInMinutes;
|
|
1034
|
+
let minutes = Math.ceil(minutesFloat);
|
|
1035
|
+
|
|
1036
|
+
if ((wordCount > 0 || imageCount > 0) && minutes < 1) minutes = 1;
|
|
1037
|
+
if (wordCount === 0 && imageCount === 0) minutes = 0;
|
|
1038
|
+
|
|
1039
|
+
return minutes;
|
|
1040
|
+
},
|
|
1041
|
+
|
|
978
1042
|
init: function(root) {
|
|
979
1043
|
const scope = root || document;
|
|
980
1044
|
if (scope.closest && scope.closest('[data-bb-disable]')) return;
|
|
981
1045
|
const elements = scope.querySelectorAll(bbContents._attrSelector('reading-time'));
|
|
1046
|
+
const self = this;
|
|
982
1047
|
|
|
983
1048
|
elements.forEach(function(element) {
|
|
984
1049
|
if (element.bbProcessed) return;
|
|
985
1050
|
element.bbProcessed = true;
|
|
986
1051
|
|
|
987
|
-
|
|
1052
|
+
const targetSelector = bbContents._getAttr(element, 'bb-reading-time-target');
|
|
988
1053
|
const speedAttr = bbContents._getAttr(element, 'bb-reading-time-speed');
|
|
989
1054
|
const imageSpeedAttr = bbContents._getAttr(element, 'bb-reading-time-image-speed');
|
|
990
1055
|
const format = bbContents._getAttr(element, 'bb-reading-time-format') || '{minutes} min';
|
|
1056
|
+
const urlAttr = bbContents._getAttr(element, 'bb-reading-time-url');
|
|
991
1057
|
|
|
992
1058
|
const wordsPerMinute = Number(speedAttr) > 0 ? Number(speedAttr) : 230;
|
|
993
1059
|
const secondsPerImage = Number(imageSpeedAttr) > 0 ? Number(imageSpeedAttr) : 12;
|
|
994
1060
|
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1061
|
+
// Validation des valeurs
|
|
1062
|
+
if (isNaN(wordsPerMinute) || wordsPerMinute <= 0) {
|
|
1063
|
+
bbContents.utils.log('Vitesse de lecture invalide, utilisation de la valeur par défaut (230)');
|
|
1064
|
+
}
|
|
1065
|
+
if (isNaN(secondsPerImage) || secondsPerImage < 0) {
|
|
1066
|
+
bbContents.utils.log('Temps par image invalide, utilisation de la valeur par défaut (12)');
|
|
1067
|
+
}
|
|
1002
1068
|
|
|
1069
|
+
// Détecter l'URL : priorité 1 = lien parent, priorité 2 = attribut
|
|
1070
|
+
let articleUrl = null;
|
|
1071
|
+
|
|
1072
|
+
// Priorité 1 : Chercher un lien parent
|
|
1073
|
+
let linkElement = element.closest('a');
|
|
1074
|
+
if (linkElement && linkElement.href) {
|
|
1075
|
+
articleUrl = linkElement.href;
|
|
1076
|
+
}
|
|
1077
|
+
|
|
1078
|
+
// Priorité 2 : Utiliser l'attribut si pas de lien trouvé
|
|
1079
|
+
if (!articleUrl && urlAttr) {
|
|
1080
|
+
articleUrl = urlAttr;
|
|
1081
|
+
// Si l'URL est relative, la transformer en absolue
|
|
1082
|
+
if (articleUrl && !bbContents.utils.isValidUrl(articleUrl)) {
|
|
1083
|
+
articleUrl = new URL(articleUrl, window.location.origin).href;
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// Si une URL est trouvée, faire un fetch
|
|
1088
|
+
if (articleUrl && bbContents.utils.isValidUrl(articleUrl)) {
|
|
1089
|
+
// Afficher un état de chargement (optionnel, on peut laisser vide ou mettre "...")
|
|
1090
|
+
const originalText = element.textContent;
|
|
1091
|
+
|
|
1092
|
+
self.fetchContentFromUrl(articleUrl, targetSelector)
|
|
1093
|
+
.then(function(data) {
|
|
1094
|
+
const minutes = self.calculateReadingTime(data.text, data.images, wordsPerMinute, secondsPerImage);
|
|
1095
|
+
const output = format.replace('{minutes}', String(minutes));
|
|
1096
|
+
element.textContent = output;
|
|
1097
|
+
})
|
|
1098
|
+
.catch(function(error) {
|
|
1099
|
+
bbContents.utils.log('Erreur lors de la récupération du contenu pour reading-time:', error);
|
|
1100
|
+
// En cas d'erreur, on affiche un message ou on laisse le contenu original
|
|
1101
|
+
element.textContent = originalText || '';
|
|
1102
|
+
});
|
|
1103
|
+
|
|
1104
|
+
return; // Sortir de la fonction pour cet élément (traitement async)
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// Comportement par défaut : analyser le contenu de la page actuelle
|
|
1003
1108
|
let sourceNode = element;
|
|
1004
1109
|
if (targetSelector) {
|
|
1005
1110
|
const found = document.querySelector(targetSelector);
|
|
@@ -1007,18 +1112,8 @@
|
|
|
1007
1112
|
}
|
|
1008
1113
|
|
|
1009
1114
|
const text = (sourceNode.textContent || '').trim();
|
|
1010
|
-
const wordCount = text ? (text.match(/\b\w+\b/g) || []).length : 0;
|
|
1011
|
-
|
|
1012
|
-
// Compter les images dans le contenu ciblé
|
|
1013
1115
|
const images = sourceNode.querySelectorAll('img');
|
|
1014
|
-
const
|
|
1015
|
-
const imageTimeInMinutes = (imageCount * secondsPerImage) / 60;
|
|
1016
|
-
|
|
1017
|
-
let minutesFloat = (wordCount / wordsPerMinute) + imageTimeInMinutes;
|
|
1018
|
-
let minutes = Math.ceil(minutesFloat);
|
|
1019
|
-
|
|
1020
|
-
if ((wordCount > 0 || imageCount > 0) && minutes < 1) minutes = 1; // affichage minimal 1 min si contenu non vide
|
|
1021
|
-
if (wordCount === 0 && imageCount === 0) minutes = 0;
|
|
1116
|
+
const minutes = self.calculateReadingTime(text, images, wordsPerMinute, secondsPerImage);
|
|
1022
1117
|
|
|
1023
1118
|
const output = format.replace('{minutes}', String(minutes));
|
|
1024
1119
|
element.textContent = output;
|