@jarijokinen/consent 0.0.4 → 0.0.5

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/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
  The MIT License (MIT)
2
- Copyright (c) 2022 Jari Jokinen
2
+ Copyright (c) 2022 - 2025 Jari Jokinen
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy of
5
5
  this software and associated documentation files (the "Software"), to deal in
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # consent
2
2
 
3
- A zero-dependency, vanilla JavaScript consent manager with native Consent Mode
4
- support.
3
+ A zero-dependency, vanilla JavaScript consent manager with native Google
4
+ Consent Mode v2 support.
5
5
 
6
6
  ## Installation
7
7
 
@@ -9,13 +9,18 @@ NPM:
9
9
 
10
10
  npm install @jarijokinen/consent
11
11
 
12
- Yarn:
12
+ ## Usage
13
13
 
14
- yarn add @jarijokinen/consent
14
+ Load the consent-loader.js in the HEAD section deferred before any GTM tags.
15
15
 
16
- ## Usage
16
+ <head>
17
+ <script src="path/to/consent-loader.js" defer></script>
18
+ <script>
19
+ // GTM and any other scripts
20
+ </script>
21
+ </head>
17
22
 
18
- Initialization:
23
+ Import consent from the npm package and initialize it later on the page.
19
24
 
20
25
  import { consent } from '@jarijokinen/consent';
21
26
 
@@ -25,7 +30,7 @@ Initialization:
25
30
 
26
31
  Add link that opens the dialog:
27
32
 
28
- <a href="#" class="consent-settings-link">Cookie Settings</a>
33
+ <a href="#" class="consent-settings">Cookie Settings</a>
29
34
 
30
35
  ## Configuration
31
36
 
@@ -48,15 +53,10 @@ argument to the consent() function.
48
53
  <div class="consent-fields"></div>
49
54
  <div class="consent-buttons"></div>
50
55
  `,
51
- dialogTitle: 'Cookies',
52
- dialogMessage: 'We would like to get your permission to use cookies for:',
53
56
  dialogClass: 'consent',
54
- settingsLinkSelector: '.consent-settings-link',
55
- countryCookie: 'cf_country',
56
- countries: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
57
- 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
58
- 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO',
59
- 'GB', 'CH', 'MC', 'SM', 'VA', 'JE', 'GG', 'IM']
57
+ dialogTitle: 'Cookie Consent',
58
+ dialogMessage: 'This website uses cookies for:',
59
+ settingsLinkSelector: '.consent-settings'
60
60
  };
61
61
 
62
62
  consent(options);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@jarijokinen/consent",
3
- "version": "0.0.4",
4
- "description": "A zero-dependency, vanilla JavaScript consent manager with native Consent Mode support.",
3
+ "version": "0.0.5",
4
+ "description": "A zero-dependency, vanilla JavaScript consent manager with native Google Consent Mode v2 support.",
5
5
  "main": "src/consent.js",
6
6
  "repository": {
7
7
  "type": "git",
@@ -0,0 +1,39 @@
1
+ window.dataLayer = window.dataLayer || [];
2
+ function gtag() { dataLayer.push(arguments); }
3
+
4
+ window.consent = window.consent || {};
5
+ window.consent.countries = window.consent.countries || [
6
+ 'AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR', 'DE', 'GR', 'HU',
7
+ 'IS', 'IE', 'IT', 'LV', 'LI', 'LT', 'LU', 'MT', 'NL', 'NO', 'PL', 'PT', 'RO',
8
+ 'SK', 'SI', 'ES', 'SE', 'UK', 'GB', 'CH'
9
+ ];
10
+ window.consent.consentMissing = true
11
+ window.consent.state = {
12
+ analytics_storage: null,
13
+ ad_storage: null,
14
+ ad_user_data: null,
15
+ ad_personalization: null
16
+ };
17
+
18
+ Object.keys(window.consent.state).forEach(k => {
19
+ try {
20
+ const v = localStorage.getItem('consent_' + k);
21
+ if (v === 'granted' || v === 'denied') {
22
+ window.consent.state[k] = v;
23
+ window.consent.consentMissing = false;
24
+ }
25
+ }
26
+ catch (e) {
27
+ window.consent.state[k] = null;
28
+ }
29
+ });
30
+
31
+ const country = document.cookie.split(/(?:^|;\s*)cc=/).pop().split(';')[0]
32
+ .trim().toUpperCase();
33
+ const countryDefault = !country || window.consent.countries.includes(country)
34
+ ? 'denied' : 'granted';
35
+
36
+ gtag('consent', 'default', {
37
+ ...Object.fromEntries(Object.entries(window.consent.state).map(([k, v]) =>
38
+ [k, v ?? countryDefault]))
39
+ });
package/src/consent.js CHANGED
@@ -5,9 +5,9 @@ export const consent = (options) => {
5
5
  ad_storage: 'Marketing'
6
6
  },
7
7
  actions: {
8
- denyAll: 'Deny All',
8
+ allowAll: 'Allow All',
9
9
  allowSelected: 'Allow Selected',
10
- allowAll: 'Allow All'
10
+ denyAll: 'Deny All'
11
11
  },
12
12
  dialogMarkup: `
