@phantomstudios/ft-lib
Version:
A collection of Javascript UI & tracking utils for FT sites
169 lines (143 loc) • 5.03 kB
text/typescript
import {
initSourcepointCmp,
interceptManageCookiesLinks,
properties,
} from "@financial-times/cmp-client";
import Debug from "debug";
import { enqueueCmpCallback, loadFtCmpScript } from "../cmp/loadFtCmp";
const debug = Debug("@phantomstudios/ft-lib/consentMonitor");
const DEFAULT_DEV_HOSTS = ["localhost", "phq", ".app", "preview"];
interface ConsentReadyInfo {
consentedToAll: boolean;
}
type ConsentReadyHandler = (
legislation: string,
uuid: string,
tcData: unknown,
info: ConsentReadyInfo,
) => void;
type MessageChoiceHandler = (
legislation: string,
choiceId: number,
choiceTypeId: number,
) => void;
const CMP_CHOICE_ACCEPT_ALL = 11;
const CMP_CHOICE_REJECT_ALL = 13;
export class ConsentMonitor {
private _consent = false;
private _devHosts: string[];
private _isDevEnvironment = false;
private _isInitialized = false;
private _hostname: string;
public get consent(): boolean {
return this._consent;
}
public get devHosts(): string[] {
return this._devHosts;
}
public get isDevEnvironment(): boolean {
return this._isDevEnvironment;
}
public get isInitialized(): boolean {
return this._isInitialized;
}
public get userHasConsented(): boolean {
return this._consent;
}
getCookieValue = (name: string) =>
document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)")?.pop() || "";
constructor(hostname?: string, devHosts?: string[] | string) {
if (Array.isArray(devHosts)) {
this._devHosts = [...devHosts, ...DEFAULT_DEV_HOSTS];
} else if (devHosts === undefined) {
this._devHosts = [...DEFAULT_DEV_HOSTS];
} else {
this._devHosts = [...DEFAULT_DEV_HOSTS, devHosts];
}
this._hostname = hostname || window.location.hostname;
this._isDevEnvironment = this._devHosts.some((h) =>
this._hostname.includes(h),
);
loadFtCmpScript()
.then(() => {
this.attachCmpListeners();
const propertyConfig = window.location.hostname.endsWith(".ft.com")
? properties["FT_DOTCOM_PROD"]
: properties["FT_DOTCOM_TEST"];
// initialize CMP
initSourcepointCmp({ propertyConfig });
// use cmp client lib to intercept footer 'Manage Cookies' links (opens privacy modal)
// Note, function requires very specific link: text = 'Manage Cookies' and href = 'https://ft.com/preferences/manage-cookies'
interceptManageCookiesLinks();
this._isInitialized = true;
})
.catch((err) => console.error(err));
}
private attachCmpListeners(): void {
enqueueCmpCallback(() => {
const onReady: ConsentReadyHandler = (_l, _u, _t, info) => {
debug("onConsentReady:", info);
if (info.consentedToAll) {
this.enablePermutive();
} else {
this.disablePermutive();
}
};
const onChoice: MessageChoiceHandler = (_l, _c, typeId) => {
debug("onMessageChoiceSelect:", typeId);
if (typeId === CMP_CHOICE_ACCEPT_ALL) this.enablePermutive();
else if (typeId === CMP_CHOICE_REJECT_ALL) this.disablePermutive();
//Simulate cookie consent behaviour in non-prod environments as banner does not set cookies in non .ft.com domains
this._devHosts.map(
(devHost) =>
this._hostname.includes(devHost) &&
typeId === CMP_CHOICE_ACCEPT_ALL &&
this.setDevConsentCookies(),
);
// banner updated - check new cookie value to fire consent_update event
setTimeout(this.cookieConsentTest, 3000);
};
window._sp_.addEventListener?.("onConsentReady", onReady);
window._sp_.addEventListener?.("onMessageChoiceSelect", onChoice);
});
}
private enablePermutive(): void {
if (this._consent) return;
debug("Permutive consent: ON");
window.permutive?.consent({
opt_in: true,
token: "behaviouraladsOnsite:on",
});
this._consent = true;
}
private disablePermutive(): void {
if (!this._consent) return;
debug("Permutive consent: OFF");
window.permutive?.consent({ opt_in: false });
this._consent = false;
}
//check for FTConsent - cookiesOnSite to trigger custom consent_update event for GTM tags (banner updated)
cookieConsentTest = () => {
if (!this._isInitialized || !window || !window.dataLayer) return;
if (this.getCookieValue("FTConsent").includes("cookiesOnsite%3Aon")) {
//send consent_update event
window.dataLayer.push({
event: "consent_update",
consent: true,
});
} else {
window.dataLayer.push({
event: "consent_update",
consent: false,
});
}
};
setDevConsentCookies = () => {
this._isDevEnvironment = true;
debug("setting development FT consent cookies");
document.cookie =
"FTConsent=behaviouraladsOnsite%3Aon%2CcookiesOnsite%3Aon%2CpermutiveadsOnsite%3Aon";
document.cookie = "FTCookieConsentGDPR=true";
};
}
export { ConsentMonitor as consentMonitor };