@financial-times/cmp-client 2.2.2 → 3.0.0

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.
Files changed (66) hide show
  1. package/README.md +154 -46
  2. package/dist/index.cjs +176 -121
  3. package/dist/index.js +176 -121
  4. package/dist/src/client.d.ts.map +1 -0
  5. package/dist/{consent-ready → src/consent-ready}/index.d.ts +1 -1
  6. package/dist/src/consent-ready/index.d.ts.map +1 -0
  7. package/dist/src/consent-ready/utils/__fixtures__/strings.d.ts.map +1 -0
  8. package/dist/src/consent-ready/utils/__tests__/check-consent.test.d.ts.map +1 -0
  9. package/dist/src/consent-ready/utils/__tests__/get-parsed-consent.test.d.ts.map +1 -0
  10. package/dist/src/consent-ready/utils/__tests__/validators.test.d.ts.map +1 -0
  11. package/dist/src/consent-ready/utils/get-consent-payload.d.ts.map +1 -0
  12. package/dist/src/consent-ready/utils/get-parsed-consent.d.ts.map +1 -0
  13. package/dist/src/consent-ready/utils/has-consent-changed.d.ts.map +1 -0
  14. package/dist/src/consent-ready/utils/validators.d.ts.map +1 -0
  15. package/dist/src/html/__tests__/cmp-footer-links.test.d.ts.map +1 -0
  16. package/dist/src/html/__tests__/cmp-scripts.test.d.ts +2 -0
  17. package/dist/src/html/__tests__/cmp-scripts.test.d.ts.map +1 -0
  18. package/dist/src/html/cmp-footer-link.d.ts.map +1 -0
  19. package/dist/src/html/cmp-scripts.d.ts +7 -0
  20. package/dist/src/html/cmp-scripts.d.ts.map +1 -0
  21. package/dist/{index.d.ts → src/index.d.ts} +1 -2
  22. package/dist/src/index.d.ts.map +1 -0
  23. package/dist/src/lib/constants.d.ts.map +1 -0
  24. package/dist/src/lib/debug.d.ts +15 -0
  25. package/dist/src/lib/debug.d.ts.map +1 -0
  26. package/dist/src/lib/properties.d.ts +94 -0
  27. package/dist/src/lib/properties.d.ts.map +1 -0
  28. package/dist/src/utils/dom.d.ts +3 -0
  29. package/dist/src/utils/dom.d.ts.map +1 -0
  30. package/package.json +1 -1
  31. package/typings/globals.d.ts +4 -12
  32. package/typings/types.d.ts +11 -1
  33. package/dist/client.d.ts.map +0 -1
  34. package/dist/consent-ready/index.d.ts.map +0 -1
  35. package/dist/consent-ready/utils/__fixtures__/strings.d.ts.map +0 -1
  36. package/dist/consent-ready/utils/__tests__/check-consent.test.d.ts.map +0 -1
  37. package/dist/consent-ready/utils/__tests__/get-parsed-consent.test.d.ts.map +0 -1
  38. package/dist/consent-ready/utils/__tests__/validators.test.d.ts.map +0 -1
  39. package/dist/consent-ready/utils/get-consent-payload.d.ts.map +0 -1
  40. package/dist/consent-ready/utils/get-parsed-consent.d.ts.map +0 -1
  41. package/dist/consent-ready/utils/has-consent-changed.d.ts.map +0 -1
  42. package/dist/consent-ready/utils/validators.d.ts.map +0 -1
  43. package/dist/html/__tests__/cmp-footer-links.test.d.ts.map +0 -1
  44. package/dist/html/cmp-footer-link.d.ts.map +0 -1
  45. package/dist/html/cmp-scripts.d.ts +0 -16
  46. package/dist/html/cmp-scripts.d.ts.map +0 -1
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/lib/constants.d.ts.map +0 -1
  49. package/dist/lib/debug.d.ts +0 -12
  50. package/dist/lib/debug.d.ts.map +0 -1
  51. package/dist/lib/properties.d.ts +0 -17
  52. package/dist/lib/properties.d.ts.map +0 -1
  53. package/dist/utils/dom.d.ts +0 -3
  54. package/dist/utils/dom.d.ts.map +0 -1
  55. /package/dist/{client.d.ts → src/client.d.ts} +0 -0
  56. /package/dist/{consent-ready → src/consent-ready}/utils/__fixtures__/strings.d.ts +0 -0
  57. /package/dist/{consent-ready → src/consent-ready}/utils/__tests__/check-consent.test.d.ts +0 -0
  58. /package/dist/{consent-ready → src/consent-ready}/utils/__tests__/get-parsed-consent.test.d.ts +0 -0
  59. /package/dist/{consent-ready → src/consent-ready}/utils/__tests__/validators.test.d.ts +0 -0
  60. /package/dist/{consent-ready → src/consent-ready}/utils/get-consent-payload.d.ts +0 -0
  61. /package/dist/{consent-ready → src/consent-ready}/utils/get-parsed-consent.d.ts +0 -0
  62. /package/dist/{consent-ready → src/consent-ready}/utils/has-consent-changed.d.ts +0 -0
  63. /package/dist/{consent-ready → src/consent-ready}/utils/validators.d.ts +0 -0
  64. /package/dist/{html → src/html}/__tests__/cmp-footer-links.test.d.ts +0 -0
  65. /package/dist/{html → src/html}/cmp-footer-link.d.ts +0 -0
  66. /package/dist/{lib → src/lib}/constants.d.ts +0 -0
