cookiecraft 1.0.6 → 1.0.8
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/README.md +171 -304
- package/dist/cookiecraft.css +1 -1
- package/dist/cookiecraft.esm.js +224 -137
- package/dist/cookiecraft.esm.js.map +1 -1
- package/dist/cookiecraft.js +224 -137
- package/dist/cookiecraft.js.map +1 -1
- package/dist/cookiecraft.min.js +1 -1
- package/dist/cookiecraft.min.js.map +1 -1
- package/dist/stats.html +1 -1
- package/dist/types/blocking/CategoryManager.d.ts +1 -0
- package/dist/types/core/ConsentManager.d.ts +1 -1
- package/dist/types/core/CookieConsent.d.ts +5 -4
- package/dist/types/core/EventEmitter.d.ts +3 -2
- package/dist/types/integrations/GTMConsentMode.d.ts +0 -6
- package/dist/types/types/index.d.ts +3 -7
- package/dist/types/ui/Banner.d.ts +6 -0
- package/dist/types/ui/PreferenceCenter.d.ts +1 -0
- package/package.json +1 -1
package/dist/cookiecraft.js
CHANGED
|
@@ -49,13 +49,20 @@
|
|
|
49
49
|
* Clear consent record from localStorage
|
|
50
50
|
*/
|
|
51
51
|
clear() {
|
|
52
|
-
|
|
52
|
+
try {
|
|
53
|
+
localStorage.removeItem(StorageManager.STORAGE_KEY);
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
console.error('Failed to clear consent:', e);
|
|
57
|
+
}
|
|
53
58
|
}
|
|
54
59
|
/**
|
|
55
60
|
* Check if consent record has expired
|
|
56
61
|
*/
|
|
57
62
|
isExpired(consent) {
|
|
58
63
|
const expiry = new Date(consent.expiresAt);
|
|
64
|
+
if (isNaN(expiry.getTime()))
|
|
65
|
+
return true;
|
|
59
66
|
return expiry < new Date();
|
|
60
67
|
}
|
|
61
68
|
/**
|
|
@@ -66,7 +73,6 @@
|
|
|
66
73
|
typeof data.version === 'number' &&
|
|
67
74
|
typeof data.timestamp === 'string' &&
|
|
68
75
|
typeof data.categories === 'object' &&
|
|
69
|
-
typeof data.userAgent === 'string' &&
|
|
70
76
|
typeof data.expiresAt === 'string');
|
|
71
77
|
}
|
|
72
78
|
/**
|
|
@@ -82,11 +88,20 @@
|
|
|
82
88
|
const now = new Date();
|
|
83
89
|
const expiryDate = new Date(now);
|
|
84
90
|
expiryDate.setMonth(expiryDate.getMonth() + StorageManager.EXPIRY_MONTHS);
|
|
91
|
+
// Coerce category values to booleans
|
|
92
|
+
const rawCategories = record.categories;
|
|
93
|
+
const categories = {
|
|
94
|
+
necessary: rawCategories.necessary === true,
|
|
95
|
+
analytics: rawCategories.analytics === true,
|
|
96
|
+
marketing: rawCategories.marketing === true,
|
|
97
|
+
};
|
|
98
|
+
if ('preferences' in rawCategories) {
|
|
99
|
+
categories.preferences = rawCategories.preferences === true;
|
|
100
|
+
}
|
|
85
101
|
return {
|
|
86
102
|
version: typeof record.version === 'number' ? record.version : 1,
|
|
87
103
|
timestamp: typeof record.timestamp === 'string' ? record.timestamp : now.toISOString(),
|
|
88
|
-
categories
|
|
89
|
-
userAgent: typeof record.userAgent === 'string' ? record.userAgent : navigator.userAgent,
|
|
104
|
+
categories,
|
|
90
105
|
expiresAt: typeof record.expiresAt === 'string' ? record.expiresAt : expiryDate.toISOString(),
|
|
91
106
|
};
|
|
92
107
|
}
|
|
@@ -101,6 +116,7 @@
|
|
|
101
116
|
*/
|
|
102
117
|
class ConsentManager {
|
|
103
118
|
constructor(config) {
|
|
119
|
+
this.consent = null;
|
|
104
120
|
this.config = config;
|
|
105
121
|
}
|
|
106
122
|
/**
|
|
@@ -119,6 +135,10 @@
|
|
|
119
135
|
}
|
|
120
136
|
}
|
|
121
137
|
}
|
|
138
|
+
// Coerce all values to booleans
|
|
139
|
+
for (const key of Object.keys(categories)) {
|
|
140
|
+
categories[key] = categories[key] === true;
|
|
141
|
+
}
|
|
122
142
|
return true;
|
|
123
143
|
}
|
|
124
144
|
/**
|
|
@@ -135,13 +155,12 @@
|
|
|
135
155
|
* Check if user needs to give consent
|
|
136
156
|
*/
|
|
137
157
|
needsConsent() {
|
|
138
|
-
return this.consent ===
|
|
158
|
+
return this.consent === null;
|
|
139
159
|
}
|
|
140
160
|
/**
|
|
141
161
|
* Check if stored consent needs update due to policy change
|
|
142
162
|
*/
|
|
143
163
|
needsUpdate(storedConsent) {
|
|
144
|
-
// Check if policy version has changed
|
|
145
164
|
return storedConsent.version < this.config.revision;
|
|
146
165
|
}
|
|
147
166
|
/**
|
|
@@ -161,7 +180,6 @@
|
|
|
161
180
|
version: this.config.revision,
|
|
162
181
|
timestamp: now.toISOString(),
|
|
163
182
|
categories: Object.assign({}, categories),
|
|
164
|
-
userAgent: navigator.userAgent,
|
|
165
183
|
expiresAt: expiryDate.toISOString(),
|
|
166
184
|
};
|
|
167
185
|
}
|
|
@@ -402,7 +420,6 @@
|
|
|
402
420
|
class CategoryManager {
|
|
403
421
|
constructor() {
|
|
404
422
|
this.categories = new Map();
|
|
405
|
-
// Initialize with common patterns
|
|
406
423
|
this.initializeDefaultPatterns();
|
|
407
424
|
}
|
|
408
425
|
/**
|
|
@@ -439,11 +456,11 @@
|
|
|
439
456
|
}
|
|
440
457
|
/**
|
|
441
458
|
* Initialize default URL patterns for common tracking services
|
|
459
|
+
* Note: GTM is NOT auto-categorized — it should be managed via GTM Consent Mode v2
|
|
442
460
|
*/
|
|
443
461
|
initializeDefaultPatterns() {
|
|
444
462
|
this.categories.set('analytics', [
|
|
445
463
|
'google-analytics.com',
|
|
446
|
-
'googletagmanager.com',
|
|
447
464
|
'analytics.google.com',
|
|
448
465
|
'plausible.io',
|
|
449
466
|
'matomo.org',
|
|
@@ -453,6 +470,7 @@
|
|
|
453
470
|
'amplitude.com',
|
|
454
471
|
]);
|
|
455
472
|
this.categories.set('marketing', [
|
|
473
|
+
'googletagmanager.com',
|
|
456
474
|
'facebook.net',
|
|
457
475
|
'facebook.com/tr',
|
|
458
476
|
'connect.facebook.net',
|
|
@@ -464,6 +482,7 @@
|
|
|
464
482
|
'adroll.com',
|
|
465
483
|
'taboola.com',
|
|
466
484
|
'outbrain.com',
|
|
485
|
+
'tiktok.com',
|
|
467
486
|
]);
|
|
468
487
|
this.categories.set('necessary', []);
|
|
469
488
|
}
|
|
@@ -514,18 +533,47 @@
|
|
|
514
533
|
// Allow hsl/hsla
|
|
515
534
|
if (/^hsla?\(\s*[\d\s,./%deg]+\)$/.test(trimmed))
|
|
516
535
|
return trimmed;
|
|
517
|
-
// Allow CSS named colors (basic set)
|
|
518
|
-
|
|
536
|
+
// Allow CSS named colors (basic set) but block CSS keywords that could be abused
|
|
537
|
+
const CSS_KEYWORDS = ['inherit', 'initial', 'unset', 'revert', 'revert-layer'];
|
|
538
|
+
if (/^[a-zA-Z]+$/.test(trimmed) && !CSS_KEYWORDS.includes(trimmed.toLowerCase())) {
|
|
519
539
|
return trimmed;
|
|
540
|
+
}
|
|
520
541
|
return '';
|
|
521
542
|
}
|
|
522
543
|
|
|
544
|
+
/**
|
|
545
|
+
* Normalize any supported color format to 6-digit hex
|
|
546
|
+
* Supports: #RGB, #RRGGBB, #RRGGBBAA, named colors
|
|
547
|
+
* Returns null if conversion fails
|
|
548
|
+
*/
|
|
549
|
+
function normalizeToHex6(color) {
|
|
550
|
+
const trimmed = color.trim();
|
|
551
|
+
// Already 6-digit hex
|
|
552
|
+
if (/^#[0-9a-fA-F]{6}$/.test(trimmed)) {
|
|
553
|
+
return trimmed;
|
|
554
|
+
}
|
|
555
|
+
// 3-digit hex → expand to 6-digit
|
|
556
|
+
if (/^#[0-9a-fA-F]{3}$/.test(trimmed)) {
|
|
557
|
+
const r = trimmed[1];
|
|
558
|
+
const g = trimmed[2];
|
|
559
|
+
const b = trimmed[3];
|
|
560
|
+
return `#${r}${r}${g}${g}${b}${b}`;
|
|
561
|
+
}
|
|
562
|
+
// 8-digit hex (with alpha) → strip alpha
|
|
563
|
+
if (/^#[0-9a-fA-F]{8}$/.test(trimmed)) {
|
|
564
|
+
return trimmed.substring(0, 7);
|
|
565
|
+
}
|
|
566
|
+
return null;
|
|
567
|
+
}
|
|
523
568
|
/**
|
|
524
569
|
* Adjust a hex color brightness by a percentage
|
|
525
570
|
* Negative = darker, positive = lighter
|
|
526
571
|
*/
|
|
527
572
|
function adjustColorBrightness(color, percent) {
|
|
528
|
-
const
|
|
573
|
+
const hex6 = normalizeToHex6(color);
|
|
574
|
+
if (!hex6)
|
|
575
|
+
return color;
|
|
576
|
+
const hex = hex6.replace('#', '');
|
|
529
577
|
const r = parseInt(hex.substring(0, 2), 16);
|
|
530
578
|
const g = parseInt(hex.substring(2, 4), 16);
|
|
531
579
|
const b = parseInt(hex.substring(4, 6), 16);
|
|
@@ -545,8 +593,13 @@
|
|
|
545
593
|
function buildColorStyle(safeColor) {
|
|
546
594
|
if (!safeColor)
|
|
547
595
|
return '';
|
|
548
|
-
|
|
549
|
-
|
|
596
|
+
// Only generate hover color for hex colors
|
|
597
|
+
const hex6 = normalizeToHex6(safeColor);
|
|
598
|
+
if (!hex6) {
|
|
599
|
+
return `--cc-primary: ${safeColor};`;
|
|
600
|
+
}
|
|
601
|
+
const hover = adjustColorBrightness(hex6, -15);
|
|
602
|
+
return `--cc-primary: ${hex6}; --cc-primary-hover: ${hover};`;
|
|
550
603
|
}
|
|
551
604
|
|
|
552
605
|
/**
|
|
@@ -555,6 +608,8 @@
|
|
|
555
608
|
class Banner {
|
|
556
609
|
constructor(config, eventEmitter) {
|
|
557
610
|
this.element = null;
|
|
611
|
+
this.hideTimeout = null;
|
|
612
|
+
this.previousActiveElement = null;
|
|
558
613
|
this.config = config;
|
|
559
614
|
this.eventEmitter = eventEmitter;
|
|
560
615
|
}
|
|
@@ -562,8 +617,14 @@
|
|
|
562
617
|
* Show the banner
|
|
563
618
|
*/
|
|
564
619
|
show() {
|
|
620
|
+
// Clear any pending hide timeout
|
|
621
|
+
if (this.hideTimeout) {
|
|
622
|
+
clearTimeout(this.hideTimeout);
|
|
623
|
+
this.hideTimeout = null;
|
|
624
|
+
}
|
|
565
625
|
const append = () => {
|
|
566
626
|
if (!this.element) {
|
|
627
|
+
this.previousActiveElement = document.activeElement;
|
|
567
628
|
this.element = this.createDOM();
|
|
568
629
|
document.body.appendChild(this.element);
|
|
569
630
|
this.attachListeners();
|
|
@@ -576,9 +637,9 @@
|
|
|
576
637
|
// Disable page interaction if configured
|
|
577
638
|
if (this.config.disablePageInteraction) {
|
|
578
639
|
document.body.style.overflow = 'hidden';
|
|
640
|
+
this.trapFocus();
|
|
579
641
|
}
|
|
580
642
|
};
|
|
581
|
-
// Wait for body if not yet available
|
|
582
643
|
if (!document.body) {
|
|
583
644
|
document.addEventListener('DOMContentLoaded', append);
|
|
584
645
|
return;
|
|
@@ -591,22 +652,30 @@
|
|
|
591
652
|
hide() {
|
|
592
653
|
var _a;
|
|
593
654
|
(_a = this.element) === null || _a === void 0 ? void 0 : _a.classList.remove('is-visible');
|
|
594
|
-
// Re-enable page interaction
|
|
595
655
|
if (this.config.disablePageInteraction) {
|
|
596
656
|
document.body.style.overflow = '';
|
|
597
657
|
}
|
|
598
|
-
setTimeout(() => {
|
|
658
|
+
this.hideTimeout = setTimeout(() => {
|
|
599
659
|
this.destroy();
|
|
600
|
-
}, 300);
|
|
660
|
+
}, 300);
|
|
601
661
|
}
|
|
602
662
|
/**
|
|
603
663
|
* Destroy the banner
|
|
604
664
|
*/
|
|
605
665
|
destroy() {
|
|
666
|
+
if (this.hideTimeout) {
|
|
667
|
+
clearTimeout(this.hideTimeout);
|
|
668
|
+
this.hideTimeout = null;
|
|
669
|
+
}
|
|
606
670
|
if (this.element) {
|
|
607
671
|
this.element.remove();
|
|
608
672
|
this.element = null;
|
|
609
673
|
}
|
|
674
|
+
// Restore focus
|
|
675
|
+
if (this.previousActiveElement && document.contains(this.previousActiveElement)) {
|
|
676
|
+
this.previousActiveElement.focus();
|
|
677
|
+
this.previousActiveElement = null;
|
|
678
|
+
}
|
|
610
679
|
}
|
|
611
680
|
/**
|
|
612
681
|
* Create DOM structure for banner
|
|
@@ -617,12 +686,14 @@
|
|
|
617
686
|
const position = this.config.position || 'bottom';
|
|
618
687
|
const layout = this.config.layout || 'bar';
|
|
619
688
|
const backdropBlur = this.config.backdropBlur !== false;
|
|
689
|
+
const isModal = this.config.disablePageInteraction;
|
|
620
690
|
const safeColor = this.config.primaryColor ? sanitizeColor(this.config.primaryColor) : '';
|
|
621
691
|
const colorStyle = buildColorStyle(safeColor);
|
|
622
692
|
const template = `
|
|
623
693
|
<div
|
|
624
694
|
class="cc-banner cc-banner--${escapeHtml(position)} cc-banner--${escapeHtml(layout)} ${backdropBlur ? 'cc-backdrop-blur' : ''}"
|
|
625
|
-
role="region"
|
|
695
|
+
role="${isModal ? 'dialog' : 'region'}"
|
|
696
|
+
${isModal ? 'aria-modal="true"' : ''}
|
|
626
697
|
aria-label="Cookie consent"
|
|
627
698
|
aria-live="polite"
|
|
628
699
|
data-theme="${escapeHtml(theme)}"
|
|
@@ -631,7 +702,7 @@
|
|
|
631
702
|
<div class="cc-banner__container">
|
|
632
703
|
<div class="cc-banner__content">
|
|
633
704
|
<h2 class="cc-banner__title">
|
|
634
|
-
${escapeHtml(translations.title || '
|
|
705
|
+
${escapeHtml(translations.title || 'We use cookies')}
|
|
635
706
|
</h2>
|
|
636
707
|
<p class="cc-banner__description">
|
|
637
708
|
${this.getDescriptionHTML()}
|
|
@@ -641,23 +712,23 @@
|
|
|
641
712
|
<button
|
|
642
713
|
class="cc-btn cc-btn--ghost"
|
|
643
714
|
data-action="reject"
|
|
644
|
-
aria-label="${escapeHtml(translations.rejectAll || '
|
|
715
|
+
aria-label="${escapeHtml(translations.rejectAll || 'Essentials only')}"
|
|
645
716
|
>
|
|
646
|
-
${escapeHtml(translations.rejectAll || '
|
|
717
|
+
${escapeHtml(translations.rejectAll || 'Essentials only')}
|
|
647
718
|
</button>
|
|
648
719
|
<button
|
|
649
720
|
class="cc-btn cc-btn--tertiary"
|
|
650
721
|
data-action="customize"
|
|
651
|
-
aria-label="${escapeHtml(translations.customize || '
|
|
722
|
+
aria-label="${escapeHtml(translations.customize || 'Customize')}"
|
|
652
723
|
>
|
|
653
|
-
${escapeHtml(translations.customize || '
|
|
724
|
+
${escapeHtml(translations.customize || 'Customize')}
|
|
654
725
|
</button>
|
|
655
726
|
<button
|
|
656
727
|
class="cc-btn cc-btn--accept"
|
|
657
728
|
data-action="accept"
|
|
658
|
-
aria-label="${escapeHtml(translations.acceptAll || '
|
|
729
|
+
aria-label="${escapeHtml(translations.acceptAll || 'Accept all')}"
|
|
659
730
|
>
|
|
660
|
-
${escapeHtml(translations.acceptAll || '
|
|
731
|
+
${escapeHtml(translations.acceptAll || 'Accept all')}
|
|
661
732
|
</button>
|
|
662
733
|
</div>
|
|
663
734
|
</div>
|
|
@@ -689,10 +760,8 @@
|
|
|
689
760
|
break;
|
|
690
761
|
}
|
|
691
762
|
});
|
|
692
|
-
// Keyboard support
|
|
693
763
|
(_b = this.element) === null || _b === void 0 ? void 0 : _b.addEventListener('keydown', (e) => {
|
|
694
764
|
if (e.key === 'Escape' && this.config.disablePageInteraction) {
|
|
695
|
-
// Allow ESC to close if page interaction is disabled
|
|
696
765
|
this.handleRejectAll();
|
|
697
766
|
}
|
|
698
767
|
});
|
|
@@ -701,36 +770,24 @@
|
|
|
701
770
|
* Handle accept all action
|
|
702
771
|
*/
|
|
703
772
|
handleAcceptAll() {
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
marketing: true,
|
|
709
|
-
};
|
|
710
|
-
// Only add preferences if it's configured
|
|
711
|
-
if ((_a = this.config.categories) === null || _a === void 0 ? void 0 : _a.preferences) {
|
|
712
|
-
allCategories.preferences = true;
|
|
773
|
+
const allCategories = { necessary: true, analytics: true, marketing: true };
|
|
774
|
+
// Add all configured categories
|
|
775
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
776
|
+
allCategories[key] = true;
|
|
713
777
|
}
|
|
714
778
|
this.eventEmitter.emit('consent:accept', allCategories);
|
|
715
|
-
(_c = (_b = this.config).onAccept) === null || _c === void 0 ? void 0 : _c.call(_b, allCategories);
|
|
716
779
|
this.hide();
|
|
717
780
|
}
|
|
718
781
|
/**
|
|
719
782
|
* Handle reject all action
|
|
720
783
|
*/
|
|
721
784
|
handleRejectAll() {
|
|
722
|
-
|
|
723
|
-
const
|
|
724
|
-
necessary
|
|
725
|
-
|
|
726
|
-
marketing: false,
|
|
727
|
-
};
|
|
728
|
-
// Only add preferences if it's configured
|
|
729
|
-
if ((_a = this.config.categories) === null || _a === void 0 ? void 0 : _a.preferences) {
|
|
730
|
-
necessaryOnly.preferences = false;
|
|
785
|
+
const necessaryOnly = { necessary: true, analytics: false, marketing: false };
|
|
786
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
787
|
+
if (key !== 'necessary')
|
|
788
|
+
necessaryOnly[key] = false;
|
|
731
789
|
}
|
|
732
790
|
this.eventEmitter.emit('consent:reject', necessaryOnly);
|
|
733
|
-
(_c = (_b = this.config).onReject) === null || _c === void 0 ? void 0 : _c.call(_b);
|
|
734
791
|
this.hide();
|
|
735
792
|
}
|
|
736
793
|
/**
|
|
@@ -740,17 +797,41 @@
|
|
|
740
797
|
this.eventEmitter.emit('preferences:show');
|
|
741
798
|
this.hide();
|
|
742
799
|
}
|
|
800
|
+
/**
|
|
801
|
+
* Trap focus within banner (when disablePageInteraction is true)
|
|
802
|
+
*/
|
|
803
|
+
trapFocus() {
|
|
804
|
+
var _a, _b;
|
|
805
|
+
const focusableElements = (_a = this.element) === null || _a === void 0 ? void 0 : _a.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])');
|
|
806
|
+
if (!focusableElements || focusableElements.length === 0)
|
|
807
|
+
return;
|
|
808
|
+
const firstFocusable = focusableElements[0];
|
|
809
|
+
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
810
|
+
firstFocusable === null || firstFocusable === void 0 ? void 0 : firstFocusable.focus();
|
|
811
|
+
(_b = this.element) === null || _b === void 0 ? void 0 : _b.addEventListener('keydown', (e) => {
|
|
812
|
+
if (e.key === 'Tab') {
|
|
813
|
+
if (e.shiftKey && document.activeElement === firstFocusable) {
|
|
814
|
+
e.preventDefault();
|
|
815
|
+
lastFocusable.focus();
|
|
816
|
+
}
|
|
817
|
+
else if (!e.shiftKey && document.activeElement === lastFocusable) {
|
|
818
|
+
e.preventDefault();
|
|
819
|
+
firstFocusable.focus();
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
}
|
|
743
824
|
/**
|
|
744
825
|
* Generate description HTML with privacy policy link
|
|
745
826
|
*/
|
|
746
827
|
getDescriptionHTML() {
|
|
747
828
|
const translations = this.config.translations || {};
|
|
748
|
-
const defaultDescription = '
|
|
829
|
+
const defaultDescription = 'We use cookies to improve your experience on our site. You can choose which cookies you accept.';
|
|
749
830
|
const description = escapeHtml(translations.description || defaultDescription);
|
|
750
831
|
if (translations.privacyPolicyUrl) {
|
|
751
832
|
const safeUrl = sanitizeUrl(translations.privacyPolicyUrl);
|
|
752
833
|
if (safeUrl) {
|
|
753
|
-
const linkLabel = escapeHtml(translations.privacyPolicyLabel || '
|
|
834
|
+
const linkLabel = escapeHtml(translations.privacyPolicyLabel || 'Privacy Policy');
|
|
754
835
|
return `${description} <a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${linkLabel}</a>`;
|
|
755
836
|
}
|
|
756
837
|
}
|
|
@@ -764,6 +845,7 @@
|
|
|
764
845
|
class PreferenceCenter {
|
|
765
846
|
constructor(config, eventEmitter, currentConsent) {
|
|
766
847
|
this.element = null;
|
|
848
|
+
this.previousActiveElement = null;
|
|
767
849
|
this.config = config;
|
|
768
850
|
this.eventEmitter = eventEmitter;
|
|
769
851
|
this.currentConsent = currentConsent;
|
|
@@ -774,13 +856,13 @@
|
|
|
774
856
|
show() {
|
|
775
857
|
const append = () => {
|
|
776
858
|
if (!this.element) {
|
|
859
|
+
this.previousActiveElement = document.activeElement;
|
|
777
860
|
this.element = this.createDOM();
|
|
778
861
|
document.body.appendChild(this.element);
|
|
779
862
|
this.attachListeners();
|
|
780
863
|
}
|
|
781
864
|
this.element.classList.add('is-visible');
|
|
782
865
|
this.trapFocus();
|
|
783
|
-
// Prevent body scroll
|
|
784
866
|
document.body.style.overflow = 'hidden';
|
|
785
867
|
};
|
|
786
868
|
if (!document.body) {
|
|
@@ -796,6 +878,11 @@
|
|
|
796
878
|
var _a;
|
|
797
879
|
(_a = this.element) === null || _a === void 0 ? void 0 : _a.classList.remove('is-visible');
|
|
798
880
|
document.body.style.overflow = '';
|
|
881
|
+
// Restore focus to triggering element
|
|
882
|
+
if (this.previousActiveElement && document.contains(this.previousActiveElement)) {
|
|
883
|
+
this.previousActiveElement.focus();
|
|
884
|
+
this.previousActiveElement = null;
|
|
885
|
+
}
|
|
799
886
|
setTimeout(() => {
|
|
800
887
|
this.destroy();
|
|
801
888
|
}, 300);
|
|
@@ -830,7 +917,7 @@
|
|
|
830
917
|
<polyline points="15 3 21 3 21 9"/>
|
|
831
918
|
<line x1="10" y1="14" x2="21" y2="3"/>
|
|
832
919
|
</svg>
|
|
833
|
-
${escapeHtml(translations.privacyPolicyLabel || '
|
|
920
|
+
${escapeHtml(translations.privacyPolicyLabel || 'Privacy Policy')}
|
|
834
921
|
</a>
|
|
835
922
|
`;
|
|
836
923
|
})()
|
|
@@ -844,21 +931,12 @@
|
|
|
844
931
|
data-theme="${escapeHtml(theme)}"
|
|
845
932
|
style="${colorStyle}"
|
|
846
933
|
>
|
|
847
|
-
<div class="cc-modal__overlay"
|
|
934
|
+
<div class="cc-modal__overlay"></div>
|
|
848
935
|
<div class="cc-modal__content">
|
|
849
936
|
<div class="cc-modal__header">
|
|
850
937
|
<h2 id="cc-modal-title">
|
|
851
|
-
${escapeHtml(translations.preferencesTitle || translations.title || '
|
|
938
|
+
${escapeHtml(translations.preferencesTitle || translations.title || 'Cookie Preferences')}
|
|
852
939
|
</h2>
|
|
853
|
-
<button
|
|
854
|
-
class="cc-modal__close"
|
|
855
|
-
aria-label="Fermer"
|
|
856
|
-
data-action="close"
|
|
857
|
-
>
|
|
858
|
-
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
|
859
|
-
<path d="M18 6L6 18M6 6l12 12" stroke-width="2" stroke-linecap="round"/>
|
|
860
|
-
</svg>
|
|
861
|
-
</button>
|
|
862
940
|
</div>
|
|
863
941
|
|
|
864
942
|
<div class="cc-modal__body">
|
|
@@ -874,13 +952,13 @@
|
|
|
874
952
|
class="cc-btn cc-btn--secondary"
|
|
875
953
|
data-action="reject"
|
|
876
954
|
>
|
|
877
|
-
${escapeHtml(translations.essentialsOnly || '
|
|
955
|
+
${escapeHtml(translations.essentialsOnly || 'Essentials only')}
|
|
878
956
|
</button>
|
|
879
957
|
<button
|
|
880
958
|
class="cc-btn cc-btn--primary"
|
|
881
959
|
data-action="save"
|
|
882
960
|
>
|
|
883
|
-
${escapeHtml(translations.savePreferences || '
|
|
961
|
+
${escapeHtml(translations.savePreferences || 'Save preferences')}
|
|
884
962
|
</button>
|
|
885
963
|
</div>
|
|
886
964
|
</div>
|
|
@@ -898,7 +976,7 @@
|
|
|
898
976
|
const categories = Object.entries(this.config.categories);
|
|
899
977
|
return categories
|
|
900
978
|
.map(([key, config]) => {
|
|
901
|
-
const checked = this.currentConsent[key];
|
|
979
|
+
const checked = this.currentConsent[key] === true;
|
|
902
980
|
const disabled = config.readOnly;
|
|
903
981
|
return `
|
|
904
982
|
<div class="cc-category">
|
|
@@ -927,40 +1005,34 @@
|
|
|
927
1005
|
* Attach event listeners
|
|
928
1006
|
*/
|
|
929
1007
|
attachListeners() {
|
|
930
|
-
var _a
|
|
1008
|
+
var _a;
|
|
931
1009
|
(_a = this.element) === null || _a === void 0 ? void 0 : _a.addEventListener('click', (e) => {
|
|
932
1010
|
const target = e.target.closest('[data-action]');
|
|
933
1011
|
if (!target)
|
|
934
1012
|
return;
|
|
935
1013
|
const action = target.getAttribute('data-action');
|
|
936
|
-
if (action === '
|
|
937
|
-
this.hide();
|
|
938
|
-
}
|
|
939
|
-
else if (action === 'save') {
|
|
1014
|
+
if (action === 'save') {
|
|
940
1015
|
this.handleSave();
|
|
941
1016
|
}
|
|
942
1017
|
else if (action === 'reject') {
|
|
943
1018
|
this.handleRejectAll();
|
|
944
1019
|
}
|
|
945
1020
|
});
|
|
946
|
-
// Keyboard shortcuts
|
|
947
|
-
(_b = this.element) === null || _b === void 0 ? void 0 : _b.addEventListener('keydown', (e) => {
|
|
948
|
-
if (e.key === 'Escape') {
|
|
949
|
-
this.hide();
|
|
950
|
-
}
|
|
951
|
-
});
|
|
952
1021
|
}
|
|
953
1022
|
/**
|
|
954
1023
|
* Handle save preferences
|
|
955
1024
|
*/
|
|
956
1025
|
handleSave() {
|
|
957
|
-
var _a
|
|
1026
|
+
var _a;
|
|
958
1027
|
const checkboxes = (_a = this.element) === null || _a === void 0 ? void 0 : _a.querySelectorAll('input[data-category]');
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
1028
|
+
// Initialize all configured categories to false
|
|
1029
|
+
const categories = { necessary: true, analytics: false, marketing: false };
|
|
1030
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
1031
|
+
if (key !== 'necessary') {
|
|
1032
|
+
categories[key] = false;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
// Override with actual checkbox values
|
|
964
1036
|
checkboxes === null || checkboxes === void 0 ? void 0 : checkboxes.forEach((checkbox) => {
|
|
965
1037
|
if (checkbox instanceof HTMLInputElement) {
|
|
966
1038
|
const category = checkbox.getAttribute('data-category');
|
|
@@ -970,22 +1042,16 @@
|
|
|
970
1042
|
}
|
|
971
1043
|
});
|
|
972
1044
|
this.eventEmitter.emit('consent:update', categories);
|
|
973
|
-
(_c = (_b = this.config).onChange) === null || _c === void 0 ? void 0 : _c.call(_b, categories);
|
|
974
1045
|
this.hide();
|
|
975
1046
|
}
|
|
976
1047
|
/**
|
|
977
1048
|
* Handle reject all
|
|
978
1049
|
*/
|
|
979
1050
|
handleRejectAll() {
|
|
980
|
-
|
|
981
|
-
const
|
|
982
|
-
necessary
|
|
983
|
-
|
|
984
|
-
marketing: false,
|
|
985
|
-
};
|
|
986
|
-
// Only add preferences if it's configured
|
|
987
|
-
if ((_a = this.config.categories) === null || _a === void 0 ? void 0 : _a.preferences) {
|
|
988
|
-
necessaryOnly.preferences = false;
|
|
1051
|
+
const necessaryOnly = { necessary: true, analytics: false, marketing: false };
|
|
1052
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
1053
|
+
if (key !== 'necessary')
|
|
1054
|
+
necessaryOnly[key] = false;
|
|
989
1055
|
}
|
|
990
1056
|
this.eventEmitter.emit('consent:reject', necessaryOnly);
|
|
991
1057
|
this.hide();
|
|
@@ -1000,9 +1066,7 @@
|
|
|
1000
1066
|
return;
|
|
1001
1067
|
const firstFocusable = focusableElements[0];
|
|
1002
1068
|
const lastFocusable = focusableElements[focusableElements.length - 1];
|
|
1003
|
-
// Focus first element
|
|
1004
1069
|
firstFocusable === null || firstFocusable === void 0 ? void 0 : firstFocusable.focus();
|
|
1005
|
-
// Trap focus
|
|
1006
1070
|
(_b = this.element) === null || _b === void 0 ? void 0 : _b.addEventListener('keydown', (e) => {
|
|
1007
1071
|
if (e.key === 'Tab') {
|
|
1008
1072
|
if (e.shiftKey && document.activeElement === firstFocusable) {
|
|
@@ -1090,7 +1154,7 @@
|
|
|
1090
1154
|
<div
|
|
1091
1155
|
class="cc-widget cc-widget--${escapeHtml(widgetPosition)} cc-widget--${escapeHtml(widgetStyle)}"
|
|
1092
1156
|
role="button"
|
|
1093
|
-
aria-label="${escapeHtml(translations.cookieSettings || '
|
|
1157
|
+
aria-label="${escapeHtml(translations.cookieSettings || 'Cookie settings')}"
|
|
1094
1158
|
tabindex="0"
|
|
1095
1159
|
data-theme="${escapeHtml(theme)}"
|
|
1096
1160
|
style="${colorStyle}"
|
|
@@ -1141,11 +1205,6 @@
|
|
|
1141
1205
|
|
|
1142
1206
|
/**
|
|
1143
1207
|
* GTMConsentMode - Full integration with Google Consent Mode v2
|
|
1144
|
-
*
|
|
1145
|
-
* Implements all required signals:
|
|
1146
|
-
* - ad_storage, ad_user_data, ad_personalization, analytics_storage (core GCM v2)
|
|
1147
|
-
* - functionality_storage, personalization_storage, security_storage (non-core)
|
|
1148
|
-
* - wait_for_update, url_passthrough, ads_data_redaction (advanced features)
|
|
1149
1208
|
*/
|
|
1150
1209
|
class GTMConsentMode {
|
|
1151
1210
|
constructor(dataLayerManager, config) {
|
|
@@ -1165,15 +1224,13 @@
|
|
|
1165
1224
|
analytics_storage: 'denied',
|
|
1166
1225
|
functionality_storage: 'denied',
|
|
1167
1226
|
personalization_storage: 'denied',
|
|
1168
|
-
security_storage: 'granted',
|
|
1227
|
+
security_storage: 'granted',
|
|
1169
1228
|
};
|
|
1170
|
-
// Add wait_for_update to give CMP time to restore returning visitor consent
|
|
1171
1229
|
const waitForUpdate = (_a = this.config.gtmWaitForUpdate) !== null && _a !== void 0 ? _a : 500;
|
|
1172
1230
|
if (waitForUpdate > 0) {
|
|
1173
1231
|
defaults['wait_for_update'] = waitForUpdate;
|
|
1174
1232
|
}
|
|
1175
1233
|
this.dataLayerManager.pushConsent('default', defaults);
|
|
1176
|
-
// Set advanced features via gtag('set', ...)
|
|
1177
1234
|
if (this.config.gtmUrlPassthrough) {
|
|
1178
1235
|
this.dataLayerManager.pushSet('url_passthrough', true);
|
|
1179
1236
|
}
|
|
@@ -1183,7 +1240,6 @@
|
|
|
1183
1240
|
}
|
|
1184
1241
|
/**
|
|
1185
1242
|
* Update consent state based on user choices
|
|
1186
|
-
* Called both on new consent and on page load for returning visitors
|
|
1187
1243
|
*/
|
|
1188
1244
|
updateConsent(categories) {
|
|
1189
1245
|
const gtmConsent = this.mapCategoriesToGTM(categories);
|
|
@@ -1193,14 +1249,19 @@
|
|
|
1193
1249
|
* Map consent categories to GTM Consent Mode v2 format
|
|
1194
1250
|
*/
|
|
1195
1251
|
mapCategoriesToGTM(categories) {
|
|
1252
|
+
// When preferences category is not configured, default functionality to granted
|
|
1253
|
+
const hasPreferencesCategory = 'preferences' in this.config.categories;
|
|
1254
|
+
const preferencesGranted = hasPreferencesCategory
|
|
1255
|
+
? categories.preferences === true
|
|
1256
|
+
: true;
|
|
1196
1257
|
return {
|
|
1197
1258
|
ad_storage: categories.marketing ? 'granted' : 'denied',
|
|
1198
1259
|
ad_user_data: categories.marketing ? 'granted' : 'denied',
|
|
1199
1260
|
ad_personalization: categories.marketing ? 'granted' : 'denied',
|
|
1200
1261
|
analytics_storage: categories.analytics ? 'granted' : 'denied',
|
|
1201
|
-
functionality_storage:
|
|
1202
|
-
personalization_storage:
|
|
1203
|
-
security_storage: 'granted',
|
|
1262
|
+
functionality_storage: preferencesGranted ? 'granted' : 'denied',
|
|
1263
|
+
personalization_storage: preferencesGranted ? 'granted' : 'denied',
|
|
1264
|
+
security_storage: 'granted',
|
|
1204
1265
|
};
|
|
1205
1266
|
}
|
|
1206
1267
|
}
|
|
@@ -1345,6 +1406,16 @@
|
|
|
1345
1406
|
this.preferenceCenter = null;
|
|
1346
1407
|
this.floatingWidget = null;
|
|
1347
1408
|
this.gtmIntegration = null;
|
|
1409
|
+
this.hideTimeout = null;
|
|
1410
|
+
// SSR guard
|
|
1411
|
+
if (typeof window === 'undefined') {
|
|
1412
|
+
this.config = config;
|
|
1413
|
+
this.consentManager = null;
|
|
1414
|
+
this.storageManager = null;
|
|
1415
|
+
this.eventEmitter = null;
|
|
1416
|
+
this.scriptBlocker = null;
|
|
1417
|
+
return;
|
|
1418
|
+
}
|
|
1348
1419
|
this.config = this.validateConfig(config);
|
|
1349
1420
|
this.consentManager = new ConsentManager(this.config);
|
|
1350
1421
|
this.storageManager = new StorageManager();
|
|
@@ -1353,25 +1424,32 @@
|
|
|
1353
1424
|
if (this.config.gtmConsentMode) {
|
|
1354
1425
|
this.gtmIntegration = new GTMConsentMode(new DataLayerManager(), this.config);
|
|
1355
1426
|
}
|
|
1356
|
-
// Listen for
|
|
1357
|
-
this.eventEmitter.on('preferences:show', () => {
|
|
1358
|
-
this.showPreferences();
|
|
1359
|
-
});
|
|
1360
|
-
// Listen for consent updates
|
|
1427
|
+
// Listen for consent events — callbacks are fired AFTER consent is persisted
|
|
1361
1428
|
this.eventEmitter.on('consent:accept', (categories) => {
|
|
1429
|
+
var _a, _b;
|
|
1362
1430
|
this.updateConsent(categories);
|
|
1431
|
+
(_b = (_a = this.config).onAccept) === null || _b === void 0 ? void 0 : _b.call(_a, categories);
|
|
1363
1432
|
});
|
|
1364
1433
|
this.eventEmitter.on('consent:reject', (categories) => {
|
|
1434
|
+
var _a, _b;
|
|
1365
1435
|
this.updateConsent(categories);
|
|
1436
|
+
(_b = (_a = this.config).onReject) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1366
1437
|
});
|
|
1367
1438
|
this.eventEmitter.on('consent:update', (categories) => {
|
|
1439
|
+
var _a, _b;
|
|
1368
1440
|
this.updateConsent(categories);
|
|
1441
|
+
(_b = (_a = this.config).onChange) === null || _b === void 0 ? void 0 : _b.call(_a, categories);
|
|
1442
|
+
});
|
|
1443
|
+
this.eventEmitter.on('preferences:show', () => {
|
|
1444
|
+
this.showPreferences();
|
|
1369
1445
|
});
|
|
1370
1446
|
}
|
|
1371
1447
|
/**
|
|
1372
1448
|
* Initialize the cookie consent system
|
|
1373
1449
|
*/
|
|
1374
1450
|
init() {
|
|
1451
|
+
if (typeof window === 'undefined')
|
|
1452
|
+
return;
|
|
1375
1453
|
// 1. Start blocking scripts immediately
|
|
1376
1454
|
this.scriptBlocker.init();
|
|
1377
1455
|
// 2. Set GTM default consent BEFORE checking storage
|
|
@@ -1381,35 +1459,28 @@
|
|
|
1381
1459
|
// 3. Check for existing consent
|
|
1382
1460
|
const storedConsent = this.storageManager.load();
|
|
1383
1461
|
if (storedConsent && !this.storageManager.isExpired(storedConsent)) {
|
|
1384
|
-
// Valid consent exists
|
|
1385
1462
|
if (this.consentManager.needsUpdate(storedConsent)) {
|
|
1386
|
-
// Policy updated, show banner again
|
|
1387
1463
|
if (this.config.autoShow) {
|
|
1388
1464
|
this.showBanner();
|
|
1389
1465
|
}
|
|
1390
1466
|
}
|
|
1391
1467
|
else {
|
|
1392
|
-
// Apply stored consent
|
|
1393
1468
|
this.applyConsent(storedConsent.categories);
|
|
1394
|
-
// Restore GTM consent for returning visitors (within wait_for_update window)
|
|
1395
1469
|
if (this.gtmIntegration) {
|
|
1396
1470
|
this.gtmIntegration.updateConsent(storedConsent.categories);
|
|
1397
1471
|
}
|
|
1398
1472
|
this.eventEmitter.emit('consent:load', storedConsent);
|
|
1399
|
-
// Show floating widget if enabled
|
|
1400
1473
|
if (this.config.showWidget) {
|
|
1401
1474
|
this.showFloatingWidget();
|
|
1402
1475
|
}
|
|
1403
1476
|
}
|
|
1404
1477
|
}
|
|
1405
1478
|
else {
|
|
1406
|
-
// No consent or expired
|
|
1407
1479
|
if (this.config.autoShow) {
|
|
1408
1480
|
this.showBanner();
|
|
1409
1481
|
}
|
|
1410
1482
|
}
|
|
1411
|
-
// Store instance globally
|
|
1412
|
-
// when used without a variable (e.g. new CookieConsent({}).init())
|
|
1483
|
+
// Store instance globally
|
|
1413
1484
|
window.cookieConsent = this;
|
|
1414
1485
|
this.eventEmitter.emit('consent:init');
|
|
1415
1486
|
}
|
|
@@ -1432,14 +1503,18 @@
|
|
|
1432
1503
|
showPreferences() {
|
|
1433
1504
|
var _a;
|
|
1434
1505
|
const stored = (_a = this.storageManager.load()) === null || _a === void 0 ? void 0 : _a.categories;
|
|
1435
|
-
// Default to all ON when no prior consent
|
|
1506
|
+
// Default to all ON when no prior consent
|
|
1436
1507
|
const currentConsent = stored || {
|
|
1437
1508
|
necessary: true,
|
|
1438
1509
|
analytics: true,
|
|
1439
1510
|
marketing: true,
|
|
1440
|
-
preferences: true,
|
|
1441
1511
|
};
|
|
1442
|
-
//
|
|
1512
|
+
// Add any configured categories not in current consent
|
|
1513
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
1514
|
+
if (!(key in currentConsent)) {
|
|
1515
|
+
currentConsent[key] = key === 'necessary';
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1443
1518
|
if (this.preferenceCenter) {
|
|
1444
1519
|
this.preferenceCenter.destroy();
|
|
1445
1520
|
}
|
|
@@ -1456,11 +1531,11 @@
|
|
|
1456
1531
|
if (this.gtmIntegration) {
|
|
1457
1532
|
this.gtmIntegration.updateConsent(categories);
|
|
1458
1533
|
}
|
|
1459
|
-
// Show floating widget after consent is given
|
|
1534
|
+
// Show floating widget after consent is given
|
|
1460
1535
|
if (this.config.showWidget) {
|
|
1461
1536
|
setTimeout(() => {
|
|
1462
1537
|
this.showFloatingWidget();
|
|
1463
|
-
}, 400);
|
|
1538
|
+
}, 400);
|
|
1464
1539
|
}
|
|
1465
1540
|
}
|
|
1466
1541
|
/**
|
|
@@ -1473,14 +1548,19 @@
|
|
|
1473
1548
|
* Reset consent (clear stored data and show banner)
|
|
1474
1549
|
*/
|
|
1475
1550
|
reset() {
|
|
1551
|
+
var _a, _b;
|
|
1476
1552
|
this.storageManager.clear();
|
|
1477
1553
|
this.scriptBlocker.block();
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1554
|
+
const denied = { necessary: true, analytics: false, marketing: false };
|
|
1555
|
+
for (const key of Object.keys(this.config.categories)) {
|
|
1556
|
+
if (key !== 'necessary')
|
|
1557
|
+
denied[key] = false;
|
|
1558
|
+
}
|
|
1559
|
+
clearDeniedCookies(denied);
|
|
1481
1560
|
if (this.gtmIntegration) {
|
|
1482
|
-
this.gtmIntegration.updateConsent(
|
|
1561
|
+
this.gtmIntegration.updateConsent(denied);
|
|
1483
1562
|
}
|
|
1563
|
+
(_b = (_a = this.config).onReject) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
1484
1564
|
this.showBanner();
|
|
1485
1565
|
}
|
|
1486
1566
|
/**
|
|
@@ -1500,6 +1580,10 @@
|
|
|
1500
1580
|
*/
|
|
1501
1581
|
destroy() {
|
|
1502
1582
|
var _a, _b, _c, _d;
|
|
1583
|
+
if (this.hideTimeout) {
|
|
1584
|
+
clearTimeout(this.hideTimeout);
|
|
1585
|
+
this.hideTimeout = null;
|
|
1586
|
+
}
|
|
1503
1587
|
(_a = this.banner) === null || _a === void 0 ? void 0 : _a.destroy();
|
|
1504
1588
|
this.banner = null;
|
|
1505
1589
|
(_b = this.preferenceCenter) === null || _b === void 0 ? void 0 : _b.destroy();
|
|
@@ -1507,6 +1591,10 @@
|
|
|
1507
1591
|
(_c = this.floatingWidget) === null || _c === void 0 ? void 0 : _c.destroy();
|
|
1508
1592
|
this.floatingWidget = null;
|
|
1509
1593
|
(_d = this.scriptBlocker) === null || _d === void 0 ? void 0 : _d.destroy();
|
|
1594
|
+
this.eventEmitter.clear();
|
|
1595
|
+
if (window.cookieConsent === this) {
|
|
1596
|
+
window.cookieConsent = undefined;
|
|
1597
|
+
}
|
|
1510
1598
|
}
|
|
1511
1599
|
/**
|
|
1512
1600
|
* Show the banner
|
|
@@ -1532,7 +1620,6 @@
|
|
|
1532
1620
|
*/
|
|
1533
1621
|
applyConsent(categories) {
|
|
1534
1622
|
this.scriptBlocker.unblock(categories);
|
|
1535
|
-
// CNIL/GDPR: actively delete cookies for denied categories
|
|
1536
1623
|
clearDeniedCookies(categories);
|
|
1537
1624
|
}
|
|
1538
1625
|
/**
|
|
@@ -1543,22 +1630,22 @@
|
|
|
1543
1630
|
necessary: {
|
|
1544
1631
|
enabled: true,
|
|
1545
1632
|
readOnly: true,
|
|
1546
|
-
label: '
|
|
1547
|
-
description: '
|
|
1633
|
+
label: 'Essential',
|
|
1634
|
+
description: 'Required for the website to function properly.',
|
|
1548
1635
|
},
|
|
1549
1636
|
analytics: {
|
|
1550
1637
|
enabled: true,
|
|
1551
1638
|
readOnly: false,
|
|
1552
|
-
label: '
|
|
1553
|
-
description: '
|
|
1639
|
+
label: 'Analytics',
|
|
1640
|
+
description: 'Help us understand how you use our site.',
|
|
1554
1641
|
},
|
|
1555
1642
|
marketing: {
|
|
1556
1643
|
enabled: true,
|
|
1557
1644
|
readOnly: false,
|
|
1558
1645
|
label: 'Marketing',
|
|
1559
|
-
description: '
|
|
1646
|
+
description: 'Used to deliver relevant advertisements.',
|
|
1560
1647
|
},
|
|
1561
|
-
}, mode: config.mode || 'opt-in', autoShow: config.autoShow !== undefined ? config.autoShow : true, revision: config.revision || 1, gtmConsentMode: config.gtmConsentMode
|
|
1648
|
+
}, mode: config.mode || 'opt-in', autoShow: config.autoShow !== undefined ? config.autoShow : true, revision: config.revision || 1, gtmConsentMode: config.gtmConsentMode || false, disablePageInteraction: config.disablePageInteraction || false, theme: config.theme || 'light', position: config.position || 'bottom-left', layout: config.layout || 'box', backdropBlur: config.backdropBlur !== false, animationStyle: config.animationStyle || 'smooth', preferencesPosition: config.preferencesPosition || 'center', showWidget: config.showWidget !== undefined ? config.showWidget : true, widgetPosition: config.widgetPosition || 'bottom-left', widgetStyle: config.widgetStyle || 'compact' });
|
|
1562
1649
|
}
|
|
1563
1650
|
}
|
|
1564
1651
|
|