@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.
Files changed (2) hide show
  1. package/bb-contents.js +127 -22
  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.115
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.115');
35
+ console.log('bb-contents | v1.0.116');
36
36
 
37
37
  // Configuration
38
38
  const config = {
39
- version: '1.0.115',
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
- const targetSelector = bbContents._getAttr(element, 'bb-reading-time-target');
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
- // Validation des valeurs
986
- if (isNaN(wordsPerMinute) || wordsPerMinute <= 0) {
987
- bbContents.utils.log('Vitesse de lecture invalide, utilisation de la valeur par défaut (230)');
988
- }
989
- if (isNaN(secondsPerImage) || secondsPerImage < 0) {
990
- bbContents.utils.log('Temps par image invalide, utilisation de la valeur par défaut (12)');
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 imageCount = images.length;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bebranded/bb-contents",
3
- "version": "1.0.115",
3
+ "version": "1.0.116",
4
4
  "description": "Contenus additionnels français pour Webflow",
5
5
  "main": "bb-contents.js",
6
6
  "scripts": {