Changeset 3477123
- Timestamp:
- 03/07/2026 06:02:52 PM (4 weeks ago)
- Location:
- webful/trunk
- Files:
-
- 3 edited
-
assets/js/webful-tracker.js (modified) (20 diffs)
-
readme.txt (modified) (3 diffs)
-
webful.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
webful/trunk/assets/js/webful-tracker.js
r3400049 r3477123 1 1 /** 2 2 * WEBFUL Analytics - Script de tracking ultra-léger 3 * Version: 2.1.1 4 * Taille minifiée: < 3ko 3 * Version: 2.3.0 5 4 * 5 * v2.3.0 - Ajout scroll depth, screen resolution, liens sortants, telechargements, 6 * erreurs JavaScript, metriques RUM (Real User Monitoring) 7 * v2.1.1 - Fix mineur 6 8 * v2.1.0 - Ajout tendances de recherche + paramètres UTM 7 9 */ … … 11 13 12 14 // Marqueur de version pour détection de mise à jour 13 window.WEBFUL_PLUGIN_VERSION = '2. 1.1';15 window.WEBFUL_PLUGIN_VERSION = '2.3.0'; 14 16 15 17 // Configuration injectée par WordPress … … 29 31 var sent = false; 30 32 33 // v2.3.0 : Scroll depth tracking 34 var maxScrollDepth = 0; 35 var scrollTimeout; 36 37 function trackScrollDepth() { 38 var docHeight = Math.max( 39 document.body.scrollHeight || 0, document.documentElement.scrollHeight || 0, 40 document.body.offsetHeight || 0, document.documentElement.offsetHeight || 0 41 ); 42 var viewportHeight = window.innerHeight; 43 var scrollTop = window.scrollY || window.pageYOffset || 0; 44 45 if (docHeight <= viewportHeight) { 46 maxScrollDepth = 100; 47 return; 48 } 49 50 var depth = Math.round(((scrollTop + viewportHeight) / docHeight) * 100); 51 if (depth > maxScrollDepth) { 52 maxScrollDepth = Math.min(depth, 100); 53 } 54 } 55 56 window.addEventListener('scroll', function() { 57 if (scrollTimeout) return; 58 scrollTimeout = setTimeout(function() { 59 scrollTimeout = null; 60 trackScrollDepth(); 61 }, 200); 62 }, {passive: true}); 63 31 64 /** 32 65 * Générer ou récupérer l'ID de session … … 79 112 * Supporte : Bing, Yahoo, DuckDuckGo, Ecosia, Qwant 80 113 * Google : retourne null (HTTPS chiffré) 81 *82 * @param {string} referrer - URL du referrer83 * @return {string|null} - Mot-clé extrait ou null84 114 */ 85 115 function extractSearchQuery(referrer) { … … 90 120 var hostname = url.hostname.toLowerCase(); 91 121 92 // Bing: ?q=... 93 if (hostname.indexOf('bing.') !== -1) { 94 return url.searchParams.get('q'); 95 } 96 97 // Yahoo: ?p=... 98 if (hostname.indexOf('yahoo.') !== -1) { 99 return url.searchParams.get('p'); 100 } 101 102 // DuckDuckGo: ?q=... 103 if (hostname.indexOf('duckduckgo.') !== -1) { 104 return url.searchParams.get('q'); 105 } 106 107 // Ecosia: ?q=... 108 if (hostname.indexOf('ecosia.') !== -1) { 109 return url.searchParams.get('q'); 110 } 111 112 // Qwant: ?q=... 113 if (hostname.indexOf('qwant.') !== -1) { 114 return url.searchParams.get('q'); 115 } 116 117 // Google et autres : null (HTTPS chiffré) 122 if (hostname.indexOf('bing.') !== -1) return url.searchParams.get('q'); 123 if (hostname.indexOf('yahoo.') !== -1) return url.searchParams.get('p'); 124 if (hostname.indexOf('duckduckgo.') !== -1) return url.searchParams.get('q'); 125 if (hostname.indexOf('ecosia.') !== -1) return url.searchParams.get('q'); 126 if (hostname.indexOf('qwant.') !== -1) return url.searchParams.get('q'); 127 118 128 return null; 119 120 129 } catch(e) { 121 // URL invalide122 130 return null; 123 131 } … … 126 134 /** 127 135 * Récupérer un paramètre URL depuis l'URL courante 128 *129 * @param {string} name - Nom du paramètre130 * @return {string|null} - Valeur du paramètre ou null131 136 */ 132 137 function getUrlParam(name) { … … 151 156 ip_hash: getVisitorId(), 152 157 url_page: window.location.href, 153 page_title: document.title || null, // NOUVEAU v2.1.0 : Titre de la page158 page_title: document.title || null, 154 159 referrer: document.referrer || null, 155 160 156 // NOUVEAU v2.1.0 :Extraction mot-clé de recherche161 // Extraction mot-clé de recherche 157 162 search_query: extractSearchQuery(document.referrer), 158 163 159 // NOUVEAU v2.1.0 :Paramètres UTM (campagnes marketing)164 // Paramètres UTM (campagnes marketing) 160 165 utm_source: getUrlParam('utm_source'), 161 166 utm_medium: getUrlParam('utm_medium'), … … 166 171 user_agent: navigator.userAgent, 167 172 temps_passe: getTimeSpent(), 168 plugin_version: window.WEBFUL_PLUGIN_VERSION 173 plugin_version: window.WEBFUL_PLUGIN_VERSION, 174 175 // v2.3.0 : Nouvelles metriques 176 scroll_depth: maxScrollDepth, 177 screen_resolution: window.screen.width + 'x' + window.screen.height 169 178 }; 170 179 … … 180 189 body: JSON.stringify(data), 181 190 keepalive: true 182 }).catch(function() { 183 // Ignorer les erreurs silencieusement 184 }); 191 }).catch(function() {}); 185 192 } 186 193 } … … 203 210 }; 204 211 205 // Envoyer avec fetch206 212 if (navigator.sendBeacon) { 207 213 var blob = new Blob([JSON.stringify(data)], { type: 'application/json' }); … … 212 218 headers: { 'Content-Type': 'application/json' }, 213 219 body: JSON.stringify(data) 214 }).catch(function() { 215 // Ignorer les erreurs silencieusement 216 }); 220 }).catch(function() {}); 217 221 } 218 222 } … … 222 226 */ 223 227 function setupConversionTracking() { 224 // 1. Détecter les clics sur tel: et mailto:228 // 1. Détecter les clics sur tel:, mailto:, liens sortants et téléchargements 225 229 document.addEventListener('click', function(e) { 226 230 var target = e.target; … … 240 244 var phone = href.replace('tel:', '').replace(/\s/g, ''); 241 245 sendConversion('tel_click', phone); 246 return; 242 247 } 243 248 … … 246 251 var email = href.replace('mailto:', '').split('?')[0]; 247 252 sendConversion('email_click', email); 253 return; 254 } 255 256 // v2.3.0 : Téléchargement de fichier 257 var fileExtensions = ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.zip', '.rar', '.csv']; 258 var hrefLower = href.toLowerCase(); 259 for (var j = 0; j < fileExtensions.length; j++) { 260 if (hrefLower.indexOf(fileExtensions[j]) !== -1) { 261 sendConversion('file_download', href); 262 return; 263 } 264 } 265 266 // v2.3.0 : Lien sortant (domaine externe) 267 if (href.indexOf('http') === 0) { 268 try { 269 var linkHost = new URL(href).hostname; 270 var currentHost = window.location.hostname; 271 if (linkHost !== currentHost && linkHost.indexOf(currentHost) === -1 && currentHost.indexOf(linkHost) === -1) { 272 sendConversion('outbound_click', href); 273 } 274 } catch(ignored) {} 248 275 } 249 276 }); … … 268 295 } 269 296 } 297 } 298 299 // =================================================================== 300 // v2.3.0 : TRACKING ERREURS JAVASCRIPT 301 // =================================================================== 302 303 var jsErrorCount = 0; 304 305 window.addEventListener('error', function(event) { 306 if (jsErrorCount >= 5) return; 307 jsErrorCount++; 308 309 var errorEndpoint = apiUrl.replace('/track.php', '/track-error.php'); 310 var data = { 311 site_id: siteId, 312 session_id: sessionId, 313 error_message: (event.message || 'Unknown error').substring(0, 500), 314 error_source: (event.filename || '').substring(0, 500), 315 error_line: event.lineno || null, 316 error_column: event.colno || null, 317 page_url: window.location.href, 318 user_agent: navigator.userAgent 319 }; 320 321 if (typeof fetch !== 'undefined') { 322 fetch(errorEndpoint, { 323 method: 'POST', 324 headers: { 'Content-Type': 'application/json' }, 325 body: JSON.stringify(data) 326 }).catch(function() {}); 327 } 328 }); 329 330 // =================================================================== 331 // v2.3.0 : REAL USER MONITORING (RUM) 332 // =================================================================== 333 334 function collectRumMetrics() { 335 if (!window.PerformanceObserver && !window.performance) return; 336 337 var metrics = {}; 338 var rumEndpoint = apiUrl.replace('/track.php', '/track-rum.php'); 339 340 // Navigation timing 341 try { 342 var navEntries = performance.getEntriesByType('navigation'); 343 if (navEntries && navEntries.length > 0) { 344 var nav = navEntries[0]; 345 metrics.ttfb = Math.round(nav.responseStart); 346 metrics.dom_load = Math.round(nav.domContentLoadedEventEnd); 347 metrics.page_load = Math.round(nav.loadEventEnd); 348 } 349 } catch(e) {} 350 351 // First Contentful Paint 352 try { 353 var paintEntries = performance.getEntriesByType('paint'); 354 for (var i = 0; i < paintEntries.length; i++) { 355 if (paintEntries[i].name === 'first-contentful-paint') { 356 metrics.fcp = Math.round(paintEntries[i].startTime); 357 } 358 } 359 } catch(e) {} 360 361 // LCP 362 try { 363 if (window.PerformanceObserver) { 364 new PerformanceObserver(function(entryList) { 365 var entries = entryList.getEntries(); 366 if (entries.length > 0) { 367 metrics.lcp = Math.round(entries[entries.length - 1].startTime); 368 } 369 }).observe({type: 'largest-contentful-paint', buffered: true}); 370 } 371 } catch(e) {} 372 373 // CLS 374 try { 375 if (window.PerformanceObserver) { 376 var clsValue = 0; 377 new PerformanceObserver(function(entryList) { 378 entryList.getEntries().forEach(function(entry) { 379 if (!entry.hadRecentInput) { 380 clsValue += entry.value; 381 } 382 }); 383 metrics.cls = Math.round(clsValue * 10000) / 10000; 384 }).observe({type: 'layout-shift', buffered: true}); 385 } 386 } catch(e) {} 387 388 // Envoyer apres 5 secondes 389 setTimeout(function() { 390 if (Object.keys(metrics).length === 0) return; 391 392 metrics.site_id = siteId; 393 metrics.session_id = sessionId; 394 metrics.page_url = window.location.href; 395 396 if (typeof fetch !== 'undefined') { 397 fetch(rumEndpoint, { 398 method: 'POST', 399 headers: { 'Content-Type': 'application/json' }, 400 body: JSON.stringify(metrics) 401 }).catch(function() {}); 402 } 403 }, 5000); 270 404 } 271 405 … … 301 435 } 302 436 437 // RUM apres page load 438 if (document.readyState === 'complete') { 439 collectRumMetrics(); 440 } else { 441 window.addEventListener('load', function() { 442 setTimeout(collectRumMetrics, 1000); 443 }); 444 } 445 303 446 // Envoyer aussi après un certain temps (fallback) 304 447 setTimeout(function() { … … 306 449 sendTracking(); 307 450 } 308 }, 30000); // Après 30 secondes451 }, 30000); 309 452 } 310 453 311 454 /** 312 455 * Injecter le badge WEBFUL dans le footer 313 *314 * Règles :315 * - Badge affiché selon config.showBadge316 * - Commentaire HTML TOUJOURS présent (SEO)317 *318 * @return void319 456 */ 320 457 function injectBadge() { … … 330 467 // Afficher le badge visible si activé 331 468 if (config.showBadge && siteId) { 332 // Créer le conteneur du badge333 469 var badge = document.createElement('div'); 334 470 badge.className = 'webful-badge'; … … 341 477 'margin-top: 40px;'; 342 478 343 // Créer le lien344 479 var link = document.createElement('a'); 345 480 link.href = 'https://webful.fr?ref=' + encodeURIComponent(siteId); … … 352 487 'opacity: 0.7;'; 353 488 354 // Effet hover355 489 link.addEventListener('mouseenter', function() { 356 490 this.style.opacity = '1'; -
webful/trunk/readme.txt
r3468457 r3477123 6 6 Tested up to: 6.9 7 7 Requires PHP: 7.4 8 Stable tag: 2. 4.08 Stable tag: 2.5.0 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 133 133 == Changelog == 134 134 135 = 2.5.0 - 2026-03-07 = 136 * **NEW**: Scroll depth tracking per page (0-100%) 137 * **NEW**: Screen resolution collection for device analytics 138 * **NEW**: Outbound link click tracking (external links) 139 * **NEW**: File download tracking (PDF, DOC, ZIP, etc.) 140 * **NEW**: JavaScript error monitoring (window.onerror) 141 * **NEW**: Real User Monitoring (LCP, FCP, CLS, TTFB, page load) 142 * **IMPROVEMENT**: Enhanced tracking script with 6 new data points 143 * **COMPATIBILITY**: Fully backward compatible - old plugin versions continue to work 144 135 145 = 2.4.0 - 2025-12-03 = 136 146 * **NEW**: Weekly stats display (7 days) instead of daily stats … … 214 224 == Upgrade Notice == 215 225 226 = 2.5.0 = 227 Enhanced tracking with scroll depth, screen resolution, outbound clicks, file downloads, JS error monitoring and Real User Monitoring (Core Web Vitals). Fully backward compatible. 228 216 229 = 2.0.0 = 217 230 **MAJOR VERSION**: Automatic conversion tracking! After updating, you MUST visit your site in private browsing mode (CTRL+SHIFT+N) to register the new version in your WEBFUL dashboard. Without this visit, the update alert will continue to display. -
webful/trunk/webful.php
r3409217 r3477123 3 3 * Plugin Name: WEBFUL Analytics 4 4 * Description: Système d'analyse de trafic ultra-léger et respectueux de la vie privée. Suivez vos statistiques sans ralentir votre site. 5 * Version: 2. 4.05 * Version: 2.5.0 6 6 * Author: WEBFUL 7 7 * Author URI: https://webful.fr
Note: See TracChangeset
for help on using the changeset viewer.