Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/vielhuber/chefcookie

đź‘» chefcookie is a cookie solution. đź‘»
https://github.com/vielhuber/chefcookie

Last synced: 19 days ago
JSON representation

đź‘» chefcookie is a cookie solution. đź‘»

Awesome Lists containing this project

README

        

# đź‘» chefcookie đź‘»

[![NPM](https://img.shields.io/npm/v/chefcookie.svg)](https://www.npmjs.com/package/chefcookie)

chefcookie is a gdpr cookie solution without compromises.

## features

- opt in/out
- highly customizable
- custom event tracking
- duration tracking
- scroll depth tracking
- includes basic styling
- multi language support
- ships multiple layouts (overlay, bottombar, topbar)
- supports custom tracking scripts
- auto disable tracking for logged in wordpress users
- ie11 support available
- script grouping with optional optin on script level
- (intentionally) supports [i don't care about cookies](https://www.i-dont-care-about-cookies.eu)

## included

- [google analytics](https://analytics.google.com)
- [google tag manager](https://tagmanager.google.com)
- [facebook ads](https://de-de.facebook.com/business/products/ads)
- [twitter ads](https://ads.twitter.com)
- [taboola ads](https://www.taboola.com)
- [match2one ads](https://www.match2one.com)
- [microsoft ads](https://ads.microsoft.com)
- [linkedin](https://business.linkedin.com/marketing-solutions/conversion-tracking)
- [etracker](https://www.etracker.com)
- [matomo analytics](https://matomo.org)
- [smartlook](https://www.smartlook.com)
- [crazy egg](https://www.crazyegg.com)
- [google maps](https://developers.google.com/maps/documentation/javascript/tutorial)

## references

- [changelog](https://github.com/vielhuber/chefcookie/blob/master/CHANGELOG.md)

## installation

use it as a module:

```
npm install chefcookie
```

```js
import chefcookie from 'chefcookie';
```

or include it the traditional way:

```html

```

## usage

```js
const cc = new chefcookie({
message: {
de: `

Wir verwenden Cookies



Unsere Website verwendet Cookies, die uns helfen, unsere Website zu verbessern, den bestmöglichen Service zu bieten und ein optimales Kundenerlebnis zu ermöglichen. Hier können Sie Ihre Einstellungen verwalten. Indem Sie auf "Akzeptieren" klicken, erklären Sie sich damit einverstanden, dass Ihre Cookies für diesen Zweck verwendet werden. Weitere Informationen dazu finden Sie in unserer Datenschutzerklärung sowie im Impressum. Sollten Sie hiermit nicht einverstanden sein, können Sie die Verwendung von Cookies hier ablehnen.


`,
en: `

We use cookies



Our website uses cookies that help us to improve our website, provide the best possible service and enable an optimal customer experience. Here you can manage your settings. By clicking on "Accept", you agree that your cookies may be used for this purpose. You can find further information on this in our data protection declaration and in the imprint. If you do not agree to this, you can refuse the use of cookies here reject the use of cookies.


`
},
accept_all_if_settings_closed: true,
show_decline_button: false,
scripts_selection: 'collapse', // false|true|'collapse'
debug_log: false,
bypass_parameter: null, // e.g. {'ignore': '1', 'foo': 'bar'}
consent_tracking: null, // '/wp-json/v1/track-consent.php'
lng_fallback: null, // lng code used in labels when current lng is not found (e.g. "en")
expiration: 7, // in days
cookie_prefix: 'cc_', // switch cookie prefix (e.g. for different pages on the same top level domain)
exclude_ua_regex:
/(Mozilla\/5\.0 \(Linux; Android 11; moto g power \(2022\)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/109\.0.0.0 Mobile Safari\/537\.36)|(Mozilla\/5\.0 \(Macintosh; Intel Mac OS X 10_15_7\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/109\.0\.0\.0 Safari\/537\.36)|(Speed Insights)|(Chrome-Lighthouse)|(PSTS[\d\.]+)/,
//domain: undefined,
style: {
layout: 'overlay', // overlay|bottombar|topbar
size: 3, // 1|2|3|4|5
color_text: '#595f60',
color_highlight: '#ff0000',
color_background: '#eeeeee',
highlight_accept: true,
show_disabled_checkbox: true,
noscroll: true,
fade: true,
blur: true,
css_replace: ``, // replace plugin's styles with custom css
css_add: `` // enhance plugin's styles with custom css
},
labels: {
accept: { de: 'Akzeptieren', en: 'Accept' },
accept_all: { de: 'Alles akzeptieren', en: 'Accept all' },
settings_open: { de: 'Einstellungen festlegen', en: 'Change settings' },
settings_close: { de: 'Einstellungen schliessen', en: 'Close settings' },
group_open: { de: 'Weitere Informationen anzeigen', en: 'Show more information' },
group_close: { de: 'Weitere Informationen schliessen', en: 'Close more information' },
decline: { de: 'Nur erforderliche Cookies', en: 'Only necessary cookies' },
details_open: { de: 'Details anzeigen', en: 'Show details' },
details_close: { de: 'Details schliessen', en: 'Close details' }
},
exclude: [
// exclude privacy site if needed
'/de/datenschutz',
'/en/privacy',
'/de/impressum',
'/en/imprint',
// exclude wordpress users
() => {
return document.cookie !== undefined && document.cookie.indexOf('wp-settings-time') > -1;
}
],
settings: [
{
title: { de: 'Analysen', en: 'Analyses' },
description: {
de: 'Tools, die anonyme Daten über Website-Nutzung und -Funktionalität sammeln. Wir nutzen die Erkenntnisse, um unsere Produkte, Dienstleistungen und das Benutzererlebnis zu verbessern.',
en: 'Tools that collect anonymous data about website usage and functionality. We use this information to improve our products, services and user experience.'
},
checked_by_default: true,
cannot_be_modified: false,
initial_tracking: false,
scripts: {
analytics: 'G-xxxxxxxxxx',
// extended syntax
analytics: {
id: 'G-xxxxxxxxxx',
title: { de: 'Google Analytics', en: 'Google Analytics' },
description: {
de: 'Die Verwendung der Analyse Cookies erfolgt zu dem Zweck, die Qualität unserer Website und ihre Inhalte zu verbessern und die Funktionsfähigkeit von eingebundenen Diensten unserer Partner sicherzustellen.',
en: `


Google Analytics cookies are used to improve the quality of our website and its content and to ensure the functionality of integrated services of our partners.



Name:_gid
Host:www.tld.com
Duration:Session
Type:1st Party
Description:This is a pattern type cookie name associated with a marketing cloud. It stores an unique visitor identifier, and uses an organisation identifier.

`
}
}
}
},
{
title: { de: 'Werbung', en: 'Advertising' },
description: {
de: 'Anonyme Informationen, die wir sammeln, um Ihnen nützliche Produkte und Dienstleistungen empfehlen zu können.',
en: 'Anonymous information that we collect in order to recommend useful products and services to you.'
},
checked_by_default: true,
cannot_be_modified: false,
initial_tracking: false,
scripts: {
tagmanager: 'GTM-XXXXXXX',
facebook: 'xxxxxxxxxxxxxxx',
twitter: 'single',
taboola: 'xxxxxxx',
match2one: 'xxxxxxxx',
microsoft: 'xxxxxxxx',
linkedin: 'xxxxxxx',
etracker: 'xxxxxx',
matomo: 'xxxxxx#x', // domain/siteid
smartlook: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
crazyegg: 'xxxx/xxxx',
google_maps: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
}
},
{
title: { de: 'Drittanbieter', en: 'Third-party' },
description: {
de: 'Tools, die interaktive Services wie beispielsweise Video- und Kartendienste unterstĂĽtzen.',
en: 'Tools that support interactive services such as video and map services.'
},
checked_by_default: true,
cannot_be_modified: false,
initial_tracking: false,
scripts: {}
},
{
title: { de: 'Grundlegendes', en: 'Basics' },
description: {
de: 'Tools, die wesentliche Services und Funktionen ermöglichen, einschließlich Identitätsprüfung, Servicekontinuität und Standortsicherheit. Diese Option kann nicht abgelehnt werden.',
en: 'Tools that enable essential services and functions, including identity verification, service continuity, and site security. This option cannot be declined.'
},
checked_by_default: true,
cannot_be_modified: true,
initial_tracking: true,
scripts: {
example_script1: {}, // this immediately gets "resolved"
example_script2: {
accept: (cc, resolve, isInit) => {
/* example: load default scripts inside custom script */
cc.load('analytics', 'G-xxxxxxxxxx');
cc.load('tagmanager', 'GTM-XXXXXXX');
cc.load('facebook', 'xxxxxxxxxxxxxxx');
cc.load('twitter', 'single');
cc.load('taboola', 'xxxxxxx');
cc.load('match2one', 'xxxxxxxx');
cc.load('microsoft', 'xxxxxxxxx');
cc.load('linkedin', 'xxxxxxx');
cc.load('etracker', 'xxxxxx');
cc.load('matomo', 'xxxxxx#x');
cc.load('smartlook', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
cc.load('crazyegg', 'xxxx/xxxx');
cc.load('google_maps', 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');

/* example: load (multiple) custom javascripts */
cc.loadJs([
'script1.js',
'script2.js',
'https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx'
]).then(() => {
resolve();
});

/* example: enable uninitialized iframes */
if (document.querySelector('iframe[alt-src*="google.com/maps"]') !== null) {
document.querySelectorAll('iframe[alt-src*="google.com/maps"]').forEach(el => {
el.setAttribute('src', el.getAttribute('alt-src'));
});
}

/* example: load scripts manually */
let script1 = document.createElement('script');
script1.setAttribute('src', 'https://www.googletagmanager.com/gtag/js?id=G-xxxxxxxxxx');
script1.onload = () => {
resolve();
};
document.head.appendChild(script1);

/* example: load scripts manually */
let script2 = document.createElement('script');
let html = "alert('OK');";
script2.innerHTML = html;
document.head.appendChild(script2);

/* example: load scripts manually (with custom callback) */
window.captchaCallback = () => {
resolve();
};
cc.loadJs('https://www.google.com/recaptcha/api.js?onload=captchaCallback&render=explicit');

/*
important: always call resolve to show that the script fully has loaded!
if you don't want the accept logic to be inside this function, only call resolve() and use waitFor outside this function to fire further actions */
resolve();

/* some other helpers */
cc.url(); // gets the current url
cc.lng(); // gets the current lng
isInit; // true|false (accepted actively through click and not via cookie)
},
exclude: () => {
return document.cookie !== undefined && document.cookie.indexOf('wp-settings-time') > -1;
},
title: { de: '...', en: '...' },
description: { de: '...', en: '...' }
}
}
}
]
});
document.addEventListener('DOMContentLoaded', () => {
cc.init();
});
```

#### multi language support

instead of plain text you can provide language objects for any label:

```js
{
title: 'Werbung';
}
```

```js
{
title: { de: 'Werbung', en: 'Advertising' }
}
```

chefcookie will find out the current language and show the appropriate strings.

#### opt out links

its recommended to place those kind of links inside your privacy page:

```html
Google Analytics deaktivieren
Google Tag Manager deaktivieren
Facebook Pixel deaktivieren
Twitter Pixel deaktivieren
Taboola Pixel deaktivieren
Match2One Pixel deaktivieren
Microsoft Ads deaktivieren
LinkedIn Pixel deaktivieren
etracker deaktivieren
Matomo deaktivieren
Smartlook deaktivieren
Crazy Egg deaktivieren
Google Maps deaktivieren
```

you can style those links with:

```css
[data-cc-disable] {
}
[data-cc-disable].disabled {
}
```

#### (one time) opt in links

these links self destroy if the respective script is accepted:

```html


Ich möchte Google Maps-Inhalte aktivieren und stimme zu, dass Daten von Google geladen werden (siehe
Datenschutz).

```

it is recommended to place them inside your via js populated divs (like a google maps wrapper).\
if you click on it, the script is explicitly accepted.

#### manually accept/decline

```js
cc.accept('analytics');
cc.decline('analytics');
cc.isAccepted('analytics'); // true|false
```

#### bypass / backdoor

add `?accept=1` to your urls to completely bypass chefcookie and\
ignore user consent which means all scripts are in fact accepted.\
you also can define custom parameter name/value pairs via `bypass_parameter`.

#### custom scripts

the following keywords as keys are reserved:

- `analytics`
- `tagmanager`
- `facebook`
- `twitter`
- `taboola`
- `match2one`
- `microsoft`
- `linkedin`
- `etracker`
- `matomo`
- `smartlook`
- `crazyegg`
- `google_maps`

if you provide strings as values, chefcookie interprets them appropriately. chefcookie then loads the libraries with reasonable default settings. however, you can execute your own functions in either overwriting the values of these reserved keywords (and provide an object) or use any other keyword.

#### script blocking

the best strategy is to add no scripts at all and let chefcookie add the scripts later.

if you cannot do that (e.g. when you cannot manipulate the page content), there are a lot of techniques and strategies out there to prevent existing scripts from executing:

- use http content-security-policy headers
- manipulate embeds (set `type="javascript/blocked"` or `alt-src="..."`)
- monkey patch `document.createElement`
- watch and modify with `MutationObserver`
- abuse `document.write`

chefcookie is flexible and very well works together with e.g. [yett](https://github.com/snipsco/yett):

- init `yett` before chefcookie to block scripts
- call `unblock()` inside chefcookies custom scripts

#### dynamically load a script

chefcookie provides a `load`-helper, where you can provide one or multiple urls to load:

```js
cc.loadJs(['script1.js', 'script2.js']);
```

in order to call `resolve()` (see below), you can use:

```js
cc.loadJs(['script1.js', 'script2.js']).then(() => { resolve(); };
cc.loadJs(['script1.js', 'script2.js'], () => { resolve(); }); // also supported
```

#### wait for a tracker

if your javascript is dependent on a specific script loaded by chefcookie, you should handle that case and wait for the tracker being executed:

```js
cc.waitFor('google_recaptcha').then(() => {});
cc.waitFor('google_recaptcha', () => {}); // also supported
```

this only gets executed when you call `resolve()` inside your custom tracking function.

#### more

the cookie banner is shown always, if no consent is saved.\
if done so, it is shown again, after the cookie expired (see the `expiration`-setting).

cookies are valid by default for the 2nd level domain of the site. e.g if the banner is on www.foo.bar, the cookie domain will be foo.bar, so the user's choices will be valid for www.foo.bar, xxx.foo.bar, ...\
there is an option to customize the domain using the `domain`-settings. setting the domain to `undefined` means using cookies only for the current site.

it is recommended to include a (re)opening link in the privacy policy, for example.\
here you can also use the following code:

```html
Consent-Einstellungen bearbeiten
```

you can programmatically control chefcookie via javascript:

- `cc.open()`: open the cookie banner manually
- `cc.openWithSettings()`: open the cookie banner including settings manually
- `cc.isOpen()`: check if cookie banner is opened
- `cc.close()`: close the cookie banner manually
- `cc.isClosed()`: check if cookie banner is closed
- `cc.destroy()`: destroy the cookie banner and all event listeners
- `cc.updateOptOutOptIn()`: refreshes the state of opt out / opt in buttons

#### exclude user agent

you can avoid showing the banner for specific user agents (i.e. bots or crawlers), using a regular expression.

```js
const cc = new chefcookie({
...
exclude_ua_regex: /(myBOT)/
...
});
```

currently the default regex `/(Mozilla\/5\.0 \(Linux; Android 11; moto g power \(2022\)\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/109\.0.0.0 Mobile Safari\/537\.36)|(Mozilla\/5\.0 \(Macintosh; Intel Mac OS X 10_15_7\) AppleWebKit\/537\.36 \(KHTML, like Gecko\) Chrome\/109\.0\.0\.0 Safari\/537\.36)|(Speed Insights)|(Chrome-Lighthouse)|(PSTS[\d\.]+)/` excludes Google PageSpeed Insights, Chrome Lighthouse and WebPageTest.org.

#### consent manager tracking

to test the acceptance of the consent manager, it is recommended to use the `consent_tracking`-option. if you specify an url (relative or absolute) there, chefcookie sends a post-request with analysis data for every action that a user performs in the consent manager. these requests have the form:

```json
{
"action": "accept_all",
"date": "2021-01-01 10:00:00",
"url": "https://tld.com?foo=bar",
"providers": "analytics,facebook,twitter",
"viewport": "the viewport size of the user device"
}
```

`url` as you can see consists of potential get-paramaters (so that you can analyze e.g. google ads campaigns).\
take note that nor ip or user agent are posted, but feel free to catch and store these parts with the help of a php endpoint.

the `action`-key can have the following values:

- `open`: the consent manager is shown
- `accept_all`: all providers are accepted
- `accept_partially`: providers are accepted partially
- `decline`: all providers are declined
- `settings_open`: settings are opened
- `settings_close`: settings are closed
- `first_user_interaction`: first mouse/touch/scroll interaction happened

here it makes sense to temporarily store this data in a database and evaluate it – for example, to measure the discrepancy between the real visitor numbers and the numbers in google analytics or to optimize the appearance of the consent manager (e.g. using the `layout`-option).

#### etracker optimizer

by default the etracker optimizer is disabled, since it creates a nasty fouc. you can enable it via:

```js
window._etr = { eoBlocked: false };
cc.load('etracker', 'xxxxxx');
```

#### event tracking

chefcookie additionally comes with event tracking for all major analytics platforms.\
you can even use this feature with side loaded scripts.

```js
window.addEventListener('load', () => {
// track duration (sends action "xxs" in 30 second intervals)
cc.trackDuration();
// track scroll depth (sends "xx%" in scroll steps 1, 10, 25, 50, 75, 100)
cc.trackScrollDepth();
// custom duration
cc.trackDurationCustom(60, () => {
cc.eventAnalytics('60s');
});
// custom scroll depth
cc.trackScrollDepthCustom(25, () => {
cc.eventAnalytics('25%');
});
// custom tracking
document.querySelector('.conversion').addEventListener('click', e => {
cc.eventAnalytics('custom_category', 'custom_action');
cc.eventAnalytics('custom_action');
cc.eventFacebook('custom_action_name');
cc.eventTwitter('conversion_id');
cc.eventTaboola('custom_event_name');
cc.eventMatch2one('id=xxxxxx&seg=xxxxxx');
cc.eventLinkedin('conversion_id');
cc.eventEtracker('custom_category', 'custom_action');
cc.eventEtracker('custom_action');
e.preventDefault();
});
});
```