13
13
  <h2 class="consent-title"></h2>
@@ -15,76 +15,18 @@ export const consent = (options) => {
15
15
  <div class="consent-fields"></div>
16
16
  <div class="consent-buttons"></div>
17
17
  `,
18
- dialogTitle: 'Cookies',
19
- dialogMessage: 'We would like to get your permission to use cookies for:',
20
18
  dialogClass: 'consent',
21
- settingsLinkSelector: '.consent-settings-link',
22
- countryCookie: 'cf_country',
23
- countries: ['AT', 'BE', 'BG', 'HR', 'CY', 'CZ', 'DK', 'EE', 'FI', 'FR',
24
- 'DE', 'GR', 'HU', 'IE', 'IT', 'LV', 'LT', 'LU', 'MT', 'NL',
25
- 'PL', 'PT', 'RO', 'SK', 'SI', 'ES', 'SE', 'IS', 'LI', 'NO',
26
- 'GB', 'CH', 'MC', 'SM', 'VA', 'JE', 'GG', 'IM'],
19
+ dialogTitle: 'Cookie Consent',
20
+ dialogMessage: 'This website uses cookies for:',
21
+ settingsLinkSelector: '.consent-settings',
27
22
  ...options
28
23
  };
29
-
30
- let consentState = {
31
- ad_storage: 'denied',
32
- analytics_storage: 'denied',
33
- functionality_storage: 'denied',
34
- personalization_storage: 'denied',
35
- security_storage: 'denied'
36
- };
24
+
25
+ function gtag() { dataLayer.push(arguments); }
37
26
 
38
27
  let dialog = document.createElement('div');
39
28
  dialog.classList.add(opts.dialogClass);
40
29
 
41
- const detectCountry = () => {
42
- const country = document.cookie.split('; ').find(r =>
43
- r.startsWith(opts.countryCookie + '=')
44
- )?.split('=')[1];
45
- if (country ? opts.countries.includes(country) : true) {
46
- return;
47
- }
48
- Object.keys(opts.storages).forEach((storage) => {
49
- consentState[storage] = 'granted';
50
- });
51
- updateConsent();
52
- };
53
-
54
- const getConsent = (storage) => {
55
- return localStorage.getItem('consent_' + storage);
56
- };
57
-
58
- const updateConsent = () => {
59
- Object.keys(opts.storages).forEach((storage) => {
60
- localStorage.setItem('consent_' + storage, consentState[storage]);
61
- });
62
-
63
- if (typeof gtag === 'function') {
64
- gtag('consent', 'update', consentState);
65
- }
66
- };
67
-
68
- const updateFields = () => {
69
- Object.keys(opts.storages).forEach((storage) => {
70
- dialog.querySelector('#consent-field-' + storage).checked =
71
- consentState[storage] == 'granted' ? true : false;
72
- });
73
- };
74
-
75
- const loadConsentState = () => {
76
- Object.keys(opts.storages).forEach((storage) => {
77
- consentState[storage] = getConsent(storage);
78
- if (consentState[storage] === null) {
79
- showDialog();
80
- }
81
- });
82
-
83
- if (typeof gtag === 'function') {
84
- gtag('consent', 'update', consentState);
85
- }
86
- };
87
-
88
30
  const createDialog = () => {
89
31
  dialog.innerHTML = opts.dialogMarkup;
90
32
  dialog.querySelector('.consent-title').innerText = opts.dialogTitle;
@@ -96,7 +38,7 @@ export const consent = (options) => {
96
38
  checkbox.setAttribute('type', 'checkbox');
97
39
  checkbox.setAttribute('name', 'consent-field-' + storage);
98
40
  checkbox.setAttribute('id', 'consent-field-' + storage);
99
- if (getConsent(storage) === 'granted') {
41
+ if (window.consent.state[storage] === 'granted') {
100
42
  checkbox.setAttribute('checked', 'checked');
101
43
  }
102
44
  const label = document.createElement('label');
@@ -115,34 +57,31 @@ export const consent = (options) => {
115
57
  switch (action) {
116
58
  case 'denyAll':
117
59
  button.onclick = () => {
118
- Object.keys(opts.storages).forEach((storage) => {
119
- consentState[storage] = 'denied';
60
+ Object.keys(opts.storages).forEach(k => {
61
+ window.consent.state[k] = 'denied'
120
62
  });
121
- updateConsent();
63
+ saveState();
122
64
  updateFields();
123
65
  hideDialog();
124
66
  };
125
67
  break;
126
68
  case 'allowSelected':
127
69
  button.onclick = () => {
128
- Object.keys(opts.storages).forEach((storage) => {
129
- consentState[storage] = document.querySelector(
130
- '#consent-field-' + storage
131
- ).checked
132
- ? 'granted'
133
- : 'denied';
70
+ Object.keys(opts.storages).forEach(k => {
71
+ const el = document.querySelector('#consent-field-' + k);
72
+ window.consent.state[k] = el.checked ? 'granted' : 'denied';
134
73
  });
135
- updateConsent();
74
+ saveState();
136
75
  updateFields();
137
76
  hideDialog();
138
77
  };
139
78
  break;
140
79
  case 'allowAll':
141
80
  button.onclick = () => {
142
- Object.keys(opts.storages).forEach((storage) => {
143
- consentState[storage] = 'granted';
81
+ Object.keys(opts.storages).forEach(k => {
82
+ window.consent.state[k] = 'granted';
144
83
  });
145
- updateConsent();
84
+ saveState();
146
85
  updateFields();
147
86
  hideDialog();
148
87
  };
@@ -155,6 +94,26 @@ export const consent = (options) => {
155
94
  document.querySelector('body').appendChild(dialog);
156
95
  };
157
96
 
97
+ const saveState = () => {
98
+ Object.keys(window.consent.state).forEach(k => {
99
+ try {
100
+ localStorage.setItem('consent_' + k, window.consent.state[k]);
101
+ }
102
+ catch (e) {
103
+ console.log('Unable to save consent to local storage');
104
+ }
105
+ });
106
+
107
+ gtag('consent', 'update', window.consent.state);
108
+ };
109
+
110
+ const updateFields = () => {
111
+ Object.keys(opts.storages).forEach(k => {
112
+ dialog.querySelector('#consent-field-' + k).checked =
113
+ window.consent.state[k] === 'granted' ? true : false;
114
+ });
115
+ };
116
+
158
117
  const showDialog = () => {
159
118
  dialog.style.display = 'block';
160
119
  };
@@ -162,13 +121,18 @@ export const consent = (options) => {
162
121
  const hideDialog = () => {
163
122
  dialog.style.display = 'none';
164
123
  };
165
-
124
+
166
125
  document.querySelector(opts.settingsLinkSelector).onclick = (ev) => {
167
126
  ev.preventDefault();
168
127
  showDialog();
169
128
  };
170
129
 
171
- detectCountry();
172
- loadConsentState();
173
130
  createDialog();
131
+
132
+ if (window.consent.consentMissing) {
133
+ showDialog();
134
+ }
135
+ else {
136
+ hideDialog();
137
+ }
174
138
  };