@bebranded/bb-contents 1.0.115 → 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 +127 -22
- 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)
|
|
@@ -965,31 +965,146 @@
|
|
|
965
965
|
const s = scope || document;
|
|
966
966
|
return s.querySelector(bbContents._attrSelector('reading-time')) !== null;
|
|
967
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
|
+
|
|
968
1042
|
init: function(root) {
|
|
969
1043
|
const scope = root || document;
|
|
970
1044
|
if (scope.closest && scope.closest('[data-bb-disable]')) return;
|
|
971
1045
|
const elements = scope.querySelectorAll(bbContents._attrSelector('reading-time'));
|
|
1046
|
+
const self = this;
|
|
972
1047
|
|
|
973
1048
|
elements.forEach(function(element) {
|
|
974
1049
|
if (element.bbProcessed) return;
|
|
975
1050
|
element.bbProcessed = true;
|
|
976
1051
|
|
|
977
|
-
|
|
1052
|
+
const targetSelector = bbContents._getAttr(element, 'bb-reading-time-target');
|
|
978
1053
|
const speedAttr = bbContents._getAttr(element, 'bb-reading-time-speed');
|
|
979
1054
|
const imageSpeedAttr = bbContents._getAttr(element, 'bb-reading-time-image-speed');
|
|
980
1055
|
const format = bbContents._getAttr(element, 'bb-reading-time-format') || '{minutes} min';
|
|
1056
|
+
const urlAttr = bbContents._getAttr(element, 'bb-reading-time-url');
|
|
981
1057
|
|
|
982
1058
|
const wordsPerMinute = Number(speedAttr) > 0 ? Number(speedAttr) : 230;
|
|
983
1059
|
const secondsPerImage = Number(imageSpeedAttr) > 0 ? Number(imageSpeedAttr) : 12;
|
|
984
1060
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
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
|
+
}
|
|
992
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
|
|
993
1108
|
let sourceNode = element;
|
|
994
1109
|
if (targetSelector) {
|
|
995
1110
|
const found = document.querySelector(targetSelector);
|
|
@@ -997,18 +1112,8 @@
|
|
|
997
1112
|
}
|
|
998
1113
|
|
|
999
1114
|
const text = (sourceNode.textContent || '').trim();
|
|
1000
|
-
const wordCount = text ? (text.match(/\b\w+\b/g) || []).length : 0;
|
|
1001
|
-
|
|
1002
|
-
// Compter les images dans le contenu ciblé
|
|
1003
1115
|
const images = sourceNode.querySelectorAll('img');
|
|
1004
|
-
const
|
|
1005
|
-
const imageTimeInMinutes = (imageCount * secondsPerImage) / 60;
|
|
1006
|
-
|
|
1007
|
-
let minutesFloat = (wordCount / wordsPerMinute) + imageTimeInMinutes;
|
|
1008
|
-
let minutes = Math.ceil(minutesFloat);
|
|
1009
|
-
|
|
1010
|
-
if ((wordCount > 0 || imageCount > 0) && minutes < 1) minutes = 1; // affichage minimal 1 min si contenu non vide
|
|
1011
|
-
if (wordCount === 0 && imageCount === 0) minutes = 0;
|
|
1116
|
+
const minutes = self.calculateReadingTime(text, images, wordsPerMinute, secondsPerImage);
|
|
1012
1117
|
|
|
1013
1118
|
const output = format.replace('{minutes}', String(minutes));
|
|
1014
1119
|
element.textContent = output;
|