PageSourceSearch

https://autheuil-authouillet.fr/wp-content/plugins/ric-ultimate-co…/public/js/public-script.js?ver=2.1.1

js autheuil-authouillet.fr collected 2026-10-02 20:26:09 UTC 25,046 bytes, 685 lines download raw bytes

1/**
2 * Public JavaScript for Ric Ultimate Cookie Consent.
3 * Handles granular consent, consent expiry, floating widget, preference modal,
4 * script blocking, Google Consent Mode v2, and the rucc_consent API.
5 */
6
7// =============== IMMEDIATE CONSENT CHECK & GCM UPDATE ===============
8// These run immediately when the script loads (in footer) to provide
9// the earliest possible consent state to Google Consent Mode.
10
11(function() {
12    var expiryKey = 'ric_cookie_expiry';
13    var storageKey = 'ric_cookie_consent';
14    var catsKey = 'ric_cookie_categories';
15
16    function ruccIsExpired() {
17        try {
18            var expiry = localStorage.getItem(expiryKey);
19            if (!expiry) return true;
20            return Date.now() > parseInt(expiry, 10);
21        } catch(e) {
22            console.error('Ric Cookie Consent: Error checking expiry', e);
23            return true;
24        }
25    }
26
27    function ruccGetSavedCategories() {
28        try {
29            var data = localStorage.getItem(catsKey);
30            return data ? JSON.parse(data) : [];
31        } catch(e) {
32            console.error('Ric Cookie Consent: Error parsing saved categories', e);
33            return [];
34        }
35    }
36
37    function ruccIsCCPAOptedOut() {
38        try {
39            var val = localStorage.getItem('ric_ccpa_optout');
40            var expiry = localStorage.getItem('ric_ccpa_expiry');
41            if (val !== 'opted_out' || !expiry) return false;
42            return Date.now() < parseInt(expiry, 10);
43        } catch(e) {
44            console.error('Ric Cookie Consent: Error checking CCPA opt-out', e);
45            return false;
46        }
47    }
48
49    function ruccUpdateGoogleConsent(consentType, categories) {
50        if (typeof window.dataLayer === 'undefined') return;
51        if (typeof gtag !== 'function') return;
52
53        var isAccepted = (consentType === 'accepted' || consentType === 'custom');
54        var hasAnalytics = isAccepted && (consentType === 'accepted' || categories.indexOf('analytics') !== -1);
55        var hasMarketing = isAccepted && (consentType === 'accepted' || categories.indexOf('marketing') !== -1);
56        var hasFunctional = isAccepted && (consentType === 'accepted' || categories.indexOf('functional') !== -1);
57
58        // CCPA opt-out overrides ad-related signals regardless of cookie consent
59        var ccpaOptedOut = ruccIsCCPAOptedOut();
60        var adGranted = hasMarketing && !ccpaOptedOut;
61
62        gtag('consent', 'update', {
63            'ad_storage': adGranted ? 'granted' : 'denied',
64            'ad_user_data': adGranted ? 'granted' : 'denied',
65            'ad_personalization': adGranted ? 'granted' : 'denied',
66            'analytics_storage': hasAnalytics ? 'granted' : 'denied',
67            'functionality_storage': hasFunctional ? 'granted' : 'denied',
68            'personalization_storage': hasFunctional ? 'granted' : 'denied',
69            'security_storage': 'granted'
70        });
71    }
72
73    // Check for saved consent and immediately update GCM
74    var savedConsent = null;
75    try { savedConsent = localStorage.getItem(storageKey); } catch(e) {}
76    if (savedConsent && !ruccIsExpired()) {
77        var savedCats = ruccGetSavedCategories();
78        ruccUpdateGoogleConsent(savedConsent, savedCats);
79    }
80
81})();
82
83// =============== window.rucc_consent PUBLIC API ===============
84// Provides a JavaScript API for other plugins/themes to check consent status.
85(function() {
86    var expiryKey = 'ric_cookie_expiry';
87    var storageKey = 'ric_cookie_consent';
88    var catsKey = 'ric_cookie_categories';
89
90    window.rucc_consent = {
91        /**
92         * Get the current consent status.
93         * @returns {string|null} 'accepted', 'declined', 'custom', or null if no consent given.
94         */
95        getConsent: function() {
96            try {
97                return localStorage.getItem(storageKey);
98            } catch(e) {
99                console.error('Ric Cookie Consent: Error getting consent', e);
100                return null;
101            }
102        },
103
104        /**
105         * Get the list of consented category keys.
106         * @returns {string[]} Array of category keys (e.g., ['necessary', 'analytics']).
107         */
108        getCategories: function() {
109            try {
110                var data = localStorage.getItem(catsKey);
111                return data ? JSON.parse(data) : [];
112            } catch(e) {
113                console.error('Ric Cookie Consent: Error parsing categories', e);
114                return [];
115            }
116        },
117
118        /**
119         * Check if a specific cookie category has been consented to.
120         * @param {string} category - The category key to check (e.g., 'analytics', 'marketing').
121         * @returns {boolean} True if the category is consented.
122         */
123        hasCategory: function(category) {
124            var consent = this.getConsent();
125            if (!consent) return false;
126            if (consent === 'accepted') return true;
127            if (consent === 'declined') return false;
128            // 'custom' mode — check categories list
129            var cats = this.getCategories();
130            return cats.indexOf(category) !== -1;
131        },
132
133        /**
134         * Register a callback that fires when consent status changes.
135         * The callback receives (consentType, categories).
136         * @param {function} callback - Function to call on consent change.
137         */
138        onChange: function(callback) {
139            if (typeof callback !== 'function') return;
140            if (!this._callbacks) this._callbacks = [];
141            this._callbacks.push(callback);
142        },
143
144        /**
145         * Check if the user has opted out under CCPA.
146         * The opt-out persists for 12 months and denies ad-related data sharing/selling.
147         * @returns {boolean} True if the user has an active CCPA opt-out.
148         */
149        getCCPAOptOut: function() {
150            try {
151                var val = localStorage.getItem('ric_ccpa_optout');
152                var expiry = localStorage.getItem('ric_ccpa_expiry');
153                if (val !== 'opted_out' || !expiry) return false;
154                return Date.now() < parseInt(expiry, 10);
155            } catch(e) {
156                console.error('Ric Cookie Consent: Error checking CCPA opt-out', e);
157                return false;
158            }
159        },
160
161        /**
162         * Internal: notify all registered callbacks.
163         * @private
164         */
165        _notify: function(consentType, categories) {
166            if (!this._callbacks) return;
167            for (var i = 0; i < this._callbacks.length; i++) {
168                try {
169                    this._callbacks[i](consentType, categories);
170                } catch(e) {
171                    console.error('Ric Cookie Consent: Error in consent change callback', e);
172                }
173            }
174            // Also dispatch a DOM event for broad compatibility
175            var event = new CustomEvent('rucc_consent_change', {
176                detail: { consent: consentType, categories: categories }
177            });
178            document.dispatchEvent(event);
179        }
180    };
181})();
182
183// =============== SCRIPT BLOCKING ENGINE ===============
184// Scans the DOM for blocked scripts (type="text/plain" with data-category)
185// and selectively loads them based on user consent.
186
187window.ruccScriptBlocker = {
188    /**
189     * Get the data-category attribute value(s) from a script element.
190     * Supports comma-separated values.
191     */
192    getScriptCategories: function(scriptEl) {
193        var catAttr = scriptEl.getAttribute('data-category');
194        if (!catAttr) return [];
195        return catAttr.split(',').map(function(c) { return c.trim(); });
196    },
197
198    /**
199     * Check if a script should be loaded given the user's consented categories.
200     */
201    shouldLoad: function(scriptEl, userCategories) {
202        var scriptCats = this.getScriptCategories(scriptEl);
203        if (!scriptCats.length) return false;
204        if (userCategories.indexOf('all') !== -1) return true;
205        for (var i = 0; i < scriptCats.length; i++) {
206            if (userCategories.indexOf(scriptCats[i]) !== -1) return true;
207        }
208        return false;
209    },
210
211    /**
212     * Resolve consent type ('accepted', 'declined', 'custom') into a flat category array.
213     */
214    resolveCategories: function(consentType, categories) {
215        if (consentType === 'accepted') {
216            return ['necessary', 'analytics', 'marketing', 'functional', 'all'];
217        }
218        if (consentType === 'declined') {
219            return ['necessary'];
220        }
221        // 'custom' or any other mode — use the provided or saved categories
222        return categories || window.rucc_consent.getCategories();
223    },
224
225    /**
226     * Load a blocked script by creating a new active script element.
227     */
228    loadScript: function(scriptEl) {
229        var newScript = document.createElement('script');
230        for (var i = 0; i < scriptEl.attributes.length; i++) {
231            var attr = scriptEl.attributes[i];
232            var name = attr.name;
233            if (name === 'type') continue;
234            if (name === 'data-category') continue;
235            if (name === 'data-src') {
236                newScript.setAttribute('src', attr.value);
237                continue;
238            }
239            newScript.setAttribute(name, attr.value);
240        }
241        newScript.type = 'text/javascript';
242        newScript.textContent = scriptEl.textContent;
243        newScript.setAttribute('data-rucc-loaded', 'true');
244        scriptEl.parentNode.replaceChild(newScript, scriptEl);
245    },
246
247    /**
248     * Apply consent to all blocked scripts on the page.
249     */
250    applyConsent: function(consentType, categories) {
251        var userCats = this.resolveCategories(consentType, categories);
252        var blockedScripts = document.querySelectorAll('script[type="text/plain"][data-category]');
253
254        for (var i = 0; i < blockedScripts.length; i++) {
255            var script = blockedScripts[i];
256            if (script.getAttribute('data-rucc-loaded')) continue;
257            if (this.shouldLoad(script, userCats)) {
258                this.loadScript(script);
259            }
260        }
261    }
262};
263
264// =============== DOMContentLoaded — BANNER & CONSENT LOGIC ===============
265document.addEventListener("DOMContentLoaded", function () {
266    const banner = document.getElementById('cookie-banner-glass');
267    const overlay = document.getElementById('cookie-banner-overlay');
268    const floatingWidget = document.getElementById('ric-floating-widget');
269    const preferenceModal = document.getElementById('ric-preference-modal');
270    const preferenceOverlay = document.getElementById('ric-preference-overlay');
271
272    if (!banner) {
273        return; // Exit if banner element not found
274    }
275
276    const storageKey = 'ric_cookie_consent';
277    const expiryKey = 'ric_cookie_expiry';
278    const catsKey = 'ric_cookie_categories';
279    const consentMode = rucc_settings.consent_mode || 'simple';
280    const expiryDays = parseInt(rucc_settings.expiry_days) || 365;
281    const showWidget = rucc_settings.show_widget || 'yes';
282    const enableCCPA = rucc_settings.enable_ccpa || 'no';
283    const ccpaButtonText = rucc_settings.ccpa_button_text || 'Do Not Sell My Personal Information';
284
285    /**
286     * Check if consent is expired.
287     */
288    function isConsentExpired() {
289        var expiry = localStorage.getItem(expiryKey);
290        if (!expiry) return true;
291        return Date.now() > parseInt(expiry);
292    }
293
294    /**
295     * Show the banner and overlay.
296     */
297    function showBanner() {
298        banner.classList.add('show');
299        if (overlay) {
300            overlay.classList.add('show');
301        }
302    }
303
304    /**
305     * Hide banner, overlay, and preference modal.
306     */
307    function hideAll() {
308        banner.classList.remove('show');
309        if (overlay) {
310            overlay.classList.remove('show');
311        }
312        if (preferenceModal) {
313            preferenceModal.classList.remove('show');
314        }
315        if (preferenceOverlay) {
316            preferenceOverlay.classList.remove('show');
317        }
318    }
319
320    /**
321     * Send consent to the server via AJAX.
322     */
323    function sendConsent(consentType, categories) {
324        var params = 'action=rucc_handle_consent&nonce=' + rucc_settings.nonce + '&consent=' + encodeURIComponent(consentType);
325        if (categories && categories.length > 0) {
326            for (var i = 0; i < categories.length; i++) {
327                params += '&categories[]=' + encodeURIComponent(categories[i]);
328            }
329        }
330        var xhr = new XMLHttpRequest();
331        xhr.open('POST', rucc_settings.ajax_url, true);
332        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
333        xhr.send(params);
334    }
335
336    /**
337     * Get selected categories from checkboxes.
338     */
339    function getSelectedCategories() {
340        var checkboxes = document.querySelectorAll('.cookie-cat-checkbox:checked');
341        var cats = [];
342        for (var i = 0; i < checkboxes.length; i++) {
343            cats.push(checkboxes[i].value);
344        }
345        return cats;
346    }
347
348    /**
349     * Get all available category keys.
350     */
351    function getAllCategoryKeys() {
352        var checkboxes = document.querySelectorAll('.cookie-cat-checkbox');
353        var cats = [];
354        for (var i = 0; i < checkboxes.length; i++) {
355            cats.push(checkboxes[i].value);
356        }
357        return cats;
358    }
359
360    /**
361     * Set expiry timestamp in localStorage.
362     */
363    function setExpiry() {
364        if (expiryDays > 0) {
365            var expiry = Date.now() + (expiryDays * 24 * 60 * 60 * 1000);
366            localStorage.setItem(expiryKey, expiry.toString());
367        }
368    }
369
370    /**
371     * Update Google Consent Mode v2 with the given consent choices.
372     */
373    function isCCPAOptedOut() {
374        try {
375            var val = localStorage.getItem('ric_ccpa_optout');
376            var expiry = localStorage.getItem('ric_ccpa_expiry');
377            if (val !== 'opted_out' || !expiry) return false;
378            return Date.now() < parseInt(expiry, 10);
379        } catch(e) {
380            return false;
381        }
382    }
383
384    function updateGoogleConsent(consentType, categories) {
385        if (typeof window.dataLayer === 'undefined') return;
386        if (typeof gtag !== 'function') return;
387
388        var isAccepted = (consentType === 'accepted' || consentType === 'custom');
389        var hasAnalytics = isAccepted && (consentType === 'accepted' || categories.indexOf('analytics') !== -1);
390        var hasMarketing = isAccepted && (consentType === 'accepted' || categories.indexOf('marketing') !== -1);
391        var hasFunctional = isAccepted && (consentType === 'accepted' || categories.indexOf('functional') !== -1);
392
393        // CCPA opt-out overrides ad-related signals regardless of cookie consent
394        var ccpaOptedOut = isCCPAOptedOut();
395        var adGranted = hasMarketing && !ccpaOptedOut;
396
397        gtag('consent', 'update', {
398            'ad_storage': adGranted ? 'granted' : 'denied',
399            'ad_user_data': adGranted ? 'granted' : 'denied',
400            'ad_personalization': adGranted ? 'granted' : 'denied',
401            'analytics_storage': hasAnalytics ? 'granted' : 'denied',
402            'functionality_storage': hasFunctional ? 'granted' : 'denied',
403            'personalization_storage': hasFunctional ? 'granted' : 'denied',
404            'security_storage': 'granted'
405        });
406    }
407
408    /**
409     * Load or block scripts based on the user's consent categories.
410     * Delegates to the global ruccScriptBlocker for consistency.
411     */
412    function applyConsentToScripts(consentType, categories) {
413        if (window.ruccScriptBlocker) {
414            window.ruccScriptBlocker.applyConsent(consentType, categories);
415        }
416    }
417
418    /**
419     * Send CCPA opt-out to the server via AJAX.
420     */
421    function sendCCPAOptOut() {
422        var params = 'action=rucc_handle_ccpa_optout&nonce=' + rucc_settings.nonce;
423        var xhr = new XMLHttpRequest();
424        xhr.open('POST', rucc_settings.ajax_url, true);
425        xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
426        xhr.send(params);
427    }
428
429    /**
430     * Handle CCPA opt-out action.
431     */
432    function handleCCPAOptOut() {
433        var ccpaKey = 'ric_ccpa_optout';
434        var ccpaExpiryKey = 'ric_ccpa_expiry';
435
436        localStorage.setItem(ccpaKey, 'opted_out');
437        var expiry = Date.now() + (365 * 24 * 60 * 60 * 1000); // 12 months
438        localStorage.setItem(ccpaExpiryKey, expiry.toString());
439
440        // Update GCM to deny ad-related signals
441        updateGoogleConsent(localStorage.getItem(storageKey) || 'declined', []);
442
443        sendCCPAOptOut();
444
445        // Hide the banner if it's still visible
446        hideAll();
447        showFloatingWidget();
448    }
449
450    /**
451     * Handle consent action: save to localStorage, update GCM, notify API, block/load scripts.
452     */
453    function handleConsent(type, categories) {
454        localStorage.setItem(storageKey, type);
455        localStorage.setItem(catsKey, JSON.stringify(categories));
456        setExpiry();
457        updateGoogleConsent(type, categories);
458        applyConsentToScripts(type, categories);
459        hideAll();
460        sendConsent(type, categories);
461        showFloatingWidget();
462
463        // Notify the rucc_consent API listeners
464        if (window.rucc_consent && window.rucc_consent._notify) {
465            window.rucc_consent._notify(type, categories);
466        }
467    }
468
469    /**
470     * Show floating widget after consent is given.
471     */
472    function showFloatingWidget() {
473        if (floatingWidget && showWidget === 'yes') {
474            floatingWidget.classList.add('show');
475        }
476    }
477
478    /**
479     * Hide floating widget.
480     */
481    function hideFloatingWidget() {
482        if (floatingWidget) {
483            floatingWidget.classList.remove('show');
484        }
485    }
486
487    /**
488     * Open preference modal and pre-fill from saved preferences.
489     */
490    function openPreferences() {
491        // Pre-fill checkboxes from saved preferences
492        var savedCats = localStorage.getItem(catsKey);
493        if (savedCats) {
494            try {
495                var cats = JSON.parse(savedCats);
496                var checkboxes = document.querySelectorAll('.cookie-cat-checkbox:not([disabled])');
497                for (var i = 0; i < checkboxes.length; i++) {
498                    checkboxes[i].checked = cats.indexOf(checkboxes[i].value) !== -1;
499                }
500            } catch(e) {}
501        }
502        if (preferenceModal) {
503            preferenceModal.classList.add('show');
504        }
505        if (preferenceOverlay) {
506            preferenceOverlay.classList.add('show');
507        }
508    }
509
510    /**
511     * Close preference modal.
512     */
513    function closePreferences() {
514        if (preferenceModal) {
515            preferenceModal.classList.remove('show');
516        }
517        if (preferenceOverlay) {
518            preferenceOverlay.classList.remove('show');
519        }
520    }
521
522    // =============== INIT ===============
523
524    var savedConsent = localStorage.getItem(storageKey);
525    var expired = isConsentExpired();
526
527    if (!savedConsent || expired) {
528        // Show banner (no floating widget yet)
529        hideFloatingWidget();
530        showBanner();
531    } else {
532        // Consent exists and is valid: apply scripts, show floating widget, hide banner
533        hideAll();
534        applyConsentToScripts(savedConsent, window.rucc_consent ? window.rucc_consent.getCategories() : []);
535        showFloatingWidget();
536    }
537
538    // =============== CCPA OPT-OUT CHECK ON LOAD ===============
539    // If a CCPA opt-out was previously saved, respect it by updating GCM
540    if (enableCCPA === 'yes' && isCCPAOptedOut()) {
541        updateGoogleConsent(localStorage.getItem(storageKey) || 'declined', []);
542    }
543
544    // =============== BANNER BUTTONS ===============
545
546    var acceptButton = document.getElementById('cookie-accept');
547    var declineButton = document.getElementById('cookie-decline');
548    var customizeButton = document.getElementById('cookie-customize');
549    var savePrefsButton = document.getElementById('cookie-save-prefs');
550    var categoriesSection = document.getElementById('cookie-categories');
551
552    if (acceptButton) {
553        acceptButton.addEventListener('click', function () {
554            var cats = (consentMode === 'granular') ? getAllCategoryKeys() : ['all'];
555            handleConsent('accepted', cats);
556        });
557    }
558
559    if (declineButton) {
560        declineButton.addEventListener('click', function () {
561            handleConsent('declined', []);
562        });
563    }
564
565    // CCPA "Do Not Sell" button in the main banner
566    var ccpaButton = document.getElementById('cookie-ccpa-optout');
567    if (ccpaButton) {
568        ccpaButton.addEventListener('click', function () {
569            handleCCPAOptOut();
570        });
571    }
572
573    // Customize link: expand categories, hide link, show save prefs
574    if (customizeButton && categoriesSection) {
575        customizeButton.addEventListener('click', function () {
576            categoriesSection.classList.add('expanded');
577            customizeButton.style.display = 'none';
578            if (savePrefsButton) savePrefsButton.style.display = '';
579        });
580    }
581
582    if (savePrefsButton) {
583        savePrefsButton.addEventListener('click', function () {
584            var cats = getSelectedCategories();
585            handleConsent('custom', cats);
586        });
587    }
588
589    // Note: Overlay click no longer dismisses the banner.
590    // The banner must stay visible until the user explicitly clicks Accept or Decline.
591
592    // =============== FLOATING WIDGET ===============
593
594    if (floatingWidget) {
595        floatingWidget.addEventListener('click', function () {
596            if (preferenceModal) {
597                openPreferences();
598            } else if (banner) {
599                // If no preference modal, show the banner again
600                showBanner();
601                if (overlay) overlay.classList.add('show');
602            }
603        });
604    }
605
606    // =============== PREFERENCE MODAL ===============
607
608    if (preferenceOverlay) {
609        preferenceOverlay.addEventListener('click', function () {
610            closePreferences();
611        });
612    }
613
614    // Close preference modal with Escape key
615    document.addEventListener('keydown', function (e) {
616        if (e.key === 'Escape') {
617            closePreferences();
618        }
619    });
620
621    // If preference modal exists, add buttons
622    if (preferenceModal) {
623        var prefAccept = document.getElementById('pref-accept');
624        var prefSave = document.getElementById('pref-save');
625        var prefDecline = document.getElementById('pref-decline');
626
627        if (prefAccept) {
628            prefAccept.addEventListener('click', function () {
629                var cats = (consentMode === 'granular') ? getAllCategoryKeys() : ['all'];
630                handleConsent('accepted', cats);
631            });
632        }
633
634        if (prefSave) {
635            prefSave.addEventListener('click', function () {
636                var cats = getSelectedCategories();
637                handleConsent('custom', cats);
638            });
639        }
640
641        if (prefDecline) {
642            prefDecline.addEventListener('click', function () {
643                handleConsent('declined', []);
644            });
645        }
646
647        // CCPA "Do Not Sell" button in the preference modal
648        var prefCCPAButton = document.getElementById('pref-ccpa-optout');
649        if (prefCCPAButton) {
650            prefCCPAButton.addEventListener('click', function () {
651                handleCCPAOptOut();
652            });
653        }
654    }
655
656    // =============== APPLY SCRIPT BLOCKING ON NEW SCRIPTS (MUTATION OBSERVER) ===============
657    // Watch for dynamically added blocked scripts and apply consent if already given.
658    var savedConsentType = localStorage.getItem(storageKey);
659    if (savedConsentType && !isConsentExpired() && window.ruccScriptBlocker) {
660        var observer = new MutationObserver(function(mutations) {
661            for (var m = 0; m < mutations.length; m++) {
662                var mutation = mutations[m];
663                if (mutation.type === 'childList' && mutation.addedNodes.length) {
664                    for (var n = 0; n < mutation.addedNodes.length; n++) {
665                        var node = mutation.addedNodes[n];
666                        if (node.nodeType === 1 && node.matches && node.matches('script[type="text/plain"][data-category]')) {
667                            var userCats = window.ruccScriptBlocker.resolve
667Categories(
668                                window.rucc_consent.getConsent(),
669                                window.rucc_consent.getCategories()
670                            );
671                            if (window.ruccScriptBlocker.shouldLoad(node, userCats)) {
672                                window.ruccScriptBlocker.loadScript(node);
673                            }
674                        }
675                    }
676                }
677            }
678        });
679
680        observer.observe(document.documentElement, {
681            childList: true,
682            subtree: true
683        });
684    }
685});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.