@reldens/cms 0.27.0 → 0.29.0

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.
@@ -0,0 +1,411 @@
1
+ /**
2
+ *
3
+ * Reldens CMS - Cookie Consent - GDPR-compliant with granular controls
4
+ *
5
+ */
6
+
7
+ window.gtmID = 'GTM-XXXXXXXX';
8
+
9
+ let CookieConsent = {
10
+ config: {
11
+ storageKey: 'reldens_cookie_consent',
12
+ preferencesKey: 'reldens_cookie_preferences',
13
+ gtmId: window.gtmID || null,
14
+ gaId: window.gaID || 'G-XXXXXXX',
15
+ privacyPolicyUrl: '/privacy',
16
+ waitForUpdate: 500,
17
+ useGTM: false
18
+ },
19
+
20
+ elements: {
21
+ banner: null,
22
+ acceptBtn: null,
23
+ rejectBtn: null,
24
+ customizeBtn: null,
25
+ manageBtn: null,
26
+ preferencesModal: null,
27
+ savePreferencesBtn: null,
28
+ cancelPreferencesBtn: null
29
+ },
30
+
31
+ categories: {
32
+ essential: {
33
+ name: 'Essential Cookies',
34
+ description: 'Required for basic site functionality and cannot be disabled.',
35
+ required: true,
36
+ enabled: true
37
+ },
38
+ analytics: {
39
+ name: 'Analytics Cookies',
40
+ description: 'Help us understand how visitors interact with our website.',
41
+ required: false,
42
+ enabled: false
43
+ },
44
+ marketing: {
45
+ name: 'Marketing Cookies',
46
+ description: 'Used to track visitors across websites for personalized advertising.',
47
+ required: false,
48
+ enabled: false
49
+ }
50
+ },
51
+
52
+ init()
53
+ {
54
+ this.detectTrackingMethod();
55
+ if('undefined' === typeof window.dataLayer){
56
+ window.dataLayer = [];
57
+ }
58
+ if('undefined' === typeof window.gtag){
59
+ window.gtag = function() { window.dataLayer.push(arguments); };
60
+ }
61
+ this.setDefaultConsent();
62
+ this.initElements();
63
+ this.bindEvents();
64
+ this.checkExistingConsent();
65
+ },
66
+
67
+ detectTrackingMethod()
68
+ {
69
+ if(this.config.gtmId){
70
+ this.config.useGTM = true;
71
+ return;
72
+ }
73
+ this.config.useGTM = false;
74
+ },
75
+
76
+ setDefaultConsent()
77
+ {
78
+ window.gtag('consent', 'default', {
79
+ ad_storage: 'denied',
80
+ analytics_storage: 'denied',
81
+ ad_user_data: 'denied',
82
+ ad_personalization: 'denied',
83
+ wait_for_update: this.config.waitForUpdate
84
+ });
85
+ },
86
+
87
+ initElements()
88
+ {
89
+ this.elements.banner = document.getElementById('cookie-banner');
90
+ this.elements.acceptBtn = document.getElementById('accept-cookies');
91
+ this.elements.rejectBtn = document.getElementById('reject-cookies');
92
+ this.elements.customizeBtn = document.getElementById('customize-cookies');
93
+ this.elements.manageBtn = document.getElementById('manage-cookies');
94
+ this.elements.preferencesModal = document.getElementById('cookie-preferences');
95
+ this.elements.savePreferencesBtn = document.getElementById('save-preferences');
96
+ this.elements.cancelPreferencesBtn = document.getElementById('cancel-preferences');
97
+ },
98
+
99
+ bindEvents()
100
+ {
101
+ if(this.elements.acceptBtn){
102
+ this.elements.acceptBtn.onclick = () => this.acceptAll();
103
+ }
104
+ if(this.elements.rejectBtn){
105
+ this.elements.rejectBtn.onclick = () => this.rejectAll();
106
+ }
107
+ if(this.elements.customizeBtn){
108
+ this.elements.customizeBtn.onclick = () => this.showPreferences();
109
+ }
110
+ if(this.elements.manageBtn){
111
+ this.elements.manageBtn.onclick = () => this.showPreferences();
112
+ }
113
+ if(this.elements.savePreferencesBtn){
114
+ this.elements.savePreferencesBtn.onclick = () => this.savePreferences();
115
+ }
116
+ if(this.elements.cancelPreferencesBtn){
117
+ this.elements.cancelPreferencesBtn.onclick = () => this.hidePreferences();
118
+ }
119
+ if(this.elements.preferencesModal){
120
+ this.elements.preferencesModal.onclick = (e) => {
121
+ if(e.target === this.elements.preferencesModal){
122
+ this.hidePreferences();
123
+ }
124
+ };
125
+ }
126
+ document.addEventListener('keydown', (e) => {
127
+ if(
128
+ 27 === e.keyCode && this.elements.preferencesModal
129
+ && !this.elements.preferencesModal.classList.contains('hidden')
130
+ ){
131
+ this.hidePreferences();
132
+ }
133
+ });
134
+ },
135
+
136
+ checkExistingConsent()
137
+ {
138
+ let savedConsent = this.getStoredConsent();
139
+ if(!savedConsent){
140
+ this.showBanner();
141
+ return;
142
+ }
143
+ this.loadSavedPreferences();
144
+ this.updateConsentMode();
145
+ if(this.shouldLoadAnalytics()){
146
+ this.loadAnalytics();
147
+ }
148
+ },
149
+
150
+ getStoredConsent()
151
+ {
152
+ try {
153
+ return localStorage.getItem(this.config.storageKey);
154
+ } catch(e) {
155
+ return null;
156
+ }
157
+ },
158
+
159
+ getStoredPreferences()
160
+ {
161
+ try {
162
+ let stored = localStorage.getItem(this.config.preferencesKey);
163
+ if(stored){
164
+ return JSON.parse(stored);
165
+ }
166
+ return null;
167
+ } catch(e) {
168
+ return null;
169
+ }
170
+ },
171
+
172
+ saveConsent(value)
173
+ {
174
+ try {
175
+ localStorage.setItem(this.config.storageKey, value);
176
+ return true;
177
+ } catch(e) {
178
+ return false;
179
+ }
180
+ },
181
+
182
+ savePreferencesToStorage(preferences)
183
+ {
184
+ try {
185
+ localStorage.setItem(this.config.preferencesKey, JSON.stringify(preferences));
186
+ return true;
187
+ } catch(e) {
188
+ return false;
189
+ }
190
+ },
191
+
192
+ loadSavedPreferences()
193
+ {
194
+ let saved = this.getStoredPreferences();
195
+ if(!saved){
196
+ return;
197
+ }
198
+ for(let categoryKey of Object.keys(this.categories)){
199
+ if(saved.hasOwnProperty(categoryKey)){
200
+ this.categories[categoryKey].enabled = saved[categoryKey];
201
+ }
202
+ }
203
+ },
204
+
205
+ showBanner()
206
+ {
207
+ if(!this.elements.banner){
208
+ return;
209
+ }
210
+ this.elements.banner.classList.remove('hidden');
211
+ this.elements.banner.classList.add('animate-in');
212
+ this.elements.banner.setAttribute('aria-hidden', 'false');
213
+ let firstButton = this.elements.banner.querySelector('button');
214
+ if(firstButton){
215
+ firstButton.focus();
216
+ }
217
+ },
218
+
219
+ hideBanner()
220
+ {
221
+ if(!this.elements.banner){
222
+ return;
223
+ }
224
+ this.elements.banner.classList.add('animate-out');
225
+ this.elements.banner.setAttribute('aria-hidden', 'true');
226
+ setTimeout(() => {
227
+ this.elements.banner.classList.add('hidden');
228
+ this.elements.banner.classList.remove('animate-in', 'animate-out');
229
+ }, 300);
230
+ },
231
+
232
+ showPreferences()
233
+ {
234
+ if(!this.elements.preferencesModal){
235
+ return;
236
+ }
237
+ this.updatePreferencesUI();
238
+ this.elements.preferencesModal.classList.remove('hidden');
239
+ this.elements.preferencesModal.setAttribute('aria-hidden', 'false');
240
+ let firstInput = this.elements.preferencesModal.querySelector('input, button');
241
+ if(firstInput){
242
+ firstInput.focus();
243
+ }
244
+ this.hideBanner();
245
+ },
246
+
247
+ hidePreferences()
248
+ {
249
+ if(!this.elements.preferencesModal){
250
+ return;
251
+ }
252
+ this.elements.preferencesModal.classList.add('hidden');
253
+ this.elements.preferencesModal.setAttribute('aria-hidden', 'true');
254
+ },
255
+
256
+ updatePreferencesUI()
257
+ {
258
+ for(let categoryKey of Object.keys(this.categories)){
259
+ let toggle = document.getElementById('cookie-' + categoryKey);
260
+ if(toggle){
261
+ toggle.checked = this.categories[categoryKey].enabled;
262
+ toggle.disabled = this.categories[categoryKey].required;
263
+ }
264
+ }
265
+ },
266
+
267
+ acceptAll()
268
+ {
269
+ for(let categoryKey of Object.keys(this.categories)){
270
+ this.categories[categoryKey].enabled = true;
271
+ }
272
+ this.saveConsent('granted');
273
+ this.saveCurrentPreferences();
274
+ this.updateConsentMode();
275
+ this.loadAnalytics();
276
+ this.hideBanner();
277
+ },
278
+
279
+ rejectAll()
280
+ {
281
+ for(let categoryKey of Object.keys(this.categories)){
282
+ this.categories[categoryKey].enabled = this.categories[categoryKey].required;
283
+ }
284
+ this.saveConsent('denied');
285
+ this.saveCurrentPreferences();
286
+ this.updateConsentMode();
287
+ this.hideBanner();
288
+ },
289
+
290
+ savePreferences()
291
+ {
292
+ this.readPreferencesFromUI();
293
+ this.saveCurrentPreferences();
294
+ let hasAnalytics = this.categories.analytics.enabled;
295
+ let hasMarketing = this.categories.marketing.enabled;
296
+ if(hasAnalytics || hasMarketing){
297
+ this.saveConsent('granted');
298
+ this.updateConsentMode();
299
+ if(hasAnalytics){
300
+ this.loadAnalytics();
301
+ }
302
+ } else {
303
+ this.saveConsent('denied');
304
+ this.updateConsentMode();
305
+ }
306
+ this.hidePreferences();
307
+ },
308
+
309
+ readPreferencesFromUI()
310
+ {
311
+ for(let categoryKey of Object.keys(this.categories)){
312
+ let toggle = document.getElementById('cookie-' + categoryKey);
313
+ if(toggle && !this.categories[categoryKey].required){
314
+ this.categories[categoryKey].enabled = toggle.checked;
315
+ }
316
+ }
317
+ },
318
+
319
+ saveCurrentPreferences()
320
+ {
321
+ let preferences = {};
322
+ for(let categoryKey of Object.keys(this.categories)){
323
+ preferences[categoryKey] = this.categories[categoryKey].enabled;
324
+ }
325
+ this.savePreferencesToStorage(preferences);
326
+ },
327
+
328
+ updateConsentMode()
329
+ {
330
+ let analyticsStorage = this.categories.analytics.enabled ? 'granted' : 'denied';
331
+ let adStorage = this.categories.marketing.enabled ? 'granted' : 'denied';
332
+ window.gtag('consent', 'update', {
333
+ analytics_storage: analyticsStorage,
334
+ ad_storage: adStorage,
335
+ ad_user_data: adStorage,
336
+ ad_personalization: adStorage
337
+ });
338
+ },
339
+
340
+ shouldLoadAnalytics()
341
+ {
342
+ return this.categories.analytics.enabled;
343
+ },
344
+
345
+ loadAnalytics()
346
+ {
347
+ if(this.config.useGTM){
348
+ this.loadGTM();
349
+ return;
350
+ }
351
+ this.loadGA();
352
+ },
353
+
354
+ loadGTM()
355
+ {
356
+ if(document.querySelector('script[src*="googletagmanager.com/gtm.js"]')){
357
+ return;
358
+ }
359
+ let script = document.createElement('script');
360
+ script.src = 'https://www.googletagmanager.com/gtm.js?id=' + this.config.gtmId;
361
+ script.async = true;
362
+ script.onload = () => {
363
+ window.gtag('js', new Date());
364
+ window.gtag('config', this.config.gtmId);
365
+ };
366
+ document.head.appendChild(script);
367
+ let noscript = document.createElement('noscript');
368
+ let iframe = document.createElement('iframe');
369
+ iframe.src = 'https://www.googletagmanager.com/ns.html?id=' + this.config.gtmId;
370
+ iframe.height = '0';
371
+ iframe.width = '0';
372
+ iframe.style.display = 'none';
373
+ iframe.style.visibility = 'hidden';
374
+ noscript.appendChild(iframe);
375
+ document.body.appendChild(noscript);
376
+ },
377
+
378
+ loadGA()
379
+ {
380
+ if(document.querySelector('script[src*="googletagmanager.com/gtag/js"]')){
381
+ return;
382
+ }
383
+ let script = document.createElement('script');
384
+ script.src = 'https://www.googletagmanager.com/gtag/js?id=' + this.config.gaId;
385
+ script.async = true;
386
+ script.onload = () => {
387
+ window.gtag('js', new Date());
388
+ window.gtag('config', this.config.gaId, {
389
+ anonymize_ip: true,
390
+ allow_google_signals: this.categories.marketing.enabled,
391
+ allow_ad_personalization_signals: this.categories.marketing.enabled
392
+ });
393
+ };
394
+ document.head.appendChild(script);
395
+ },
396
+
397
+ reset()
398
+ {
399
+ try {
400
+ localStorage.removeItem(this.config.storageKey);
401
+ localStorage.removeItem(this.config.preferencesKey);
402
+ location.reload();
403
+ } catch(e) {
404
+ location.reload();
405
+ }
406
+ }
407
+ };
408
+
409
+ document.addEventListener('DOMContentLoaded', () => {
410
+ CookieConsent.init();
411
+ });