@phantomstudios/ft-lib 0.5.2-rc8 → 0.5.3
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/lib/cmp/vendorConsent.d.ts +1 -17
- package/lib/cmp/vendorConsent.d.ts.map +1 -1
- package/lib/cmp/vendorConsent.js +57 -31
- package/lib/cmp/vendorConsent.js.map +1 -1
- package/lib/consentMonitor/index.d.ts +1 -1
- package/lib/consentMonitor/index.d.ts.map +1 -1
- package/lib/consentMonitor/index.js +7 -20
- package/lib/consentMonitor/index.js.map +1 -1
- package/package.json +1 -1
- package/src/cmp/vendorConsent.ts +69 -62
- package/src/consentMonitor/index.ts +9 -23
- package/src/types.d.ts +28 -1
|
@@ -1,25 +1,9 @@
|
|
|
1
|
-
declare global {
|
|
2
|
-
interface Window {
|
|
3
|
-
__tcfapi?: (command: string, version: number, callback: (tcData: TCFData | null, success: boolean) => void, parameter?: any) => void;
|
|
4
|
-
}
|
|
5
|
-
}
|
|
6
|
-
interface TCFData {
|
|
7
|
-
eventStatus: "tcloaded" | "useractioncomplete" | "cmpuishown";
|
|
8
|
-
listenerId: number;
|
|
9
|
-
purpose?: {
|
|
10
|
-
consents?: Record<number, boolean>;
|
|
11
|
-
};
|
|
12
|
-
vendor?: {
|
|
13
|
-
consents?: Record<number, boolean>;
|
|
14
|
-
};
|
|
15
|
-
gdprApplies?: boolean | undefined;
|
|
16
|
-
}
|
|
17
1
|
export interface VendorConsentResults {
|
|
18
2
|
brandmetrics: boolean;
|
|
19
3
|
linkedIn: boolean;
|
|
20
4
|
purpose1: boolean;
|
|
21
5
|
gdprApplies: boolean;
|
|
6
|
+
usnatApplies: boolean;
|
|
22
7
|
}
|
|
23
8
|
export declare function initVendorConsentListener(onConsentUpdate: (results: VendorConsentResults) => void): void;
|
|
24
|
-
export {};
|
|
25
9
|
//# sourceMappingURL=vendorConsent.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vendorConsent.d.ts","sourceRoot":"","sources":["../../src/cmp/vendorConsent.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"vendorConsent.d.ts","sourceRoot":"","sources":["../../src/cmp/vendorConsent.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;IACrB,YAAY,EAAE,OAAO,CAAC;CACvB;AAED,wBAAgB,yBAAyB,CACvC,eAAe,EAAE,CAAC,OAAO,EAAE,oBAAoB,KAAK,IAAI,GACvD,IAAI,CAqDN"}
|
package/lib/cmp/vendorConsent.js
CHANGED
|
@@ -2,38 +2,64 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.initVendorConsentListener = initVendorConsentListener;
|
|
4
4
|
function initVendorConsentListener(onConsentUpdate) {
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
// --- Path 1: Check TCF API First ---
|
|
6
|
+
if (typeof window.__tcfapi === "function") {
|
|
7
|
+
window.__tcfapi("addEventListener", 2, function (tcData, success) {
|
|
8
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
9
|
+
if (!success || !tcData)
|
|
10
|
+
return;
|
|
11
|
+
var isConsentReady = tcData.eventStatus === "tcloaded" ||
|
|
12
|
+
tcData.eventStatus === "useractioncomplete";
|
|
13
|
+
if (isConsentReady) {
|
|
14
|
+
// If GDPR actually applies, process TCF consents (Opt-In model)
|
|
15
|
+
if (tcData.gdprApplies !== false) {
|
|
16
|
+
var results = {
|
|
17
|
+
brandmetrics: ((_b = (_a = tcData.vendor) === null || _a === void 0 ? void 0 : _a.consents) === null || _b === void 0 ? void 0 : _b[422]) === true,
|
|
18
|
+
linkedIn: ((_d = (_c = tcData.vendor) === null || _c === void 0 ? void 0 : _c.consents) === null || _d === void 0 ? void 0 : _d[804]) === true,
|
|
19
|
+
purpose1: ((_f = (_e = tcData.purpose) === null || _e === void 0 ? void 0 : _e.consents) === null || _f === void 0 ? void 0 : _f[1]) === true,
|
|
20
|
+
gdprApplies: true,
|
|
21
|
+
usnatApplies: false,
|
|
22
|
+
};
|
|
23
|
+
onConsentUpdate(results);
|
|
24
|
+
// Clean up TCF listener
|
|
25
|
+
if (typeof tcData.listenerId === "number") {
|
|
26
|
+
(_g = window.__tcfapi) === null || _g === void 0 ? void 0 : _g.call(window, "removeEventListener", 2, function () { }, tcData.listenerId);
|
|
27
|
+
}
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
// --- Path 2: TCF explicitly returned gdprApplies: false -> Fall back to USNat ---
|
|
31
|
+
evaluateUSNatConsent(onConsentUpdate);
|
|
32
|
+
// Clean up TCF listener since GDPR does not apply
|
|
33
|
+
if (typeof tcData.listenerId === "number") {
|
|
34
|
+
(_h = window.__tcfapi) === null || _h === void 0 ? void 0 : _h.call(window, "removeEventListener", 2, function () { }, tcData.listenerId);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
// If __tcfapi doesn't exist at all, try evaluating USNat directly
|
|
41
|
+
evaluateUSNatConsent(onConsentUpdate);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Helper to evaluate USNat / US Privacy consent (Opt-Out model)
|
|
46
|
+
*/
|
|
47
|
+
function evaluateUSNatConsent(onConsentUpdate) {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
// Option A: Preferred Sourcepoint SDK method
|
|
50
|
+
if (typeof ((_b = (_a = window._sp_) === null || _a === void 0 ? void 0 : _a.usnat) === null || _b === void 0 ? void 0 : _b.getUserConsents) === "function") {
|
|
51
|
+
window._sp_.usnat.getUserConsents(function (consents) {
|
|
52
|
+
// US Privacy is OPT-OUT by default, categories[1] = cookies
|
|
53
|
+
var isConsented = consents === null || consents === void 0 ? void 0 : consents.categories[1].consented;
|
|
54
|
+
onConsentUpdate({
|
|
55
|
+
brandmetrics: isConsented,
|
|
56
|
+
linkedIn: isConsented,
|
|
57
|
+
purpose1: isConsented,
|
|
58
|
+
gdprApplies: false,
|
|
59
|
+
usnatApplies: true,
|
|
60
|
+
});
|
|
61
|
+
});
|
|
7
62
|
return;
|
|
8
63
|
}
|
|
9
|
-
window.__tcfapi("addEventListener", 2, function (tcData, success) {
|
|
10
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
11
|
-
if (!success || !tcData)
|
|
12
|
-
return;
|
|
13
|
-
// Handles BOTH returning visitors ('tcloaded') AND live banner accepts ('useractioncomplete')
|
|
14
|
-
var isConsentReady = tcData.eventStatus === "tcloaded" ||
|
|
15
|
-
tcData.eventStatus === "useractioncomplete";
|
|
16
|
-
if (isConsentReady) {
|
|
17
|
-
// Brandmetrics (IAB Vendor ID: 422)
|
|
18
|
-
var brandmetrics = ((_b = (_a = tcData.vendor) === null || _a === void 0 ? void 0 : _a.consents) === null || _b === void 0 ? void 0 : _b[422]) === true;
|
|
19
|
-
// LinkedIn (IAB Vendor ID: 804)
|
|
20
|
-
var linkedIn = ((_d = (_c = tcData.vendor) === null || _c === void 0 ? void 0 : _c.consents) === null || _d === void 0 ? void 0 : _d[804]) === true;
|
|
21
|
-
// Purpose 1: Device storage/access (Cookies)
|
|
22
|
-
var purpose1 = ((_f = (_e = tcData.purpose) === null || _e === void 0 ? void 0 : _e.consents) === null || _f === void 0 ? void 0 : _f[1]) === true;
|
|
23
|
-
var gdprApplies = tcData.gdprApplies !== false ? true : false;
|
|
24
|
-
var results = {
|
|
25
|
-
brandmetrics: brandmetrics,
|
|
26
|
-
linkedIn: linkedIn,
|
|
27
|
-
purpose1: purpose1,
|
|
28
|
-
gdprApplies: gdprApplies,
|
|
29
|
-
};
|
|
30
|
-
// Trigger your callback handler with the updated states
|
|
31
|
-
onConsentUpdate(results);
|
|
32
|
-
// Clean up listener after handling the event
|
|
33
|
-
if (typeof tcData.listenerId === "number") {
|
|
34
|
-
(_g = window.__tcfapi) === null || _g === void 0 ? void 0 : _g.call(window, "removeEventListener", 2, function () { }, tcData.listenerId);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
64
|
}
|
|
39
65
|
//# sourceMappingURL=vendorConsent.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vendorConsent.js","sourceRoot":"","sources":["../../src/cmp/vendorConsent.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"vendorConsent.js","sourceRoot":"","sources":["../../src/cmp/vendorConsent.ts"],"names":[],"mappings":";;AAQA,8DAuDC;AAvDD,SAAgB,yBAAyB,CACvC,eAAwD;IAExD,sCAAsC;IACtC,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,EAAE,UAAC,MAAM,EAAE,OAAO;;YACrD,IAAI,CAAC,OAAO,IAAI,CAAC,MAAM;gBAAE,OAAO;YAEhC,IAAM,cAAc,GAClB,MAAM,CAAC,WAAW,KAAK,UAAU;gBACjC,MAAM,CAAC,WAAW,KAAK,oBAAoB,CAAC;YAE9C,IAAI,cAAc,EAAE,CAAC;gBACnB,gEAAgE;gBAChE,IAAI,MAAM,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;oBACjC,IAAM,OAAO,GAAyB;wBACpC,YAAY,EAAE,CAAA,MAAA,MAAA,MAAM,CAAC,MAAM,0CAAE,QAAQ,0CAAG,GAAG,CAAC,MAAK,IAAI;wBACrD,QAAQ,EAAE,CAAA,MAAA,MAAA,MAAM,CAAC,MAAM,0CAAE,QAAQ,0CAAG,GAAG,CAAC,MAAK,IAAI;wBACjD,QAAQ,EAAE,CAAA,MAAA,MAAA,MAAM,CAAC,OAAO,0CAAE,QAAQ,0CAAG,CAAC,CAAC,MAAK,IAAI;wBAChD,WAAW,EAAE,IAAI;wBACjB,YAAY,EAAE,KAAK;qBACpB,CAAC;oBAEF,eAAe,CAAC,OAAO,CAAC,CAAC;oBAEzB,wBAAwB;oBACxB,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;wBAC1C,MAAA,MAAM,CAAC,QAAQ,uDACb,qBAAqB,EACrB,CAAC,EACD,cAAO,CAAC,EACR,MAAM,CAAC,UAAU,CAClB,CAAC;oBACJ,CAAC;oBACD,OAAO;gBACT,CAAC;gBAED,mFAAmF;gBACnF,oBAAoB,CAAC,eAAe,CAAC,CAAC;gBAEtC,kDAAkD;gBAClD,IAAI,OAAO,MAAM,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;oBAC1C,MAAA,MAAM,CAAC,QAAQ,uDACb,qBAAqB,EACrB,CAAC,EACD,cAAO,CAAC,EACR,MAAM,CAAC,UAAU,CAClB,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC;SAAM,CAAC;QACN,kEAAkE;QAClE,oBAAoB,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,SAAS,oBAAoB,CAC3B,eAAwD;;IAExD,6CAA6C;IAC7C,IAAI,OAAO,CAAA,MAAA,MAAA,MAAM,CAAC,IAAI,0CAAE,KAAK,0CAAE,eAAe,CAAA,KAAK,UAAU,EAAE,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,UAAC,QAAQ;YACzC,4DAA4D;YAC5D,IAAM,WAAW,GAAG,QAAQ,aAAR,QAAQ,uBAAR,QAAQ,CAAE,UAAU,CAAC,CAAC,EAAE,SAAS,CAAC;YAEtD,eAAe,CAAC;gBACd,YAAY,EAAE,WAAW;gBACzB,QAAQ,EAAE,WAAW;gBACrB,QAAQ,EAAE,WAAW;gBACrB,WAAW,EAAE,KAAK;gBAClB,YAAY,EAAE,IAAI;aACnB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QACH,OAAO;IACT,CAAC;AACH,CAAC"}
|
|
@@ -19,7 +19,7 @@ export declare class ConsentMonitor {
|
|
|
19
19
|
private enablePermutive;
|
|
20
20
|
private disablePermutive;
|
|
21
21
|
cookieConsentTest: () => void;
|
|
22
|
-
setDevConsentCookies: () => void;
|
|
22
|
+
setDevConsentCookies: (allow: boolean) => void;
|
|
23
23
|
}
|
|
24
24
|
export { ConsentMonitor as consentMonitor };
|
|
25
25
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consentMonitor/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAwB9B,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/consentMonitor/index.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,oBAAoB,EACrB,MAAM,sBAAsB,CAAC;AAwB9B,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAW;IAC5B,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAMG;IAE1B,IAAW,OAAO,IAAI,OAAO,CAE5B;IACD,IAAW,QAAQ,IAAI,MAAM,EAAE,CAE9B;IACD,IAAW,gBAAgB,IAAI,OAAO,CAErC;IACD,IAAW,aAAa,IAAI,OAAO,CAElC;IACD,IAAW,gBAAgB,IAAI,OAAO,CAErC;IACD,IAAW,cAAc,IAAI,oBAAoB,CAEhD;IAED,cAAc,GAAI,MAAM,MAAM,YACgD;gBAElE,QAAQ,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM;IA4B3D,OAAO,CAAC,4BAA4B;IAapC,OAAO,CAAC,kBAAkB;IAkC1B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,gBAAgB;IAQxB,iBAAiB,aAcf;IAEF,oBAAoB,GAAI,OAAO,OAAO,UAMpC;CACH;AAED,OAAO,EAAE,cAAc,IAAI,cAAc,EAAE,CAAC"}
|
|
@@ -31,6 +31,8 @@ var ConsentMonitor = /** @class */ (function () {
|
|
|
31
31
|
brandmetrics: false,
|
|
32
32
|
linkedIn: false,
|
|
33
33
|
purpose1: false,
|
|
34
|
+
gdprApplies: false,
|
|
35
|
+
usnatApplies: false,
|
|
34
36
|
};
|
|
35
37
|
this.getCookieValue = function (name) { var _a; return ((_a = document.cookie.match("(^|;)\\s*" + name + "\\s*=\\s*([^;]+)")) === null || _a === void 0 ? void 0 : _a.pop()) || ""; };
|
|
36
38
|
//check for FTConsent - cookiesOnSite to trigger custom consent_update event for GTM tags (banner updated)
|
|
@@ -51,12 +53,12 @@ var ConsentMonitor = /** @class */ (function () {
|
|
|
51
53
|
});
|
|
52
54
|
}
|
|
53
55
|
};
|
|
54
|
-
this.setDevConsentCookies = function () {
|
|
56
|
+
this.setDevConsentCookies = function (allow) {
|
|
55
57
|
_this._isDevEnvironment = true;
|
|
58
|
+
var value = allow ? "on" : "off";
|
|
56
59
|
debug("setting development FT consent cookies");
|
|
57
|
-
document.cookie =
|
|
58
|
-
|
|
59
|
-
document.cookie = "FTCookieConsentGDPR=true";
|
|
60
|
+
document.cookie = "FTConsent=behaviouraladsOnsite%3A".concat(value, "%2CcookiesOnsite%3A").concat(value, "%2CpermutiveadsOnsite%3A").concat(value);
|
|
61
|
+
document.cookie = "FTCookieConsentGDPR=".concat(allow);
|
|
60
62
|
};
|
|
61
63
|
if (Array.isArray(devHosts)) {
|
|
62
64
|
this._devHosts = __spreadArray(__spreadArray([], devHosts, true), DEFAULT_DEV_HOSTS, true);
|
|
@@ -76,13 +78,6 @@ var ConsentMonitor = /** @class */ (function () {
|
|
|
76
78
|
// load banner
|
|
77
79
|
(0, loadFtCmp_1.loadFtCmpScript)()
|
|
78
80
|
.then(function () {
|
|
79
|
-
// const propertyConfig = window.location.hostname.endsWith(".ft.com")
|
|
80
|
-
// ? properties["FT_DOTCOM_PROD"]
|
|
81
|
-
// : properties["FT_DOTCOM_TEST"];
|
|
82
|
-
// // initialize CMP
|
|
83
|
-
// initSourcepointCmp({ propertyConfig });
|
|
84
|
-
// // use cmp client lib to intercept footer 'Manage Cookies' links (opens privacy modal)
|
|
85
|
-
// // Note, function requires very specific link: text = 'Manage Cookies' and href = 'https://ft.com/preferences/manage-cookies'
|
|
86
81
|
(0, cmp_client_1.interceptManageCookiesLinks)();
|
|
87
82
|
_this._isInitialized = true;
|
|
88
83
|
})
|
|
@@ -154,13 +149,6 @@ var ConsentMonitor = /** @class */ (function () {
|
|
|
154
149
|
else {
|
|
155
150
|
_this.disablePermutive();
|
|
156
151
|
}
|
|
157
|
-
(0, vendorConsent_1.initVendorConsentListener)(function (vendorConsents) {
|
|
158
|
-
var vendorConsentEvent = new CustomEvent("cmp_vendorConsent", {
|
|
159
|
-
detail: vendorConsents,
|
|
160
|
-
});
|
|
161
|
-
window.dispatchEvent(vendorConsentEvent);
|
|
162
|
-
debug("[CMP Consent lookup Event", vendorConsents);
|
|
163
|
-
});
|
|
164
152
|
};
|
|
165
153
|
var onChoice = function (_l, _c, typeId) {
|
|
166
154
|
debug("onMessageChoiceSelect:", typeId);
|
|
@@ -171,8 +159,7 @@ var ConsentMonitor = /** @class */ (function () {
|
|
|
171
159
|
//Simulate cookie consent behaviour in non-prod environments as banner does not set cookies in non .ft.com domains
|
|
172
160
|
_this._devHosts.map(function (devHost) {
|
|
173
161
|
return _this._hostname.includes(devHost) &&
|
|
174
|
-
typeId === CMP_CHOICE_ACCEPT_ALL
|
|
175
|
-
_this.setDevConsentCookies();
|
|
162
|
+
_this.setDevConsentCookies(typeId === CMP_CHOICE_ACCEPT_ALL ? true : false);
|
|
176
163
|
});
|
|
177
164
|
// banner updated - check new cookie value to fire consent_update event
|
|
178
165
|
setTimeout(_this.cookieConsentTest, 3000);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consentMonitor/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,0DAA0E;AAC1E,gDAA0B;AAE1B,8CAAuE;AACvE,sDAG8B;AAE9B,IAAM,KAAK,GAAG,IAAA,eAAK,EAAC,uCAAuC,CAAC,CAAC;AAE7D,IAAM,iBAAiB,GAAG,CAAC,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;AAiBlE,IAAM,qBAAqB,GAAG,EAAE,CAAC;AACjC,IAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/consentMonitor/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phantomstudios/ft-lib",
|
|
3
3
|
"description": "A collection of Javascript UI & tracking utils for FT sites",
|
|
4
|
-
"version": "0.5.
|
|
4
|
+
"version": "0.5.3",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
7
7
|
"homepage": "https://github.com/phantomstudios/ft-lib#readme",
|
package/src/cmp/vendorConsent.ts
CHANGED
|
@@ -1,81 +1,88 @@
|
|
|
1
|
-
// 1. Declare Global Window Types for TCF API
|
|
2
|
-
declare global {
|
|
3
|
-
interface Window {
|
|
4
|
-
__tcfapi?: (
|
|
5
|
-
command: string,
|
|
6
|
-
version: number,
|
|
7
|
-
callback: (tcData: TCFData | null, success: boolean) => void,
|
|
8
|
-
parameter?: any,
|
|
9
|
-
) => void;
|
|
10
|
-
}
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
interface TCFData {
|
|
14
|
-
eventStatus: "tcloaded" | "useractioncomplete" | "cmpuishown";
|
|
15
|
-
listenerId: number;
|
|
16
|
-
purpose?: {
|
|
17
|
-
consents?: Record<number, boolean>;
|
|
18
|
-
};
|
|
19
|
-
vendor?: {
|
|
20
|
-
consents?: Record<number, boolean>;
|
|
21
|
-
};
|
|
22
|
-
gdprApplies?: boolean | undefined;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
1
|
export interface VendorConsentResults {
|
|
26
2
|
brandmetrics: boolean;
|
|
27
3
|
linkedIn: boolean;
|
|
28
4
|
purpose1: boolean;
|
|
29
5
|
gdprApplies: boolean;
|
|
6
|
+
usnatApplies: boolean;
|
|
30
7
|
}
|
|
31
8
|
|
|
32
9
|
export function initVendorConsentListener(
|
|
33
10
|
onConsentUpdate: (results: VendorConsentResults) => void,
|
|
34
11
|
): void {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
window.__tcfapi("addEventListener", 2, (tcData, success) => {
|
|
41
|
-
if (!success || !tcData) return;
|
|
12
|
+
// --- Path 1: Check TCF API First ---
|
|
13
|
+
if (typeof window.__tcfapi === "function") {
|
|
14
|
+
window.__tcfapi("addEventListener", 2, (tcData, success) => {
|
|
15
|
+
if (!success || !tcData) return;
|
|
42
16
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
tcData.eventStatus === "useractioncomplete";
|
|
17
|
+
const isConsentReady =
|
|
18
|
+
tcData.eventStatus === "tcloaded" ||
|
|
19
|
+
tcData.eventStatus === "useractioncomplete";
|
|
47
20
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
21
|
+
if (isConsentReady) {
|
|
22
|
+
// If GDPR actually applies, process TCF consents (Opt-In model)
|
|
23
|
+
if (tcData.gdprApplies !== false) {
|
|
24
|
+
const results: VendorConsentResults = {
|
|
25
|
+
brandmetrics: tcData.vendor?.consents?.[422] === true,
|
|
26
|
+
linkedIn: tcData.vendor?.consents?.[804] === true,
|
|
27
|
+
purpose1: tcData.purpose?.consents?.[1] === true,
|
|
28
|
+
gdprApplies: true,
|
|
29
|
+
usnatApplies: false,
|
|
30
|
+
};
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
const linkedIn = tcData.vendor?.consents?.[804] === true;
|
|
32
|
+
onConsentUpdate(results);
|
|
54
33
|
|
|
55
|
-
|
|
56
|
-
|
|
34
|
+
// Clean up TCF listener
|
|
35
|
+
if (typeof tcData.listenerId === "number") {
|
|
36
|
+
window.__tcfapi?.(
|
|
37
|
+
"removeEventListener",
|
|
38
|
+
2,
|
|
39
|
+
() => {},
|
|
40
|
+
tcData.listenerId,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
57
45
|
|
|
58
|
-
|
|
46
|
+
// --- Path 2: TCF explicitly returned gdprApplies: false -> Fall back to USNat ---
|
|
47
|
+
evaluateUSNatConsent(onConsentUpdate);
|
|
59
48
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
49
|
+
// Clean up TCF listener since GDPR does not apply
|
|
50
|
+
if (typeof tcData.listenerId === "number") {
|
|
51
|
+
window.__tcfapi?.(
|
|
52
|
+
"removeEventListener",
|
|
53
|
+
2,
|
|
54
|
+
() => {},
|
|
55
|
+
tcData.listenerId,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
} else {
|
|
61
|
+
// If __tcfapi doesn't exist at all, try evaluating USNat directly
|
|
62
|
+
evaluateUSNatConsent(onConsentUpdate);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
66
65
|
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Helper to evaluate USNat / US Privacy consent (Opt-Out model)
|
|
68
|
+
*/
|
|
69
|
+
function evaluateUSNatConsent(
|
|
70
|
+
onConsentUpdate: (results: VendorConsentResults) => void,
|
|
71
|
+
): void {
|
|
72
|
+
// Option A: Preferred Sourcepoint SDK method
|
|
73
|
+
if (typeof window._sp_?.usnat?.getUserConsents === "function") {
|
|
74
|
+
window._sp_.usnat.getUserConsents((consents) => {
|
|
75
|
+
// US Privacy is OPT-OUT by default, categories[1] = cookies
|
|
76
|
+
const isConsented = consents?.categories[1].consented;
|
|
69
77
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
});
|
|
78
|
+
onConsentUpdate({
|
|
79
|
+
brandmetrics: isConsented,
|
|
80
|
+
linkedIn: isConsented,
|
|
81
|
+
purpose1: isConsented,
|
|
82
|
+
gdprApplies: false,
|
|
83
|
+
usnatApplies: true,
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
81
88
|
}
|
|
@@ -39,6 +39,8 @@ export class ConsentMonitor {
|
|
|
39
39
|
brandmetrics: false,
|
|
40
40
|
linkedIn: false,
|
|
41
41
|
purpose1: false,
|
|
42
|
+
gdprApplies: false,
|
|
43
|
+
usnatApplies: false,
|
|
42
44
|
} as VendorConsentResults;
|
|
43
45
|
|
|
44
46
|
public get consent(): boolean {
|
|
@@ -84,14 +86,6 @@ export class ConsentMonitor {
|
|
|
84
86
|
// load banner
|
|
85
87
|
loadFtCmpScript()
|
|
86
88
|
.then(() => {
|
|
87
|
-
// const propertyConfig = window.location.hostname.endsWith(".ft.com")
|
|
88
|
-
// ? properties["FT_DOTCOM_PROD"]
|
|
89
|
-
// : properties["FT_DOTCOM_TEST"];
|
|
90
|
-
|
|
91
|
-
// // initialize CMP
|
|
92
|
-
// initSourcepointCmp({ propertyConfig });
|
|
93
|
-
// // use cmp client lib to intercept footer 'Manage Cookies' links (opens privacy modal)
|
|
94
|
-
// // Note, function requires very specific link: text = 'Manage Cookies' and href = 'https://ft.com/preferences/manage-cookies'
|
|
95
89
|
interceptManageCookiesLinks();
|
|
96
90
|
this._isInitialized = true;
|
|
97
91
|
})
|
|
@@ -121,15 +115,6 @@ export class ConsentMonitor {
|
|
|
121
115
|
} else {
|
|
122
116
|
this.disablePermutive();
|
|
123
117
|
}
|
|
124
|
-
|
|
125
|
-
initVendorConsentListener((vendorConsents: VendorConsentResults) => {
|
|
126
|
-
const vendorConsentEvent = new CustomEvent("cmp_vendorConsent", {
|
|
127
|
-
detail: vendorConsents,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
window.dispatchEvent(vendorConsentEvent);
|
|
131
|
-
debug("[CMP Consent lookup Event", vendorConsents);
|
|
132
|
-
});
|
|
133
118
|
};
|
|
134
119
|
|
|
135
120
|
const onChoice: MessageChoiceHandler = (_l, _c, typeId) => {
|
|
@@ -141,8 +126,9 @@ export class ConsentMonitor {
|
|
|
141
126
|
this._devHosts.map(
|
|
142
127
|
(devHost) =>
|
|
143
128
|
this._hostname.includes(devHost) &&
|
|
144
|
-
|
|
145
|
-
|
|
129
|
+
this.setDevConsentCookies(
|
|
130
|
+
typeId === CMP_CHOICE_ACCEPT_ALL ? true : false,
|
|
131
|
+
),
|
|
146
132
|
);
|
|
147
133
|
|
|
148
134
|
// banner updated - check new cookie value to fire consent_update event
|
|
@@ -188,12 +174,12 @@ export class ConsentMonitor {
|
|
|
188
174
|
}
|
|
189
175
|
};
|
|
190
176
|
|
|
191
|
-
setDevConsentCookies = () => {
|
|
177
|
+
setDevConsentCookies = (allow: boolean) => {
|
|
192
178
|
this._isDevEnvironment = true;
|
|
179
|
+
const value = allow ? "on" : "off";
|
|
193
180
|
debug("setting development FT consent cookies");
|
|
194
|
-
document.cookie =
|
|
195
|
-
|
|
196
|
-
document.cookie = "FTCookieConsentGDPR=true";
|
|
181
|
+
document.cookie = `FTConsent=behaviouraladsOnsite%3A${value}%2CcookiesOnsite%3A${value}%2CpermutiveadsOnsite%3A${value}`;
|
|
182
|
+
document.cookie = `FTCookieConsentGDPR=${allow}`;
|
|
197
183
|
};
|
|
198
184
|
}
|
|
199
185
|
|
package/src/types.d.ts
CHANGED
|
@@ -15,17 +15,44 @@ declare module "@financial-times/cmp-client" {
|
|
|
15
15
|
propertyConfig?: any;
|
|
16
16
|
}): Promise<void>;
|
|
17
17
|
}
|
|
18
|
+
|
|
19
|
+
interface TCFData {
|
|
20
|
+
eventStatus: "tcloaded" | "useractioncomplete" | "cmpuishown";
|
|
21
|
+
listenerId: number;
|
|
22
|
+
purpose?: {
|
|
23
|
+
consents?: Record<number, boolean>;
|
|
24
|
+
};
|
|
25
|
+
vendor?: {
|
|
26
|
+
consents?: Record<number, boolean>;
|
|
27
|
+
};
|
|
28
|
+
gdprApplies?: boolean | undefined;
|
|
29
|
+
}
|
|
18
30
|
interface SourcepointCmpAPI {
|
|
19
31
|
queue?: Array<() => void>;
|
|
20
32
|
addEventListener?: (
|
|
21
33
|
eventName: string,
|
|
22
|
-
|
|
23
34
|
callback: (...args: any[]) => void,
|
|
24
35
|
) => void;
|
|
36
|
+
usnat?: {
|
|
37
|
+
getUserConsents: (callback: (consents: any) => void) => void;
|
|
38
|
+
};
|
|
25
39
|
}
|
|
40
|
+
|
|
26
41
|
interface Window {
|
|
27
42
|
_sp_: SourcepointCmpAPI;
|
|
28
43
|
_sp_queue?: Array<() => void>;
|
|
44
|
+
__tcfapi?: (
|
|
45
|
+
command: string,
|
|
46
|
+
version: number,
|
|
47
|
+
callback: (tcData: TCFData | null, success: boolean) => void,
|
|
48
|
+
parameter?: any,
|
|
49
|
+
) => void;
|
|
50
|
+
__gpp?: (
|
|
51
|
+
command: string,
|
|
52
|
+
callback?: (data: any, success: boolean) => void,
|
|
53
|
+
parameter?: any,
|
|
54
|
+
) => void;
|
|
55
|
+
|
|
29
56
|
dataLayer: any;
|
|
30
57
|
gtag: any;
|
|
31
58
|
permutive: {
|