Plugin Directory

Changeset 3477123


Ignore:
Timestamp:
03/07/2026 06:02:52 PM (4 weeks ago)
Author:
webfulchris
Message:

v2.5.0: Enhanced tracking - scroll depth, screen resolution, outbound clicks, file downloads, JS errors, RUM

Location:
webful/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • webful/trunk/assets/js/webful-tracker.js

    r3400049 r3477123  
    11/**
    22 * WEBFUL Analytics - Script de tracking ultra-léger
    3  * Version: 2.1.1
    4  * Taille minifiée: < 3ko
     3 * Version: 2.3.0
    54 *
     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
    68 * v2.1.0 - Ajout tendances de recherche + paramètres UTM
    79 */
     
    1113
    1214    // 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';
    1416
    1517    // Configuration injectée par WordPress
     
    2931    var sent = false;
    3032
     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
    3164    /**
    3265     * Générer ou récupérer l'ID de session
     
    79112     * Supporte : Bing, Yahoo, DuckDuckGo, Ecosia, Qwant
    80113     * Google : retourne null (HTTPS chiffré)
    81      *
    82      * @param {string} referrer - URL du referrer
    83      * @return {string|null} - Mot-clé extrait ou null
    84114     */
    85115    function extractSearchQuery(referrer) {
     
    90120            var hostname = url.hostname.toLowerCase();
    91121
    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
    118128            return null;
    119 
    120129        } catch(e) {
    121             // URL invalide
    122130            return null;
    123131        }
     
    126134    /**
    127135     * Récupérer un paramètre URL depuis l'URL courante
    128      *
    129      * @param {string} name - Nom du paramètre
    130      * @return {string|null} - Valeur du paramètre ou null
    131136     */
    132137    function getUrlParam(name) {
     
    151156            ip_hash: getVisitorId(),
    152157            url_page: window.location.href,
    153             page_title: document.title || null, // NOUVEAU v2.1.0 : Titre de la page
     158            page_title: document.title || null,
    154159            referrer: document.referrer || null,
    155160
    156             // NOUVEAU v2.1.0 : Extraction mot-clé de recherche
     161            // Extraction mot-clé de recherche
    157162            search_query: extractSearchQuery(document.referrer),
    158163
    159             // NOUVEAU v2.1.0 : Paramètres UTM (campagnes marketing)
     164            // Paramètres UTM (campagnes marketing)
    160165            utm_source: getUrlParam('utm_source'),
    161166            utm_medium: getUrlParam('utm_medium'),
     
    166171            user_agent: navigator.userAgent,
    167172            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
    169178        };
    170179
     
    180189                body: JSON.stringify(data),
    181190                keepalive: true
    182             }).catch(function() {
    183                 // Ignorer les erreurs silencieusement
    184             });
     191            }).catch(function() {});
    185192        }
    186193    }
     
    203210        };
    204211
    205         // Envoyer avec fetch
    206212        if (navigator.sendBeacon) {
    207213            var blob = new Blob([JSON.stringify(data)], { type: 'application/json' });
     
    212218                headers: { 'Content-Type': 'application/json' },
    213219                body: JSON.stringify(data)
    214             }).catch(function() {
    215                 // Ignorer les erreurs silencieusement
    216             });
     220            }).catch(function() {});
    217221        }
    218222    }
     
    222226     */
    223227    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
    225229        document.addEventListener('click', function(e) {
    226230            var target = e.target;
     
    240244                var phone = href.replace('tel:', '').replace(/\s/g, '');
    241245                sendConversion('tel_click', phone);
     246                return;
    242247            }
    243248
     
    246251                var email = href.replace('mailto:', '').split('?')[0];
    247252                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) {}
    248275            }
    249276        });
     
    268295            }
    269296        }
     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);
    270404    }
    271405
     
    301435        }
    302436
     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
    303446        // Envoyer aussi après un certain temps (fallback)
    304447        setTimeout(function() {
     
    306449                sendTracking();
    307450            }
    308         }, 30000); // Après 30 secondes
     451        }, 30000);
    309452    }
    310453
    311454    /**
    312455     * Injecter le badge WEBFUL dans le footer
    313      *
    314      * Règles :
    315      * - Badge affiché selon config.showBadge
    316      * - Commentaire HTML TOUJOURS présent (SEO)
    317      *
    318      * @return void
    319456     */
    320457    function injectBadge() {
     
    330467        // Afficher le badge visible si activé
    331468        if (config.showBadge && siteId) {
    332             // Créer le conteneur du badge
    333469            var badge = document.createElement('div');
    334470            badge.className = 'webful-badge';
     
    341477                'margin-top: 40px;';
    342478
    343             // Créer le lien
    344479            var link = document.createElement('a');
    345480            link.href = 'https://webful.fr?ref=' + encodeURIComponent(siteId);
     
    352487                'opacity: 0.7;';
    353488
    354             // Effet hover
    355489            link.addEventListener('mouseenter', function() {
    356490                this.style.opacity = '1';
  • webful/trunk/readme.txt

    r3468457 r3477123  
    66Tested up to: 6.9
    77Requires PHP: 7.4
    8 Stable tag: 2.4.0
     8Stable tag: 2.5.0
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    133133== Changelog ==
    134134
     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
    135145= 2.4.0 - 2025-12-03 =
    136146* **NEW**: Weekly stats display (7 days) instead of daily stats
     
    214224== Upgrade Notice ==
    215225
     226= 2.5.0 =
     227Enhanced tracking with scroll depth, screen resolution, outbound clicks, file downloads, JS error monitoring and Real User Monitoring (Core Web Vitals). Fully backward compatible.
     228
    216229= 2.0.0 =
    217230**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  
    33 * Plugin Name: WEBFUL Analytics
    44 * 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.0
     5 * Version: 2.5.0
    66 * Author: WEBFUL
    77 * Author URI: https://webful.fr
Note: See TracChangeset for help on using the changeset viewer.