package/README.md CHANGED
@@ -1,87 +1,195 @@
1
1
  # CMP Client
2
2
 
3
- A package to help you add a CMP (currently provided by Sourcepoint) to your application.
3
+ A client-side package to help you add a CMP (currently provided by Sourcepoint) to your application.
4
4
 
5
- ## Installation
5
+ <details>
6
+ <summary>How it works</summary>
6
7
 
7
- Install the package from npm:
8
+ [(Source on Confluence)](https://financialtimes.atlassian.net/wiki/spaces/ADS/pages/8151990292/New+Consent+Migration+Guide+for+teams#What-happens-when-a-user-visits-%5BinlineCard%5D--in-the-new-consent-framework%3F)
8
9
 
9
- ```copy
10
- npm install @financial-times/cmp-client
11
- ```
10
+ ![CMP Client Overview](docs/overview.jpg)
12
11
 
13
- There are reference implementations that you can look at in `src/examples/cmp-client`.
12
+ </details>
14
13
 
15
- To see a demo in action in your browser, run:
14
+ ## Installation
15
+
16
+ Install the package from npm:
16
17
 
17
18
  ```copy
18
- npm run dev -w src/examples/cmp-client
19
+ npm install @financial-times/cmp-client
19
20
  ```
20
21
 
21
- Visit https://localhost:5173 (see setup details in `src/examples/cmp-client`) to interact with the banner and see how cookies are set accordingly
22
-
23
22
  ## Usage
24
23
 
25
24
  ```js
26
25
  import { initSourcepointCmp } from "@financial-times/cmp-client/client";
27
- import { FT_DOTCOM_LOCAL } from "@financial-times/cmp-client/properties";
26
+ import { FT_DOTCOM_TEST } from "@financial-times/cmp-client/properties";
28
27
 
29
28
  // we suggest using a feature flag to disable the existing cookie banner and enable the new one
30
29
  if (flagsClient.get("adsDisableInternalCMP")) {
31
30
  initSourcepointCmp({
32
- propertyConfig: FT_DOTCOM_LOCAL,
33
- // useConsentStore: false (set to false e.g for non-FT.com properties or websites)
31
+ propertyConfig: FT_DOTCOM_TEST,
32
+ // useConsentStore: false (set to false for non-FT.com properties or websites)
34
33
  });
35
34
  }
36
35
  ```
37
36
 
38
- We will be adding new properties and their configs as we start rolling out. Please reach out to the Ads & Privacy team if you think you need to create a new property for your domain e.g it is on a different domain from the `ft.com` domain
37
+ We will be adding new properties (websites under FT group) and their configs as we start rolling out. Please reach out to the Ads & Privacy team if you think you need to create a new property for your domain - for example, if it is on a different domain from the `ft.com` domain.
39
38
 
40
39
  ### CMP configuration options:
41
40
 
42
- | Option | Description |
43
- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
44
- | `propertyConfig` (SourcepointConfig) | This config object is directly passed to Sourcepoint (our external CMP) and presets (imported like example usage) will be made available for different properties/titles under the FT group. This currently defaults to the FT_DOTCOM_PROD preset |
45
- | `userId` (string) | Please provide a userId if the user is a logged in user. If your app uses the FT Secure Session token, you can leave empty and set `useFTSession` to true instead. Defaults to `undefined` for anonymous users |
46
- | `useFTSession` (boolean) | Set this flag if you want the user's Id to be automatically derived from their FT Secure Session . Defaults to `true` |
47
- | `consentProxyHost` (string) | The fully qualified domain name for your consent proxy instance e.g `https://consent.thebanker.com`. Defaults to `https://consent.ft.com` |
48
- | `cookieDomain` (string) | The domain (and subdomains) that the FTConsent cookie will apply to e.g `.thebanker.com`. Defaults to `.ft.com` |
49
- | `formOfWordsId` (string) | The IAB custom categories that you have adapted might be tracked under a different FOW ID. At the moment, all custom categories are tracked under the `sourcepointCmp` form-of-words and its ID is default value |
50
- | `useConsentStore` (boolean) | Specifies whether the user's consent record should also be backed by the Single Consent Store. Properties like Specialist titles will need to explicitly set this to `false` as `true` is the default behavior |
41
+ <table>
42
+ <thead>
43
+ <tr>
44
+ <th>Option</th>
45
+ <th>Description</th>
46
+ </tr>
47
+ <thead/>
48
+ <tbody>
49
+ <tr>
50
+ <td><code>propertyConfig</code> (SourcepointConfig)</td>
51
+ <td>This config object is directly passed to Sourcepoint (our external CMP) and presets (imported like example usage) will be made available for different properties/titles under the FT group. This currently defaults to the <code>FT_DOTCOM_PROD</code> preset</td>
52
+ </tr>
53
+ <tr>
54
+ <td><code>userId</code> (string)</td>
55
+ <td>Please provide a userId if the user is a logged in user. If your app uses the FT Secure Session token, you can leave empty and set <code>useFTSession</code> to true instead. Defaults to <code>undefined</code> for anonymous users</td>
56
+ </tr>
57
+ <tr>
58
+ <td><code>useFTSession</code> (boolean)</td>
59
+ <td>Set this flag if you want the user's Id to be automatically derived from their FT Secure Session. Set to <code>false</code> if your app doesn't use FT Secure Session. Defaults to <code>true</code></td>
60
+ </tr>
61
+ <tr>
62
+ <td><code>consentProxyHost</code> (string)</td>
63
+ <td>The fully qualified domain name for your consent proxy instance e.g <code>https://consent.thebanker.com</code>. Defaults to <code>https://consent.ft.com</code></td>
64
+ </tr>
65
+ <tr>
66
+ <td><code>cookieDomain</code> (string)</td>
67
+ <td>The domain (and subdomains) that the FTConsent cookie will apply to e.g <code>.thebanker.com</code>. Defaults to <code>.ft.com</code></td>
68
+ </tr>
69
+ <tr>
70
+ <td><code>formOfWordsId</code> (string)</td>
71
+ <td>The IAB custom categories that you have adapted might be tracked under a different FOW ID. At the moment, all custom categories are tracked under the <code>sourcepointCmp</code> form-of-words and it defaults to <code>sourcepointCmp/VngD.XycZut.595cp9fWdp5XYP9vlFvk</code>. Properties using the consent banner as presented should use the default value</td>
72
+ </tr>
73
+ <tr>
74
+ <td><code>useConsentStore</code> (boolean)</td>
75
+ <td>Specifies whether the user's consent record should also be backed by the Single Consent Store. Properties like Specialist titles will need to explicitly set this to <code>false</code> as <code>true</code> is the default behavior</td>
76
+ </tr>
77
+ <tr>
78
+ <td><code>events</code> (object)</td>
79
+ <td>Used internally to define handlers for events emitted by the Vendor-supplied CMP module. See notes on "Responding to CMP events" below for details on how to define custom event handlers</td>
80
+ </tr>
81
+ </tbody>
82
+ <table/>
51
83
 
52
84
  ### Available Properties (constantly being updated)
53
85
 
54
- | Property Key | Details |
55
- | ----------------- | ------------------------------------------------------------------------------------------------------------------- |
56
- | `FT_DOTCOM_LOCAL` | Configuration preset for all properties on FT.com domain operating in a local environment - `https://local.ft.com` |
57
- | `FT_DOTCOM_PROD` | Configuration preset for all properties on FT.com domain operating in a production environment - `https://*.ft.com` |
58
-
59
- ## Consent Banner scripts only
60
-
61
- If you only want the scripts to setup the consent banner for your page with interacting without interacting with the FT's consent APIs, the package also exposes a `getCmpScripts` method, which returns a `DocumentFragment` containing block of scripts that you can to your page's `<head>` element.
86
+ <table>
87
+ <thead>
88
+ <tr>
89
+ <th>Property Key</th>
90
+ <th>Details</th>
91
+ </tr>
92
+ <thead/>
93
+ <tbody>
94
+ <tr>
95
+ <td><code>FT_DOTCOM_TEST</code></td>
96
+ <td>
97
+ <p>Use this configuration preset for testing the CMP on a property that has not yet been registered in the Sourcepoint portal.</p>
98
+ <p>All <code>*.ft.com</code> are currently registered and this config will spoof your request as coming from <code>https://local.ft.com</code>
99
+ </p>
100
+ </td>
101
+ </tr>
102
+ <tr>
103
+ <td><code>FT_DOTCOM_PROD</code></td>
104
+ <td>Configuration preset for all properties on FT.com domain operating in a production environment - <code>https://*.ft.com</code></td>
105
+ </tr>
106
+ <tr>
107
+ <td><code>SP_THE_BANKER</code></td>
108
+ <td>Configuration preset for properties on thebanker.com domain</td>
109
+ </tr>
110
+ <tr>
111
+ <td><code>SP_PWMNET</code></td>
112
+ <td>Configuration preset for properties on pwmnet.com domain</td>
113
+ </tr>
114
+ <tr>
115
+ <td><code>SP_FDI_INTELLIGENCE</code></td>
116
+ <td>Configuration preset for properties on fdiintelligence.com domain</td>
117
+ </tr>
118
+ <tr>
119
+ <td><code>SP_BANKING_RR</code></td>
120
+ <td>Configuration preset for properties on bankingriskandregulation.com domain</td>
121
+ </tr>
122
+ <tr>
123
+ <td><code>SP_SUSTAINABLE_VIEWS</code></td>
124
+ <td>Configuration preset for properties on sustainableviews.com domain</td>
125
+ </tr>
126
+ <tr>
127
+ <td><code>SP_FT_ADVISER</code></td>
128
+ <td>Configuration preset for properties on ftadviser.com domain</td>
129
+ </tr>
130
+ <tr>
131
+ <td><code>SP_INVESTORS_CHRONICLE</code></td>
132
+ <td>Configuration preset for properties on investorschronicle.co.uk domain</td>
133
+ </tr>
134
+ <tr>
135
+ <td><code>MM_IGNITES_ASIA</code></td>
136
+ <td>Configuration preset for properties on ignitesasia.com domain</td>
137
+ </tr>
138
+ <tr>
139
+ <td><code>MM_IGNITES_EUROPE</code></td>
140
+ <td>Configuration preset for properties on igniteseurope.com domain</td>
141
+ </tr>
142
+ </tbody>
143
+ <table/>
144
+
145
+ ## Responding to CMP events
146
+
147
+ If you need to do additional work in response to events emitted by the CMP Vendor module – e.g. initialising vendor packages that match the purposes granted by the user – you can do so via the `window._sp_.addEventListener` method:
62
148
 
63
149
  ```js
64
- import { getCmpScripts } from "@financial-times/cmp-client/client";
65
- import { FT_DOTCOM_LOCAL } from "@financial-times/cmp-client/properties";
66
-
67
- const cmpScripts = getCmpScripts(FT_DOTCOM_LOCAL);
68
- document.head.appendChild(cmpScripts);
150
+ window._sp_.addEventListener("onMessageReady", (messageType) => {... });
151
+ window._sp_.addEventListener("onConsentReady", (legislation, consentUUID, consentString, consentMeta) => { ... });
69
152
  ```
70
153
 
154
+ > [!Note]
155
+ > The `onConsentReady` event is fired
156
+ >
157
+ > 1. As soon as the CMP has finished loading and the user's consent choices are available
158
+ > 1. Subsequently, whenever the user's consent choices change
159
+
160
+ See the [Sourcepoint docs](https://docs.sourcepoint.com/hc/en-us/articles/4412176150035-Queue-event-callbacks) for a full listing of the events you can listen for and the arguments passed to the callbacks.
161
+
71
162
  ## Debugging
72
163
 
73
- You can verify that the CMP is correctly configured by adding an `events` map to your configuration: the `debug` module used in the examples provides a sample implementation:
164
+ You can verify that the CMP is correctly configured using the `logCmpEvents` method exported from the `debug` module:
74
165
 
75
166
  ```js
76
- import { getCmpScripts } from "@financial-times/cmp-client/client";
77
- import { ft } from "@financial-times/cmp-client/properties";
78
- import { events } from "@financial-times/cmp-client/debug";
167
+ import { debug } from "@financial-times/cmp-client";
79
168
 
80
- // Merge the events map into the domain configuration
81
- const config = { ...ft, events };
82
- document.head.appendChild(getCmpScripts(config));
169
+ debug.logCmpEvents();
83
170
  ```
84
171
 
85
- If everything's working then when running the demo app you'll see the CMP log its lifecycle events to the console, _even if its UI isn't displayed_.
172
+ If everything's working then you'll see the CMP Module log its lifecycle events to the console, _even if its UI isn't displayed_.
173
+
174
+ This can easily happen when you've made a previous consent choice and the CMP is now picking it up from local storage. For this reason we recommend running in an incognito window during development.
175
+
176
+ > [!Note]
177
+ > Please ensure that your app always uses the latest version of the CMP Client package.
178
+ >
179
+ > You can check the version your live app is using by running the following in the browser console:
180
+ >
181
+ > ```copy
182
+ > window.FT_CMP_CLIENT_VERSION
183
+ > ```
184
+
185
+ ## Development
186
+
187
+ If you have access to the source on Github you can take a look at [the reference implementations in `src/examples/cmp-client`](https://github.com/Financial-Times/privacy/tree/main/src/examples).
86
188
 
87
- This can easily happen when you've made a previous consent choice and the CMP is now picking it up from local storage. We recommend running in an incognito window during development.
189
+ To see a demo in action in your browser, run:
190
+
191
+ ```copy
192
+ npm run dev -w src/examples/cmp-client
193
+ ```
194
+
195
+ Visit https://localhost:5173 (see setup details in `src/examples/cmp-client`) to interact with the banner and see how cookies are set accordingly
package/dist/index.cjs CHANGED
@@ -7,6 +7,150 @@ var __publicField = (obj, key, value) => {
7
7
  };
8
8
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r;
9
9
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
10
+ const version = "3.0.0";
11
+ const events = {
12
+ onMessageChoiceSelect: (...args) => {
13
+ console.log("[debug] onMessageChoiceSelect", args);
14
+ },
15
+ onMessageReady: (...args) => {
16
+ console.log("[debug] onMessageReady", args);
17
+ },
18
+ onMessageChoiceError: (...args) => {
19
+ console.log("[debug] onMessageChoiceError", args);
20
+ },
21
+ onPrivacyManagerAction: (...args) => {
22
+ console.log("[debug] onPrivacyManagerAction", args);
23
+ },
24
+ onPMCancel: (...args) => {
25
+ console.log("[debug] onPMCancel", args);
26
+ },
27
+ onMessageReceiveData: (...args) => {
28
+ console.log("[debug] onMessageReceiveData", args);
29
+ },
30
+ onSPPMObjectReady: (...args) => {
31
+ console.log("[debug] onSPPMObjectReady", args);
32
+ },
33
+ onConsentReady: async (...args) => {
34
+ const [legislation2, consentUUID, consentString, consentMeta] = args;
35
+ console.log("[debug] onConsentReady", { legislation: legislation2, consentString, consentMeta, consentUUID });
36
+ },
37
+ onError: (...args) => {
38
+ console.log("[debug] onError", args);
39
+ }
40
+ };
41
+ function logCmpEvents() {
42
+ window._sp_queue = window._sp_queue ?? [];
43
+ window._sp_queue.push(() => {
44
+ var _a2, _b2;
45
+ for (const [eventId, eventHandler] of Object.entries(events)) {
46
+ (_b2 = (_a2 = window._sp_).addEventListener) == null ? void 0 : _b2.call(_a2, eventId, eventHandler);
47
+ }
48
+ });
49
+ }
50
+ const debug = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
51
+ __proto__: null,
52
+ events,
53
+ logCmpEvents
54
+ }, Symbol.toStringTag, { value: "Module" }));
55
+ const defaults = {
56
+ joinHref: true,
57
+ gdpr: {},
58
+ ccpa: {}
59
+ };
60
+ const FT_DOTCOM_TEST = {
61
+ ...defaults,
62
+ accountId: 1906,
63
+ baseEndpoint: "https://consent-manager.ft.com",
64
+ propertyHref: "https://local.ft.com"
65
+ };
66
+ const FT_DOTCOM_PROD = {
67
+ ...defaults,
68
+ accountId: 1906,
69
+ baseEndpoint: "https://consent-manager.ft.com",
70
+ propertyId: 31642
71
+ };
72
+ const SP_PWMNET = {
73
+ ...defaults,
74
+ accountId: 1906,
75
+ baseEndpoint: "https://consent-manager.pwmnet.com",
76
+ propertyId: 33414
77
+ };
78
+ const SP_FDI_INTELLIGENCE = {
79
+ ...defaults,
80
+ accountId: 1906,
81
+ baseEndpoint: "https://consent-manager.fdiintelligence.com",
82
+ propertyId: 34061
83
+ };
84
+ const SP_THE_BANKER = {
85
+ ...defaults,
86
+ accountId: 1906,
87
+ baseEndpoint: "https://consent-manager.thebanker.com",
88
+ propertyId: 34060
89
+ };
90
+ const SP_BANKING_RR = {
91
+ ...defaults,
92
+ accountId: 1906,
93
+ baseEndpoint: "https://consent-manager.bankingriskandregulation.com",
94
+ propertyId: 34059
95
+ };
96
+ const SP_SUSTAINABLE_VIEWS = {
97
+ ...defaults,
98
+ accountId: 1906,
99
+ baseEndpoint: "https://consent-manager.sustainableviews.com",
100
+ propertyId: 34058
101
+ };
102
+ const SP_FT_ADVISER = {
103
+ ...defaults,
104
+ accountId: 1906,
105
+ baseEndpoint: "https://consent-manager.ftadviser.com",
106
+ propertyId: 33416
107
+ };
108
+ const SP_INVESTORS_CHRONICLE = {
109
+ ...defaults,
110
+ accountId: 1906,
111
+ baseEndpoint: "https://consent-manager.investorschronicle.co.uk",
112
+ propertyId: 33415
113
+ };
114
+ const MM_IGNITES_ASIA = {
115
+ ...defaults,
116
+ accountId: 1906,
117
+ baseEndpoint: "https://cdn.privacy-mgmt.com",
118
+ propertyId: 33947
119
+ };
120
+ const MM_IGNITES_EUROPE = {
121
+ ...defaults,
122
+ accountId: 1906,
123
+ baseEndpoint: "https://cdn.privacy-mgmt.com",
124
+ propertyId: 33946
125
+ };
126
+ const properties = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
127
+ __proto__: null,
128
+ FT_DOTCOM_PROD,
129
+ FT_DOTCOM_TEST,
130
+ MM_IGNITES_ASIA,
131
+ MM_IGNITES_EUROPE,
132
+ SP_BANKING_RR,
133
+ SP_FDI_INTELLIGENCE,
134
+ SP_FT_ADVISER,
135
+ SP_INVESTORS_CHRONICLE,
136
+ SP_PWMNET,
137
+ SP_SUSTAINABLE_VIEWS,
138
+ SP_THE_BANKER
139
+ }, Symbol.toStringTag, { value: "Module" }));
140
+ function updateFooterLinkCMP() {
141
+ const cookieLink = document.querySelector(
142
+ "[href='https://www.ft.com/preferences/manage-cookies']"
143
+ );
144
+ if (cookieLink) {
145
+ cookieLink.href = "#";
146
+ cookieLink.setAttribute("onclick", "window._sp_.gdpr.loadPrivacyManagerModal(827767);");
147
+ cookieLink.dataset.cmpLink = "updated";
148
+ return true;
149
+ } else {
150
+ console.warn("CMP Footer Link was not found and not updated");
151
+ return false;
152
+ }
153
+ }
10
154
  const request = (url, { credentials = "omit" } = {}) => {
11
155
  return fetch(`https://session-next.ft.com${url}`, {
12
156
  credentials,
@@ -113,30 +257,9 @@ const iabCustomCategories = {
113
257
  specialFeatures: []
114
258
  }
115
259
  };
116
- const defaults = {
117
- joinHref: true,
118
- gdpr: {},
119
- ccpa: {}
120
- };
121
- const FT_DOTCOM_LOCAL = {
122
- ...defaults,
123
- accountId: 1906,
124
- baseEndpoint: "https://consent-manager.ft.com",
125
- propertyHref: "https://local.ft.com"
126
- };
127
- const FT_DOTCOM_PROD = {
128
- ...defaults,
129
- accountId: 1906,
130
- baseEndpoint: "https://consent-manager.ft.com",
131
- propertyId: 31642
132
- };
133
- const properties = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
134
- __proto__: null,
135
- FT_DOTCOM_LOCAL,
136
- FT_DOTCOM_PROD
137
- }, Symbol.toStringTag, { value: "Module" }));
138
- function createContentScript(content) {
260
+ function createContentScript(cmpScript, content) {
139
261
  const s = document.createElement("script");
262
+ s.dataset.cmpScript = cmpScript;
140
263
  s.innerHTML = content;
141
264
  return s;
142
265
  }
@@ -145,47 +268,25 @@ function createSourceScript(src) {
145
268
  s.src = src;
146
269
  return s;
147
270
  }
148
- function encodeConfig(obj) {
149
- let str = "";
150
- if (!obj)
151
- return str;
152
- for (const [key, val] of Object.entries(obj)) {
153
- switch (typeof val) {
154
- case "function":
155
- str += `${key}:${val.toString().replace(/"/g, "'").replace(/\s/g, "")},`;
156
- break;
157
- case "string":
158
- str += `${key}:"${val.replace(/"/g, "'").replace(/\s/g, "")}",`;
159
- break;
160
- case "object":
161
- str += `${key}:{${encodeConfig(val)}},`;
162
- break;
163
- default:
164
- str += `${key}:${val},`;
165
- break;
166
- }
167
- }
168
- return str.slice(0, -1);
169
- }
170
- function getSPConfig(config) {
171
- return `window._sp_queue=[];window._sp_={config:{${encodeConfig(config)}}}`;
172
- }
173
271
  const scriptSources = {
174
272
  cmpFrames: "https://consent-manager.ft.com/unified/wrapperMessagingWithoutDetection.js"
175
273
  };
176
274
  const scriptContent = {
177
- getSPConfig,
178
275
  tcfStub: `"use strict";function _typeof(t){return(_typeof="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}!function(){var t=function(){var t,e,o=[],n=window,r=n;for(;r;){try{if(r.frames.__tcfapiLocator){t=r;break}}catch(t){}if(r===n.top)break;r=r.parent}t||(!function t(){var e=n.document,o=!!n.frames.__tcfapiLocator;if(!o)if(e.body){var r=e.createElement("iframe");r.style.cssText="display:none",r.name="__tcfapiLocator",e.body.appendChild(r)}else setTimeout(t,5);return!o}(),n.__tcfapi=function(){for(var t=arguments.length,n=new Array(t),r=0;r<t;r++)n[r]=arguments[r];if(!n.length)return o;"setGdprApplies"===n[0]?n.length>3&&2===parseInt(n[1],10)&&"boolean"==typeof n[3]&&(e=n[3],"function"==typeof n[2]&&n[2]("set",!0)):"ping"===n[0]?"function"==typeof n[2]&&n[2]({gdprApplies:e,cmpLoaded:!1,cmpStatus:"stub"}):o.push(n)},n.addEventListener("message",(function(t){var e="string"==typeof t.data,o={};if(e)try{o=JSON.parse(t.data)}catch(t){}else o=t.data;var n="object"===_typeof(o)&&null!==o?o.__tcfapiCall:null;n&&window.__tcfapi(n.command,n.version,(function(o,r){var a={__tcfapiReturn:{returnValue:o,success:r,callId:n.callId}};t&&t.source&&t.source.postMessage&&t.source.postMessage(e?JSON.stringify(a):a,"*")}),n.parameter)}),!1))};"undefined"!=typeof module?module.exports=t:t()}();`,
179
276
  uspStub: `"use strict";(function () { var e = false; var c = window; var t = document; function r() { if (!c.frames["__uspapiLocator"]) { if (t.body) { var a = t.body; var e = t.createElement("iframe"); e.style.cssText = "display:none"; e.name = "__uspapiLocator"; a.appendChild(e) } else { setTimeout(r, 5) } } } r(); function p() { var a = arguments; __uspapi.a = __uspapi.a || []; if (!a.length) { return __uspapi.a } else if (a[0] === "ping") { a[2]({ gdprAppliesGlobally: e, cmpLoaded: false }, true) } else { __uspapi.a.push([].slice.apply(a)) } } function l(t) { var r = typeof t.data === "string"; try { var a = r ? JSON.parse(t.data) : t.data; if (a.__cmpCall) { var n = a.__cmpCall; c.__uspapi(n.command, n.parameter, function (a, e) { var c = { __cmpReturn: { returnValue: a, success: e, callId: n.callId } }; t.source.postMessage(r ? JSON.stringify(c) : c, "*") }) } } catch (a) { } } if (typeof __uspapi !== "function") { c.__uspapi = p; __uspapi.msgHandler = l; c.addEventListener("message", l, false) } })();`
180
277
  };
181
- function getCmpScripts(config) {
278
+ function getCmpScripts() {
182
279
  const fragment = document.createDocumentFragment();
183
- fragment.appendChild(createContentScript(scriptContent.tcfStub));
184
- fragment.appendChild(createContentScript(scriptContent.uspStub));
185
- fragment.appendChild(createContentScript(scriptContent.getSPConfig(config)));
280
+ fragment.appendChild(createContentScript("tcf", scriptContent.tcfStub));
281
+ fragment.appendChild(createContentScript("usp", scriptContent.uspStub));
186
282
  fragment.appendChild(createSourceScript(scriptSources.cmpFrames));
187
283
  return fragment;
188
284
  }
285
+ function bootstrapCmp(config) {
286
+ window._sp_ = { config };
287
+ window._sp_queue ?? (window._sp_queue = []);
288
+ document.head.appendChild(getCmpScripts());
289
+ }
189
290
  function getConsentPayload(parsedConsent, { shouldUpdateConsentStore, formOfWordsId, cookieDomain }) {
190
291
  const categoryNames = Object.keys(parsedConsent);
191
292
  const data = categoryNames.reduce(
@@ -1247,7 +1348,7 @@ class VendorVectorEncoder {
1247
1348
  }
1248
1349
  return retrString;
1249
1350
  }
1250
- static decode(value, version) {
1351
+ static decode(value, version2) {
1251
1352
  let vector;
1252
1353
  let index = 0;
1253
1354
  const maxId = IntEncoder.decode(value.substr(index, BitLength.maxId), BitLength.maxId);
@@ -1256,7 +1357,7 @@ class VendorVectorEncoder {
1256
1357
  index += BitLength.encodingType;
1257
1358
  if (encodingType === VectorEncodingType.RANGE) {
1258
1359
  vector = new Vector();
1259
- if (version === 1) {
1360
+ if (version2 === 1) {
1260
1361
  if (value.substr(index, 1) === "1") {
1261
1362
  throw new DecodingError("Unable to decode default consent=1");
1262
1363
  }
@@ -1721,12 +1822,12 @@ const _GVL = class _GVL extends Cloneable {
1721
1822
  throw new GVLError("must specify GVL.baseUrl before loading GVL json");
1722
1823
  }
1723
1824
  if (versionOrVendorList > 0) {
1724
- const version = versionOrVendorList;
1725
- if (_GVL.CACHE.has(version)) {
1726
- this.populate(_GVL.CACHE.get(version));
1825
+ const version2 = versionOrVendorList;
1826
+ if (_GVL.CACHE.has(version2)) {
1827
+ this.populate(_GVL.CACHE.get(version2));
1727
1828
  this.readyPromise = Promise.resolve();
1728
1829
  } else {
1729
- url += _GVL.versionedFilename.replace("[VERSION]", String(version));
1830
+ url += _GVL.versionedFilename.replace("[VERSION]", String(version2));
1730
1831
  this.readyPromise = this.fetchJson(url);
1731
1832
  }
1732
1833
  } else {
@@ -2800,7 +2901,7 @@ async function saveConsent(consentEndpoint, payload) {
2800
2901
  }
2801
2902
  function consentReadyHandlerFn(props) {
2802
2903
  const { userId, consentProxyHost, cookieDomain, formOfWordsId, useConsentStore } = props;
2803
- return async function consentReadyHandler(legislation2, _, consentString, consentMeta) {
2904
+ return async function consentReadyHandler(legislation2, _consentUUID, consentString, consentMeta) {
2804
2905
  const activeLegislation = consentMeta.applies ? legislation2 : "gdpr";
2805
2906
  if (activeLegislation !== legislation2 || !consentString) {
2806
2907
  return;
@@ -2837,6 +2938,7 @@ async function initSourcepointCmp({
2837
2938
  formOfWordsId = SOURCEPOINT_FOW_ID,
2838
2939
  useConsentStore = true
2839
2940
  } = {}) {
2941
+ var _a2;
2840
2942
  if (!userId && useFTSession) {
2841
2943
  try {
2842
2944
  const response = await getUuid();
@@ -2851,70 +2953,23 @@ async function initSourcepointCmp({
2851
2953
  if (userId) {
2852
2954
  propertyConfig.authId = userId;
2853
2955
  }
2854
- const scripts = getCmpScripts(propertyConfig);
2855
- document.head.appendChild(scripts);
2856
- window._sp_queue.push(() => {
2857
- window._sp_.addEventListener(
2858
- "onConsentReady",
2859
- consentReadyHandlerFn({
2860
- userId,
2861
- consentProxyHost,
2862
- cookieDomain,
2863
- formOfWordsId,
2864
- useConsentStore
2865
- })
2866
- );
2867
- });
2868
- }
2869
- const events = {
2870
- onMessageChoiceSelect: (...args) => {
2871
- console.log("[event] onMessageChoiceSelect", args);
2872
- },
2873
- onMessageReady: (...args) => {
2874
- console.log("[event] onMessageReady", args);
2875
- },
2876
- onMessageChoiceError: (...args) => {
2877
- console.log("[event] onMessageChoiceError", args);
2878
- },
2879
- onPrivacyManagerAction: (...args) => {
2880
- console.log("[event] onPrivacyManagerAction", args);
2881
- },
2882
- onPMCancel: (...args) => {
2883
- console.log("[event] onPMCancel", args);
2884
- },
2885
- onMessageReceiveData: (...args) => {
2886
- console.log("[event] onMessageReceiveData", args);
2887
- },
2888
- onSPPMObjectReady: (...args) => {
2889
- console.log("[event] onSPPMObjectReady", args);
2890
- },
2891
- onConsentReady: (...args) => {
2892
- console.log("[event] onConsentReady", args);
2893
- },
2894
- onError: (...args) => {
2895
- console.log("[event] onError", args);
2896
- }
2897
- };
2898
- const debug = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2899
- __proto__: null,
2900
- events
2901
- }, Symbol.toStringTag, { value: "Module" }));
2902
- function updateFooterLinkCMP() {
2903
- const cookieLink = document.querySelector(
2904
- "[href='https://www.ft.com/preferences/manage-cookies']"
2905
- );
2906
- if (cookieLink) {
2907
- cookieLink.href = "#";
2908
- cookieLink.setAttribute("onclick", "window._sp_.gdpr.loadPrivacyManagerModal(827767);");
2909
- cookieLink.dataset.cmpLink = "updated";
2910
- return true;
2911
- } else {
2912
- console.warn("CMP Footer Link was not found and not updated");
2913
- return false;
2956
+ if ((_a2 = propertyConfig.events) == null ? void 0 : _a2.onConsentReady) {
2957
+ console.warn("[cmp-client] The supplied 'onConsentReady' event handler will be overwritten.");
2914
2958
  }
2959
+ const events2 = {
2960
+ ...propertyConfig.events ?? {},
2961
+ onConsentReady: consentReadyHandlerFn({
2962
+ userId,
2963
+ consentProxyHost,
2964
+ cookieDomain,
2965
+ formOfWordsId,
2966
+ useConsentStore
2967
+ })
2968
+ };
2969
+ bootstrapCmp({ ...propertyConfig, events: events2 });
2915
2970
  }
2971
+ window.FT_CMP_CLIENT_VERSION = version;
2916
2972
  exports.debug = debug;
2917
- exports.getCmpScripts = getCmpScripts;
2918
2973
  exports.initSourcepointCmp = initSourcepointCmp;
2919
2974
  exports.properties = properties;
2920
2975
  exports.updateFooterLinkCMP = updateFooterLinkCMP;