|
// To get started, make sure you're using the latest version of the analytics.js snippet (4.1.0 or above) |
|
// and remove the `analytics.load("YOUR_WRITE_KEY")` call. then drop this script below the snippet. |
|
|
|
// this is a standalone script for modern browsers. if you'd like to target older browsers, include |
|
// a fetch polyfill and use that instead of window.fetch. This would require that you build and package the |
|
// script somehow (rollup, webpack, browserify, etc). You may also want to transpile it to ES5 w eg Babel. |
|
|
|
// This script is configured to make all collection opt-in (rather than opt-out) for all users. |
|
// If you want to conditionally require whether or not to block data collection before affirmative consent, use something |
|
// like inEU below and pass that function into `conditionallyLoadAnalytics`. If you want to collect data until the user |
|
// opts out, just change OPT_IN to false. |
|
|
|
// import fetch from 'isomorphic-fetch' |
|
// import inEU from '@segment/in-eu' |
|
|
|
// CONFIG — EDIT ME! |
|
// CONFIG — EDIT ME! |
|
const OPT_IN = true; // false = only disable after opt out, can replace with a function such as inEU above |
|
const YOUR_DOMAIN = "domain.com"; // the hostname of your website |
|
const WEBSITE_WRITE_KEY = "sNOks4InIbuBaPcSQa76ny0neogp6yDf"; // your segment website source write key |
|
const SEGMENT_MAPPING = 1; |
|
const OTHER_WRITE_KEYS = []; // any other sources w destinations that you want to include in config |
|
|
|
// gets enabled destination configurations from across your source (and optionally other sources in workspace) |
|
fetchDestinations([WEBSITE_WRITE_KEY, ...OTHER_WRITE_KEYS]).then( |
|
destinations => { |
|
// KEY LOGIC HERE |
|
// eg. could instead use trustArc's other APIs such as getConsentCategories |
|
// and map between the returned vendor domains to specific Segment Integrationss, etc |
|
// review this logic carefully, and edit per your business requirements |
|
// |
|
// in this case, we go with all when customer provides full consent |
|
// remove Advertising tools when the customer provides functional |
|
// and mark Segment as required (for data to flow to warehouse etc— if you make that clear in your policy) |
|
// note that though Segment will receive data we wont forward (even server side) to other sources because we |
|
// decorate the calls with the full set of integrations you have enabled with "false." |
|
// |
|
// no preference here goes with all on — you may want to flip that! |
|
const { consentDecision } = truste.cma.callApi( |
|
"getGDPRConsentDecision", |
|
YOUR_DOMAIN |
|
); |
|
|
|
const destinationPreferences = destinations |
|
.map(function(dest) { |
|
if (consentDecision.includes(3)) return { [dest.id]: true }; |
|
if (consentDecision.includes(2)) return { [dest.id]: dest.category === "Advertising" ? false : true }; |
|
if (consentDecision.includes(1)) return { [dest.id]: false }; |
|
if (consentDecision.includes(0)) return { [dest.id]: !OPT_IN }; |
|
}) |
|
.reduce( |
|
(acc, val) => { |
|
return { |
|
...val, |
|
...acc |
|
}; |
|
}, |
|
{ "Segment.io": consentDecision.some(d => d === SEGMENT_MAPPING) } |
|
); |
|
|
|
conditionallyLoadAnalytics({ |
|
writeKey: WEBSITE_WRITE_KEY, |
|
destinations, |
|
destinationPreferences, |
|
isConsentRequired: OPT_IN |
|
}); |
|
} |
|
); |
|
|
|
// instructs trustarc to notify on changes to consent |
|
window.top.postMessage(JSON.stringify({ |
|
PrivacyManagerAPI: { |
|
action: "getConsent", |
|
timestamp: new Date().getTime(), |
|
self: YOUR_DOMAIN |
|
} |
|
}), "*"); |
|
|
|
// registers listener for consent changes. |
|
// some care has been taken here to handle messages safely. |
|
// will reload window if they've "denied"... to reset tracking. |
|
window.addEventListener("message", function reload(e) { |
|
let data = e.data; |
|
if (typeof data === "string") { |
|
try { |
|
data = JSON.parse(data); |
|
} catch (e) { /* weird message, bail */} |
|
} |
|
if ( |
|
data && |
|
data.PrivacyManagerAPI && |
|
data.PrivacyManagerAPI.consent === "denied" |
|
) { |
|
return window.location.reload(); |
|
} |
|
// otherwise approved... carry on! |
|
}, false); |
|
|
|
// helper functions below... |
|
function conditionallyLoadAnalytics({ |
|
writeKey, |
|
destinations, |
|
destinationPreferences, |
|
isConsentRequired, |
|
shouldReload = true // change if you dont want to reload on consent changes |
|
}) { |
|
let isAnythingEnabled = false; |
|
|
|
if (!destinationPreferences) { |
|
if (isConsentRequired) { |
|
return; |
|
} |
|
|
|
// Load a.js normally when consent isn't required and there's no preferences |
|
if (!window.analytics.initialized) { |
|
window.analytics.load(writeKey); |
|
} |
|
return; |
|
} |
|
|
|
for (const destination of destinationPreferences) { |
|
const isEnabled = destinationPreferences[destination]; |
|
if (isEnabled) { |
|
isAnythingEnabled = true; |
|
} |
|
} |
|
|
|
// Reload the page if the trackers have already been initialized so that |
|
// the user's new preferences can take effect |
|
if (window.analytics.initialized) { |
|
if (shouldReload) { |
|
window.location.reload(); |
|
} |
|
return; |
|
} |
|
|
|
// Don't load a.js at all if nothing has been enabled |
|
if (isAnythingEnabled) { |
|
window.analytics.load(writeKey, { integrations: destinationPreferences }); |
|
} |
|
} |
|
|
|
async function fetchDestinationForWriteKey(writeKey) { |
|
const res = await window.fetch( |
|
`https://cdn.segment.com/v1/projects/${writeKey}/integrations` |
|
); |
|
|
|
if (!res.ok) { |
|
throw new Error( |
|
`Failed to fetch integrations for write key ${writeKey}: HTTP ${ |
|
res.status |
|
} ${res.statusText}` |
|
); |
|
} |
|
|
|
const destinations = await res.json(); |
|
|
|
// Rename creationName to id to abstract the weird data model |
|
for (const destination of destinations) { |
|
destination.id = destination.creationName; |
|
delete destination.creationName; |
|
} |
|
|
|
return destinations; |
|
} |
|
|
|
async function fetchDestinations(...writeKeys) { |
|
const destinationsRequests = []; |
|
for (const writeKey of writeKeys) { |
|
destinationsRequests.push(fetchDestinationForWriteKey(writeKey)); |
|
} |
|
|
|
let destinations = await Promise.all(destinationsRequests); |
|
|
|
// unique list of destination across all sources |
|
destinations = [ |
|
...destinations |
|
.reduce((a, b) => a.concat(b), []) // flatten multi-d array |
|
.reduce((map, item) => { |
|
if (d.id === "Repeater") return map; // remove repeater |
|
map.has(item["id"]) || map.set(item["id"], item); |
|
return map; |
|
}, new Map()) // return object |
|
.values() |
|
]; |
|
|
|
return destinations; |
|
} |