@redacto.io/consent-sdk-js 0.1.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.
- package/LICENSE +176 -0
- package/dist/redacto.d.mts +95 -0
- package/dist/redacto.d.ts +95 -0
- package/dist/redacto.global.js +133 -0
- package/dist/redacto.global.js.map +1 -0
- package/dist/redacto.js +4770 -0
- package/dist/redacto.mjs +4747 -0
- package/package.json +50 -0
package/dist/redacto.js
ADDED
|
@@ -0,0 +1,4770 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
default: () => src_default,
|
|
24
|
+
destroy: () => destroy,
|
|
25
|
+
digilockerCallback: () => digilockerCallback,
|
|
26
|
+
init: () => init,
|
|
27
|
+
inline: () => inline
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(src_exports);
|
|
30
|
+
|
|
31
|
+
// src/core/state.ts
|
|
32
|
+
var Store = class {
|
|
33
|
+
constructor(initialState) {
|
|
34
|
+
this.listeners = /* @__PURE__ */ new Set();
|
|
35
|
+
this.state = { ...initialState };
|
|
36
|
+
}
|
|
37
|
+
getState() {
|
|
38
|
+
return this.state;
|
|
39
|
+
}
|
|
40
|
+
setState(partial) {
|
|
41
|
+
const prevState = this.state;
|
|
42
|
+
this.state = { ...this.state, ...partial };
|
|
43
|
+
this.listeners.forEach((listener) => listener(this.state, prevState));
|
|
44
|
+
}
|
|
45
|
+
subscribe(listener) {
|
|
46
|
+
this.listeners.add(listener);
|
|
47
|
+
return () => {
|
|
48
|
+
this.listeners.delete(listener);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
destroy() {
|
|
52
|
+
this.listeners.clear();
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
// src/core/dom.ts
|
|
57
|
+
function el(tag, attrs, ...children) {
|
|
58
|
+
const element = document.createElement(tag);
|
|
59
|
+
if (attrs) applyAttrs(element, attrs);
|
|
60
|
+
appendChildren(element, children);
|
|
61
|
+
return element;
|
|
62
|
+
}
|
|
63
|
+
function svg(tag, attrs, ...children) {
|
|
64
|
+
const element = document.createElementNS(
|
|
65
|
+
"http://www.w3.org/2000/svg",
|
|
66
|
+
tag
|
|
67
|
+
);
|
|
68
|
+
if (attrs) {
|
|
69
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
70
|
+
if (value == null || value === false) continue;
|
|
71
|
+
if (key === "style" && typeof value === "object") {
|
|
72
|
+
applyStyles(element, value);
|
|
73
|
+
} else {
|
|
74
|
+
element.setAttribute(key, String(value));
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
appendChildren(element, children);
|
|
79
|
+
return element;
|
|
80
|
+
}
|
|
81
|
+
function applyAttrs(element, attrs) {
|
|
82
|
+
for (const [key, value] of Object.entries(attrs)) {
|
|
83
|
+
if (value == null || value === false) continue;
|
|
84
|
+
if (key === "style" && typeof value === "object") {
|
|
85
|
+
applyStyles(element, value);
|
|
86
|
+
} else if (key === "className") {
|
|
87
|
+
element.className = value;
|
|
88
|
+
} else if (key === "dataset" && typeof value === "object") {
|
|
89
|
+
Object.assign(element.dataset, value);
|
|
90
|
+
} else if (key.startsWith("on") && typeof value === "function") {
|
|
91
|
+
const event = key.slice(2).toLowerCase();
|
|
92
|
+
element.addEventListener(event, value);
|
|
93
|
+
} else if (key === "checked" || key === "disabled" || key === "readOnly") {
|
|
94
|
+
element[key] = value;
|
|
95
|
+
} else {
|
|
96
|
+
element.setAttribute(key, String(value));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function applyStyles(element, styles2) {
|
|
101
|
+
for (const [key, value] of Object.entries(styles2)) {
|
|
102
|
+
if (value == null) continue;
|
|
103
|
+
element.style[key] = typeof value === "number" ? `${value}px` : value;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function appendChildren(parent, children) {
|
|
107
|
+
for (const child of children) {
|
|
108
|
+
if (child == null || child === false) continue;
|
|
109
|
+
if (typeof child === "string") {
|
|
110
|
+
parent.appendChild(document.createTextNode(child));
|
|
111
|
+
} else {
|
|
112
|
+
parent.appendChild(child);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function clearElement(element) {
|
|
117
|
+
while (element.firstChild) {
|
|
118
|
+
element.removeChild(element.firstChild);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// src/core/media-query.ts
|
|
123
|
+
function createMediaQuery(query) {
|
|
124
|
+
const mql = window.matchMedia(query);
|
|
125
|
+
let currentMatches = mql.matches;
|
|
126
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
127
|
+
const handler = (e) => {
|
|
128
|
+
currentMatches = e.matches;
|
|
129
|
+
listeners.forEach((cb) => cb(currentMatches));
|
|
130
|
+
};
|
|
131
|
+
mql.addEventListener("change", handler);
|
|
132
|
+
return {
|
|
133
|
+
get matches() {
|
|
134
|
+
return currentMatches;
|
|
135
|
+
},
|
|
136
|
+
subscribe(cb) {
|
|
137
|
+
listeners.add(cb);
|
|
138
|
+
},
|
|
139
|
+
destroy() {
|
|
140
|
+
mql.removeEventListener("change", handler);
|
|
141
|
+
listeners.clear();
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// src/core/jwt-decode.ts
|
|
147
|
+
var import_jwt_decode = require("jwt-decode");
|
|
148
|
+
function decodeTokenSafely(token) {
|
|
149
|
+
try {
|
|
150
|
+
return (0, import_jwt_decode.jwtDecode)(token);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.warn("Failed to decode JWT token:", error);
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// src/services/cache.ts
|
|
158
|
+
var apiCache = /* @__PURE__ */ new Map();
|
|
159
|
+
var CACHE_TTL = 5 * 60 * 1e3;
|
|
160
|
+
function getCachedData(key) {
|
|
161
|
+
const cached = apiCache.get(key);
|
|
162
|
+
if (cached && Date.now() - cached.timestamp < cached.ttl) {
|
|
163
|
+
return cached.data;
|
|
164
|
+
}
|
|
165
|
+
if (cached) {
|
|
166
|
+
apiCache.delete(key);
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
function setCachedData(key, data, ttl = CACHE_TTL) {
|
|
171
|
+
apiCache.set(key, { data, timestamp: Date.now(), ttl });
|
|
172
|
+
}
|
|
173
|
+
function clearApiCache() {
|
|
174
|
+
apiCache.clear();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// src/api/index.ts
|
|
178
|
+
var BASE_URL = "https://api.redacto.io/consent";
|
|
179
|
+
var BASE_URL_PUBLIC = "https://api.redacto.tech/consent";
|
|
180
|
+
var fetchConsentContent = async ({
|
|
181
|
+
noticeId,
|
|
182
|
+
accessToken,
|
|
183
|
+
baseUrl,
|
|
184
|
+
language = "en",
|
|
185
|
+
specific_uuid,
|
|
186
|
+
validate_against = "all",
|
|
187
|
+
include_fully_consented_data = false,
|
|
188
|
+
signal
|
|
189
|
+
}) => {
|
|
190
|
+
if (!noticeId || !accessToken || !validate_against) {
|
|
191
|
+
throw new Error("noticeId and accessToken are required");
|
|
192
|
+
}
|
|
193
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
194
|
+
if (!decodedToken) throw new Error("Invalid access token");
|
|
195
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
196
|
+
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
197
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
198
|
+
const cacheKey = `${accessToken}-${noticeId}-${validate_against}-${language}-${specific_uuid || ""}-${include_fully_consented_data}`;
|
|
199
|
+
if (validate_against === "all" && !specific_uuid) {
|
|
200
|
+
const cached = getCachedData(cacheKey);
|
|
201
|
+
if (cached) return cached;
|
|
202
|
+
}
|
|
203
|
+
const url = new URL(`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeId}`);
|
|
204
|
+
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
205
|
+
url.searchParams.append("validate_against", validate_against);
|
|
206
|
+
if (include_fully_consented_data) url.searchParams.append("include_fully_consented_data", "true");
|
|
207
|
+
const response = await fetch(url.toString(), {
|
|
208
|
+
method: "GET",
|
|
209
|
+
headers: {
|
|
210
|
+
Authorization: `Bearer ${accessToken}`,
|
|
211
|
+
"Accept-Language": language
|
|
212
|
+
},
|
|
213
|
+
signal
|
|
214
|
+
});
|
|
215
|
+
if (!response.ok) {
|
|
216
|
+
let errorMessage = `Failed to fetch consent content: ${response.status} ${response.statusText}`;
|
|
217
|
+
if (response.status === 401) errorMessage = "Unauthorized: Invalid or expired token";
|
|
218
|
+
else if (response.status === 403) errorMessage = "Forbidden: Access denied";
|
|
219
|
+
else if (response.status === 404) errorMessage = "Notice not found";
|
|
220
|
+
else if (response.status === 409) errorMessage = "User has already provided consent";
|
|
221
|
+
else if (response.status >= 500) errorMessage = "Server error: Please try again later";
|
|
222
|
+
const error = new Error(errorMessage);
|
|
223
|
+
error.status = response.status;
|
|
224
|
+
throw error;
|
|
225
|
+
}
|
|
226
|
+
const data = await response.json();
|
|
227
|
+
if (validate_against === "all" && !specific_uuid) {
|
|
228
|
+
setCachedData(cacheKey, data);
|
|
229
|
+
}
|
|
230
|
+
return data;
|
|
231
|
+
};
|
|
232
|
+
var fetchConsentContentPublic = async ({
|
|
233
|
+
org_uuid,
|
|
234
|
+
workspace_uuid,
|
|
235
|
+
notice_uuid,
|
|
236
|
+
baseUrl,
|
|
237
|
+
language = "en",
|
|
238
|
+
specific_uuid
|
|
239
|
+
}) => {
|
|
240
|
+
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
241
|
+
const url = new URL(
|
|
242
|
+
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/notices/get-notice/${notice_uuid}`
|
|
243
|
+
);
|
|
244
|
+
if (specific_uuid) url.searchParams.append("specific_uuid", specific_uuid);
|
|
245
|
+
const response = await fetch(url.toString(), {
|
|
246
|
+
method: "GET",
|
|
247
|
+
headers: { "Content-Type": "application/json" }
|
|
248
|
+
});
|
|
249
|
+
if (response.status === 401) {
|
|
250
|
+
const error = new Error("Unauthorized");
|
|
251
|
+
error.status = 401;
|
|
252
|
+
throw error;
|
|
253
|
+
}
|
|
254
|
+
if (response.status === 409) {
|
|
255
|
+
const error = new Error("User has already provided consent");
|
|
256
|
+
error.status = 409;
|
|
257
|
+
throw error;
|
|
258
|
+
}
|
|
259
|
+
if (!response.ok) {
|
|
260
|
+
throw new Error(`Failed to fetch consent content: ${response.statusText}`);
|
|
261
|
+
}
|
|
262
|
+
return await response.json();
|
|
263
|
+
};
|
|
264
|
+
var submitConsentEvent = async ({
|
|
265
|
+
accessToken,
|
|
266
|
+
baseUrl,
|
|
267
|
+
noticeUuid,
|
|
268
|
+
purposes,
|
|
269
|
+
declined,
|
|
270
|
+
meta_data,
|
|
271
|
+
guardianVerificationReference,
|
|
272
|
+
selfDeclaredAdult,
|
|
273
|
+
signal
|
|
274
|
+
}) => {
|
|
275
|
+
if (!noticeUuid || !accessToken || !Array.isArray(purposes)) {
|
|
276
|
+
throw new Error("noticeUuid, accessToken, and purposes array are required");
|
|
277
|
+
}
|
|
278
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
279
|
+
if (!decodedToken) throw new Error("Invalid access token");
|
|
280
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
281
|
+
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
282
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
283
|
+
const validatedPurposes = purposes.map((purpose) => {
|
|
284
|
+
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
285
|
+
const dataElements = (purpose.data_elements || []).filter((element) => element.enabled).map((element) => ({
|
|
286
|
+
uuid: element.uuid,
|
|
287
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
288
|
+
}));
|
|
289
|
+
return {
|
|
290
|
+
purpose_uuid: purpose.uuid,
|
|
291
|
+
selected: Boolean(purpose.selected),
|
|
292
|
+
data_elements: dataElements.length > 0 ? dataElements : void 0
|
|
293
|
+
};
|
|
294
|
+
});
|
|
295
|
+
const payload = {
|
|
296
|
+
notice_uuid: noticeUuid,
|
|
297
|
+
purposes: validatedPurposes,
|
|
298
|
+
select_all_mandatory: false,
|
|
299
|
+
source: "WEB",
|
|
300
|
+
declined: Boolean(declined),
|
|
301
|
+
meta_data: meta_data ? { specific_uuid: meta_data.specific_uuid, minor_age: meta_data.minor_age } : void 0,
|
|
302
|
+
guardian_verification_reference: guardianVerificationReference,
|
|
303
|
+
self_declared_adult: selfDeclaredAdult
|
|
304
|
+
};
|
|
305
|
+
const response = await fetch(
|
|
306
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/submit-consent`,
|
|
307
|
+
{
|
|
308
|
+
method: "POST",
|
|
309
|
+
headers: {
|
|
310
|
+
Authorization: `Bearer ${accessToken}`,
|
|
311
|
+
"Content-Type": "application/json"
|
|
312
|
+
},
|
|
313
|
+
body: JSON.stringify(payload),
|
|
314
|
+
signal
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
if (!response.ok) {
|
|
318
|
+
let errorMessage = `Failed to submit consent: ${response.status} ${response.statusText}`;
|
|
319
|
+
if (response.status === 400) {
|
|
320
|
+
try {
|
|
321
|
+
const errorData = await response.json();
|
|
322
|
+
errorMessage = errorData.message || "Invalid request data";
|
|
323
|
+
} catch {
|
|
324
|
+
errorMessage = "Invalid request data";
|
|
325
|
+
}
|
|
326
|
+
} else if (response.status === 401) errorMessage = "Unauthorized: Invalid or expired token";
|
|
327
|
+
else if (response.status === 403) errorMessage = "Forbidden: Access denied";
|
|
328
|
+
else if (response.status === 404) errorMessage = "Notice not found";
|
|
329
|
+
else if (response.status >= 500) errorMessage = "Server error: Please try again later";
|
|
330
|
+
const error = new Error(errorMessage);
|
|
331
|
+
error.status = response.status;
|
|
332
|
+
throw error;
|
|
333
|
+
}
|
|
334
|
+
clearApiCache();
|
|
335
|
+
};
|
|
336
|
+
var submitConsentEventPublic = async ({
|
|
337
|
+
org_uuid,
|
|
338
|
+
workspace_uuid,
|
|
339
|
+
accessToken,
|
|
340
|
+
baseUrl,
|
|
341
|
+
noticeUuid,
|
|
342
|
+
purposes,
|
|
343
|
+
declined,
|
|
344
|
+
meta_data
|
|
345
|
+
}) => {
|
|
346
|
+
var _a, _b;
|
|
347
|
+
const apiBaseUrl = baseUrl || BASE_URL_PUBLIC;
|
|
348
|
+
const payload = {
|
|
349
|
+
notice_uuid: noticeUuid,
|
|
350
|
+
purposes: purposes.map((purpose) => {
|
|
351
|
+
if (!purpose.uuid) throw new Error("Invalid purpose data: missing uuid");
|
|
352
|
+
const dataElements = purpose.data_elements.filter((element) => element.enabled).map((element) => ({
|
|
353
|
+
uuid: element.uuid,
|
|
354
|
+
selected: element.required ? true : Boolean(element.selected)
|
|
355
|
+
}));
|
|
356
|
+
return {
|
|
357
|
+
purpose_uuid: purpose.uuid,
|
|
358
|
+
selected: Boolean(purpose.selected),
|
|
359
|
+
data_elements: dataElements.length > 0 ? dataElements : void 0
|
|
360
|
+
};
|
|
361
|
+
}),
|
|
362
|
+
select_all_mandatory: false,
|
|
363
|
+
source: "WEB",
|
|
364
|
+
declined,
|
|
365
|
+
meta_data
|
|
366
|
+
};
|
|
367
|
+
const response = await fetch(
|
|
368
|
+
`${apiBaseUrl}/public/organisations/${org_uuid}/workspaces/${workspace_uuid}/submit-consent`,
|
|
369
|
+
{
|
|
370
|
+
method: "POST",
|
|
371
|
+
headers: {
|
|
372
|
+
Authorization: `Bearer ${accessToken}`,
|
|
373
|
+
"Content-Type": "application/json"
|
|
374
|
+
},
|
|
375
|
+
body: JSON.stringify(payload)
|
|
376
|
+
}
|
|
377
|
+
);
|
|
378
|
+
if (!response.ok) {
|
|
379
|
+
let errorData;
|
|
380
|
+
try {
|
|
381
|
+
errorData = await response.json();
|
|
382
|
+
} catch {
|
|
383
|
+
errorData = { message: response.statusText };
|
|
384
|
+
}
|
|
385
|
+
if (response.status === 409) {
|
|
386
|
+
const error2 = new Error(
|
|
387
|
+
((_a = errorData.detail) == null ? void 0 : _a.message) || errorData.message || "User has already provided consent"
|
|
388
|
+
);
|
|
389
|
+
error2.status = 409;
|
|
390
|
+
error2.code = errorData.code || 409;
|
|
391
|
+
throw error2;
|
|
392
|
+
}
|
|
393
|
+
const error = new Error(
|
|
394
|
+
((_b = errorData.detail) == null ? void 0 : _b.message) || errorData.message || `Failed to submit consent event: ${response.statusText}`
|
|
395
|
+
);
|
|
396
|
+
error.status = response.status;
|
|
397
|
+
error.code = errorData.code;
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
var fetchTTSAudioUrls = async ({
|
|
402
|
+
accessToken,
|
|
403
|
+
baseUrl,
|
|
404
|
+
noticeUuid,
|
|
405
|
+
language,
|
|
406
|
+
signal
|
|
407
|
+
}) => {
|
|
408
|
+
if (!accessToken || !noticeUuid || !language) {
|
|
409
|
+
throw new Error("accessToken, noticeUuid, and language are required");
|
|
410
|
+
}
|
|
411
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
412
|
+
if (!decodedToken) throw new Error("Invalid access token");
|
|
413
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
414
|
+
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
415
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
416
|
+
const url = `${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/notices/${noticeUuid}/audio/${language}`;
|
|
417
|
+
const response = await fetch(url, {
|
|
418
|
+
method: "GET",
|
|
419
|
+
headers: {
|
|
420
|
+
Authorization: `Bearer ${accessToken}`,
|
|
421
|
+
Accept: "application/json"
|
|
422
|
+
},
|
|
423
|
+
signal
|
|
424
|
+
});
|
|
425
|
+
if (!response.ok) {
|
|
426
|
+
let errorMessage = `Failed to fetch TTS audio URLs: ${response.status} ${response.statusText}`;
|
|
427
|
+
if (response.status === 401) errorMessage = "Unauthorized: Invalid or expired token";
|
|
428
|
+
else if (response.status === 403) errorMessage = "Forbidden: Access denied";
|
|
429
|
+
else if (response.status === 404) errorMessage = "Notice or audio not found";
|
|
430
|
+
else if (response.status >= 500) errorMessage = "Server error: Please try again later";
|
|
431
|
+
const error = new Error(errorMessage);
|
|
432
|
+
error.status = response.status;
|
|
433
|
+
throw error;
|
|
434
|
+
}
|
|
435
|
+
return await response.json();
|
|
436
|
+
};
|
|
437
|
+
var initiateGuardianVerification = async ({
|
|
438
|
+
accessToken,
|
|
439
|
+
baseUrl,
|
|
440
|
+
guardianName,
|
|
441
|
+
guardianContact,
|
|
442
|
+
guardianRelationship,
|
|
443
|
+
frontendCallbackUrl,
|
|
444
|
+
metadata,
|
|
445
|
+
signal
|
|
446
|
+
}) => {
|
|
447
|
+
var _a;
|
|
448
|
+
if (!accessToken) throw new Error("accessToken is required");
|
|
449
|
+
if (!(guardianName == null ? void 0 : guardianName.trim())) throw new Error("Guardian name is required");
|
|
450
|
+
if (!(guardianContact == null ? void 0 : guardianContact.trim())) throw new Error("Guardian contact is required");
|
|
451
|
+
if (!(guardianRelationship == null ? void 0 : guardianRelationship.trim())) throw new Error("Guardian relationship is required");
|
|
452
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
453
|
+
if (!decodedToken) throw new Error("Invalid access token");
|
|
454
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
455
|
+
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
456
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
457
|
+
const payload = {
|
|
458
|
+
guardian_name: guardianName.trim(),
|
|
459
|
+
guardian_contact: guardianContact.trim(),
|
|
460
|
+
guardian_relationship: guardianRelationship.trim()
|
|
461
|
+
};
|
|
462
|
+
if (frontendCallbackUrl) payload.frontend_callback_url = frontendCallbackUrl;
|
|
463
|
+
if (metadata) payload.metadata = metadata;
|
|
464
|
+
const response = await fetch(
|
|
465
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/guardian/initiate-verification`,
|
|
466
|
+
{
|
|
467
|
+
method: "POST",
|
|
468
|
+
headers: {
|
|
469
|
+
Authorization: `Bearer ${accessToken}`,
|
|
470
|
+
"Content-Type": "application/json"
|
|
471
|
+
},
|
|
472
|
+
body: JSON.stringify(payload),
|
|
473
|
+
signal
|
|
474
|
+
}
|
|
475
|
+
);
|
|
476
|
+
if (!response.ok) {
|
|
477
|
+
let errorMessage = `Failed to initiate verification: ${response.status} ${response.statusText}`;
|
|
478
|
+
if (response.status === 400) {
|
|
479
|
+
try {
|
|
480
|
+
const errorData = await response.json();
|
|
481
|
+
errorMessage = errorData.message || "Invalid request data";
|
|
482
|
+
} catch {
|
|
483
|
+
errorMessage = "Invalid request data";
|
|
484
|
+
}
|
|
485
|
+
} else if (response.status === 401) errorMessage = "Unauthorized: Invalid or expired token";
|
|
486
|
+
else if (response.status === 404) errorMessage = "Organization or workspace not found";
|
|
487
|
+
else if (response.status === 422) {
|
|
488
|
+
try {
|
|
489
|
+
const errorData = await response.json();
|
|
490
|
+
errorMessage = errorData.message || "Validation error: Please check your input";
|
|
491
|
+
} catch {
|
|
492
|
+
errorMessage = "Validation error: Please check your input";
|
|
493
|
+
}
|
|
494
|
+
} else if (response.status >= 500) errorMessage = "Server error: Please try again later";
|
|
495
|
+
const error = new Error(errorMessage);
|
|
496
|
+
error.status = response.status;
|
|
497
|
+
throw error;
|
|
498
|
+
}
|
|
499
|
+
const responseData = await response.json();
|
|
500
|
+
const detail = responseData.detail || responseData;
|
|
501
|
+
if (detail.already_verified) {
|
|
502
|
+
return {
|
|
503
|
+
success: true,
|
|
504
|
+
guardian_info_uuid: detail.guardian_info_uuid,
|
|
505
|
+
already_verified: true,
|
|
506
|
+
verification_reference: detail.verification_reference
|
|
507
|
+
};
|
|
508
|
+
}
|
|
509
|
+
if (!detail.session_token || !detail.digilocker_redirect_url) {
|
|
510
|
+
throw new Error("Invalid response: missing session_token or digilocker_redirect_url");
|
|
511
|
+
}
|
|
512
|
+
return {
|
|
513
|
+
success: (_a = detail.success) != null ? _a : true,
|
|
514
|
+
guardian_info_uuid: detail.guardian_info_uuid,
|
|
515
|
+
already_verified: false,
|
|
516
|
+
session_token: detail.session_token,
|
|
517
|
+
digilocker_redirect_url: detail.digilocker_redirect_url,
|
|
518
|
+
expires_at: detail.expires_at
|
|
519
|
+
};
|
|
520
|
+
};
|
|
521
|
+
var verifyGuardianStatus = async ({
|
|
522
|
+
accessToken,
|
|
523
|
+
baseUrl,
|
|
524
|
+
sessionToken,
|
|
525
|
+
signal
|
|
526
|
+
}) => {
|
|
527
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
528
|
+
if (!accessToken || !sessionToken) {
|
|
529
|
+
throw new Error("accessToken and sessionToken are required");
|
|
530
|
+
}
|
|
531
|
+
const decodedToken = decodeTokenSafely(accessToken);
|
|
532
|
+
if (!decodedToken) throw new Error("Invalid access token");
|
|
533
|
+
const { organisation_uuid: ORG, workspace_uuid: WS } = decodedToken;
|
|
534
|
+
if (!ORG || !WS) throw new Error("Invalid token: missing organization or workspace UUID");
|
|
535
|
+
const apiBaseUrl = baseUrl || BASE_URL;
|
|
536
|
+
const response = await fetch(
|
|
537
|
+
`${apiBaseUrl}/public/organisations/${ORG}/workspaces/${WS}/guardian/verify-status`,
|
|
538
|
+
{
|
|
539
|
+
method: "POST",
|
|
540
|
+
headers: {
|
|
541
|
+
Authorization: `Bearer ${accessToken}`,
|
|
542
|
+
"Content-Type": "application/json"
|
|
543
|
+
},
|
|
544
|
+
body: JSON.stringify({ session_token: sessionToken }),
|
|
545
|
+
signal
|
|
546
|
+
}
|
|
547
|
+
);
|
|
548
|
+
if (!response.ok) {
|
|
549
|
+
let errorMessage = `Failed to check verification status: ${response.status} ${response.statusText}`;
|
|
550
|
+
if (response.status === 400) {
|
|
551
|
+
try {
|
|
552
|
+
const errorData = await response.json();
|
|
553
|
+
errorMessage = errorData.message || ((_a = errorData.detail) == null ? void 0 : _a.message) || "Invalid session token";
|
|
554
|
+
} catch {
|
|
555
|
+
errorMessage = "Invalid session token";
|
|
556
|
+
}
|
|
557
|
+
} else if (response.status === 401) errorMessage = "Unauthorized: Invalid or expired token";
|
|
558
|
+
else if (response.status === 404)
|
|
559
|
+
errorMessage = "Verification session not found or expired. Please restart the verification process.";
|
|
560
|
+
else if (response.status === 410)
|
|
561
|
+
errorMessage = "Verification session has expired. Please restart the verification process.";
|
|
562
|
+
else if (response.status >= 500) errorMessage = "Server error: Please try again later";
|
|
563
|
+
const error = new Error(errorMessage);
|
|
564
|
+
error.status = response.status;
|
|
565
|
+
throw error;
|
|
566
|
+
}
|
|
567
|
+
const responseData = await response.json();
|
|
568
|
+
const detail = responseData.detail || responseData;
|
|
569
|
+
return {
|
|
570
|
+
status: (_b = detail.status) != null ? _b : "in_progress",
|
|
571
|
+
guardian_info_uuid: (_c = detail.guardian_info_uuid) != null ? _c : null,
|
|
572
|
+
verification_reference: (_d = detail.verification_reference) != null ? _d : null,
|
|
573
|
+
guardian_details: (_e = detail.guardian_details) != null ? _e : null,
|
|
574
|
+
error: (_f = detail.error) != null ? _f : null,
|
|
575
|
+
error_code: (_g = detail.error_code) != null ? _g : null,
|
|
576
|
+
can_retry: (_h = detail.can_retry) != null ? _h : true
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
|
|
580
|
+
// src/styles/inject.ts
|
|
581
|
+
var stylesInjected = false;
|
|
582
|
+
var injectCheckboxStyles = () => {
|
|
583
|
+
if (stylesInjected || typeof document === "undefined") {
|
|
584
|
+
return;
|
|
585
|
+
}
|
|
586
|
+
const STYLE_ID = "redacto-consent-checkbox-styles";
|
|
587
|
+
if (document.getElementById(STYLE_ID)) {
|
|
588
|
+
stylesInjected = true;
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
const style = document.createElement("style");
|
|
592
|
+
style.id = STYLE_ID;
|
|
593
|
+
style.textContent = `
|
|
594
|
+
/* Redacto Consent SDK Checkbox Styles */
|
|
595
|
+
.redacto-checkbox-large {
|
|
596
|
+
appearance: none !important;
|
|
597
|
+
-webkit-appearance: none !important;
|
|
598
|
+
-moz-appearance: none !important;
|
|
599
|
+
height: 20px !important;
|
|
600
|
+
width: 20px !important;
|
|
601
|
+
border: 2px solid #d0d5dd !important;
|
|
602
|
+
border-radius: 5px !important;
|
|
603
|
+
background-color: transparent !important;
|
|
604
|
+
cursor: pointer !important;
|
|
605
|
+
position: relative !important;
|
|
606
|
+
box-sizing: border-box !important;
|
|
607
|
+
margin: 0 !important;
|
|
608
|
+
padding: 4px !important;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.redacto-checkbox-small {
|
|
612
|
+
appearance: none !important;
|
|
613
|
+
-webkit-appearance: none !important;
|
|
614
|
+
-moz-appearance: none !important;
|
|
615
|
+
height: 16px !important;
|
|
616
|
+
width: 16px !important;
|
|
617
|
+
border: 2px solid #d0d5dd !important;
|
|
618
|
+
border-radius: 5px !important;
|
|
619
|
+
background-color: transparent !important;
|
|
620
|
+
cursor: pointer !important;
|
|
621
|
+
position: relative !important;
|
|
622
|
+
box-sizing: border-box !important;
|
|
623
|
+
margin: 0 !important;
|
|
624
|
+
padding: 4px !important;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
.redacto-checkbox-large:checked {
|
|
628
|
+
background-color: #4f87ff !important;
|
|
629
|
+
border-color: #4f87ff !important;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
.redacto-checkbox-small:checked {
|
|
633
|
+
background-color: #4f87ff !important;
|
|
634
|
+
border-color: #4f87ff !important;
|
|
635
|
+
}
|
|
636
|
+
|
|
637
|
+
.redacto-checkbox-large:checked::after {
|
|
638
|
+
content: '' !important;
|
|
639
|
+
position: absolute !important;
|
|
640
|
+
left: 50% !important;
|
|
641
|
+
top: 50% !important;
|
|
642
|
+
transform: translate(-50%, -50%) rotate(45deg) !important;
|
|
643
|
+
width: 4px !important;
|
|
644
|
+
height: 8px !important;
|
|
645
|
+
border: solid white !important;
|
|
646
|
+
border-width: 0 2px 2px 0 !important;
|
|
647
|
+
box-sizing: border-box !important;
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
.redacto-checkbox-small:checked::after {
|
|
651
|
+
content: '' !important;
|
|
652
|
+
position: absolute !important;
|
|
653
|
+
left: 50% !important;
|
|
654
|
+
top: 50% !important;
|
|
655
|
+
transform: translate(-50%, -50%) rotate(45deg) !important;
|
|
656
|
+
width: 3px !important;
|
|
657
|
+
height: 6px !important;
|
|
658
|
+
border: solid white !important;
|
|
659
|
+
border-width: 0 1.5px 1.5px 0 !important;
|
|
660
|
+
box-sizing: border-box !important;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.redacto-checkbox-large:focus {
|
|
664
|
+
outline: 2px solid #4f87ff !important;
|
|
665
|
+
outline-offset: 2px !important;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
.redacto-checkbox-small:focus {
|
|
669
|
+
outline: 2px solid #4f87ff !important;
|
|
670
|
+
outline-offset: 2px !important;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/* TTS Highlight Styles */
|
|
674
|
+
.redacto-tts-highlight {
|
|
675
|
+
position: relative !important;
|
|
676
|
+
transition: background-color 0.3s ease, outline 0.3s ease !important;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
h2.redacto-tts-highlight,
|
|
680
|
+
h3.redacto-tts-highlight {
|
|
681
|
+
background-color: #FFF9C4 !important;
|
|
682
|
+
border-radius: 3px !important;
|
|
683
|
+
padding: 2px 4px !important;
|
|
684
|
+
margin: -2px -4px !important;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
span.redacto-tts-highlight,
|
|
688
|
+
a.redacto-tts-highlight {
|
|
689
|
+
background-color: #FFF9C4 !important;
|
|
690
|
+
border-radius: 3px !important;
|
|
691
|
+
padding: 2px 4px !important;
|
|
692
|
+
margin: -2px -4px !important;
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
p.redacto-tts-highlight {
|
|
696
|
+
background-color: #FFF9C4 !important;
|
|
697
|
+
border-radius: 3px !important;
|
|
698
|
+
padding: 2px 4px !important;
|
|
699
|
+
margin: -2px -4px !important;
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
button.redacto-tts-highlight {
|
|
703
|
+
outline: 3px solid #FFF9C4 !important;
|
|
704
|
+
outline-offset: 2px !important;
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
/* Overlay fade-in */
|
|
708
|
+
@keyframes overlayFadeIn {
|
|
709
|
+
from { opacity: 0; }
|
|
710
|
+
to { opacity: 1; }
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/* Spinner animation */
|
|
714
|
+
@keyframes spin {
|
|
715
|
+
from { transform: rotate(0deg); }
|
|
716
|
+
to { transform: rotate(360deg); }
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
/* DigiLocker callback retry button hover */
|
|
720
|
+
.redacto-dl-retry-btn:hover {
|
|
721
|
+
background-color: #2563eb !important;
|
|
722
|
+
}
|
|
723
|
+
`;
|
|
724
|
+
document.head.appendChild(style);
|
|
725
|
+
stylesInjected = true;
|
|
726
|
+
};
|
|
727
|
+
|
|
728
|
+
// src/styles/modal.ts
|
|
729
|
+
var styles = {
|
|
730
|
+
overlay: {
|
|
731
|
+
position: "fixed",
|
|
732
|
+
top: 0,
|
|
733
|
+
left: 0,
|
|
734
|
+
width: "100%",
|
|
735
|
+
height: "100%",
|
|
736
|
+
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
737
|
+
display: "flex",
|
|
738
|
+
alignItems: "center",
|
|
739
|
+
justifyContent: "center",
|
|
740
|
+
zIndex: 999,
|
|
741
|
+
animation: "overlayFadeIn 0.2s cubic-bezier(0.16, 1, 0.3, 1)",
|
|
742
|
+
pointerEvents: "all",
|
|
743
|
+
margin: 0,
|
|
744
|
+
padding: 0,
|
|
745
|
+
boxSizing: "border-box"
|
|
746
|
+
},
|
|
747
|
+
modal: {
|
|
748
|
+
position: "fixed",
|
|
749
|
+
top: "50%",
|
|
750
|
+
left: "50%",
|
|
751
|
+
transform: "translate(-50%, -50%)",
|
|
752
|
+
width: "700px",
|
|
753
|
+
borderRadius: "8px",
|
|
754
|
+
backgroundColor: "#ffffff",
|
|
755
|
+
boxShadow: "4px 0px 4px 0px #00000040",
|
|
756
|
+
border: "none",
|
|
757
|
+
display: "flex",
|
|
758
|
+
flexDirection: "column",
|
|
759
|
+
maxHeight: "90vh",
|
|
760
|
+
zIndex: 1e3,
|
|
761
|
+
margin: 0,
|
|
762
|
+
padding: 0,
|
|
763
|
+
boxSizing: "border-box",
|
|
764
|
+
textAlign: "left",
|
|
765
|
+
fontFamily: "inherit"
|
|
766
|
+
},
|
|
767
|
+
content: {
|
|
768
|
+
margin: "22px",
|
|
769
|
+
display: "flex",
|
|
770
|
+
flexDirection: "column",
|
|
771
|
+
flexGrow: 1,
|
|
772
|
+
overflow: "hidden",
|
|
773
|
+
minHeight: 0,
|
|
774
|
+
boxSizing: "border-box",
|
|
775
|
+
textAlign: "left"
|
|
776
|
+
},
|
|
777
|
+
topSection: {
|
|
778
|
+
display: "flex",
|
|
779
|
+
justifyContent: "space-between",
|
|
780
|
+
alignItems: "center",
|
|
781
|
+
paddingBottom: "15px",
|
|
782
|
+
borderBottom: "none",
|
|
783
|
+
backgroundColor: "#ffffff",
|
|
784
|
+
margin: 0,
|
|
785
|
+
boxSizing: "border-box",
|
|
786
|
+
textAlign: "left"
|
|
787
|
+
},
|
|
788
|
+
topLeft: {
|
|
789
|
+
display: "flex",
|
|
790
|
+
alignItems: "center",
|
|
791
|
+
gap: "10px",
|
|
792
|
+
margin: 0,
|
|
793
|
+
padding: 0,
|
|
794
|
+
boxSizing: "border-box"
|
|
795
|
+
},
|
|
796
|
+
logo: {
|
|
797
|
+
height: "32px",
|
|
798
|
+
width: "auto",
|
|
799
|
+
objectFit: "contain",
|
|
800
|
+
margin: 0,
|
|
801
|
+
padding: 0,
|
|
802
|
+
display: "block"
|
|
803
|
+
},
|
|
804
|
+
title: {
|
|
805
|
+
fontSize: "18px",
|
|
806
|
+
fontWeight: 700,
|
|
807
|
+
lineHeight: "150%",
|
|
808
|
+
letterSpacing: "0.2px",
|
|
809
|
+
verticalAlign: "middle",
|
|
810
|
+
color: "#101828",
|
|
811
|
+
margin: 0,
|
|
812
|
+
padding: 0,
|
|
813
|
+
textAlign: "left"
|
|
814
|
+
},
|
|
815
|
+
topRight: {
|
|
816
|
+
borderRadius: "8px",
|
|
817
|
+
padding: "3px 9px",
|
|
818
|
+
border: "1px solid #d0d5dd",
|
|
819
|
+
display: "flex",
|
|
820
|
+
alignItems: "center",
|
|
821
|
+
justifyContent: "center",
|
|
822
|
+
fontWeight: 400,
|
|
823
|
+
fontSize: "12px",
|
|
824
|
+
lineHeight: "150%",
|
|
825
|
+
letterSpacing: "0.2px",
|
|
826
|
+
verticalAlign: "middle",
|
|
827
|
+
color: "#344054",
|
|
828
|
+
gap: "5px",
|
|
829
|
+
margin: 0,
|
|
830
|
+
boxSizing: "border-box",
|
|
831
|
+
textAlign: "left",
|
|
832
|
+
cursor: "pointer",
|
|
833
|
+
backgroundColor: "#ffffff"
|
|
834
|
+
},
|
|
835
|
+
middleSection: {
|
|
836
|
+
margin: "0px",
|
|
837
|
+
display: "flex",
|
|
838
|
+
flexDirection: "column",
|
|
839
|
+
gap: "12px",
|
|
840
|
+
flexGrow: 1,
|
|
841
|
+
overflowY: "auto",
|
|
842
|
+
minHeight: 0,
|
|
843
|
+
paddingRight: "15px",
|
|
844
|
+
boxSizing: "border-box",
|
|
845
|
+
textAlign: "left"
|
|
846
|
+
},
|
|
847
|
+
privacyText: {
|
|
848
|
+
fontSize: "16px",
|
|
849
|
+
fontWeight: 400,
|
|
850
|
+
lineHeight: "150%",
|
|
851
|
+
color: "#344054",
|
|
852
|
+
margin: 0,
|
|
853
|
+
padding: 0,
|
|
854
|
+
textAlign: "left"
|
|
855
|
+
},
|
|
856
|
+
link: {
|
|
857
|
+
color: "#4f87ff",
|
|
858
|
+
textDecoration: "none",
|
|
859
|
+
margin: 0,
|
|
860
|
+
padding: 0,
|
|
861
|
+
boxSizing: "border-box"
|
|
862
|
+
},
|
|
863
|
+
subTitle: {
|
|
864
|
+
fontWeight: 600,
|
|
865
|
+
fontSize: "16px",
|
|
866
|
+
lineHeight: "150%",
|
|
867
|
+
letterSpacing: "0.2px",
|
|
868
|
+
color: "#101828",
|
|
869
|
+
margin: 0,
|
|
870
|
+
padding: 0,
|
|
871
|
+
textAlign: "left"
|
|
872
|
+
},
|
|
873
|
+
optionsContainer: {
|
|
874
|
+
display: "flex",
|
|
875
|
+
flexDirection: "column",
|
|
876
|
+
gap: "10px",
|
|
877
|
+
margin: 0,
|
|
878
|
+
padding: 0,
|
|
879
|
+
boxSizing: "border-box"
|
|
880
|
+
},
|
|
881
|
+
optionItem: {
|
|
882
|
+
display: "flex",
|
|
883
|
+
justifyContent: "space-between",
|
|
884
|
+
alignItems: "center",
|
|
885
|
+
margin: 0,
|
|
886
|
+
padding: 0,
|
|
887
|
+
boxSizing: "border-box"
|
|
888
|
+
},
|
|
889
|
+
optionLeft: {
|
|
890
|
+
display: "flex",
|
|
891
|
+
gap: "12px",
|
|
892
|
+
alignItems: "center",
|
|
893
|
+
cursor: "pointer",
|
|
894
|
+
margin: 0,
|
|
895
|
+
padding: 0,
|
|
896
|
+
boxSizing: "border-box"
|
|
897
|
+
},
|
|
898
|
+
optionTextContainer: {
|
|
899
|
+
display: "flex",
|
|
900
|
+
flexDirection: "column",
|
|
901
|
+
margin: 0,
|
|
902
|
+
padding: 0,
|
|
903
|
+
boxSizing: "border-box"
|
|
904
|
+
},
|
|
905
|
+
optionTitle: {
|
|
906
|
+
fontWeight: 500,
|
|
907
|
+
fontSize: "16px",
|
|
908
|
+
lineHeight: "150%",
|
|
909
|
+
letterSpacing: "0.2px",
|
|
910
|
+
color: "#101828",
|
|
911
|
+
margin: 0,
|
|
912
|
+
padding: 0,
|
|
913
|
+
textAlign: "left"
|
|
914
|
+
},
|
|
915
|
+
optionDescription: {
|
|
916
|
+
fontWeight: 400,
|
|
917
|
+
fontSize: "12px",
|
|
918
|
+
lineHeight: "150%",
|
|
919
|
+
letterSpacing: "0.2px",
|
|
920
|
+
color: "#475467",
|
|
921
|
+
verticalAlign: "middle",
|
|
922
|
+
margin: 0,
|
|
923
|
+
padding: 0,
|
|
924
|
+
textAlign: "left"
|
|
925
|
+
},
|
|
926
|
+
checkboxLarge: {
|
|
927
|
+
height: "20px",
|
|
928
|
+
width: "20px",
|
|
929
|
+
padding: "4px",
|
|
930
|
+
borderRadius: "5px",
|
|
931
|
+
border: "2px solid #d0d5dd",
|
|
932
|
+
margin: 0,
|
|
933
|
+
boxSizing: "border-box",
|
|
934
|
+
appearance: "none",
|
|
935
|
+
WebkitAppearance: "none",
|
|
936
|
+
MozAppearance: "none",
|
|
937
|
+
cursor: "pointer",
|
|
938
|
+
position: "relative",
|
|
939
|
+
backgroundColor: "transparent"
|
|
940
|
+
},
|
|
941
|
+
dataElementsContainer: {
|
|
942
|
+
marginLeft: "27px",
|
|
943
|
+
display: "flex",
|
|
944
|
+
flexDirection: "column",
|
|
945
|
+
margin: 0,
|
|
946
|
+
padding: 0,
|
|
947
|
+
boxSizing: "border-box",
|
|
948
|
+
paddingLeft: "22px"
|
|
949
|
+
},
|
|
950
|
+
dataElementItem: {
|
|
951
|
+
display: "flex",
|
|
952
|
+
alignItems: "center",
|
|
953
|
+
gap: "10px",
|
|
954
|
+
justifyContent: "space-between",
|
|
955
|
+
margin: 0,
|
|
956
|
+
padding: 0,
|
|
957
|
+
boxSizing: "border-box",
|
|
958
|
+
minHeight: "24px"
|
|
959
|
+
},
|
|
960
|
+
dataElementText: {
|
|
961
|
+
fontWeight: 400,
|
|
962
|
+
fontSize: "14px",
|
|
963
|
+
lineHeight: "150%",
|
|
964
|
+
letterSpacing: "0.2px",
|
|
965
|
+
color: "#344054",
|
|
966
|
+
margin: 0,
|
|
967
|
+
padding: 0,
|
|
968
|
+
textAlign: "left"
|
|
969
|
+
},
|
|
970
|
+
checkboxSmall: {
|
|
971
|
+
height: "16px",
|
|
972
|
+
width: "16px",
|
|
973
|
+
padding: "4px",
|
|
974
|
+
borderRadius: "5px",
|
|
975
|
+
border: "2px solid #d0d5dd",
|
|
976
|
+
margin: 0,
|
|
977
|
+
boxSizing: "border-box",
|
|
978
|
+
appearance: "none",
|
|
979
|
+
WebkitAppearance: "none",
|
|
980
|
+
MozAppearance: "none",
|
|
981
|
+
cursor: "pointer",
|
|
982
|
+
position: "relative",
|
|
983
|
+
backgroundColor: "transparent"
|
|
984
|
+
},
|
|
985
|
+
bottomSection: {
|
|
986
|
+
display: "flex",
|
|
987
|
+
justifyContent: "space-between",
|
|
988
|
+
gap: "29px",
|
|
989
|
+
paddingTop: "15px",
|
|
990
|
+
borderTop: "none",
|
|
991
|
+
backgroundColor: "#ffffff",
|
|
992
|
+
margin: 0,
|
|
993
|
+
boxSizing: "border-box"
|
|
994
|
+
},
|
|
995
|
+
button: {
|
|
996
|
+
width: "100%",
|
|
997
|
+
borderRadius: "8px",
|
|
998
|
+
padding: "9px 45px",
|
|
999
|
+
fontWeight: 400,
|
|
1000
|
+
fontSize: "16px",
|
|
1001
|
+
lineHeight: "150%",
|
|
1002
|
+
letterSpacing: "0.2px",
|
|
1003
|
+
cursor: "pointer",
|
|
1004
|
+
margin: 0,
|
|
1005
|
+
boxSizing: "border-box",
|
|
1006
|
+
textAlign: "center"
|
|
1007
|
+
},
|
|
1008
|
+
acceptButton: {
|
|
1009
|
+
backgroundColor: "#4f87ff",
|
|
1010
|
+
border: "none",
|
|
1011
|
+
color: "#ffffff"
|
|
1012
|
+
},
|
|
1013
|
+
cancelButton: {
|
|
1014
|
+
border: "1px solid #d0d5dd",
|
|
1015
|
+
backgroundColor: "#ffffff",
|
|
1016
|
+
color: "#000000"
|
|
1017
|
+
},
|
|
1018
|
+
languageSelectorContainer: {
|
|
1019
|
+
position: "relative",
|
|
1020
|
+
margin: 0,
|
|
1021
|
+
padding: 0,
|
|
1022
|
+
boxSizing: "border-box"
|
|
1023
|
+
},
|
|
1024
|
+
languageDropdown: {
|
|
1025
|
+
position: "absolute",
|
|
1026
|
+
top: "100%",
|
|
1027
|
+
right: 0,
|
|
1028
|
+
backgroundColor: "#ffffff",
|
|
1029
|
+
border: "1px solid #d0d5dd",
|
|
1030
|
+
borderRadius: "4px",
|
|
1031
|
+
boxShadow: "0px 2px 4px rgba(0, 0, 0, 0.1)",
|
|
1032
|
+
zIndex: 10,
|
|
1033
|
+
width: "max-content",
|
|
1034
|
+
maxHeight: "200px",
|
|
1035
|
+
overflowY: "auto",
|
|
1036
|
+
marginTop: "4px",
|
|
1037
|
+
padding: 0,
|
|
1038
|
+
boxSizing: "border-box",
|
|
1039
|
+
textAlign: "left"
|
|
1040
|
+
},
|
|
1041
|
+
languageItem: {
|
|
1042
|
+
padding: "8px 12px",
|
|
1043
|
+
cursor: "pointer",
|
|
1044
|
+
fontSize: "12px",
|
|
1045
|
+
color: "#344054",
|
|
1046
|
+
backgroundColor: "#ffffff",
|
|
1047
|
+
borderBottom: "1px solid #e5e7eb",
|
|
1048
|
+
margin: 0,
|
|
1049
|
+
boxSizing: "border-box",
|
|
1050
|
+
textAlign: "left"
|
|
1051
|
+
},
|
|
1052
|
+
selectedLanguageItem: {
|
|
1053
|
+
backgroundColor: "#f3f4f6",
|
|
1054
|
+
fontWeight: 600
|
|
1055
|
+
},
|
|
1056
|
+
loadingContainer: {
|
|
1057
|
+
display: "flex",
|
|
1058
|
+
flexDirection: "column",
|
|
1059
|
+
alignItems: "center",
|
|
1060
|
+
justifyContent: "center",
|
|
1061
|
+
padding: "2rem",
|
|
1062
|
+
minHeight: "200px",
|
|
1063
|
+
margin: 0,
|
|
1064
|
+
boxSizing: "border-box"
|
|
1065
|
+
},
|
|
1066
|
+
loadingSpinner: {
|
|
1067
|
+
width: "40px",
|
|
1068
|
+
height: "40px",
|
|
1069
|
+
border: "4px solid #f3f3f3",
|
|
1070
|
+
borderTop: "4px solid #3498db",
|
|
1071
|
+
borderRadius: "50%",
|
|
1072
|
+
animation: "spin 1s linear infinite",
|
|
1073
|
+
marginBottom: "1rem",
|
|
1074
|
+
boxSizing: "border-box"
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
|
|
1078
|
+
// src/modal/purposes.ts
|
|
1079
|
+
function renderModalPurposes(purposes, options) {
|
|
1080
|
+
var _a, _b;
|
|
1081
|
+
const {
|
|
1082
|
+
selectedPurposes,
|
|
1083
|
+
collapsedPurposes,
|
|
1084
|
+
selectedDataElements,
|
|
1085
|
+
initialDataElementSelections = {},
|
|
1086
|
+
settings,
|
|
1087
|
+
isMobile,
|
|
1088
|
+
isAlreadyConsented,
|
|
1089
|
+
getTranslatedText,
|
|
1090
|
+
onPurposeToggle,
|
|
1091
|
+
onPurposeCollapse,
|
|
1092
|
+
onDataElementToggle
|
|
1093
|
+
} = options;
|
|
1094
|
+
const checkboxes = /* @__PURE__ */ new Map();
|
|
1095
|
+
const collapseButtons = /* @__PURE__ */ new Map();
|
|
1096
|
+
const dataElementContainers = /* @__PURE__ */ new Map();
|
|
1097
|
+
const chevrons = /* @__PURE__ */ new Map();
|
|
1098
|
+
const titleElements = /* @__PURE__ */ new Map();
|
|
1099
|
+
const descriptionElements = /* @__PURE__ */ new Map();
|
|
1100
|
+
const dataElementLabels = /* @__PURE__ */ new Map();
|
|
1101
|
+
const headingColor = (settings == null ? void 0 : settings.headingColor) || "#323B4B";
|
|
1102
|
+
const textColor = (settings == null ? void 0 : settings.textColor) || "#344054";
|
|
1103
|
+
const font = (settings == null ? void 0 : settings.font) || "inherit";
|
|
1104
|
+
const container = el("div", { style: styles.optionsContainer });
|
|
1105
|
+
for (const purpose of purposes) {
|
|
1106
|
+
const purposeWrapper = el("div");
|
|
1107
|
+
const chevron = svg(
|
|
1108
|
+
"svg",
|
|
1109
|
+
{
|
|
1110
|
+
width: "7",
|
|
1111
|
+
height: "12",
|
|
1112
|
+
viewBox: "0 0 7 12",
|
|
1113
|
+
fill: "none",
|
|
1114
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1115
|
+
stroke: headingColor,
|
|
1116
|
+
"stroke-width": "2",
|
|
1117
|
+
"stroke-linecap": "round",
|
|
1118
|
+
"stroke-linejoin": "round",
|
|
1119
|
+
style: {
|
|
1120
|
+
transform: !collapsedPurposes[purpose.uuid] ? "rotate(90deg)" : "rotate(0deg)",
|
|
1121
|
+
transition: "transform 0.3s ease"
|
|
1122
|
+
},
|
|
1123
|
+
"aria-hidden": "true"
|
|
1124
|
+
},
|
|
1125
|
+
svg("path", { d: "M1 1L6 6L1 11" })
|
|
1126
|
+
);
|
|
1127
|
+
chevrons.set(purpose.uuid, chevron);
|
|
1128
|
+
const chevronContainer = el(
|
|
1129
|
+
"div",
|
|
1130
|
+
{ style: { display: "flex", alignItems: "center", justifyContent: "center", marginLeft: "5px" } },
|
|
1131
|
+
chevron
|
|
1132
|
+
);
|
|
1133
|
+
const titleEl = el("h2", {
|
|
1134
|
+
id: `purpose-name-${purpose.uuid}`,
|
|
1135
|
+
style: {
|
|
1136
|
+
...styles.optionTitle,
|
|
1137
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
1138
|
+
color: headingColor,
|
|
1139
|
+
fontFamily: font
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
1143
|
+
titleElements.set(purpose.uuid, titleEl);
|
|
1144
|
+
const descEl = el("h3", {
|
|
1145
|
+
id: `purpose-description-${purpose.uuid}`,
|
|
1146
|
+
style: {
|
|
1147
|
+
...styles.optionDescription,
|
|
1148
|
+
fontSize: isMobile ? "11px" : "12px",
|
|
1149
|
+
color: textColor,
|
|
1150
|
+
fontFamily: font
|
|
1151
|
+
}
|
|
1152
|
+
});
|
|
1153
|
+
descEl.textContent = getTranslatedText("purposes.description", purpose.description, purpose.uuid);
|
|
1154
|
+
descriptionElements.set(purpose.uuid, descEl);
|
|
1155
|
+
const textContainer = el("div", { style: styles.optionTextContainer }, titleEl, descEl);
|
|
1156
|
+
const collapseBtn = el("div", {
|
|
1157
|
+
style: styles.optionLeft,
|
|
1158
|
+
role: "button",
|
|
1159
|
+
tabIndex: "0",
|
|
1160
|
+
"aria-expanded": String(!collapsedPurposes[purpose.uuid]),
|
|
1161
|
+
"aria-controls": `purpose-${purpose.uuid}`,
|
|
1162
|
+
"aria-label": `${collapsedPurposes[purpose.uuid] ? "Expand" : "Collapse"} ${getTranslatedText("purposes.name", purpose.name, purpose.uuid)} details`,
|
|
1163
|
+
onClick: () => onPurposeCollapse(purpose.uuid),
|
|
1164
|
+
onKeydown: (e) => {
|
|
1165
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1166
|
+
e.preventDefault();
|
|
1167
|
+
onPurposeCollapse(purpose.uuid);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
}, chevronContainer, textContainer);
|
|
1171
|
+
collapseButtons.set(purpose.uuid, collapseBtn);
|
|
1172
|
+
let rightElement;
|
|
1173
|
+
if (!isAlreadyConsented) {
|
|
1174
|
+
const purposeCheckbox = el("input", {
|
|
1175
|
+
className: "redacto-checkbox-large",
|
|
1176
|
+
type: "checkbox",
|
|
1177
|
+
checked: selectedPurposes[purpose.uuid] || false,
|
|
1178
|
+
"aria-label": `Select all data elements for ${getTranslatedText("purposes.name", purpose.name, purpose.uuid)}`,
|
|
1179
|
+
onChange: () => onPurposeToggle(purpose.uuid)
|
|
1180
|
+
});
|
|
1181
|
+
checkboxes.set(purpose.uuid, purposeCheckbox);
|
|
1182
|
+
rightElement = purposeCheckbox;
|
|
1183
|
+
} else {
|
|
1184
|
+
rightElement = createGreenCheckmark(
|
|
1185
|
+
20,
|
|
1186
|
+
`Already consented to ${getTranslatedText("purposes.name", purpose.name, purpose.uuid)}`
|
|
1187
|
+
);
|
|
1188
|
+
applyStyles(rightElement, { marginLeft: "12px" });
|
|
1189
|
+
}
|
|
1190
|
+
const optionRow = el("div", { style: styles.optionItem }, collapseBtn, rightElement);
|
|
1191
|
+
const deContainer = el("div", {
|
|
1192
|
+
id: `purpose-${purpose.uuid}`,
|
|
1193
|
+
style: styles.dataElementsContainer
|
|
1194
|
+
});
|
|
1195
|
+
if (((_a = purpose.data_elements) == null ? void 0 : _a.length) > 0) {
|
|
1196
|
+
for (const dataElement of purpose.data_elements) {
|
|
1197
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
1198
|
+
const labelSpan = el("span", {
|
|
1199
|
+
id: `data-element-${purpose.uuid}-${dataElement.uuid}`,
|
|
1200
|
+
style: {
|
|
1201
|
+
...styles.dataElementText,
|
|
1202
|
+
fontSize: isMobile ? "12px" : "14px",
|
|
1203
|
+
color: textColor,
|
|
1204
|
+
fontFamily: font
|
|
1205
|
+
}
|
|
1206
|
+
});
|
|
1207
|
+
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
1208
|
+
dataElementLabels.set(combinedId, labelSpan);
|
|
1209
|
+
if (dataElement.required) {
|
|
1210
|
+
const asterisk = el("span", { style: { color: "red", marginLeft: "4px" }, "aria-label": "required" });
|
|
1211
|
+
asterisk.textContent = "*";
|
|
1212
|
+
labelSpan.appendChild(asterisk);
|
|
1213
|
+
}
|
|
1214
|
+
let deRightElement;
|
|
1215
|
+
if (!isAlreadyConsented) {
|
|
1216
|
+
const deCheckbox = el("input", {
|
|
1217
|
+
className: "redacto-checkbox-small",
|
|
1218
|
+
type: "checkbox",
|
|
1219
|
+
checked: selectedDataElements[combinedId] || false,
|
|
1220
|
+
"aria-label": `Select ${getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid)}${dataElement.required ? " (required)" : ""}`,
|
|
1221
|
+
onChange: () => onDataElementToggle(dataElement.uuid, purpose.uuid)
|
|
1222
|
+
});
|
|
1223
|
+
checkboxes.set(combinedId, deCheckbox);
|
|
1224
|
+
deRightElement = deCheckbox;
|
|
1225
|
+
} else {
|
|
1226
|
+
const wasSelected = initialDataElementSelections[combinedId] || false;
|
|
1227
|
+
if (wasSelected) {
|
|
1228
|
+
deRightElement = createGreenCheckmark(
|
|
1229
|
+
16,
|
|
1230
|
+
`Consented to ${getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid)}`
|
|
1231
|
+
);
|
|
1232
|
+
} else {
|
|
1233
|
+
deRightElement = el("div", {
|
|
1234
|
+
style: {
|
|
1235
|
+
display: "flex",
|
|
1236
|
+
alignItems: "center",
|
|
1237
|
+
justifyContent: "center",
|
|
1238
|
+
width: "16px",
|
|
1239
|
+
height: "16px",
|
|
1240
|
+
borderRadius: "50%",
|
|
1241
|
+
border: "2px solid #d0d5dd",
|
|
1242
|
+
backgroundColor: "transparent"
|
|
1243
|
+
},
|
|
1244
|
+
"aria-label": `Not consented to ${getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid)}`
|
|
1245
|
+
});
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
const deRow = el("div", { style: styles.dataElementItem }, labelSpan, deRightElement);
|
|
1249
|
+
deContainer.appendChild(deRow);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
dataElementContainers.set(purpose.uuid, deContainer);
|
|
1253
|
+
deContainer.style.display = !collapsedPurposes[purpose.uuid] && ((_b = purpose.data_elements) == null ? void 0 : _b.length) > 0 ? "flex" : "none";
|
|
1254
|
+
purposeWrapper.appendChild(optionRow);
|
|
1255
|
+
purposeWrapper.appendChild(deContainer);
|
|
1256
|
+
container.appendChild(purposeWrapper);
|
|
1257
|
+
}
|
|
1258
|
+
return {
|
|
1259
|
+
container,
|
|
1260
|
+
checkboxes,
|
|
1261
|
+
collapseButtons,
|
|
1262
|
+
dataElementContainers,
|
|
1263
|
+
chevrons,
|
|
1264
|
+
titleElements,
|
|
1265
|
+
descriptionElements,
|
|
1266
|
+
dataElementLabels
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
function createGreenCheckmark(size, ariaLabel) {
|
|
1270
|
+
const svgSize = size === 20 ? 12 : 10;
|
|
1271
|
+
const strokeWidth = size === 20 ? "2" : "1.5";
|
|
1272
|
+
const pathD = size === 20 ? "M4 6L5.5 7.5L8.5 4.5" : "M3 5L4.5 6.5L7.5 3.5";
|
|
1273
|
+
const checkSvg = svg(
|
|
1274
|
+
"svg",
|
|
1275
|
+
{
|
|
1276
|
+
width: String(svgSize),
|
|
1277
|
+
height: String(svgSize),
|
|
1278
|
+
viewBox: `0 0 ${svgSize} ${svgSize}`,
|
|
1279
|
+
fill: "none",
|
|
1280
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1281
|
+
"aria-hidden": "true"
|
|
1282
|
+
},
|
|
1283
|
+
svg("path", {
|
|
1284
|
+
d: pathD,
|
|
1285
|
+
stroke: "white",
|
|
1286
|
+
"stroke-width": strokeWidth,
|
|
1287
|
+
"stroke-linecap": "round",
|
|
1288
|
+
"stroke-linejoin": "round"
|
|
1289
|
+
})
|
|
1290
|
+
);
|
|
1291
|
+
return el("div", {
|
|
1292
|
+
style: {
|
|
1293
|
+
display: "flex",
|
|
1294
|
+
alignItems: "center",
|
|
1295
|
+
justifyContent: "center",
|
|
1296
|
+
width: `${size}px`,
|
|
1297
|
+
height: `${size}px`,
|
|
1298
|
+
borderRadius: "50%",
|
|
1299
|
+
backgroundColor: "#10B981"
|
|
1300
|
+
},
|
|
1301
|
+
"aria-label": ariaLabel
|
|
1302
|
+
}, checkSvg);
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
// src/modal/language-selector.ts
|
|
1306
|
+
function renderLanguageSelector(options) {
|
|
1307
|
+
var _a, _b, _c, _d;
|
|
1308
|
+
const { languages, selectedLanguage, settings, onSelect } = options;
|
|
1309
|
+
let isOpen = false;
|
|
1310
|
+
const buttonText = el("span");
|
|
1311
|
+
buttonText.textContent = selectedLanguage;
|
|
1312
|
+
const chevronSvg = svg(
|
|
1313
|
+
"svg",
|
|
1314
|
+
{
|
|
1315
|
+
width: "8",
|
|
1316
|
+
height: "5",
|
|
1317
|
+
viewBox: "0 0 8 5",
|
|
1318
|
+
fill: "none",
|
|
1319
|
+
xmlns: "http://www.w3.org/2000/svg"
|
|
1320
|
+
},
|
|
1321
|
+
svg("path", {
|
|
1322
|
+
d: "M1 1L4 4L7 1",
|
|
1323
|
+
stroke: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.language) == null ? void 0 : _b.textColor) || "#344054",
|
|
1324
|
+
"stroke-width": "1.5",
|
|
1325
|
+
"stroke-linecap": "round",
|
|
1326
|
+
"stroke-linejoin": "round"
|
|
1327
|
+
})
|
|
1328
|
+
);
|
|
1329
|
+
const buttonStyle = { ...styles.topRight };
|
|
1330
|
+
if ((_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.language) {
|
|
1331
|
+
buttonStyle.backgroundColor = settings.button.language.backgroundColor;
|
|
1332
|
+
buttonStyle.color = settings.button.language.textColor;
|
|
1333
|
+
}
|
|
1334
|
+
const button = el("div", {
|
|
1335
|
+
style: buttonStyle,
|
|
1336
|
+
role: "button",
|
|
1337
|
+
tabIndex: "0",
|
|
1338
|
+
"aria-haspopup": "listbox",
|
|
1339
|
+
"aria-expanded": "false",
|
|
1340
|
+
"aria-label": `Language selector, current: ${selectedLanguage}`,
|
|
1341
|
+
onClick: () => toggleDropdown(),
|
|
1342
|
+
onKeydown: (e) => {
|
|
1343
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
1344
|
+
e.preventDefault();
|
|
1345
|
+
toggleDropdown();
|
|
1346
|
+
} else if (e.key === "Escape") {
|
|
1347
|
+
closeDropdown();
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}, buttonText, chevronSvg);
|
|
1351
|
+
const dropdown = el("div", {
|
|
1352
|
+
style: {
|
|
1353
|
+
position: "fixed",
|
|
1354
|
+
backgroundColor: "#ffffff",
|
|
1355
|
+
border: "1px solid #d0d5dd",
|
|
1356
|
+
borderRadius: "4px",
|
|
1357
|
+
boxShadow: "0px 4px 12px rgba(0, 0, 0, 0.15)",
|
|
1358
|
+
zIndex: 2147483647,
|
|
1359
|
+
// max int — always on top
|
|
1360
|
+
minWidth: "140px",
|
|
1361
|
+
maxHeight: "200px",
|
|
1362
|
+
overflowY: "auto",
|
|
1363
|
+
padding: 0,
|
|
1364
|
+
boxSizing: "border-box",
|
|
1365
|
+
textAlign: "left",
|
|
1366
|
+
display: "none"
|
|
1367
|
+
},
|
|
1368
|
+
role: "listbox",
|
|
1369
|
+
"aria-label": "Select language"
|
|
1370
|
+
});
|
|
1371
|
+
for (const lang of languages) {
|
|
1372
|
+
const isSelected = lang === selectedLanguage;
|
|
1373
|
+
const itemStyle = isSelected ? { ...styles.languageItem, ...styles.selectedLanguageItem } : { ...styles.languageItem };
|
|
1374
|
+
if ((_d = settings == null ? void 0 : settings.button) == null ? void 0 : _d.language) {
|
|
1375
|
+
if (isSelected && settings.button.language.selectedBackgroundColor) {
|
|
1376
|
+
itemStyle.backgroundColor = settings.button.language.selectedBackgroundColor;
|
|
1377
|
+
}
|
|
1378
|
+
if (isSelected && settings.button.language.selectedTextColor) {
|
|
1379
|
+
itemStyle.color = settings.button.language.selectedTextColor;
|
|
1380
|
+
}
|
|
1381
|
+
}
|
|
1382
|
+
const item = el("div", {
|
|
1383
|
+
style: itemStyle,
|
|
1384
|
+
role: "option",
|
|
1385
|
+
"aria-selected": String(isSelected),
|
|
1386
|
+
onClick: () => {
|
|
1387
|
+
onSelect(lang);
|
|
1388
|
+
closeDropdown();
|
|
1389
|
+
},
|
|
1390
|
+
onMouseenter: (e) => {
|
|
1391
|
+
e.target.style.backgroundColor = "#f3f4f6";
|
|
1392
|
+
},
|
|
1393
|
+
onMouseleave: (e) => {
|
|
1394
|
+
e.target.style.backgroundColor = isSelected ? "#f3f4f6" : "#ffffff";
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
item.textContent = lang;
|
|
1398
|
+
dropdown.appendChild(item);
|
|
1399
|
+
}
|
|
1400
|
+
const container = el("div", { style: styles.languageSelectorContainer }, button);
|
|
1401
|
+
if (typeof document !== "undefined") {
|
|
1402
|
+
document.body.appendChild(dropdown);
|
|
1403
|
+
}
|
|
1404
|
+
function positionDropdown() {
|
|
1405
|
+
const rect = button.getBoundingClientRect();
|
|
1406
|
+
dropdown.style.top = `${rect.bottom + 4}px`;
|
|
1407
|
+
dropdown.style.right = `${window.innerWidth - rect.right}px`;
|
|
1408
|
+
dropdown.style.left = "auto";
|
|
1409
|
+
}
|
|
1410
|
+
const clickOutsideHandler = (e) => {
|
|
1411
|
+
const target = e.target;
|
|
1412
|
+
if (!container.contains(target) && !dropdown.contains(target)) {
|
|
1413
|
+
closeDropdown();
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
const repositionHandler = () => {
|
|
1417
|
+
if (isOpen) positionDropdown();
|
|
1418
|
+
};
|
|
1419
|
+
function toggleDropdown() {
|
|
1420
|
+
isOpen ? closeDropdown() : openDropdown();
|
|
1421
|
+
}
|
|
1422
|
+
function openDropdown() {
|
|
1423
|
+
isOpen = true;
|
|
1424
|
+
positionDropdown();
|
|
1425
|
+
dropdown.style.display = "block";
|
|
1426
|
+
button.setAttribute("aria-expanded", "true");
|
|
1427
|
+
setTimeout(() => {
|
|
1428
|
+
document.addEventListener("click", clickOutsideHandler);
|
|
1429
|
+
}, 0);
|
|
1430
|
+
window.addEventListener("scroll", repositionHandler, true);
|
|
1431
|
+
window.addEventListener("resize", repositionHandler);
|
|
1432
|
+
}
|
|
1433
|
+
function closeDropdown() {
|
|
1434
|
+
isOpen = false;
|
|
1435
|
+
dropdown.style.display = "none";
|
|
1436
|
+
button.setAttribute("aria-expanded", "false");
|
|
1437
|
+
document.removeEventListener("click", clickOutsideHandler);
|
|
1438
|
+
window.removeEventListener("scroll", repositionHandler, true);
|
|
1439
|
+
window.removeEventListener("resize", repositionHandler);
|
|
1440
|
+
}
|
|
1441
|
+
return { container, button, dropdown, buttonText };
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
// src/modal/keyboard.ts
|
|
1445
|
+
function setupKeyboardTrap(modalEl, onEscape) {
|
|
1446
|
+
const handler = (e) => {
|
|
1447
|
+
if (e.key === "Escape") {
|
|
1448
|
+
e.preventDefault();
|
|
1449
|
+
onEscape();
|
|
1450
|
+
return;
|
|
1451
|
+
}
|
|
1452
|
+
if (e.key === "Tab") {
|
|
1453
|
+
const focusableElements = modalEl.querySelectorAll(
|
|
1454
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
1455
|
+
);
|
|
1456
|
+
const focusable = Array.from(focusableElements).filter(
|
|
1457
|
+
(el2) => !el2.hasAttribute("disabled") && el2.offsetParent !== null
|
|
1458
|
+
);
|
|
1459
|
+
if (focusable.length === 0) {
|
|
1460
|
+
e.preventDefault();
|
|
1461
|
+
return;
|
|
1462
|
+
}
|
|
1463
|
+
const first = focusable[0];
|
|
1464
|
+
const last = focusable[focusable.length - 1];
|
|
1465
|
+
if (e.shiftKey) {
|
|
1466
|
+
if (document.activeElement === first) {
|
|
1467
|
+
e.preventDefault();
|
|
1468
|
+
last.focus();
|
|
1469
|
+
}
|
|
1470
|
+
} else {
|
|
1471
|
+
if (document.activeElement === last) {
|
|
1472
|
+
e.preventDefault();
|
|
1473
|
+
first.focus();
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
document.addEventListener("keydown", handler);
|
|
1479
|
+
requestAnimationFrame(() => {
|
|
1480
|
+
const firstFocusable = modalEl.querySelector(
|
|
1481
|
+
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
1482
|
+
);
|
|
1483
|
+
firstFocusable == null ? void 0 : firstFocusable.focus();
|
|
1484
|
+
});
|
|
1485
|
+
return () => {
|
|
1486
|
+
document.removeEventListener("keydown", handler);
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1490
|
+
// src/services/storage.ts
|
|
1491
|
+
var STORAGE_KEY = "redacto_consent_sdk_state";
|
|
1492
|
+
var STATE_EXPIRY_MS = 30 * 60 * 1e3;
|
|
1493
|
+
var isBrowser = () => {
|
|
1494
|
+
return typeof window !== "undefined" && typeof window.sessionStorage !== "undefined";
|
|
1495
|
+
};
|
|
1496
|
+
var saveConsentState = (state) => {
|
|
1497
|
+
if (!isBrowser()) {
|
|
1498
|
+
console.warn("SessionStorage not available - state not persisted");
|
|
1499
|
+
return;
|
|
1500
|
+
}
|
|
1501
|
+
try {
|
|
1502
|
+
const stateWithTimestamp = {
|
|
1503
|
+
...state,
|
|
1504
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1505
|
+
};
|
|
1506
|
+
sessionStorage.setItem(STORAGE_KEY, JSON.stringify(stateWithTimestamp));
|
|
1507
|
+
} catch (error) {
|
|
1508
|
+
console.error("Failed to save consent state:", error);
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
var getConsentState = () => {
|
|
1512
|
+
if (!isBrowser()) return null;
|
|
1513
|
+
try {
|
|
1514
|
+
const savedState = sessionStorage.getItem(STORAGE_KEY);
|
|
1515
|
+
if (!savedState) return null;
|
|
1516
|
+
const parsed = JSON.parse(savedState);
|
|
1517
|
+
const stateAge = Date.now() - new Date(parsed.timestamp).getTime();
|
|
1518
|
+
if (stateAge > STATE_EXPIRY_MS) {
|
|
1519
|
+
clearConsentState();
|
|
1520
|
+
return null;
|
|
1521
|
+
}
|
|
1522
|
+
return parsed;
|
|
1523
|
+
} catch (error) {
|
|
1524
|
+
console.error("Failed to get consent state:", error);
|
|
1525
|
+
return null;
|
|
1526
|
+
}
|
|
1527
|
+
};
|
|
1528
|
+
var clearConsentState = () => {
|
|
1529
|
+
if (!isBrowser()) return;
|
|
1530
|
+
try {
|
|
1531
|
+
sessionStorage.removeItem(STORAGE_KEY);
|
|
1532
|
+
} catch (error) {
|
|
1533
|
+
console.error("Failed to clear consent state:", error);
|
|
1534
|
+
}
|
|
1535
|
+
};
|
|
1536
|
+
var updateConsentState = (updates) => {
|
|
1537
|
+
const currentState = getConsentState();
|
|
1538
|
+
if (currentState) {
|
|
1539
|
+
saveConsentState({
|
|
1540
|
+
...currentState,
|
|
1541
|
+
...updates,
|
|
1542
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
1543
|
+
});
|
|
1544
|
+
}
|
|
1545
|
+
};
|
|
1546
|
+
|
|
1547
|
+
// src/constants.ts
|
|
1548
|
+
var DPO_AUDIO_ELEMENT_MAP = [
|
|
1549
|
+
{ audioKey: "grievance_text_audio_url", elementId: "dpo-grievance-text" },
|
|
1550
|
+
{ audioKey: "grievance_anchor_text_audio_url", elementId: "dpo-grievance-link" },
|
|
1551
|
+
{ audioKey: "grievance_email_connector_text_audio_url", elementId: "dpo-grievance-email-connector" },
|
|
1552
|
+
{ audioKey: "grievance_email_audio_url", elementId: "dpo-grievance-email" },
|
|
1553
|
+
{ audioKey: "dp_board_text_audio_url", elementId: "dpo-dp-board-text" },
|
|
1554
|
+
{ audioKey: "dp_board_anchor_text_audio_url", elementId: "dpo-dp-board-link" },
|
|
1555
|
+
{ audioKey: "dpo_text_audio_url", elementId: "dpo-dpo-text" },
|
|
1556
|
+
{ audioKey: "dpo_anchor_text_audio_url", elementId: "dpo-dpo-link" }
|
|
1557
|
+
];
|
|
1558
|
+
|
|
1559
|
+
// src/modal/tts.ts
|
|
1560
|
+
var TTSManager = class {
|
|
1561
|
+
constructor(onStateChange) {
|
|
1562
|
+
this.audioUrls = null;
|
|
1563
|
+
this.audioSequence = [];
|
|
1564
|
+
this.elementIdSequence = [];
|
|
1565
|
+
this.currentAudio = null;
|
|
1566
|
+
this.currentIndex = 0;
|
|
1567
|
+
this._isPlaying = false;
|
|
1568
|
+
this._isPaused = false;
|
|
1569
|
+
this._isAvailable = false;
|
|
1570
|
+
this.highlightedElementId = null;
|
|
1571
|
+
this.abortController = null;
|
|
1572
|
+
this.onStateChange = onStateChange;
|
|
1573
|
+
}
|
|
1574
|
+
get isPlaying() {
|
|
1575
|
+
return this._isPlaying;
|
|
1576
|
+
}
|
|
1577
|
+
get isPaused() {
|
|
1578
|
+
return this._isPaused;
|
|
1579
|
+
}
|
|
1580
|
+
get isAvailable() {
|
|
1581
|
+
return this._isAvailable;
|
|
1582
|
+
}
|
|
1583
|
+
async fetchAudioUrls(params) {
|
|
1584
|
+
var _a;
|
|
1585
|
+
this.stop();
|
|
1586
|
+
this._isAvailable = false;
|
|
1587
|
+
this.audioUrls = null;
|
|
1588
|
+
this.onStateChange();
|
|
1589
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
1590
|
+
this.abortController = new AbortController();
|
|
1591
|
+
try {
|
|
1592
|
+
const response = await fetchTTSAudioUrls({
|
|
1593
|
+
accessToken: params.accessToken,
|
|
1594
|
+
baseUrl: params.baseUrl,
|
|
1595
|
+
noticeUuid: params.noticeUuid,
|
|
1596
|
+
language: params.language,
|
|
1597
|
+
signal: this.abortController.signal
|
|
1598
|
+
});
|
|
1599
|
+
this.audioUrls = response.detail;
|
|
1600
|
+
this._isAvailable = true;
|
|
1601
|
+
this.buildSequences(params.content);
|
|
1602
|
+
this.onStateChange();
|
|
1603
|
+
} catch (error) {
|
|
1604
|
+
if (error.name !== "AbortError") {
|
|
1605
|
+
this._isAvailable = false;
|
|
1606
|
+
this.audioUrls = null;
|
|
1607
|
+
this.onStateChange();
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1610
|
+
}
|
|
1611
|
+
buildSequences(content) {
|
|
1612
|
+
var _a, _b;
|
|
1613
|
+
if (!this.audioUrls) return;
|
|
1614
|
+
const urls = this.audioUrls;
|
|
1615
|
+
const audioSeq = [];
|
|
1616
|
+
const elementSeq = [];
|
|
1617
|
+
const push = (url, elementId) => {
|
|
1618
|
+
if (url) {
|
|
1619
|
+
audioSeq.push(url);
|
|
1620
|
+
elementSeq.push(elementId);
|
|
1621
|
+
}
|
|
1622
|
+
};
|
|
1623
|
+
push(urls.notice_banner_heading_audio_url, "privacy-notice-title");
|
|
1624
|
+
push(urls.notice_text_audio_url, "privacy-notice-text");
|
|
1625
|
+
push(urls.privacy_policy_prefix_text_audio_url, "privacy-policy-text");
|
|
1626
|
+
push(urls.privacy_policy_anchor_text_audio_url, "privacy-policy-link");
|
|
1627
|
+
push(urls.purpose_section_heading_audio_url, "purpose-section-heading");
|
|
1628
|
+
for (const purpose of content.purposes) {
|
|
1629
|
+
const purposeAudio = (_a = urls.purposes_audio) == null ? void 0 : _a[purpose.uuid];
|
|
1630
|
+
if (purposeAudio) {
|
|
1631
|
+
push(purposeAudio.name_audio_url, `purpose-name-${purpose.uuid}`);
|
|
1632
|
+
push(purposeAudio.description_audio_url, `purpose-description-${purpose.uuid}`);
|
|
1633
|
+
}
|
|
1634
|
+
for (const de of purpose.data_elements) {
|
|
1635
|
+
const deAudio = (_b = urls.data_elements_audio) == null ? void 0 : _b[de.uuid];
|
|
1636
|
+
if (deAudio) {
|
|
1637
|
+
push(deAudio.name_audio_url, `data-element-${purpose.uuid}-${de.uuid}`);
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
push(urls.additional_text_audio_url, "additional-text");
|
|
1642
|
+
push(urls.privacy_center_anchor_text_audio_url, "privacy-center-link");
|
|
1643
|
+
if (urls.dpo_info_audio) {
|
|
1644
|
+
for (const mapping of DPO_AUDIO_ELEMENT_MAP) {
|
|
1645
|
+
const audioUrl = urls.dpo_info_audio[mapping.audioKey];
|
|
1646
|
+
push(audioUrl, mapping.elementId);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
push(urls.confirm_button_text_audio_url, "confirm-button-text");
|
|
1650
|
+
push(urls.decline_button_text_audio_url, "decline-button-text");
|
|
1651
|
+
this.audioSequence = audioSeq;
|
|
1652
|
+
this.elementIdSequence = elementSeq;
|
|
1653
|
+
}
|
|
1654
|
+
toggle() {
|
|
1655
|
+
this._isPlaying ? this.pause() : this.play();
|
|
1656
|
+
}
|
|
1657
|
+
play() {
|
|
1658
|
+
if (this.audioSequence.length === 0) return;
|
|
1659
|
+
if (this._isPaused && this.currentAudio) {
|
|
1660
|
+
this.currentAudio.play().then(() => {
|
|
1661
|
+
this._isPlaying = true;
|
|
1662
|
+
this._isPaused = false;
|
|
1663
|
+
this.onStateChange();
|
|
1664
|
+
}).catch(() => {
|
|
1665
|
+
});
|
|
1666
|
+
} else {
|
|
1667
|
+
const startIndex = this._isPlaying ? this.currentIndex : 0;
|
|
1668
|
+
this.playNextAudio(startIndex);
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
pause() {
|
|
1672
|
+
if (this.currentAudio && this._isPlaying) {
|
|
1673
|
+
this.currentAudio.pause();
|
|
1674
|
+
this._isPlaying = false;
|
|
1675
|
+
this._isPaused = true;
|
|
1676
|
+
this.onStateChange();
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
stop() {
|
|
1680
|
+
if (this.currentAudio) {
|
|
1681
|
+
this.currentAudio.pause();
|
|
1682
|
+
this.currentAudio.currentTime = 0;
|
|
1683
|
+
this.currentAudio = null;
|
|
1684
|
+
}
|
|
1685
|
+
this._isPlaying = false;
|
|
1686
|
+
this._isPaused = false;
|
|
1687
|
+
this.currentIndex = 0;
|
|
1688
|
+
this.removeHighlights();
|
|
1689
|
+
this.highlightedElementId = null;
|
|
1690
|
+
this.onStateChange();
|
|
1691
|
+
}
|
|
1692
|
+
destroy() {
|
|
1693
|
+
var _a;
|
|
1694
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
1695
|
+
this.stop();
|
|
1696
|
+
}
|
|
1697
|
+
playNextAudio(index) {
|
|
1698
|
+
if (index >= this.audioSequence.length) {
|
|
1699
|
+
this.stop();
|
|
1700
|
+
return;
|
|
1701
|
+
}
|
|
1702
|
+
if (this.currentAudio) {
|
|
1703
|
+
this.currentAudio.pause();
|
|
1704
|
+
this.currentAudio = null;
|
|
1705
|
+
}
|
|
1706
|
+
const audio = new Audio(this.audioSequence[index]);
|
|
1707
|
+
this.currentAudio = audio;
|
|
1708
|
+
this.currentIndex = index;
|
|
1709
|
+
this._isPlaying = true;
|
|
1710
|
+
this._isPaused = false;
|
|
1711
|
+
const elementId = this.elementIdSequence[index];
|
|
1712
|
+
if (elementId) {
|
|
1713
|
+
this.highlightElement(elementId);
|
|
1714
|
+
}
|
|
1715
|
+
audio.onended = () => this.playNextAudio(index + 1);
|
|
1716
|
+
audio.onerror = () => {
|
|
1717
|
+
console.warn(`Failed to load audio at index ${index}`);
|
|
1718
|
+
this.playNextAudio(index + 1);
|
|
1719
|
+
};
|
|
1720
|
+
audio.play().catch((error) => {
|
|
1721
|
+
console.warn(`Failed to play audio at index ${index}:`, error);
|
|
1722
|
+
this._isPlaying = false;
|
|
1723
|
+
this.onStateChange();
|
|
1724
|
+
});
|
|
1725
|
+
this.onStateChange();
|
|
1726
|
+
}
|
|
1727
|
+
highlightElement(elementId) {
|
|
1728
|
+
this.removeHighlights();
|
|
1729
|
+
this.highlightedElementId = elementId;
|
|
1730
|
+
setTimeout(() => {
|
|
1731
|
+
const element = document.getElementById(elementId);
|
|
1732
|
+
if (element) {
|
|
1733
|
+
element.classList.add("redacto-tts-highlight");
|
|
1734
|
+
const rect = element.getBoundingClientRect();
|
|
1735
|
+
const isVisible = rect.top >= 0 && rect.left >= 0 && rect.bottom <= window.innerHeight && rect.right <= window.innerWidth;
|
|
1736
|
+
if (!isVisible) {
|
|
1737
|
+
element.scrollIntoView({
|
|
1738
|
+
behavior: "smooth",
|
|
1739
|
+
block: "center",
|
|
1740
|
+
inline: "nearest"
|
|
1741
|
+
});
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
}, 50);
|
|
1745
|
+
}
|
|
1746
|
+
removeHighlights() {
|
|
1747
|
+
document.querySelectorAll(".redacto-tts-highlight").forEach((el2) => {
|
|
1748
|
+
el2.classList.remove("redacto-tts-highlight");
|
|
1749
|
+
});
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1752
|
+
|
|
1753
|
+
// src/modal/age-verification.ts
|
|
1754
|
+
function renderAgeVerification(options) {
|
|
1755
|
+
const { settings, colors, isMobile, logoUrl, onYes, onNo, onClose } = options;
|
|
1756
|
+
const headingColor = (settings == null ? void 0 : settings.headingColor) || "#101828";
|
|
1757
|
+
const textColor = colors.text;
|
|
1758
|
+
const font = colors.font;
|
|
1759
|
+
const logo = el("img", { src: logoUrl, style: styles.logo, alt: "Redacto Logo" });
|
|
1760
|
+
const title = el("h2", {
|
|
1761
|
+
id: "age-verification-title",
|
|
1762
|
+
style: {
|
|
1763
|
+
...styles.title,
|
|
1764
|
+
fontSize: isMobile ? "16px" : "18px",
|
|
1765
|
+
color: headingColor,
|
|
1766
|
+
fontFamily: font
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1769
|
+
title.textContent = "Age Verification Required";
|
|
1770
|
+
const topLeft = el("div", { style: styles.topLeft }, logo, title);
|
|
1771
|
+
const closeBtn = el("button", {
|
|
1772
|
+
style: {
|
|
1773
|
+
background: "none",
|
|
1774
|
+
border: "none",
|
|
1775
|
+
color: headingColor,
|
|
1776
|
+
cursor: "pointer",
|
|
1777
|
+
padding: isMobile ? "2px" : "4px",
|
|
1778
|
+
display: "flex",
|
|
1779
|
+
alignItems: "center",
|
|
1780
|
+
justifyContent: "center"
|
|
1781
|
+
},
|
|
1782
|
+
type: "button",
|
|
1783
|
+
"aria-label": "Close modal",
|
|
1784
|
+
onClick: onClose
|
|
1785
|
+
});
|
|
1786
|
+
const closeSvg = svg(
|
|
1787
|
+
"svg",
|
|
1788
|
+
{
|
|
1789
|
+
width: isMobile ? "18" : "20",
|
|
1790
|
+
height: isMobile ? "18" : "20",
|
|
1791
|
+
viewBox: "0 0 20 20",
|
|
1792
|
+
fill: "none",
|
|
1793
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
1794
|
+
"aria-hidden": "true"
|
|
1795
|
+
},
|
|
1796
|
+
svg("path", {
|
|
1797
|
+
d: "M15 5L5 15M5 5L15 15",
|
|
1798
|
+
stroke: "currentColor",
|
|
1799
|
+
"stroke-width": "2",
|
|
1800
|
+
"stroke-linecap": "round",
|
|
1801
|
+
"stroke-linejoin": "round"
|
|
1802
|
+
})
|
|
1803
|
+
);
|
|
1804
|
+
closeBtn.appendChild(closeSvg);
|
|
1805
|
+
const topSection = el("div", {
|
|
1806
|
+
style: { ...styles.topSection, color: textColor, fontFamily: font }
|
|
1807
|
+
}, topLeft, closeBtn);
|
|
1808
|
+
const description = el("p", {
|
|
1809
|
+
style: {
|
|
1810
|
+
...styles.privacyText,
|
|
1811
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
1812
|
+
color: textColor,
|
|
1813
|
+
fontFamily: font,
|
|
1814
|
+
marginBottom: "16px"
|
|
1815
|
+
}
|
|
1816
|
+
});
|
|
1817
|
+
description.textContent = "To proceed with this consent form, we need to verify your age.";
|
|
1818
|
+
const question = el("p", {
|
|
1819
|
+
style: {
|
|
1820
|
+
...styles.subTitle,
|
|
1821
|
+
color: textColor,
|
|
1822
|
+
fontFamily: font,
|
|
1823
|
+
marginBottom: "24px",
|
|
1824
|
+
textAlign: "left"
|
|
1825
|
+
}
|
|
1826
|
+
});
|
|
1827
|
+
question.textContent = "Are you 18 years of age or older?";
|
|
1828
|
+
const middleSection = el("div", {
|
|
1829
|
+
style: { ...styles.middleSection, color: textColor, fontFamily: font }
|
|
1830
|
+
}, description, question);
|
|
1831
|
+
const btnBase = {
|
|
1832
|
+
...styles.button,
|
|
1833
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
1834
|
+
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
1835
|
+
};
|
|
1836
|
+
const yesBtn = el("button", {
|
|
1837
|
+
style: {
|
|
1838
|
+
...btnBase,
|
|
1839
|
+
...styles.acceptButton,
|
|
1840
|
+
backgroundColor: colors.acceptBg,
|
|
1841
|
+
color: colors.acceptText,
|
|
1842
|
+
borderRadius: colors.borderRadius
|
|
1843
|
+
},
|
|
1844
|
+
type: "button",
|
|
1845
|
+
"aria-label": "Confirm that I am 18 years of age or older",
|
|
1846
|
+
onClick: onYes
|
|
1847
|
+
});
|
|
1848
|
+
yesBtn.textContent = "Yes, I am 18 or older";
|
|
1849
|
+
const noBtn = el("button", {
|
|
1850
|
+
style: {
|
|
1851
|
+
...btnBase,
|
|
1852
|
+
...styles.cancelButton,
|
|
1853
|
+
backgroundColor: colors.declineBg,
|
|
1854
|
+
color: colors.declineText,
|
|
1855
|
+
borderColor: colors.declineBorder,
|
|
1856
|
+
borderRadius: colors.borderRadius
|
|
1857
|
+
},
|
|
1858
|
+
type: "button",
|
|
1859
|
+
"aria-label": "Indicate that I am under 18 years of age",
|
|
1860
|
+
onClick: onNo
|
|
1861
|
+
});
|
|
1862
|
+
noBtn.textContent = "No, I am under 18";
|
|
1863
|
+
const bottomSection = el("div", {
|
|
1864
|
+
style: { ...styles.bottomSection, color: textColor, fontFamily: font }
|
|
1865
|
+
}, yesBtn, noBtn);
|
|
1866
|
+
const container = el("div", { style: styles.content }, topSection, middleSection, bottomSection);
|
|
1867
|
+
return { container };
|
|
1868
|
+
}
|
|
1869
|
+
|
|
1870
|
+
// src/modal/guardian-form.ts
|
|
1871
|
+
var RELATIONSHIP_OPTIONS = [
|
|
1872
|
+
{ value: "Father", label: "Father" },
|
|
1873
|
+
{ value: "Mother", label: "Mother" },
|
|
1874
|
+
{ value: "Legal Guardian", label: "Legal Guardian" },
|
|
1875
|
+
{ value: "Grandparent", label: "Grandparent" }
|
|
1876
|
+
];
|
|
1877
|
+
function renderGuardianForm(options) {
|
|
1878
|
+
var _a;
|
|
1879
|
+
const { formData, settings, colors, isMobile, logoUrl, isSubmitting, onSubmit, onCancel, onFieldChange } = options;
|
|
1880
|
+
const headingColor = (settings == null ? void 0 : settings.headingColor) || "#101828";
|
|
1881
|
+
const textColor = colors.text;
|
|
1882
|
+
const borderColor = colors.declineBorder;
|
|
1883
|
+
const bgColor = (settings == null ? void 0 : settings.backgroundColor) || "#ffffff";
|
|
1884
|
+
const radius = colors.borderRadius;
|
|
1885
|
+
const font = colors.font;
|
|
1886
|
+
const accentColor = colors.acceptBg;
|
|
1887
|
+
const inputStyle = {
|
|
1888
|
+
width: "100%",
|
|
1889
|
+
padding: "10px 12px",
|
|
1890
|
+
border: `1px solid ${borderColor}`,
|
|
1891
|
+
borderRadius: radius,
|
|
1892
|
+
fontSize: "13px",
|
|
1893
|
+
fontFamily: font,
|
|
1894
|
+
color: textColor,
|
|
1895
|
+
backgroundColor: bgColor,
|
|
1896
|
+
marginBottom: "2px",
|
|
1897
|
+
boxSizing: "border-box",
|
|
1898
|
+
outline: "none"
|
|
1899
|
+
};
|
|
1900
|
+
const errorStyle = {
|
|
1901
|
+
color: "#DC2626",
|
|
1902
|
+
fontSize: "11px",
|
|
1903
|
+
marginTop: "4px",
|
|
1904
|
+
marginBottom: "0px",
|
|
1905
|
+
display: "none"
|
|
1906
|
+
};
|
|
1907
|
+
const labelStyle = {
|
|
1908
|
+
fontSize: "13px",
|
|
1909
|
+
fontWeight: 500,
|
|
1910
|
+
color: textColor,
|
|
1911
|
+
fontFamily: font,
|
|
1912
|
+
marginBottom: "4px",
|
|
1913
|
+
display: "block"
|
|
1914
|
+
};
|
|
1915
|
+
const hintStyle = {
|
|
1916
|
+
fontSize: "11px",
|
|
1917
|
+
color: "#667085",
|
|
1918
|
+
marginTop: "2px",
|
|
1919
|
+
marginBottom: "0px",
|
|
1920
|
+
lineHeight: "1.4"
|
|
1921
|
+
};
|
|
1922
|
+
const logo = el("img", { src: logoUrl, style: styles.logo, alt: "Logo" });
|
|
1923
|
+
const title = el("h2", {
|
|
1924
|
+
id: "guardian-form-title",
|
|
1925
|
+
style: {
|
|
1926
|
+
...styles.title,
|
|
1927
|
+
fontSize: "16px",
|
|
1928
|
+
fontWeight: 600,
|
|
1929
|
+
color: headingColor,
|
|
1930
|
+
fontFamily: font
|
|
1931
|
+
}
|
|
1932
|
+
});
|
|
1933
|
+
title.textContent = "Guardian Verification";
|
|
1934
|
+
const topLeft = el("div", { style: styles.topLeft }, logo, title);
|
|
1935
|
+
const topSection = el("div", {
|
|
1936
|
+
style: { ...styles.topSection, color: textColor, fontFamily: font }
|
|
1937
|
+
}, topLeft);
|
|
1938
|
+
const subtitle = el("p", {
|
|
1939
|
+
style: {
|
|
1940
|
+
...styles.privacyText,
|
|
1941
|
+
fontSize: "13px",
|
|
1942
|
+
lineHeight: "1.5",
|
|
1943
|
+
color: textColor,
|
|
1944
|
+
fontFamily: font,
|
|
1945
|
+
marginBottom: "12px"
|
|
1946
|
+
}
|
|
1947
|
+
});
|
|
1948
|
+
subtitle.textContent = "Since you are under the age of consent, a legal guardian must verify their identity via DigiLocker before you can proceed.";
|
|
1949
|
+
const infoIcon = svg(
|
|
1950
|
+
"svg",
|
|
1951
|
+
{
|
|
1952
|
+
width: "16",
|
|
1953
|
+
height: "16",
|
|
1954
|
+
viewBox: "0 0 24 24",
|
|
1955
|
+
fill: "none",
|
|
1956
|
+
stroke: "#2563EB",
|
|
1957
|
+
"stroke-width": "2",
|
|
1958
|
+
"stroke-linecap": "round",
|
|
1959
|
+
"stroke-linejoin": "round"
|
|
1960
|
+
},
|
|
1961
|
+
svg("circle", { cx: "12", cy: "12", r: "10" }),
|
|
1962
|
+
svg("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
|
|
1963
|
+
svg("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
|
|
1964
|
+
);
|
|
1965
|
+
infoIcon.style.cssText = "flex-shrink:0;margin-top:1px";
|
|
1966
|
+
const infoText = el("span");
|
|
1967
|
+
infoText.textContent = "The guardian's name will be matched against their DigiLocker identity document (e.g. Aadhaar). Please enter the name exactly as it appears on the document.";
|
|
1968
|
+
const infoBanner = el("div", {
|
|
1969
|
+
style: {
|
|
1970
|
+
display: "flex",
|
|
1971
|
+
alignItems: "flex-start",
|
|
1972
|
+
gap: "8px",
|
|
1973
|
+
padding: "10px 12px",
|
|
1974
|
+
backgroundColor: "#EFF6FF",
|
|
1975
|
+
border: "1px solid #BFDBFE",
|
|
1976
|
+
borderRadius: radius,
|
|
1977
|
+
marginBottom: "16px",
|
|
1978
|
+
fontSize: "12px",
|
|
1979
|
+
lineHeight: "1.5",
|
|
1980
|
+
color: "#1E40AF"
|
|
1981
|
+
}
|
|
1982
|
+
}, infoIcon, infoText);
|
|
1983
|
+
const nameLabel = el("label", { for: "guardianName", style: labelStyle });
|
|
1984
|
+
nameLabel.innerHTML = `Guardian's Full Name <span style="color:#DC2626">*</span>`;
|
|
1985
|
+
const nameInput = el("input", {
|
|
1986
|
+
id: "guardianName",
|
|
1987
|
+
type: "text",
|
|
1988
|
+
style: inputStyle,
|
|
1989
|
+
value: formData.guardianName,
|
|
1990
|
+
placeholder: "e.g. Rajesh Kumar",
|
|
1991
|
+
autoComplete: "name",
|
|
1992
|
+
"aria-describedby": "guardianName-hint",
|
|
1993
|
+
onInput: (e) => onFieldChange("guardianName", e.target.value)
|
|
1994
|
+
});
|
|
1995
|
+
const nameHint = el("p", { id: "guardianName-hint", style: hintStyle });
|
|
1996
|
+
nameHint.textContent = "Enter the full name as per government ID (Aadhaar, PAN, etc.)";
|
|
1997
|
+
const nameError = el("div", { style: errorStyle, role: "alert" });
|
|
1998
|
+
const nameGroup = el("div", null, nameLabel, nameInput, nameHint, nameError);
|
|
1999
|
+
const contactLabel = el("label", { for: "guardianContact", style: labelStyle });
|
|
2000
|
+
contactLabel.innerHTML = `Guardian's Phone Number <span style="color:#DC2626">*</span>`;
|
|
2001
|
+
const contactInput = el("input", {
|
|
2002
|
+
id: "guardianContact",
|
|
2003
|
+
type: "tel",
|
|
2004
|
+
inputMode: "tel",
|
|
2005
|
+
style: inputStyle,
|
|
2006
|
+
value: formData.guardianContact,
|
|
2007
|
+
placeholder: "e.g. 9876543210",
|
|
2008
|
+
autoComplete: "tel",
|
|
2009
|
+
"aria-describedby": "guardianContact-hint",
|
|
2010
|
+
onInput: (e) => onFieldChange("guardianContact", e.target.value)
|
|
2011
|
+
});
|
|
2012
|
+
const contactHint = el("p", { id: "guardianContact-hint", style: hintStyle });
|
|
2013
|
+
contactHint.textContent = "Mobile number linked to guardian's Aadhaar";
|
|
2014
|
+
const contactError = el("div", { style: errorStyle, role: "alert" });
|
|
2015
|
+
const contactGroup = el("div", null, contactLabel, contactInput, contactHint, contactError);
|
|
2016
|
+
const relLabel = el("label", { id: "guardianRelationship-label", style: labelStyle });
|
|
2017
|
+
relLabel.innerHTML = `Relationship <span style="color:#DC2626">*</span>`;
|
|
2018
|
+
const selectedLabel = (_a = RELATIONSHIP_OPTIONS.find((o) => o.value === formData.guardianRelationship)) == null ? void 0 : _a.label;
|
|
2019
|
+
const chevronSvg = svg(
|
|
2020
|
+
"svg",
|
|
2021
|
+
{
|
|
2022
|
+
width: "14",
|
|
2023
|
+
height: "14",
|
|
2024
|
+
viewBox: "0 0 24 24",
|
|
2025
|
+
fill: "none",
|
|
2026
|
+
stroke: "#667085",
|
|
2027
|
+
"stroke-width": "2",
|
|
2028
|
+
"stroke-linecap": "round",
|
|
2029
|
+
"stroke-linejoin": "round"
|
|
2030
|
+
},
|
|
2031
|
+
svg("polyline", { points: "6 9 12 15 18 9" })
|
|
2032
|
+
);
|
|
2033
|
+
chevronSvg.style.cssText = "flex-shrink:0;margin-left:8px;transition:transform 0.2s";
|
|
2034
|
+
const selectedSpan = el("span", {
|
|
2035
|
+
style: { overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }
|
|
2036
|
+
});
|
|
2037
|
+
selectedSpan.textContent = selectedLabel || "Select relationship";
|
|
2038
|
+
const relationshipDisplay = el("div", {
|
|
2039
|
+
role: "combobox",
|
|
2040
|
+
"aria-expanded": "false",
|
|
2041
|
+
"aria-haspopup": "listbox",
|
|
2042
|
+
"aria-labelledby": "guardianRelationship-label",
|
|
2043
|
+
tabIndex: "0",
|
|
2044
|
+
style: {
|
|
2045
|
+
...inputStyle,
|
|
2046
|
+
color: selectedLabel ? textColor : "#9CA3AF",
|
|
2047
|
+
cursor: "pointer",
|
|
2048
|
+
display: "flex",
|
|
2049
|
+
alignItems: "center",
|
|
2050
|
+
justifyContent: "space-between",
|
|
2051
|
+
userSelect: "none",
|
|
2052
|
+
transition: "border-color 0.15s, box-shadow 0.15s"
|
|
2053
|
+
}
|
|
2054
|
+
}, selectedSpan, chevronSvg);
|
|
2055
|
+
const dropdownList = el("div", {
|
|
2056
|
+
id: "guardianRelationship-listbox",
|
|
2057
|
+
role: "listbox",
|
|
2058
|
+
"aria-labelledby": "guardianRelationship-label",
|
|
2059
|
+
style: {
|
|
2060
|
+
position: "absolute",
|
|
2061
|
+
top: "calc(100% + 4px)",
|
|
2062
|
+
left: 0,
|
|
2063
|
+
right: 0,
|
|
2064
|
+
backgroundColor: bgColor,
|
|
2065
|
+
border: `1px solid ${borderColor}`,
|
|
2066
|
+
borderRadius: radius,
|
|
2067
|
+
boxShadow: "0 4px 12px rgba(0,0,0,0.1), 0 1px 3px rgba(0,0,0,0.06)",
|
|
2068
|
+
zIndex: 50,
|
|
2069
|
+
overflow: "hidden",
|
|
2070
|
+
padding: "4px 0",
|
|
2071
|
+
maxHeight: "220px",
|
|
2072
|
+
overflowY: "auto",
|
|
2073
|
+
display: "none"
|
|
2074
|
+
}
|
|
2075
|
+
});
|
|
2076
|
+
for (let i = 0; i < RELATIONSHIP_OPTIONS.length; i++) {
|
|
2077
|
+
const option = RELATIONSHIP_OPTIONS[i];
|
|
2078
|
+
const isSelected = formData.guardianRelationship === option.value;
|
|
2079
|
+
const optionEl = el("div", {
|
|
2080
|
+
id: `guardianRelationship-option-${i}`,
|
|
2081
|
+
role: "option",
|
|
2082
|
+
"aria-selected": String(isSelected),
|
|
2083
|
+
style: {
|
|
2084
|
+
padding: "9px 12px",
|
|
2085
|
+
fontSize: "13px",
|
|
2086
|
+
fontFamily: font,
|
|
2087
|
+
color: isSelected ? accentColor : textColor,
|
|
2088
|
+
backgroundColor: "transparent",
|
|
2089
|
+
cursor: "pointer",
|
|
2090
|
+
display: "flex",
|
|
2091
|
+
alignItems: "center",
|
|
2092
|
+
justifyContent: "space-between",
|
|
2093
|
+
transition: "background-color 0.1s"
|
|
2094
|
+
},
|
|
2095
|
+
onClick: () => {
|
|
2096
|
+
onFieldChange("guardianRelationship", option.value);
|
|
2097
|
+
selectedSpan.textContent = option.label;
|
|
2098
|
+
relationshipDisplay.style.color = textColor;
|
|
2099
|
+
doClose();
|
|
2100
|
+
},
|
|
2101
|
+
onMouseenter: (e) => {
|
|
2102
|
+
e.target.style.backgroundColor = "#F3F4F6";
|
|
2103
|
+
},
|
|
2104
|
+
onMouseleave: (e) => {
|
|
2105
|
+
e.target.style.backgroundColor = "transparent";
|
|
2106
|
+
}
|
|
2107
|
+
});
|
|
2108
|
+
const label = el("span");
|
|
2109
|
+
label.textContent = option.label;
|
|
2110
|
+
optionEl.appendChild(label);
|
|
2111
|
+
if (isSelected) {
|
|
2112
|
+
const checkSvg = svg(
|
|
2113
|
+
"svg",
|
|
2114
|
+
{
|
|
2115
|
+
width: "14",
|
|
2116
|
+
height: "14",
|
|
2117
|
+
viewBox: "0 0 24 24",
|
|
2118
|
+
fill: "none",
|
|
2119
|
+
stroke: accentColor,
|
|
2120
|
+
"stroke-width": "2.5",
|
|
2121
|
+
"stroke-linecap": "round",
|
|
2122
|
+
"stroke-linejoin": "round"
|
|
2123
|
+
},
|
|
2124
|
+
svg("polyline", { points: "20 6 9 17 4 12" })
|
|
2125
|
+
);
|
|
2126
|
+
checkSvg.style.flexShrink = "0";
|
|
2127
|
+
optionEl.appendChild(checkSvg);
|
|
2128
|
+
}
|
|
2129
|
+
dropdownList.appendChild(optionEl);
|
|
2130
|
+
}
|
|
2131
|
+
let dropdownOpen = false;
|
|
2132
|
+
const relContainer = el("div", { style: { position: "relative" } });
|
|
2133
|
+
const handleClickOutside = (e) => {
|
|
2134
|
+
if (!relContainer.contains(e.target)) {
|
|
2135
|
+
doClose();
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
function doOpen() {
|
|
2139
|
+
dropdownOpen = true;
|
|
2140
|
+
dropdownList.style.display = "block";
|
|
2141
|
+
relationshipDisplay.setAttribute("aria-expanded", "true");
|
|
2142
|
+
chevronSvg.style.transform = "rotate(180deg)";
|
|
2143
|
+
relationshipDisplay.style.borderColor = accentColor;
|
|
2144
|
+
relationshipDisplay.style.boxShadow = `0 0 0 1px ${accentColor}`;
|
|
2145
|
+
const rect = relContainer.getBoundingClientRect();
|
|
2146
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
2147
|
+
const dropdownHeight = 220;
|
|
2148
|
+
const shouldOpenUpward = spaceBelow < dropdownHeight && rect.top > dropdownHeight;
|
|
2149
|
+
if (shouldOpenUpward) {
|
|
2150
|
+
dropdownList.style.top = "auto";
|
|
2151
|
+
dropdownList.style.bottom = "calc(100% + 4px)";
|
|
2152
|
+
dropdownList.style.boxShadow = "0 -4px 12px rgba(0,0,0,0.1), 0 -1px 3px rgba(0,0,0,0.06)";
|
|
2153
|
+
} else {
|
|
2154
|
+
dropdownList.style.top = "calc(100% + 4px)";
|
|
2155
|
+
dropdownList.style.bottom = "auto";
|
|
2156
|
+
dropdownList.style.boxShadow = "0 4px 12px rgba(0,0,0,0.1), 0 1px 3px rgba(0,0,0,0.06)";
|
|
2157
|
+
}
|
|
2158
|
+
requestAnimationFrame(() => {
|
|
2159
|
+
dropdownList.scrollIntoView({ behavior: "smooth", block: "nearest" });
|
|
2160
|
+
});
|
|
2161
|
+
setTimeout(() => document.addEventListener("mousedown", handleClickOutside), 0);
|
|
2162
|
+
}
|
|
2163
|
+
function doClose() {
|
|
2164
|
+
dropdownOpen = false;
|
|
2165
|
+
dropdownList.style.display = "none";
|
|
2166
|
+
relationshipDisplay.setAttribute("aria-expanded", "false");
|
|
2167
|
+
chevronSvg.style.transform = "rotate(0deg)";
|
|
2168
|
+
relationshipDisplay.style.borderColor = borderColor;
|
|
2169
|
+
relationshipDisplay.style.boxShadow = "none";
|
|
2170
|
+
document.removeEventListener("mousedown", handleClickOutside);
|
|
2171
|
+
}
|
|
2172
|
+
relationshipDisplay.addEventListener("click", () => {
|
|
2173
|
+
dropdownOpen ? doClose() : doOpen();
|
|
2174
|
+
});
|
|
2175
|
+
relationshipDisplay.addEventListener("keydown", (e) => {
|
|
2176
|
+
if (e.key === "Enter" || e.key === " " || e.key === "ArrowDown") {
|
|
2177
|
+
e.preventDefault();
|
|
2178
|
+
if (!dropdownOpen) doOpen();
|
|
2179
|
+
} else if (e.key === "Escape" || e.key === "Tab") {
|
|
2180
|
+
doClose();
|
|
2181
|
+
}
|
|
2182
|
+
});
|
|
2183
|
+
const relationshipError = el("div", { style: errorStyle, role: "alert" });
|
|
2184
|
+
relContainer.appendChild(relLabel);
|
|
2185
|
+
relContainer.appendChild(relationshipDisplay);
|
|
2186
|
+
relContainer.appendChild(dropdownList);
|
|
2187
|
+
relContainer.appendChild(relationshipError);
|
|
2188
|
+
const generalError = el("div", {
|
|
2189
|
+
role: "alert",
|
|
2190
|
+
style: {
|
|
2191
|
+
color: "#DC2626",
|
|
2192
|
+
fontSize: "13px",
|
|
2193
|
+
marginTop: "16px",
|
|
2194
|
+
padding: "12px",
|
|
2195
|
+
borderRadius: "6px",
|
|
2196
|
+
border: "1px solid #FCA5A5",
|
|
2197
|
+
backgroundColor: "#FEF2F2",
|
|
2198
|
+
textAlign: "center",
|
|
2199
|
+
display: "none"
|
|
2200
|
+
}
|
|
2201
|
+
});
|
|
2202
|
+
const fieldsContainer = el("div", {
|
|
2203
|
+
style: { display: "flex", flexDirection: "column", gap: "14px" }
|
|
2204
|
+
}, nameGroup, contactGroup, relContainer);
|
|
2205
|
+
const middleSection = el("div", {
|
|
2206
|
+
style: { ...styles.middleSection, overflow: "visible", color: textColor, fontFamily: font }
|
|
2207
|
+
}, subtitle, infoBanner, fieldsContainer, generalError);
|
|
2208
|
+
const btnBase = {
|
|
2209
|
+
...styles.button,
|
|
2210
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
2211
|
+
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
2212
|
+
};
|
|
2213
|
+
const submitBtn = el("button", {
|
|
2214
|
+
style: {
|
|
2215
|
+
...btnBase,
|
|
2216
|
+
...styles.acceptButton,
|
|
2217
|
+
backgroundColor: colors.acceptBg,
|
|
2218
|
+
color: colors.acceptText,
|
|
2219
|
+
borderRadius: colors.borderRadius
|
|
2220
|
+
},
|
|
2221
|
+
type: "button",
|
|
2222
|
+
disabled: isSubmitting,
|
|
2223
|
+
"aria-label": isSubmitting ? "Verifying guardian identity" : "Proceed to guardian verification",
|
|
2224
|
+
onClick: onSubmit
|
|
2225
|
+
});
|
|
2226
|
+
submitBtn.textContent = isSubmitting ? "Verifying..." : "Verify via DigiLocker";
|
|
2227
|
+
const cancelBtn = el("button", {
|
|
2228
|
+
style: {
|
|
2229
|
+
...btnBase,
|
|
2230
|
+
...styles.cancelButton,
|
|
2231
|
+
backgroundColor: colors.declineBg,
|
|
2232
|
+
color: colors.declineText,
|
|
2233
|
+
borderColor: colors.declineBorder,
|
|
2234
|
+
borderRadius: colors.borderRadius
|
|
2235
|
+
},
|
|
2236
|
+
type: "button",
|
|
2237
|
+
"aria-label": "Cancel",
|
|
2238
|
+
onClick: onCancel
|
|
2239
|
+
});
|
|
2240
|
+
cancelBtn.textContent = "Cancel";
|
|
2241
|
+
const bottomSection = el("div", {
|
|
2242
|
+
style: { ...styles.bottomSection, paddingTop: "12px", color: textColor, fontFamily: font }
|
|
2243
|
+
}, submitBtn, cancelBtn);
|
|
2244
|
+
const container = el("div", { style: { ...styles.content, overflow: "visible" } }, topSection, middleSection, bottomSection);
|
|
2245
|
+
return {
|
|
2246
|
+
container,
|
|
2247
|
+
nameInput,
|
|
2248
|
+
contactInput,
|
|
2249
|
+
relationshipDisplay,
|
|
2250
|
+
nameError,
|
|
2251
|
+
contactError,
|
|
2252
|
+
relationshipError,
|
|
2253
|
+
submitBtn,
|
|
2254
|
+
generalError
|
|
2255
|
+
};
|
|
2256
|
+
}
|
|
2257
|
+
function validateGuardianForm(data) {
|
|
2258
|
+
const errors = {};
|
|
2259
|
+
if (!data.guardianName.trim()) errors.guardianName = "Guardian name is required";
|
|
2260
|
+
if (!data.guardianContact.trim()) errors.guardianContact = "Guardian contact is required";
|
|
2261
|
+
if (!data.guardianRelationship.trim()) errors.guardianRelationship = "Relationship to guardian is required";
|
|
2262
|
+
return errors;
|
|
2263
|
+
}
|
|
2264
|
+
|
|
2265
|
+
// src/modal/verification-screen.ts
|
|
2266
|
+
function getVerificationErrorMessage(errorCode, fallbackError) {
|
|
2267
|
+
switch (errorCode) {
|
|
2268
|
+
case "GUARDIAN_UNDER_18":
|
|
2269
|
+
return "The guardian must be 18 years or older. Please provide details of a different guardian.";
|
|
2270
|
+
case "TOKEN_FAILED":
|
|
2271
|
+
return "DigiLocker verification could not be completed. Please try again.";
|
|
2272
|
+
case "DIGILOCKER_AUTH_FAILED":
|
|
2273
|
+
return "DigiLocker authorization was cancelled or failed. Please try again.";
|
|
2274
|
+
case "NO_AUTH_CODE":
|
|
2275
|
+
return "DigiLocker did not return an authorization code. Please try again.";
|
|
2276
|
+
case "SESSION_EXPIRED":
|
|
2277
|
+
return "Verification session has expired. Please try again.";
|
|
2278
|
+
default:
|
|
2279
|
+
return fallbackError || "Verification failed. Please try again.";
|
|
2280
|
+
}
|
|
2281
|
+
}
|
|
2282
|
+
function renderVerificationScreen(options) {
|
|
2283
|
+
const {
|
|
2284
|
+
settings,
|
|
2285
|
+
colors,
|
|
2286
|
+
isMobile,
|
|
2287
|
+
logoUrl,
|
|
2288
|
+
isInitiating,
|
|
2289
|
+
isPolling,
|
|
2290
|
+
isComplete,
|
|
2291
|
+
isAutoTransitioning,
|
|
2292
|
+
error,
|
|
2293
|
+
errorCode,
|
|
2294
|
+
canRetry,
|
|
2295
|
+
onContinue,
|
|
2296
|
+
onBack
|
|
2297
|
+
} = options;
|
|
2298
|
+
const headingColor = (settings == null ? void 0 : settings.headingColor) || "#101828";
|
|
2299
|
+
const textColor = colors.text;
|
|
2300
|
+
const font = colors.font;
|
|
2301
|
+
const accentColor = colors.acceptBg;
|
|
2302
|
+
const logo = el("img", { src: logoUrl, style: styles.logo, alt: "Logo" });
|
|
2303
|
+
const title = el("h2", {
|
|
2304
|
+
id: "verification-title",
|
|
2305
|
+
style: {
|
|
2306
|
+
...styles.title,
|
|
2307
|
+
fontSize: "16px",
|
|
2308
|
+
fontWeight: 600,
|
|
2309
|
+
color: headingColor,
|
|
2310
|
+
fontFamily: font
|
|
2311
|
+
}
|
|
2312
|
+
});
|
|
2313
|
+
title.textContent = "Guardian Verification";
|
|
2314
|
+
const topLeft = el("div", { style: styles.topLeft }, logo, title);
|
|
2315
|
+
const topSection = el("div", {
|
|
2316
|
+
style: { ...styles.topSection, color: textColor, fontFamily: font }
|
|
2317
|
+
}, topLeft);
|
|
2318
|
+
const middleSection = el("div", {
|
|
2319
|
+
style: {
|
|
2320
|
+
...styles.middleSection,
|
|
2321
|
+
color: textColor,
|
|
2322
|
+
fontFamily: font,
|
|
2323
|
+
textAlign: "center"
|
|
2324
|
+
}
|
|
2325
|
+
});
|
|
2326
|
+
if (isComplete && !error) {
|
|
2327
|
+
const successCircle = el("div", {
|
|
2328
|
+
style: {
|
|
2329
|
+
width: "64px",
|
|
2330
|
+
height: "64px",
|
|
2331
|
+
borderRadius: "50%",
|
|
2332
|
+
backgroundColor: "#D1FAE5",
|
|
2333
|
+
display: "flex",
|
|
2334
|
+
alignItems: "center",
|
|
2335
|
+
justifyContent: "center"
|
|
2336
|
+
}
|
|
2337
|
+
});
|
|
2338
|
+
const checkSvg = svg(
|
|
2339
|
+
"svg",
|
|
2340
|
+
{
|
|
2341
|
+
width: "32",
|
|
2342
|
+
height: "32",
|
|
2343
|
+
viewBox: "0 0 24 24",
|
|
2344
|
+
fill: "none",
|
|
2345
|
+
stroke: "#059669",
|
|
2346
|
+
"stroke-width": "2",
|
|
2347
|
+
"stroke-linecap": "round",
|
|
2348
|
+
"stroke-linejoin": "round"
|
|
2349
|
+
},
|
|
2350
|
+
svg("polyline", { points: "20 6 9 17 4 12" })
|
|
2351
|
+
);
|
|
2352
|
+
successCircle.appendChild(checkSvg);
|
|
2353
|
+
const successText = el("p", {
|
|
2354
|
+
style: {
|
|
2355
|
+
color: headingColor,
|
|
2356
|
+
fontFamily: font,
|
|
2357
|
+
fontSize: "16px",
|
|
2358
|
+
fontWeight: 600,
|
|
2359
|
+
lineHeight: "1.5",
|
|
2360
|
+
marginTop: "12px",
|
|
2361
|
+
marginBottom: "24px"
|
|
2362
|
+
}
|
|
2363
|
+
});
|
|
2364
|
+
successText.textContent = "Verification Completed";
|
|
2365
|
+
const wrapper = el("div", {
|
|
2366
|
+
style: {
|
|
2367
|
+
display: "flex",
|
|
2368
|
+
flexDirection: "column",
|
|
2369
|
+
alignItems: "center",
|
|
2370
|
+
justifyContent: "center",
|
|
2371
|
+
flex: "1"
|
|
2372
|
+
}
|
|
2373
|
+
}, successCircle, successText);
|
|
2374
|
+
middleSection.appendChild(wrapper);
|
|
2375
|
+
} else if ((isInitiating || isPolling) && !error) {
|
|
2376
|
+
const spinner = el("div", {
|
|
2377
|
+
style: {
|
|
2378
|
+
width: "48px",
|
|
2379
|
+
height: "48px",
|
|
2380
|
+
border: "4px solid #e5e7eb",
|
|
2381
|
+
borderTopColor: accentColor,
|
|
2382
|
+
borderRadius: "50%",
|
|
2383
|
+
animation: "spin 1s linear infinite",
|
|
2384
|
+
margin: "24px auto"
|
|
2385
|
+
}
|
|
2386
|
+
});
|
|
2387
|
+
const statusMsg = el("div", {
|
|
2388
|
+
style: {
|
|
2389
|
+
color: "#059669",
|
|
2390
|
+
fontSize: "14px",
|
|
2391
|
+
marginTop: "16px",
|
|
2392
|
+
padding: "12px",
|
|
2393
|
+
borderRadius: "6px",
|
|
2394
|
+
border: "1px solid #A7F3D0",
|
|
2395
|
+
backgroundColor: "#D1FAE5",
|
|
2396
|
+
textAlign: "center"
|
|
2397
|
+
}
|
|
2398
|
+
});
|
|
2399
|
+
statusMsg.textContent = isInitiating ? "Opening DigiLocker in a new tab..." : "Please complete the verification in the DigiLocker tab. This page will update automatically when verification is complete.";
|
|
2400
|
+
middleSection.appendChild(spinner);
|
|
2401
|
+
middleSection.appendChild(statusMsg);
|
|
2402
|
+
}
|
|
2403
|
+
if (error) {
|
|
2404
|
+
const errorCircle = el("div", {
|
|
2405
|
+
style: {
|
|
2406
|
+
width: "64px",
|
|
2407
|
+
height: "64px",
|
|
2408
|
+
borderRadius: "50%",
|
|
2409
|
+
backgroundColor: "#fee2e2",
|
|
2410
|
+
display: "flex",
|
|
2411
|
+
alignItems: "center",
|
|
2412
|
+
justifyContent: "center",
|
|
2413
|
+
margin: "24px auto"
|
|
2414
|
+
}
|
|
2415
|
+
});
|
|
2416
|
+
const xSvg = svg(
|
|
2417
|
+
"svg",
|
|
2418
|
+
{
|
|
2419
|
+
width: "32",
|
|
2420
|
+
height: "32",
|
|
2421
|
+
viewBox: "0 0 24 24",
|
|
2422
|
+
fill: "none",
|
|
2423
|
+
stroke: "#dc2626",
|
|
2424
|
+
"stroke-width": "2",
|
|
2425
|
+
"stroke-linecap": "round",
|
|
2426
|
+
"stroke-linejoin": "round"
|
|
2427
|
+
},
|
|
2428
|
+
svg("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
|
|
2429
|
+
svg("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
|
|
2430
|
+
);
|
|
2431
|
+
errorCircle.appendChild(xSvg);
|
|
2432
|
+
const failedText = el("p", {
|
|
2433
|
+
style: {
|
|
2434
|
+
...styles.privacyText,
|
|
2435
|
+
color: textColor,
|
|
2436
|
+
fontFamily: font,
|
|
2437
|
+
fontSize: "14px",
|
|
2438
|
+
lineHeight: "1.5",
|
|
2439
|
+
marginBottom: "12px"
|
|
2440
|
+
}
|
|
2441
|
+
});
|
|
2442
|
+
failedText.textContent = "Verification failed";
|
|
2443
|
+
const errorBox = el("div", {
|
|
2444
|
+
style: {
|
|
2445
|
+
color: "#DC2626",
|
|
2446
|
+
fontSize: "14px",
|
|
2447
|
+
marginTop: "16px",
|
|
2448
|
+
padding: "12px",
|
|
2449
|
+
borderRadius: "6px",
|
|
2450
|
+
border: "1px solid #FCA5A5",
|
|
2451
|
+
backgroundColor: "#FEF2F2",
|
|
2452
|
+
textAlign: "center"
|
|
2453
|
+
}
|
|
2454
|
+
});
|
|
2455
|
+
errorBox.textContent = error;
|
|
2456
|
+
middleSection.appendChild(errorCircle);
|
|
2457
|
+
middleSection.appendChild(failedText);
|
|
2458
|
+
middleSection.appendChild(errorBox);
|
|
2459
|
+
}
|
|
2460
|
+
const btnBase = {
|
|
2461
|
+
...styles.button,
|
|
2462
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
2463
|
+
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
2464
|
+
};
|
|
2465
|
+
const bottomSection = el("div", {
|
|
2466
|
+
style: {
|
|
2467
|
+
...styles.bottomSection,
|
|
2468
|
+
paddingTop: "12px",
|
|
2469
|
+
display: "flex",
|
|
2470
|
+
gap: "12px",
|
|
2471
|
+
justifyContent: "center",
|
|
2472
|
+
color: textColor,
|
|
2473
|
+
fontFamily: font
|
|
2474
|
+
}
|
|
2475
|
+
});
|
|
2476
|
+
const acceptBtnStyle = {
|
|
2477
|
+
backgroundColor: colors.acceptBg,
|
|
2478
|
+
color: colors.acceptText,
|
|
2479
|
+
borderRadius: colors.borderRadius
|
|
2480
|
+
};
|
|
2481
|
+
const declineBtnStyle = {
|
|
2482
|
+
backgroundColor: colors.declineBg,
|
|
2483
|
+
color: colors.declineText,
|
|
2484
|
+
borderColor: colors.declineBorder,
|
|
2485
|
+
borderRadius: colors.borderRadius
|
|
2486
|
+
};
|
|
2487
|
+
if (!isAutoTransitioning) {
|
|
2488
|
+
if (isComplete && !error) {
|
|
2489
|
+
const continueBtn = el("button", {
|
|
2490
|
+
style: { ...btnBase, ...acceptBtnStyle },
|
|
2491
|
+
type: "button",
|
|
2492
|
+
"aria-label": "Continue to consent form",
|
|
2493
|
+
onClick: onContinue
|
|
2494
|
+
});
|
|
2495
|
+
continueBtn.textContent = "Continue";
|
|
2496
|
+
bottomSection.appendChild(continueBtn);
|
|
2497
|
+
} else if (error && canRetry) {
|
|
2498
|
+
const retryBtn = el("button", {
|
|
2499
|
+
style: { ...btnBase, ...styles.acceptButton, ...acceptBtnStyle },
|
|
2500
|
+
type: "button",
|
|
2501
|
+
"aria-label": errorCode === "GUARDIAN_UNDER_18" ? "Change guardian details" : "Try verification again",
|
|
2502
|
+
onClick: () => onBack(errorCode === "GUARDIAN_UNDER_18")
|
|
2503
|
+
});
|
|
2504
|
+
retryBtn.textContent = errorCode === "GUARDIAN_UNDER_18" ? "Change Guardian" : "Try Again";
|
|
2505
|
+
bottomSection.appendChild(retryBtn);
|
|
2506
|
+
} else if (error) {
|
|
2507
|
+
const goBackBtn = el("button", {
|
|
2508
|
+
style: { ...btnBase, ...styles.cancelButton, ...declineBtnStyle },
|
|
2509
|
+
type: "button",
|
|
2510
|
+
"aria-label": "Go back",
|
|
2511
|
+
onClick: () => onBack(false)
|
|
2512
|
+
});
|
|
2513
|
+
goBackBtn.textContent = "Go Back";
|
|
2514
|
+
bottomSection.appendChild(goBackBtn);
|
|
2515
|
+
} else {
|
|
2516
|
+
const cancelBtn = el("button", {
|
|
2517
|
+
style: { ...btnBase, ...styles.cancelButton, ...declineBtnStyle },
|
|
2518
|
+
type: "button",
|
|
2519
|
+
"aria-label": "Cancel verification",
|
|
2520
|
+
onClick: () => onBack(false)
|
|
2521
|
+
});
|
|
2522
|
+
cancelBtn.textContent = "Cancel";
|
|
2523
|
+
bottomSection.appendChild(cancelBtn);
|
|
2524
|
+
}
|
|
2525
|
+
}
|
|
2526
|
+
const container = el("div", { style: styles.content }, topSection, middleSection, bottomSection);
|
|
2527
|
+
return { container };
|
|
2528
|
+
}
|
|
2529
|
+
|
|
2530
|
+
// src/modal/colors.ts
|
|
2531
|
+
function resolveColors(settings, activeConfig) {
|
|
2532
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
2533
|
+
const primaryColor = (activeConfig == null ? void 0 : activeConfig.primary_color) || "#4f87ff";
|
|
2534
|
+
const secondaryColor = (activeConfig == null ? void 0 : activeConfig.secondary_color) || "#4f87ff";
|
|
2535
|
+
return {
|
|
2536
|
+
acceptBg: ((_b = (_a = settings == null ? void 0 : settings.button) == null ? void 0 : _a.accept) == null ? void 0 : _b.backgroundColor) || primaryColor,
|
|
2537
|
+
acceptText: ((_d = (_c = settings == null ? void 0 : settings.button) == null ? void 0 : _c.accept) == null ? void 0 : _d.textColor) || "#ffffff",
|
|
2538
|
+
declineBg: ((_f = (_e = settings == null ? void 0 : settings.button) == null ? void 0 : _e.decline) == null ? void 0 : _f.backgroundColor) || "#ffffff",
|
|
2539
|
+
declineText: ((_h = (_g = settings == null ? void 0 : settings.button) == null ? void 0 : _g.decline) == null ? void 0 : _h.textColor) || "#000000",
|
|
2540
|
+
declineBorder: (settings == null ? void 0 : settings.borderColor) || "#d0d5dd",
|
|
2541
|
+
link: (settings == null ? void 0 : settings.link) || secondaryColor,
|
|
2542
|
+
heading: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
2543
|
+
text: (settings == null ? void 0 : settings.textColor) || "#344054",
|
|
2544
|
+
font: (settings == null ? void 0 : settings.font) || "inherit",
|
|
2545
|
+
borderRadius: (settings == null ? void 0 : settings.borderRadius) || "8px",
|
|
2546
|
+
primaryColor,
|
|
2547
|
+
secondaryColor
|
|
2548
|
+
};
|
|
2549
|
+
}
|
|
2550
|
+
|
|
2551
|
+
// src/modal/index.ts
|
|
2552
|
+
var ModalController = class {
|
|
2553
|
+
constructor(options) {
|
|
2554
|
+
this.overlayEl = null;
|
|
2555
|
+
this.modalEl = null;
|
|
2556
|
+
this.purposeRefs = null;
|
|
2557
|
+
this.alreadyConsentedPurposeRefs = null;
|
|
2558
|
+
this.langSelectorRefs = null;
|
|
2559
|
+
this.keyboardCleanup = null;
|
|
2560
|
+
this.abortController = null;
|
|
2561
|
+
this.unsubscribe = null;
|
|
2562
|
+
this.guardianFormRefs = null;
|
|
2563
|
+
this.statusPollTimeout = null;
|
|
2564
|
+
this.popupCheckInterval = null;
|
|
2565
|
+
this.verificationPopup = null;
|
|
2566
|
+
this.verificationSessionToken = null;
|
|
2567
|
+
this.visibilityChangeHandler = null;
|
|
2568
|
+
// DOM refs for targeted updates
|
|
2569
|
+
this.acceptBtn = null;
|
|
2570
|
+
this.declineBtn = null;
|
|
2571
|
+
this.middleSection = null;
|
|
2572
|
+
this.errorEl = null;
|
|
2573
|
+
this.noticeTitleEl = null;
|
|
2574
|
+
this.noticeTextEl = null;
|
|
2575
|
+
this.purposeHeadingEl = null;
|
|
2576
|
+
this.additionalTextEl = null;
|
|
2577
|
+
this.privacyPolicyLinkEl = null;
|
|
2578
|
+
this.privacyCenterLinkEl = null;
|
|
2579
|
+
this.ttsButton = null;
|
|
2580
|
+
this.options = options;
|
|
2581
|
+
this.store = new Store({
|
|
2582
|
+
isLoading: true,
|
|
2583
|
+
isSubmitting: false,
|
|
2584
|
+
isSubmittingAccept: false,
|
|
2585
|
+
isSubmittingDecline: false,
|
|
2586
|
+
content: null,
|
|
2587
|
+
selectedPurposes: {},
|
|
2588
|
+
selectedDataElements: {},
|
|
2589
|
+
initialDataElementSelections: {},
|
|
2590
|
+
collapsedPurposes: {},
|
|
2591
|
+
hasAlreadyConsented: false,
|
|
2592
|
+
isFullyConsentedReviewMode: false,
|
|
2593
|
+
selectedLanguage: options.language || "en",
|
|
2594
|
+
errorMessage: null,
|
|
2595
|
+
fetchError: null,
|
|
2596
|
+
needsConsentPurposes: [],
|
|
2597
|
+
alreadyConsentedPurposes: [],
|
|
2598
|
+
isMinorFlow: false,
|
|
2599
|
+
selfDeclaredAdult: false,
|
|
2600
|
+
showAgeVerification: false,
|
|
2601
|
+
showGuardianForm: false,
|
|
2602
|
+
showVerificationScreen: false,
|
|
2603
|
+
verificationReference: null,
|
|
2604
|
+
isVerificationComplete: false,
|
|
2605
|
+
isInitiatingVerification: false,
|
|
2606
|
+
isPollingStatus: false,
|
|
2607
|
+
verificationError: null,
|
|
2608
|
+
verificationErrorCode: null,
|
|
2609
|
+
canRetryVerification: false,
|
|
2610
|
+
guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
|
|
2611
|
+
});
|
|
2612
|
+
this.mediaQuery = createMediaQuery("(max-width: 768px)");
|
|
2613
|
+
this.ttsManager = new TTSManager(() => this.updateTTSButton());
|
|
2614
|
+
}
|
|
2615
|
+
async init() {
|
|
2616
|
+
injectCheckboxStyles();
|
|
2617
|
+
this.unsubscribe = this.store.subscribe((state, prevState) => {
|
|
2618
|
+
this.handleStateChange(state, prevState);
|
|
2619
|
+
});
|
|
2620
|
+
this.mediaQuery.subscribe(() => this.rebuildUI());
|
|
2621
|
+
this.renderOverlayWithLoading();
|
|
2622
|
+
await this.fetchNotice();
|
|
2623
|
+
}
|
|
2624
|
+
destroy() {
|
|
2625
|
+
var _a, _b, _c, _d;
|
|
2626
|
+
(_a = this.abortController) == null ? void 0 : _a.abort();
|
|
2627
|
+
(_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
|
|
2628
|
+
(_c = this.unsubscribe) == null ? void 0 : _c.call(this);
|
|
2629
|
+
this.mediaQuery.destroy();
|
|
2630
|
+
this.ttsManager.destroy();
|
|
2631
|
+
this.stopPolling();
|
|
2632
|
+
this.store.destroy();
|
|
2633
|
+
if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
|
|
2634
|
+
document.body.removeChild(this.langSelectorRefs.dropdown);
|
|
2635
|
+
}
|
|
2636
|
+
if (this.overlayEl && document.body.contains(this.overlayEl)) {
|
|
2637
|
+
document.body.removeChild(this.overlayEl);
|
|
2638
|
+
}
|
|
2639
|
+
if (this.modalEl && document.body.contains(this.modalEl)) {
|
|
2640
|
+
document.body.removeChild(this.modalEl);
|
|
2641
|
+
}
|
|
2642
|
+
this.overlayEl = null;
|
|
2643
|
+
this.modalEl = null;
|
|
2644
|
+
this.purposeRefs = null;
|
|
2645
|
+
this.langSelectorRefs = null;
|
|
2646
|
+
}
|
|
2647
|
+
// ─── Private ───
|
|
2648
|
+
async fetchNotice() {
|
|
2649
|
+
var _a, _b, _c;
|
|
2650
|
+
this.abortController = new AbortController();
|
|
2651
|
+
this.store.setState({ isLoading: true, fetchError: null });
|
|
2652
|
+
try {
|
|
2653
|
+
const data = await fetchConsentContent({
|
|
2654
|
+
noticeId: this.options.noticeId,
|
|
2655
|
+
accessToken: this.options.accessToken,
|
|
2656
|
+
refreshToken: this.options.refreshToken,
|
|
2657
|
+
baseUrl: this.options.baseUrl,
|
|
2658
|
+
language: this.options.language,
|
|
2659
|
+
specific_uuid: this.options.applicationId,
|
|
2660
|
+
validate_against: this.options.validateAgainst || "all",
|
|
2661
|
+
include_fully_consented_data: this.options.includeFullyConsentedData,
|
|
2662
|
+
signal: this.abortController.signal
|
|
2663
|
+
});
|
|
2664
|
+
const activeConfig = data.detail.active_config;
|
|
2665
|
+
const purposeSelections = data.detail.purpose_selections;
|
|
2666
|
+
const initialCollapsed = {};
|
|
2667
|
+
const initialPurposes = {};
|
|
2668
|
+
const initialElements = {};
|
|
2669
|
+
const initialDataElementSelections = {};
|
|
2670
|
+
let needsConsentPurposes = [];
|
|
2671
|
+
let alreadyConsentedPurposes = [];
|
|
2672
|
+
let isFullyConsentedReviewMode = false;
|
|
2673
|
+
if (purposeSelections && data.detail.reconsent_required) {
|
|
2674
|
+
for (const purpose of activeConfig.purposes) {
|
|
2675
|
+
const selection = purposeSelections[purpose.uuid];
|
|
2676
|
+
initialCollapsed[purpose.uuid] = true;
|
|
2677
|
+
if (selection && selection.selected && !selection.needs_reconsent) {
|
|
2678
|
+
alreadyConsentedPurposes.push(purpose);
|
|
2679
|
+
initialPurposes[purpose.uuid] = true;
|
|
2680
|
+
for (const de of purpose.data_elements) {
|
|
2681
|
+
const combinedId = `${purpose.uuid}-${de.uuid}`;
|
|
2682
|
+
const deSelection = (_a = selection.data_elements) == null ? void 0 : _a[de.uuid];
|
|
2683
|
+
const wasSelected = (deSelection == null ? void 0 : deSelection.selected) || false;
|
|
2684
|
+
initialElements[combinedId] = wasSelected;
|
|
2685
|
+
initialDataElementSelections[combinedId] = wasSelected;
|
|
2686
|
+
}
|
|
2687
|
+
} else {
|
|
2688
|
+
needsConsentPurposes.push(purpose);
|
|
2689
|
+
initialPurposes[purpose.uuid] = false;
|
|
2690
|
+
for (const de of purpose.data_elements) {
|
|
2691
|
+
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
2692
|
+
}
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
} else {
|
|
2696
|
+
needsConsentPurposes = activeConfig.purposes;
|
|
2697
|
+
for (const purpose of activeConfig.purposes) {
|
|
2698
|
+
initialCollapsed[purpose.uuid] = true;
|
|
2699
|
+
initialPurposes[purpose.uuid] = false;
|
|
2700
|
+
for (const de of purpose.data_elements) {
|
|
2701
|
+
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
2702
|
+
}
|
|
2703
|
+
}
|
|
2704
|
+
}
|
|
2705
|
+
if (this.options.includeFullyConsentedData && needsConsentPurposes.length === 0 && alreadyConsentedPurposes.length > 0) {
|
|
2706
|
+
isFullyConsentedReviewMode = true;
|
|
2707
|
+
}
|
|
2708
|
+
let selectedLanguage = this.options.language || "en";
|
|
2709
|
+
if (activeConfig.default_language) {
|
|
2710
|
+
selectedLanguage = activeConfig.default_language;
|
|
2711
|
+
}
|
|
2712
|
+
let isMinorFlow = false;
|
|
2713
|
+
let showAgeVerification = false;
|
|
2714
|
+
let restoreState = {};
|
|
2715
|
+
let pollingTokenToResume = null;
|
|
2716
|
+
if (data.detail.is_minor) {
|
|
2717
|
+
isMinorFlow = true;
|
|
2718
|
+
const persistedState = getConsentState();
|
|
2719
|
+
if ((persistedState == null ? void 0 : persistedState.noticeId) && persistedState.noticeId !== this.options.noticeId) {
|
|
2720
|
+
clearConsentState();
|
|
2721
|
+
} else if ((persistedState == null ? void 0 : persistedState.flowState) === "digilocker-pending" && persistedState.sessionToken) {
|
|
2722
|
+
restoreState = {
|
|
2723
|
+
showVerificationScreen: true,
|
|
2724
|
+
isPollingStatus: true
|
|
2725
|
+
};
|
|
2726
|
+
pollingTokenToResume = persistedState.sessionToken;
|
|
2727
|
+
} else if ((persistedState == null ? void 0 : persistedState.verificationResult) === "verified" && persistedState.verificationReference) {
|
|
2728
|
+
restoreState = {
|
|
2729
|
+
showVerificationScreen: true,
|
|
2730
|
+
isVerificationComplete: true,
|
|
2731
|
+
verificationReference: persistedState.verificationReference
|
|
2732
|
+
};
|
|
2733
|
+
clearConsentState();
|
|
2734
|
+
} else if ((persistedState == null ? void 0 : persistedState.verificationResult) === "failed" || (persistedState == null ? void 0 : persistedState.verificationResult) === "expired") {
|
|
2735
|
+
restoreState = {
|
|
2736
|
+
showVerificationScreen: true,
|
|
2737
|
+
verificationError: persistedState.verificationResult === "expired" ? "Verification session has expired. Please try again." : "Verification failed. Please try again.",
|
|
2738
|
+
canRetryVerification: true
|
|
2739
|
+
};
|
|
2740
|
+
clearConsentState();
|
|
2741
|
+
} else {
|
|
2742
|
+
showAgeVerification = true;
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
this.store.setState({
|
|
2746
|
+
content: data,
|
|
2747
|
+
isLoading: false,
|
|
2748
|
+
selectedLanguage,
|
|
2749
|
+
collapsedPurposes: initialCollapsed,
|
|
2750
|
+
selectedPurposes: initialPurposes,
|
|
2751
|
+
selectedDataElements: initialElements,
|
|
2752
|
+
initialDataElementSelections,
|
|
2753
|
+
needsConsentPurposes,
|
|
2754
|
+
alreadyConsentedPurposes,
|
|
2755
|
+
isFullyConsentedReviewMode,
|
|
2756
|
+
isMinorFlow,
|
|
2757
|
+
showAgeVerification,
|
|
2758
|
+
...restoreState
|
|
2759
|
+
});
|
|
2760
|
+
if (pollingTokenToResume) {
|
|
2761
|
+
this.startStatusPolling(pollingTokenToResume);
|
|
2762
|
+
}
|
|
2763
|
+
} catch (err) {
|
|
2764
|
+
if (err.name === "AbortError") return;
|
|
2765
|
+
const error = err;
|
|
2766
|
+
if (error.status === 409) {
|
|
2767
|
+
this.store.setState({ hasAlreadyConsented: true, isLoading: false });
|
|
2768
|
+
this.options.onAccept();
|
|
2769
|
+
} else {
|
|
2770
|
+
this.store.setState({ fetchError: error, isLoading: false });
|
|
2771
|
+
(_c = (_b = this.options).onError) == null ? void 0 : _c.call(_b, error);
|
|
2772
|
+
}
|
|
2773
|
+
}
|
|
2774
|
+
}
|
|
2775
|
+
handleStateChange(state, prevState) {
|
|
2776
|
+
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
2777
|
+
this.rebuildUI();
|
|
2778
|
+
return;
|
|
2779
|
+
}
|
|
2780
|
+
if (state.hasAlreadyConsented && !prevState.hasAlreadyConsented) {
|
|
2781
|
+
this.destroy();
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
if (state.fetchError && !prevState.fetchError) {
|
|
2785
|
+
this.renderError(state.fetchError.message);
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
if (state.showAgeVerification !== prevState.showAgeVerification || state.showGuardianForm !== prevState.showGuardianForm || state.showVerificationScreen !== prevState.showVerificationScreen) {
|
|
2789
|
+
this.rebuildUI();
|
|
2790
|
+
return;
|
|
2791
|
+
}
|
|
2792
|
+
if (state.showVerificationScreen && (state.isVerificationComplete !== prevState.isVerificationComplete || state.verificationError !== prevState.verificationError || state.isPollingStatus !== prevState.isPollingStatus || state.isInitiatingVerification !== prevState.isInitiatingVerification)) {
|
|
2793
|
+
this.rebuildUI();
|
|
2794
|
+
return;
|
|
2795
|
+
}
|
|
2796
|
+
if (state.selectedPurposes !== prevState.selectedPurposes || state.selectedDataElements !== prevState.selectedDataElements) {
|
|
2797
|
+
this.updateCheckboxes(state);
|
|
2798
|
+
}
|
|
2799
|
+
if (state.collapsedPurposes !== prevState.collapsedPurposes) {
|
|
2800
|
+
this.updateCollapseState(state);
|
|
2801
|
+
}
|
|
2802
|
+
if (state.errorMessage !== prevState.errorMessage) {
|
|
2803
|
+
this.updateErrorMessage(state);
|
|
2804
|
+
}
|
|
2805
|
+
if (state.isSubmittingAccept !== prevState.isSubmittingAccept || state.isSubmittingDecline !== prevState.isSubmittingDecline) {
|
|
2806
|
+
this.updateButtonStates(state);
|
|
2807
|
+
}
|
|
2808
|
+
}
|
|
2809
|
+
renderOverlayWithLoading() {
|
|
2810
|
+
const blockUI = this.options.blockUI !== false;
|
|
2811
|
+
if (blockUI) {
|
|
2812
|
+
this.overlayEl = el("div", {
|
|
2813
|
+
style: styles.overlay,
|
|
2814
|
+
role: "presentation"
|
|
2815
|
+
});
|
|
2816
|
+
document.body.appendChild(this.overlayEl);
|
|
2817
|
+
}
|
|
2818
|
+
const settingsObj = this.options.settings;
|
|
2819
|
+
const modalStyle = { ...styles.modal };
|
|
2820
|
+
if (settingsObj == null ? void 0 : settingsObj.backgroundColor) modalStyle.backgroundColor = settingsObj.backgroundColor;
|
|
2821
|
+
if (settingsObj == null ? void 0 : settingsObj.borderRadius) modalStyle.borderRadius = settingsObj.borderRadius;
|
|
2822
|
+
if (settingsObj == null ? void 0 : settingsObj.borderColor) modalStyle.border = `1px solid ${settingsObj.borderColor}`;
|
|
2823
|
+
if (settingsObj == null ? void 0 : settingsObj.font) modalStyle.fontFamily = settingsObj.font;
|
|
2824
|
+
if (this.mediaQuery.matches) {
|
|
2825
|
+
modalStyle.width = "90%";
|
|
2826
|
+
modalStyle.maxWidth = "90%";
|
|
2827
|
+
}
|
|
2828
|
+
this.modalEl = el("div", {
|
|
2829
|
+
style: modalStyle,
|
|
2830
|
+
role: "dialog",
|
|
2831
|
+
"aria-modal": "true",
|
|
2832
|
+
"aria-label": "Privacy consent"
|
|
2833
|
+
});
|
|
2834
|
+
const loadingEl = el(
|
|
2835
|
+
"div",
|
|
2836
|
+
{ style: styles.loadingContainer },
|
|
2837
|
+
el("div", { style: styles.loadingSpinner }),
|
|
2838
|
+
el("p", { style: { ...styles.privacyText, color: (settingsObj == null ? void 0 : settingsObj.textColor) || "#344054" } }, "Loading...")
|
|
2839
|
+
);
|
|
2840
|
+
const contentEl = el("div", { style: styles.content }, loadingEl);
|
|
2841
|
+
this.modalEl.appendChild(contentEl);
|
|
2842
|
+
document.body.appendChild(this.modalEl);
|
|
2843
|
+
}
|
|
2844
|
+
rebuildUI() {
|
|
2845
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
2846
|
+
const state = this.store.getState();
|
|
2847
|
+
if (!state.content || !this.modalEl) return;
|
|
2848
|
+
const activeConfig = state.content.detail.active_config;
|
|
2849
|
+
const settingsObj = this.options.settings;
|
|
2850
|
+
const isMobile = this.mediaQuery.matches;
|
|
2851
|
+
const colors = resolveColors(settingsObj, activeConfig);
|
|
2852
|
+
if (state.showAgeVerification) {
|
|
2853
|
+
clearElement(this.modalEl);
|
|
2854
|
+
const ageRefs = renderAgeVerification({
|
|
2855
|
+
settings: settingsObj,
|
|
2856
|
+
colors,
|
|
2857
|
+
isMobile,
|
|
2858
|
+
logoUrl: activeConfig.logo_url,
|
|
2859
|
+
onYes: () => this.handleAgeVerificationYes(),
|
|
2860
|
+
onNo: () => this.handleAgeVerificationNo(),
|
|
2861
|
+
onClose: () => this.handleDecline()
|
|
2862
|
+
});
|
|
2863
|
+
this.modalEl.appendChild(ageRefs.container);
|
|
2864
|
+
(_a = this.keyboardCleanup) == null ? void 0 : _a.call(this);
|
|
2865
|
+
this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
|
|
2866
|
+
return;
|
|
2867
|
+
}
|
|
2868
|
+
if (state.showGuardianForm) {
|
|
2869
|
+
clearElement(this.modalEl);
|
|
2870
|
+
this.guardianFormRefs = renderGuardianForm({
|
|
2871
|
+
formData: state.guardianFormData,
|
|
2872
|
+
settings: settingsObj,
|
|
2873
|
+
colors,
|
|
2874
|
+
isMobile,
|
|
2875
|
+
logoUrl: activeConfig.logo_url,
|
|
2876
|
+
isSubmitting: state.isInitiatingVerification,
|
|
2877
|
+
onSubmit: () => this.handleGuardianFormSubmit(),
|
|
2878
|
+
onCancel: () => this.handleDecline(),
|
|
2879
|
+
onFieldChange: (field, value) => {
|
|
2880
|
+
const s = this.store.getState();
|
|
2881
|
+
this.store.setState({
|
|
2882
|
+
guardianFormData: { ...s.guardianFormData, [field]: value }
|
|
2883
|
+
});
|
|
2884
|
+
}
|
|
2885
|
+
});
|
|
2886
|
+
this.modalEl.appendChild(this.guardianFormRefs.container);
|
|
2887
|
+
(_b = this.keyboardCleanup) == null ? void 0 : _b.call(this);
|
|
2888
|
+
this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
|
|
2889
|
+
return;
|
|
2890
|
+
}
|
|
2891
|
+
if (state.showVerificationScreen) {
|
|
2892
|
+
clearElement(this.modalEl);
|
|
2893
|
+
const verRefs = renderVerificationScreen({
|
|
2894
|
+
settings: settingsObj,
|
|
2895
|
+
colors,
|
|
2896
|
+
isMobile,
|
|
2897
|
+
logoUrl: activeConfig.logo_url,
|
|
2898
|
+
isInitiating: state.isInitiatingVerification,
|
|
2899
|
+
isPolling: state.isPollingStatus,
|
|
2900
|
+
isComplete: state.isVerificationComplete,
|
|
2901
|
+
error: state.verificationError,
|
|
2902
|
+
errorCode: state.verificationErrorCode,
|
|
2903
|
+
canRetry: state.canRetryVerification,
|
|
2904
|
+
onContinue: () => this.handleVerificationContinue(),
|
|
2905
|
+
onBack: (clearName) => {
|
|
2906
|
+
if (clearName) {
|
|
2907
|
+
this.handleChangeGuardian();
|
|
2908
|
+
} else if (state.verificationError && state.canRetryVerification) {
|
|
2909
|
+
this.handleVerificationRetry();
|
|
2910
|
+
} else {
|
|
2911
|
+
this.handleDecline();
|
|
2912
|
+
}
|
|
2913
|
+
}
|
|
2914
|
+
});
|
|
2915
|
+
this.modalEl.appendChild(verRefs.container);
|
|
2916
|
+
(_c = this.keyboardCleanup) == null ? void 0 : _c.call(this);
|
|
2917
|
+
this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => {
|
|
2918
|
+
});
|
|
2919
|
+
return;
|
|
2920
|
+
}
|
|
2921
|
+
clearElement(this.modalEl);
|
|
2922
|
+
const headingColor = (settingsObj == null ? void 0 : settingsObj.headingColor) || "#101828";
|
|
2923
|
+
const textColor = colors.text;
|
|
2924
|
+
const linkColor = colors.link;
|
|
2925
|
+
const font = colors.font;
|
|
2926
|
+
const logoImg = activeConfig.logo_url ? el("img", { src: activeConfig.logo_url, style: styles.logo, alt: "Logo" }) : null;
|
|
2927
|
+
this.noticeTitleEl = el("span", {
|
|
2928
|
+
id: "privacy-notice-title",
|
|
2929
|
+
style: { ...styles.title, color: headingColor, fontFamily: font, fontSize: isMobile ? "16px" : "18px" }
|
|
2930
|
+
});
|
|
2931
|
+
this.noticeTitleEl.textContent = this.getTranslatedText("notice_banner_heading", activeConfig.notice_banner_heading);
|
|
2932
|
+
const topLeft = el("div", { style: styles.topLeft }, logoImg, this.noticeTitleEl);
|
|
2933
|
+
if (((_d = this.langSelectorRefs) == null ? void 0 : _d.dropdown) && document.body.contains(this.langSelectorRefs.dropdown)) {
|
|
2934
|
+
document.body.removeChild(this.langSelectorRefs.dropdown);
|
|
2935
|
+
}
|
|
2936
|
+
const supportedLangs = Object.keys(activeConfig.supported_languages_and_translations || {});
|
|
2937
|
+
const allLanguages = [activeConfig.default_language || "English", ...supportedLangs.filter((l) => l !== activeConfig.default_language)];
|
|
2938
|
+
this.langSelectorRefs = renderLanguageSelector({
|
|
2939
|
+
languages: allLanguages,
|
|
2940
|
+
selectedLanguage: state.selectedLanguage,
|
|
2941
|
+
settings: settingsObj,
|
|
2942
|
+
onSelect: (lang) => this.handleLanguageSelect(lang)
|
|
2943
|
+
});
|
|
2944
|
+
const ttsStrokeColor = ((_f = (_e = settingsObj == null ? void 0 : settingsObj.button) == null ? void 0 : _e.language) == null ? void 0 : _f.textColor) || "currentColor";
|
|
2945
|
+
const speakerSvg = svg(
|
|
2946
|
+
"svg",
|
|
2947
|
+
{
|
|
2948
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
2949
|
+
width: "16",
|
|
2950
|
+
height: "16",
|
|
2951
|
+
viewBox: "0 0 24 24",
|
|
2952
|
+
fill: "none",
|
|
2953
|
+
stroke: ttsStrokeColor,
|
|
2954
|
+
"stroke-width": "2",
|
|
2955
|
+
"stroke-linecap": "round",
|
|
2956
|
+
"stroke-linejoin": "round",
|
|
2957
|
+
"aria-hidden": "true"
|
|
2958
|
+
},
|
|
2959
|
+
svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
|
|
2960
|
+
svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
|
|
2961
|
+
);
|
|
2962
|
+
this.ttsButton = el("button", {
|
|
2963
|
+
style: {
|
|
2964
|
+
...styles.topRight,
|
|
2965
|
+
display: "none",
|
|
2966
|
+
padding: "3px 9px",
|
|
2967
|
+
alignItems: "center",
|
|
2968
|
+
justifyContent: "center"
|
|
2969
|
+
},
|
|
2970
|
+
type: "button",
|
|
2971
|
+
"aria-label": "Start audio playback",
|
|
2972
|
+
title: "Start Talkback",
|
|
2973
|
+
onClick: () => this.ttsManager.toggle()
|
|
2974
|
+
});
|
|
2975
|
+
this.ttsButton.appendChild(speakerSvg);
|
|
2976
|
+
this.ttsManager.fetchAudioUrls({
|
|
2977
|
+
accessToken: this.options.accessToken,
|
|
2978
|
+
baseUrl: this.options.baseUrl,
|
|
2979
|
+
noticeUuid: activeConfig.notice_uuid,
|
|
2980
|
+
language: this.getLanguageCode(state.selectedLanguage),
|
|
2981
|
+
content: activeConfig
|
|
2982
|
+
});
|
|
2983
|
+
const topRight = el("div", {
|
|
2984
|
+
style: { display: "flex", alignItems: "center", gap: "8px" }
|
|
2985
|
+
}, this.ttsButton, this.langSelectorRefs.container);
|
|
2986
|
+
const topSection = el("div", { style: styles.topSection }, topLeft, topRight);
|
|
2987
|
+
this.middleSection = el("div", { style: styles.middleSection });
|
|
2988
|
+
const baseTextStyle = { ...styles.privacyText, color: textColor, fontFamily: font, fontSize: isMobile ? "14px" : "16px" };
|
|
2989
|
+
const noticeTextSpan = el("span", { id: "privacy-notice-text" });
|
|
2990
|
+
noticeTextSpan.textContent = this.getTranslatedText("notice_text", activeConfig.notice_text);
|
|
2991
|
+
const policyPrefixText = this.getTranslatedText("privacy_policy_prefix_text", activeConfig.privacy_policy_prefix_text);
|
|
2992
|
+
const policyAnchorText = this.getTranslatedText("privacy_policy_anchor_text", activeConfig.privacy_policy_anchor_text);
|
|
2993
|
+
this.privacyPolicyLinkEl = el("a", {
|
|
2994
|
+
id: "privacy-policy-link",
|
|
2995
|
+
href: activeConfig.privacy_policy_url,
|
|
2996
|
+
target: "_blank",
|
|
2997
|
+
rel: "noopener noreferrer",
|
|
2998
|
+
style: { ...styles.link, color: linkColor, fontFamily: font },
|
|
2999
|
+
"aria-label": "Privacy Policy (opens in new tab)"
|
|
3000
|
+
});
|
|
3001
|
+
this.privacyPolicyLinkEl.textContent = policyAnchorText;
|
|
3002
|
+
const policySpan = el(
|
|
3003
|
+
"span",
|
|
3004
|
+
{ id: "privacy-policy-text" },
|
|
3005
|
+
`${policyPrefixText} `,
|
|
3006
|
+
this.privacyPolicyLinkEl
|
|
3007
|
+
);
|
|
3008
|
+
const noticeAndPolicyP = el(
|
|
3009
|
+
"p",
|
|
3010
|
+
{ style: baseTextStyle },
|
|
3011
|
+
noticeTextSpan,
|
|
3012
|
+
el("br"),
|
|
3013
|
+
policySpan
|
|
3014
|
+
);
|
|
3015
|
+
this.purposeHeadingEl = el("h2", {
|
|
3016
|
+
id: "purpose-section-heading",
|
|
3017
|
+
style: { ...styles.subTitle, color: headingColor, fontFamily: font }
|
|
3018
|
+
});
|
|
3019
|
+
this.purposeHeadingEl.textContent = this.getTranslatedText("purpose_section_heading", activeConfig.purpose_section_heading);
|
|
3020
|
+
this.middleSection.appendChild(noticeAndPolicyP);
|
|
3021
|
+
this.middleSection.appendChild(this.purposeHeadingEl);
|
|
3022
|
+
if (state.needsConsentPurposes.length > 0) {
|
|
3023
|
+
this.purposeRefs = renderModalPurposes(state.needsConsentPurposes, {
|
|
3024
|
+
selectedPurposes: state.selectedPurposes,
|
|
3025
|
+
collapsedPurposes: state.collapsedPurposes,
|
|
3026
|
+
selectedDataElements: state.selectedDataElements,
|
|
3027
|
+
settings: settingsObj,
|
|
3028
|
+
isMobile,
|
|
3029
|
+
isAlreadyConsented: false,
|
|
3030
|
+
getTranslatedText: (key, defaultText, itemId) => this.getTranslatedText(key, defaultText, itemId),
|
|
3031
|
+
onPurposeToggle: (uuid) => this.handlePurposeCheckboxChange(uuid),
|
|
3032
|
+
onPurposeCollapse: (uuid) => this.togglePurposeCollapse(uuid),
|
|
3033
|
+
onDataElementToggle: (euuid, puuid) => this.handleDataElementCheckboxChange(euuid, puuid)
|
|
3034
|
+
});
|
|
3035
|
+
this.middleSection.appendChild(this.purposeRefs.container);
|
|
3036
|
+
}
|
|
3037
|
+
if (state.alreadyConsentedPurposes.length > 0) {
|
|
3038
|
+
this.alreadyConsentedPurposeRefs = renderModalPurposes(state.alreadyConsentedPurposes, {
|
|
3039
|
+
selectedPurposes: state.selectedPurposes,
|
|
3040
|
+
collapsedPurposes: state.collapsedPurposes,
|
|
3041
|
+
selectedDataElements: state.selectedDataElements,
|
|
3042
|
+
initialDataElementSelections: state.initialDataElementSelections,
|
|
3043
|
+
settings: settingsObj,
|
|
3044
|
+
isMobile,
|
|
3045
|
+
isAlreadyConsented: true,
|
|
3046
|
+
getTranslatedText: (key, defaultText, itemId) => this.getTranslatedText(key, defaultText, itemId),
|
|
3047
|
+
onPurposeToggle: () => {
|
|
3048
|
+
},
|
|
3049
|
+
onPurposeCollapse: (uuid) => this.togglePurposeCollapse(uuid),
|
|
3050
|
+
onDataElementToggle: () => {
|
|
3051
|
+
}
|
|
3052
|
+
});
|
|
3053
|
+
this.middleSection.appendChild(this.alreadyConsentedPurposeRefs.container);
|
|
3054
|
+
}
|
|
3055
|
+
if (activeConfig.privacy_center_url || activeConfig.dpo_info) {
|
|
3056
|
+
const footerDiv = el("div", {
|
|
3057
|
+
style: {
|
|
3058
|
+
...baseTextStyle,
|
|
3059
|
+
fontSize: isMobile ? "13px" : "14px",
|
|
3060
|
+
lineHeight: "1.4",
|
|
3061
|
+
borderTop: "1px solid rgba(152, 162, 179, 0.3)",
|
|
3062
|
+
paddingTop: "12px",
|
|
3063
|
+
marginTop: "12px"
|
|
3064
|
+
}
|
|
3065
|
+
});
|
|
3066
|
+
const footerP = el("p", { style: { margin: "0", opacity: "0.6" } });
|
|
3067
|
+
if (activeConfig.privacy_center_url) {
|
|
3068
|
+
if (activeConfig.additional_text) {
|
|
3069
|
+
const addlSpan = el("span", { id: "additional-text" });
|
|
3070
|
+
addlSpan.textContent = this.getTranslatedText("additional_text", activeConfig.additional_text);
|
|
3071
|
+
footerP.appendChild(addlSpan);
|
|
3072
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3073
|
+
}
|
|
3074
|
+
const pcAnchorText = this.getTranslatedText("privacy_center_anchor_text", activeConfig.privacy_center_anchor_text || "Privacy Center");
|
|
3075
|
+
this.privacyCenterLinkEl = el("a", {
|
|
3076
|
+
id: "privacy-center-link",
|
|
3077
|
+
href: activeConfig.privacy_center_url,
|
|
3078
|
+
target: "_blank",
|
|
3079
|
+
rel: "noopener noreferrer",
|
|
3080
|
+
style: { ...styles.link, color: linkColor, fontFamily: font }
|
|
3081
|
+
});
|
|
3082
|
+
this.privacyCenterLinkEl.textContent = pcAnchorText;
|
|
3083
|
+
footerP.appendChild(this.privacyCenterLinkEl);
|
|
3084
|
+
footerP.appendChild(document.createTextNode("."));
|
|
3085
|
+
}
|
|
3086
|
+
if (activeConfig.dpo_info) {
|
|
3087
|
+
const dpo = activeConfig.dpo_info;
|
|
3088
|
+
if (activeConfig.privacy_center_url) {
|
|
3089
|
+
footerP.appendChild(el("br"));
|
|
3090
|
+
}
|
|
3091
|
+
if (dpo.grievance_text) {
|
|
3092
|
+
const gText = el("span", { id: "dpo-grievance-text" });
|
|
3093
|
+
gText.textContent = this.getTranslatedDpoText("grievance_text", dpo.grievance_text);
|
|
3094
|
+
footerP.appendChild(gText);
|
|
3095
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3096
|
+
if (dpo.grievance_url) {
|
|
3097
|
+
const gLink = el("a", {
|
|
3098
|
+
id: "dpo-grievance-link",
|
|
3099
|
+
href: dpo.grievance_url,
|
|
3100
|
+
target: "_blank",
|
|
3101
|
+
rel: "noopener noreferrer",
|
|
3102
|
+
style: { ...styles.link, color: linkColor, fontFamily: font }
|
|
3103
|
+
});
|
|
3104
|
+
gLink.textContent = this.getTranslatedDpoText("grievance_anchor_text", dpo.grievance_anchor_text);
|
|
3105
|
+
footerP.appendChild(gLink);
|
|
3106
|
+
}
|
|
3107
|
+
if (dpo.grievance_email) {
|
|
3108
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3109
|
+
const connectorSpan = el("span", { id: "dpo-grievance-email-connector" });
|
|
3110
|
+
connectorSpan.textContent = this.getTranslatedDpoText(
|
|
3111
|
+
"grievance_email_connector_text",
|
|
3112
|
+
dpo.grievance_email_connector_text || "or email to"
|
|
3113
|
+
);
|
|
3114
|
+
footerP.appendChild(connectorSpan);
|
|
3115
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3116
|
+
const emailLink = el("a", {
|
|
3117
|
+
id: "dpo-grievance-email",
|
|
3118
|
+
href: `mailto:${dpo.grievance_email}`,
|
|
3119
|
+
style: { ...styles.link, color: linkColor, fontFamily: font }
|
|
3120
|
+
});
|
|
3121
|
+
emailLink.textContent = dpo.grievance_email;
|
|
3122
|
+
footerP.appendChild(emailLink);
|
|
3123
|
+
}
|
|
3124
|
+
}
|
|
3125
|
+
if (dpo.dp_board_text) {
|
|
3126
|
+
footerP.appendChild(el("br"));
|
|
3127
|
+
const dpText = el("span", { id: "dpo-dp-board-text" });
|
|
3128
|
+
dpText.textContent = this.getTranslatedDpoText("dp_board_text", dpo.dp_board_text);
|
|
3129
|
+
footerP.appendChild(dpText);
|
|
3130
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3131
|
+
if (dpo.dp_board_url) {
|
|
3132
|
+
const dpLink = el("a", {
|
|
3133
|
+
id: "dpo-dp-board-link",
|
|
3134
|
+
href: dpo.dp_board_url,
|
|
3135
|
+
target: "_blank",
|
|
3136
|
+
rel: "noopener noreferrer",
|
|
3137
|
+
style: { ...styles.link, color: linkColor, fontFamily: font }
|
|
3138
|
+
});
|
|
3139
|
+
dpLink.textContent = this.getTranslatedDpoText("dp_board_anchor_text", dpo.dp_board_anchor_text);
|
|
3140
|
+
footerP.appendChild(dpLink);
|
|
3141
|
+
}
|
|
3142
|
+
}
|
|
3143
|
+
if (dpo.dpo_text) {
|
|
3144
|
+
footerP.appendChild(el("br"));
|
|
3145
|
+
const dpoTextSpan = el("span", { id: "dpo-dpo-text" });
|
|
3146
|
+
dpoTextSpan.textContent = this.getTranslatedDpoText("dpo_text", dpo.dpo_text);
|
|
3147
|
+
footerP.appendChild(dpoTextSpan);
|
|
3148
|
+
footerP.appendChild(document.createTextNode(" "));
|
|
3149
|
+
if (dpo.dpo_url) {
|
|
3150
|
+
const dpoLink = el("a", {
|
|
3151
|
+
id: "dpo-dpo-link",
|
|
3152
|
+
href: dpo.dpo_url,
|
|
3153
|
+
target: "_blank",
|
|
3154
|
+
rel: "noopener noreferrer",
|
|
3155
|
+
style: { ...styles.link, color: linkColor, fontFamily: font }
|
|
3156
|
+
});
|
|
3157
|
+
dpoLink.textContent = this.getTranslatedDpoText("dpo_anchor_text", dpo.dpo_anchor_text);
|
|
3158
|
+
footerP.appendChild(dpoLink);
|
|
3159
|
+
}
|
|
3160
|
+
}
|
|
3161
|
+
}
|
|
3162
|
+
footerDiv.appendChild(footerP);
|
|
3163
|
+
this.middleSection.appendChild(footerDiv);
|
|
3164
|
+
}
|
|
3165
|
+
this.errorEl = el("div", {
|
|
3166
|
+
role: "alert",
|
|
3167
|
+
style: {
|
|
3168
|
+
backgroundColor: "#FEE2E2",
|
|
3169
|
+
color: "#DC2626",
|
|
3170
|
+
padding: "12px",
|
|
3171
|
+
borderRadius: "6px",
|
|
3172
|
+
fontSize: "14px",
|
|
3173
|
+
border: "1px solid #FCA5A5",
|
|
3174
|
+
display: "none"
|
|
3175
|
+
}
|
|
3176
|
+
});
|
|
3177
|
+
const acceptBtnText = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText("confirm_button_text", activeConfig.confirm_button_text);
|
|
3178
|
+
const acceptStyle = {
|
|
3179
|
+
...styles.button,
|
|
3180
|
+
...styles.acceptButton,
|
|
3181
|
+
backgroundColor: colors.acceptBg,
|
|
3182
|
+
color: colors.acceptText,
|
|
3183
|
+
borderRadius: colors.borderRadius,
|
|
3184
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
3185
|
+
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3186
|
+
};
|
|
3187
|
+
this.acceptBtn = el("button", {
|
|
3188
|
+
id: "confirm-button-text",
|
|
3189
|
+
style: acceptStyle,
|
|
3190
|
+
type: "button",
|
|
3191
|
+
onClick: () => this.handleAccept()
|
|
3192
|
+
});
|
|
3193
|
+
this.acceptBtn.textContent = acceptBtnText;
|
|
3194
|
+
const declineBtnText = this.getTranslatedText("decline_button_text", activeConfig.decline_button_text);
|
|
3195
|
+
const declineStyle = {
|
|
3196
|
+
...styles.button,
|
|
3197
|
+
...styles.cancelButton,
|
|
3198
|
+
backgroundColor: colors.declineBg,
|
|
3199
|
+
color: colors.declineText,
|
|
3200
|
+
borderColor: colors.declineBorder,
|
|
3201
|
+
borderRadius: colors.borderRadius,
|
|
3202
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
3203
|
+
padding: isMobile ? "10px 20px" : "9px 45px"
|
|
3204
|
+
};
|
|
3205
|
+
this.declineBtn = el("button", {
|
|
3206
|
+
id: "decline-button-text",
|
|
3207
|
+
style: declineStyle,
|
|
3208
|
+
type: "button",
|
|
3209
|
+
onClick: () => this.handleDecline()
|
|
3210
|
+
});
|
|
3211
|
+
if (!state.isFullyConsentedReviewMode) {
|
|
3212
|
+
this.declineBtn.textContent = declineBtnText;
|
|
3213
|
+
}
|
|
3214
|
+
const bottomSection = el("div", { style: styles.bottomSection });
|
|
3215
|
+
if (!state.isFullyConsentedReviewMode) {
|
|
3216
|
+
bottomSection.appendChild(this.acceptBtn);
|
|
3217
|
+
bottomSection.appendChild(this.declineBtn);
|
|
3218
|
+
} else {
|
|
3219
|
+
bottomSection.appendChild(this.acceptBtn);
|
|
3220
|
+
}
|
|
3221
|
+
const contentWrapper = el(
|
|
3222
|
+
"div",
|
|
3223
|
+
{ style: styles.content },
|
|
3224
|
+
topSection,
|
|
3225
|
+
this.errorEl,
|
|
3226
|
+
this.middleSection,
|
|
3227
|
+
bottomSection
|
|
3228
|
+
);
|
|
3229
|
+
this.modalEl.appendChild(contentWrapper);
|
|
3230
|
+
this.updateAcceptButtonState(state);
|
|
3231
|
+
(_g = this.keyboardCleanup) == null ? void 0 : _g.call(this);
|
|
3232
|
+
this.keyboardCleanup = setupKeyboardTrap(this.modalEl, () => this.handleDecline());
|
|
3233
|
+
}
|
|
3234
|
+
getLanguageCode(displayLanguage) {
|
|
3235
|
+
var _a, _b;
|
|
3236
|
+
const content = (_a = this.store.getState().content) == null ? void 0 : _a.detail.active_config;
|
|
3237
|
+
if (!content) return "English";
|
|
3238
|
+
if (displayLanguage === "English" || displayLanguage === "en" || displayLanguage === "EN") {
|
|
3239
|
+
return "English";
|
|
3240
|
+
}
|
|
3241
|
+
if ((_b = content.supported_languages_and_translations) == null ? void 0 : _b[displayLanguage]) {
|
|
3242
|
+
return displayLanguage;
|
|
3243
|
+
}
|
|
3244
|
+
return content.default_language === "en" || content.default_language === "EN" ? "English" : content.default_language || "English";
|
|
3245
|
+
}
|
|
3246
|
+
getTranslatedDpoText(key, defaultText) {
|
|
3247
|
+
var _a;
|
|
3248
|
+
const state = this.store.getState();
|
|
3249
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3250
|
+
if (!content) return defaultText;
|
|
3251
|
+
const lang = state.selectedLanguage;
|
|
3252
|
+
if (lang === "English" || lang === "en" || lang === "EN") return defaultText;
|
|
3253
|
+
const translations = content.supported_languages_and_translations;
|
|
3254
|
+
if (!translations) return defaultText;
|
|
3255
|
+
const translationMap = translations[lang];
|
|
3256
|
+
if (!(translationMap == null ? void 0 : translationMap.dpo_info)) return defaultText;
|
|
3257
|
+
const value = translationMap.dpo_info[key];
|
|
3258
|
+
return typeof value === "string" && value ? value : defaultText;
|
|
3259
|
+
}
|
|
3260
|
+
renderError(message) {
|
|
3261
|
+
if (!this.modalEl) return;
|
|
3262
|
+
clearElement(this.modalEl);
|
|
3263
|
+
const errorContent = el(
|
|
3264
|
+
"div",
|
|
3265
|
+
{ style: { ...styles.content, alignItems: "center", justifyContent: "center" } },
|
|
3266
|
+
el("div", {
|
|
3267
|
+
role: "alert",
|
|
3268
|
+
style: {
|
|
3269
|
+
backgroundColor: "#FEE2E2",
|
|
3270
|
+
color: "#DC2626",
|
|
3271
|
+
padding: "16px",
|
|
3272
|
+
borderRadius: "8px",
|
|
3273
|
+
fontSize: "14px",
|
|
3274
|
+
border: "1px solid #FCA5A5",
|
|
3275
|
+
textAlign: "center",
|
|
3276
|
+
maxWidth: "400px"
|
|
3277
|
+
}
|
|
3278
|
+
}, message)
|
|
3279
|
+
);
|
|
3280
|
+
this.modalEl.appendChild(errorContent);
|
|
3281
|
+
}
|
|
3282
|
+
getTranslatedText(key, defaultText, itemId) {
|
|
3283
|
+
var _a, _b, _c;
|
|
3284
|
+
const state = this.store.getState();
|
|
3285
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3286
|
+
if (!content) return defaultText;
|
|
3287
|
+
const lang = state.selectedLanguage;
|
|
3288
|
+
if (lang === "English" || lang === "en" || lang === "EN") {
|
|
3289
|
+
if (key === "privacy_policy_anchor_text" && content.privacy_policy_anchor_text) {
|
|
3290
|
+
return content.privacy_policy_anchor_text;
|
|
3291
|
+
}
|
|
3292
|
+
return defaultText;
|
|
3293
|
+
}
|
|
3294
|
+
const translations = content.supported_languages_and_translations;
|
|
3295
|
+
if (!translations) return defaultText;
|
|
3296
|
+
const translationMap = translations[lang];
|
|
3297
|
+
if (!translationMap) return defaultText;
|
|
3298
|
+
if (itemId) {
|
|
3299
|
+
if (key === "purposes.name" || key === "purposes.description") {
|
|
3300
|
+
const purposeData = (_b = translationMap.purposes) == null ? void 0 : _b[itemId];
|
|
3301
|
+
if (purposeData) {
|
|
3302
|
+
if (typeof purposeData === "string") {
|
|
3303
|
+
return key === "purposes.name" ? purposeData : defaultText;
|
|
3304
|
+
}
|
|
3305
|
+
return key === "purposes.name" ? purposeData.name || defaultText : purposeData.description || defaultText;
|
|
3306
|
+
}
|
|
3307
|
+
return defaultText;
|
|
3308
|
+
}
|
|
3309
|
+
if (key.startsWith("data_elements.")) {
|
|
3310
|
+
return ((_c = translationMap.data_elements) == null ? void 0 : _c[itemId]) || defaultText;
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
const value = translationMap[key];
|
|
3314
|
+
if (typeof value === "string" && value) return value;
|
|
3315
|
+
return defaultText;
|
|
3316
|
+
}
|
|
3317
|
+
handleLanguageSelect(lang) {
|
|
3318
|
+
this.store.setState({ selectedLanguage: lang });
|
|
3319
|
+
this.rebuildUI();
|
|
3320
|
+
}
|
|
3321
|
+
togglePurposeCollapse(purposeUuid) {
|
|
3322
|
+
const state = this.store.getState();
|
|
3323
|
+
this.store.setState({
|
|
3324
|
+
collapsedPurposes: {
|
|
3325
|
+
...state.collapsedPurposes,
|
|
3326
|
+
[purposeUuid]: !state.collapsedPurposes[purposeUuid]
|
|
3327
|
+
}
|
|
3328
|
+
});
|
|
3329
|
+
}
|
|
3330
|
+
handlePurposeCheckboxChange(purposeUuid) {
|
|
3331
|
+
var _a;
|
|
3332
|
+
const state = this.store.getState();
|
|
3333
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3334
|
+
if (!content) return;
|
|
3335
|
+
const purpose = content.purposes.find((p) => p.uuid === purposeUuid);
|
|
3336
|
+
if (!purpose) return;
|
|
3337
|
+
const newPurposeState = !state.selectedPurposes[purposeUuid];
|
|
3338
|
+
const updatedElements = { ...state.selectedDataElements };
|
|
3339
|
+
for (const de of purpose.data_elements) {
|
|
3340
|
+
updatedElements[`${purposeUuid}-${de.uuid}`] = newPurposeState;
|
|
3341
|
+
}
|
|
3342
|
+
this.store.setState({
|
|
3343
|
+
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: newPurposeState },
|
|
3344
|
+
selectedDataElements: updatedElements
|
|
3345
|
+
});
|
|
3346
|
+
}
|
|
3347
|
+
handleDataElementCheckboxChange(elementUuid, purposeUuid) {
|
|
3348
|
+
var _a;
|
|
3349
|
+
const state = this.store.getState();
|
|
3350
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3351
|
+
if (!content) return;
|
|
3352
|
+
const purpose = content.purposes.find((p) => p.uuid === purposeUuid);
|
|
3353
|
+
if (!purpose) return;
|
|
3354
|
+
const combinedId = `${purposeUuid}-${elementUuid}`;
|
|
3355
|
+
const newDataElements = {
|
|
3356
|
+
...state.selectedDataElements,
|
|
3357
|
+
[combinedId]: !state.selectedDataElements[combinedId]
|
|
3358
|
+
};
|
|
3359
|
+
const requiredElements = purpose.data_elements.filter((el2) => el2.required);
|
|
3360
|
+
let shouldCheckPurpose;
|
|
3361
|
+
if (requiredElements.length > 0) {
|
|
3362
|
+
shouldCheckPurpose = requiredElements.every(
|
|
3363
|
+
(el2) => newDataElements[`${purposeUuid}-${el2.uuid}`]
|
|
3364
|
+
);
|
|
3365
|
+
} else {
|
|
3366
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
3367
|
+
(el2) => newDataElements[`${purposeUuid}-${el2.uuid}`]
|
|
3368
|
+
);
|
|
3369
|
+
}
|
|
3370
|
+
this.store.setState({
|
|
3371
|
+
selectedDataElements: newDataElements,
|
|
3372
|
+
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
3373
|
+
});
|
|
3374
|
+
}
|
|
3375
|
+
async handleAccept() {
|
|
3376
|
+
var _a, _b, _c;
|
|
3377
|
+
const state = this.store.getState();
|
|
3378
|
+
if (state.isSubmitting) return;
|
|
3379
|
+
if (state.isFullyConsentedReviewMode) {
|
|
3380
|
+
this.options.onAccept();
|
|
3381
|
+
this.destroy();
|
|
3382
|
+
return;
|
|
3383
|
+
}
|
|
3384
|
+
if (state.isMinorFlow && !state.verificationReference && !state.selfDeclaredAdult) {
|
|
3385
|
+
this.store.setState({
|
|
3386
|
+
errorMessage: "Guardian verification is required before consent can be submitted."
|
|
3387
|
+
});
|
|
3388
|
+
return;
|
|
3389
|
+
}
|
|
3390
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3391
|
+
if (!content) return;
|
|
3392
|
+
this.abortController = new AbortController();
|
|
3393
|
+
this.store.setState({ isSubmitting: true, isSubmittingAccept: true, errorMessage: null });
|
|
3394
|
+
try {
|
|
3395
|
+
const purposes = content.purposes.map((purpose) => {
|
|
3396
|
+
const dataElements = purpose.data_elements.filter((de) => de.enabled).map((de) => ({
|
|
3397
|
+
uuid: de.uuid,
|
|
3398
|
+
name: de.name,
|
|
3399
|
+
enabled: de.enabled,
|
|
3400
|
+
required: de.required,
|
|
3401
|
+
selected: state.selectedDataElements[`${purpose.uuid}-${de.uuid}`] || false
|
|
3402
|
+
}));
|
|
3403
|
+
return {
|
|
3404
|
+
uuid: purpose.uuid,
|
|
3405
|
+
name: purpose.name,
|
|
3406
|
+
description: purpose.description,
|
|
3407
|
+
selected: state.selectedPurposes[purpose.uuid] || false,
|
|
3408
|
+
data_elements: dataElements
|
|
3409
|
+
};
|
|
3410
|
+
});
|
|
3411
|
+
await submitConsentEvent({
|
|
3412
|
+
accessToken: this.options.accessToken,
|
|
3413
|
+
baseUrl: this.options.baseUrl,
|
|
3414
|
+
noticeUuid: state.content.detail.uuid,
|
|
3415
|
+
purposes,
|
|
3416
|
+
declined: false,
|
|
3417
|
+
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0,
|
|
3418
|
+
guardianVerificationReference: state.verificationReference || void 0,
|
|
3419
|
+
selfDeclaredAdult: state.selfDeclaredAdult || void 0,
|
|
3420
|
+
signal: this.abortController.signal
|
|
3421
|
+
});
|
|
3422
|
+
this.options.onAccept();
|
|
3423
|
+
this.destroy();
|
|
3424
|
+
} catch (err) {
|
|
3425
|
+
if (err.name === "AbortError") return;
|
|
3426
|
+
const error = err;
|
|
3427
|
+
const message = error.status === 409 ? "You have already provided consent." : error.status && error.status >= 500 ? "Server error: Please try again later." : error.message || "Failed to submit consent.";
|
|
3428
|
+
this.store.setState({ errorMessage: message, isSubmitting: false, isSubmittingAccept: false });
|
|
3429
|
+
(_c = (_b = this.options).onError) == null ? void 0 : _c.call(_b, error);
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3432
|
+
handleDecline() {
|
|
3433
|
+
const state = this.store.getState();
|
|
3434
|
+
if (state.isSubmitting) return;
|
|
3435
|
+
this.stopPolling();
|
|
3436
|
+
clearConsentState();
|
|
3437
|
+
this.options.onDecline();
|
|
3438
|
+
this.destroy();
|
|
3439
|
+
}
|
|
3440
|
+
updateCheckboxes(state) {
|
|
3441
|
+
if (this.purposeRefs) {
|
|
3442
|
+
for (const [key, checkbox] of this.purposeRefs.checkboxes) {
|
|
3443
|
+
if (key in state.selectedPurposes) {
|
|
3444
|
+
checkbox.checked = state.selectedPurposes[key] || false;
|
|
3445
|
+
} else {
|
|
3446
|
+
checkbox.checked = state.selectedDataElements[key] || false;
|
|
3447
|
+
}
|
|
3448
|
+
}
|
|
3449
|
+
}
|
|
3450
|
+
this.updateAcceptButtonState(state);
|
|
3451
|
+
}
|
|
3452
|
+
areAllRequiredChecked(state) {
|
|
3453
|
+
var _a, _b;
|
|
3454
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3455
|
+
if (!((_b = content == null ? void 0 : content.purposes) == null ? void 0 : _b.length)) return false;
|
|
3456
|
+
return content.purposes.every((purpose) => {
|
|
3457
|
+
const requiredElements = purpose.data_elements.filter((de) => de.required && de.enabled);
|
|
3458
|
+
if (requiredElements.length === 0) return true;
|
|
3459
|
+
return requiredElements.every(
|
|
3460
|
+
(de) => state.selectedDataElements[`${purpose.uuid}-${de.uuid}`]
|
|
3461
|
+
);
|
|
3462
|
+
});
|
|
3463
|
+
}
|
|
3464
|
+
updateAcceptButtonState(state) {
|
|
3465
|
+
if (!this.acceptBtn || state.isFullyConsentedReviewMode) return;
|
|
3466
|
+
const allRequiredChecked = this.areAllRequiredChecked(state);
|
|
3467
|
+
const disabled = !allRequiredChecked || state.isSubmittingAccept || state.isSubmittingDecline;
|
|
3468
|
+
this.acceptBtn.disabled = disabled;
|
|
3469
|
+
this.acceptBtn.style.opacity = disabled ? "0.5" : "1";
|
|
3470
|
+
this.acceptBtn.style.cursor = disabled ? "not-allowed" : "pointer";
|
|
3471
|
+
}
|
|
3472
|
+
updateCollapseState(state) {
|
|
3473
|
+
const updateRefs = (refs) => {
|
|
3474
|
+
var _a, _b;
|
|
3475
|
+
if (!refs) return;
|
|
3476
|
+
for (const [purposeUuid, container] of refs.dataElementContainers) {
|
|
3477
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3478
|
+
const purpose = content == null ? void 0 : content.purposes.find((p) => p.uuid === purposeUuid);
|
|
3479
|
+
const isCollapsed = state.collapsedPurposes[purposeUuid];
|
|
3480
|
+
container.style.display = !isCollapsed && ((_b = purpose == null ? void 0 : purpose.data_elements) == null ? void 0 : _b.length) ? "flex" : "none";
|
|
3481
|
+
const chevron = refs.chevrons.get(purposeUuid);
|
|
3482
|
+
if (chevron) {
|
|
3483
|
+
chevron.style.transform = !isCollapsed ? "rotate(90deg)" : "rotate(0deg)";
|
|
3484
|
+
}
|
|
3485
|
+
const collapseBtn = refs.collapseButtons.get(purposeUuid);
|
|
3486
|
+
if (collapseBtn) {
|
|
3487
|
+
collapseBtn.setAttribute("aria-expanded", String(!isCollapsed));
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
3490
|
+
};
|
|
3491
|
+
updateRefs(this.purposeRefs);
|
|
3492
|
+
updateRefs(this.alreadyConsentedPurposeRefs);
|
|
3493
|
+
}
|
|
3494
|
+
updateErrorMessage(state) {
|
|
3495
|
+
if (!this.errorEl) return;
|
|
3496
|
+
if (state.errorMessage) {
|
|
3497
|
+
this.errorEl.textContent = state.errorMessage;
|
|
3498
|
+
this.errorEl.style.display = "block";
|
|
3499
|
+
} else {
|
|
3500
|
+
this.errorEl.style.display = "none";
|
|
3501
|
+
}
|
|
3502
|
+
}
|
|
3503
|
+
// ─── Age Verification Handlers ───
|
|
3504
|
+
handleAgeVerificationYes() {
|
|
3505
|
+
this.store.setState({
|
|
3506
|
+
isMinorFlow: false,
|
|
3507
|
+
selfDeclaredAdult: true,
|
|
3508
|
+
showAgeVerification: false
|
|
3509
|
+
});
|
|
3510
|
+
}
|
|
3511
|
+
handleAgeVerificationNo() {
|
|
3512
|
+
this.store.setState({
|
|
3513
|
+
showAgeVerification: false,
|
|
3514
|
+
showGuardianForm: true
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
// ─── Guardian Form Handlers ───
|
|
3518
|
+
async handleGuardianFormSubmit() {
|
|
3519
|
+
const state = this.store.getState();
|
|
3520
|
+
const errors = validateGuardianForm(state.guardianFormData);
|
|
3521
|
+
if (Object.keys(errors).length > 0) {
|
|
3522
|
+
if (this.guardianFormRefs) {
|
|
3523
|
+
if (errors.guardianName) {
|
|
3524
|
+
this.guardianFormRefs.nameError.textContent = errors.guardianName;
|
|
3525
|
+
this.guardianFormRefs.nameError.style.display = "block";
|
|
3526
|
+
}
|
|
3527
|
+
if (errors.guardianContact) {
|
|
3528
|
+
this.guardianFormRefs.contactError.textContent = errors.guardianContact;
|
|
3529
|
+
this.guardianFormRefs.contactError.style.display = "block";
|
|
3530
|
+
}
|
|
3531
|
+
if (errors.guardianRelationship) {
|
|
3532
|
+
this.guardianFormRefs.relationshipError.textContent = errors.guardianRelationship;
|
|
3533
|
+
this.guardianFormRefs.relationshipError.style.display = "block";
|
|
3534
|
+
}
|
|
3535
|
+
}
|
|
3536
|
+
return;
|
|
3537
|
+
}
|
|
3538
|
+
this.store.setState({ isInitiatingVerification: true });
|
|
3539
|
+
const digilockerMode = this.options.digilockerMode || "redirect";
|
|
3540
|
+
const callbackUrl = digilockerMode === "popup" ? new URL(this.options.digilockerCallbackUrl || "/digilocker-callback", window.location.origin).href : void 0;
|
|
3541
|
+
try {
|
|
3542
|
+
const response = await initiateGuardianVerification({
|
|
3543
|
+
accessToken: this.options.accessToken,
|
|
3544
|
+
baseUrl: this.options.baseUrl,
|
|
3545
|
+
guardianName: state.guardianFormData.guardianName,
|
|
3546
|
+
guardianContact: state.guardianFormData.guardianContact,
|
|
3547
|
+
guardianRelationship: state.guardianFormData.guardianRelationship,
|
|
3548
|
+
frontendCallbackUrl: callbackUrl
|
|
3549
|
+
});
|
|
3550
|
+
if (response.already_verified && response.verification_reference) {
|
|
3551
|
+
this.store.setState({
|
|
3552
|
+
verificationReference: response.verification_reference,
|
|
3553
|
+
showGuardianForm: false,
|
|
3554
|
+
showVerificationScreen: true,
|
|
3555
|
+
isInitiatingVerification: false,
|
|
3556
|
+
isVerificationComplete: true
|
|
3557
|
+
});
|
|
3558
|
+
setTimeout(() => {
|
|
3559
|
+
this.store.setState({
|
|
3560
|
+
showVerificationScreen: false,
|
|
3561
|
+
isVerificationComplete: false
|
|
3562
|
+
});
|
|
3563
|
+
}, 1e3);
|
|
3564
|
+
return;
|
|
3565
|
+
}
|
|
3566
|
+
saveConsentState({
|
|
3567
|
+
flowState: "digilocker-pending",
|
|
3568
|
+
sessionToken: response.session_token,
|
|
3569
|
+
guardianInfoUuid: response.guardian_info_uuid,
|
|
3570
|
+
selectedPurposes: state.selectedPurposes,
|
|
3571
|
+
selectedDataElements: state.selectedDataElements,
|
|
3572
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
3573
|
+
originalUrl: window.location.href,
|
|
3574
|
+
noticeId: this.options.noticeId,
|
|
3575
|
+
baseUrl: this.options.baseUrl
|
|
3576
|
+
});
|
|
3577
|
+
this.store.setState({
|
|
3578
|
+
showGuardianForm: false,
|
|
3579
|
+
showVerificationScreen: true,
|
|
3580
|
+
isInitiatingVerification: true,
|
|
3581
|
+
isPollingStatus: false
|
|
3582
|
+
});
|
|
3583
|
+
if (digilockerMode === "popup" && response.digilocker_redirect_url) {
|
|
3584
|
+
const popup = window.open(
|
|
3585
|
+
response.digilocker_redirect_url,
|
|
3586
|
+
"digilocker_verification",
|
|
3587
|
+
"width=600,height=700,scrollbars=yes,resizable=yes"
|
|
3588
|
+
);
|
|
3589
|
+
if (popup) {
|
|
3590
|
+
this.verificationPopup = popup;
|
|
3591
|
+
this.startPopupMonitoring(popup);
|
|
3592
|
+
} else {
|
|
3593
|
+
this.store.setState({
|
|
3594
|
+
isInitiatingVerification: false,
|
|
3595
|
+
verificationError: "Popup blocked. Please allow popups for this site.",
|
|
3596
|
+
canRetryVerification: true
|
|
3597
|
+
});
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
} else if (response.digilocker_redirect_url) {
|
|
3601
|
+
const openedWindow = window.open(response.digilocker_redirect_url, "_blank");
|
|
3602
|
+
if (!openedWindow) {
|
|
3603
|
+
this.store.setState({
|
|
3604
|
+
isInitiatingVerification: false,
|
|
3605
|
+
verificationError: "Unable to open DigiLocker. Please allow popups.",
|
|
3606
|
+
canRetryVerification: true
|
|
3607
|
+
});
|
|
3608
|
+
return;
|
|
3609
|
+
}
|
|
3610
|
+
try {
|
|
3611
|
+
openedWindow.opener = null;
|
|
3612
|
+
} catch {
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
this.store.setState({
|
|
3616
|
+
isInitiatingVerification: false,
|
|
3617
|
+
isPollingStatus: true
|
|
3618
|
+
});
|
|
3619
|
+
this.startStatusPolling(response.session_token);
|
|
3620
|
+
} catch (error) {
|
|
3621
|
+
this.store.setState({ isInitiatingVerification: false });
|
|
3622
|
+
const err = error;
|
|
3623
|
+
if (this.guardianFormRefs) {
|
|
3624
|
+
this.guardianFormRefs.generalError.textContent = err.message || "Failed to initiate verification.";
|
|
3625
|
+
this.guardianFormRefs.generalError.style.display = "block";
|
|
3626
|
+
}
|
|
3627
|
+
}
|
|
3628
|
+
}
|
|
3629
|
+
// ─── Verification Polling ───
|
|
3630
|
+
startStatusPolling(token) {
|
|
3631
|
+
this.verificationSessionToken = token;
|
|
3632
|
+
let attempts = 0;
|
|
3633
|
+
let consecutiveErrors = 0;
|
|
3634
|
+
let paused = false;
|
|
3635
|
+
const MAX_ATTEMPTS = 40;
|
|
3636
|
+
const MAX_CONSECUTIVE_ERRORS = 3;
|
|
3637
|
+
const POLL_INTERVAL = 3e3;
|
|
3638
|
+
const poll = async () => {
|
|
3639
|
+
var _a;
|
|
3640
|
+
if (paused) return;
|
|
3641
|
+
attempts++;
|
|
3642
|
+
if (attempts > MAX_ATTEMPTS) {
|
|
3643
|
+
this.stopPolling();
|
|
3644
|
+
this.store.setState({
|
|
3645
|
+
verificationError: "Verification is taking longer than expected. Please try again.",
|
|
3646
|
+
verificationErrorCode: "SESSION_EXPIRED",
|
|
3647
|
+
canRetryVerification: true,
|
|
3648
|
+
isPollingStatus: false
|
|
3649
|
+
});
|
|
3650
|
+
return;
|
|
3651
|
+
}
|
|
3652
|
+
try {
|
|
3653
|
+
const response = await verifyGuardianStatus({
|
|
3654
|
+
accessToken: this.options.accessToken,
|
|
3655
|
+
baseUrl: this.options.baseUrl,
|
|
3656
|
+
sessionToken: token
|
|
3657
|
+
});
|
|
3658
|
+
consecutiveErrors = 0;
|
|
3659
|
+
if (response.status === "verified") {
|
|
3660
|
+
this.stopPolling();
|
|
3661
|
+
const ref = response.verification_reference || ((_a = response.guardian_details) == null ? void 0 : _a.verification_reference);
|
|
3662
|
+
if (!ref) {
|
|
3663
|
+
this.store.setState({
|
|
3664
|
+
verificationError: "Verification completed but no reference received. Please try again.",
|
|
3665
|
+
canRetryVerification: true,
|
|
3666
|
+
isPollingStatus: false
|
|
3667
|
+
});
|
|
3668
|
+
return;
|
|
3669
|
+
}
|
|
3670
|
+
this.store.setState({
|
|
3671
|
+
verificationReference: ref,
|
|
3672
|
+
isVerificationComplete: true,
|
|
3673
|
+
isPollingStatus: false
|
|
3674
|
+
});
|
|
3675
|
+
clearConsentState();
|
|
3676
|
+
return;
|
|
3677
|
+
}
|
|
3678
|
+
if (response.status === "failed" || response.status === "expired") {
|
|
3679
|
+
this.stopPolling();
|
|
3680
|
+
this.store.setState({
|
|
3681
|
+
verificationError: getVerificationErrorMessage(response.error_code, response.error),
|
|
3682
|
+
verificationErrorCode: response.error_code,
|
|
3683
|
+
canRetryVerification: response.can_retry,
|
|
3684
|
+
isPollingStatus: false
|
|
3685
|
+
});
|
|
3686
|
+
return;
|
|
3687
|
+
}
|
|
3688
|
+
if (!paused) {
|
|
3689
|
+
this.statusPollTimeout = setTimeout(poll, POLL_INTERVAL);
|
|
3690
|
+
}
|
|
3691
|
+
} catch (error) {
|
|
3692
|
+
const err = error;
|
|
3693
|
+
if (err.status === 404 || err.status === 410) {
|
|
3694
|
+
this.stopPolling();
|
|
3695
|
+
this.store.setState({
|
|
3696
|
+
verificationError: "Verification session not found or expired.",
|
|
3697
|
+
verificationErrorCode: "SESSION_EXPIRED",
|
|
3698
|
+
canRetryVerification: true,
|
|
3699
|
+
isPollingStatus: false
|
|
3700
|
+
});
|
|
3701
|
+
return;
|
|
3702
|
+
}
|
|
3703
|
+
consecutiveErrors++;
|
|
3704
|
+
if (consecutiveErrors >= MAX_CONSECUTIVE_ERRORS) {
|
|
3705
|
+
this.stopPolling();
|
|
3706
|
+
this.store.setState({
|
|
3707
|
+
verificationError: err.message || "Unable to check verification status.",
|
|
3708
|
+
canRetryVerification: true,
|
|
3709
|
+
isPollingStatus: false
|
|
3710
|
+
});
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
this.statusPollTimeout = setTimeout(poll, POLL_INTERVAL);
|
|
3714
|
+
}
|
|
3715
|
+
};
|
|
3716
|
+
this.visibilityChangeHandler = () => {
|
|
3717
|
+
if (document.hidden) {
|
|
3718
|
+
paused = true;
|
|
3719
|
+
if (this.statusPollTimeout) {
|
|
3720
|
+
clearTimeout(this.statusPollTimeout);
|
|
3721
|
+
this.statusPollTimeout = null;
|
|
3722
|
+
}
|
|
3723
|
+
} else {
|
|
3724
|
+
paused = false;
|
|
3725
|
+
poll();
|
|
3726
|
+
}
|
|
3727
|
+
};
|
|
3728
|
+
document.addEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
3729
|
+
this.statusPollTimeout = setTimeout(poll, POLL_INTERVAL);
|
|
3730
|
+
}
|
|
3731
|
+
stopPolling() {
|
|
3732
|
+
var _a;
|
|
3733
|
+
if (this.statusPollTimeout) {
|
|
3734
|
+
clearTimeout(this.statusPollTimeout);
|
|
3735
|
+
this.statusPollTimeout = null;
|
|
3736
|
+
}
|
|
3737
|
+
if (this.popupCheckInterval) {
|
|
3738
|
+
clearInterval(this.popupCheckInterval);
|
|
3739
|
+
this.popupCheckInterval = null;
|
|
3740
|
+
}
|
|
3741
|
+
if (this.visibilityChangeHandler) {
|
|
3742
|
+
document.removeEventListener("visibilitychange", this.visibilityChangeHandler);
|
|
3743
|
+
this.visibilityChangeHandler = null;
|
|
3744
|
+
}
|
|
3745
|
+
(_a = this.verificationPopup) == null ? void 0 : _a.close();
|
|
3746
|
+
this.verificationPopup = null;
|
|
3747
|
+
}
|
|
3748
|
+
startPopupMonitoring(popup) {
|
|
3749
|
+
this.popupCheckInterval = setInterval(() => {
|
|
3750
|
+
if (popup.closed) {
|
|
3751
|
+
if (this.popupCheckInterval) {
|
|
3752
|
+
clearInterval(this.popupCheckInterval);
|
|
3753
|
+
this.popupCheckInterval = null;
|
|
3754
|
+
}
|
|
3755
|
+
}
|
|
3756
|
+
}, 500);
|
|
3757
|
+
}
|
|
3758
|
+
// ─── Verification Screen Handlers ───
|
|
3759
|
+
handleVerificationContinue() {
|
|
3760
|
+
this.store.setState({
|
|
3761
|
+
showVerificationScreen: false,
|
|
3762
|
+
isVerificationComplete: false
|
|
3763
|
+
});
|
|
3764
|
+
}
|
|
3765
|
+
handleVerificationRetry() {
|
|
3766
|
+
this.store.setState({
|
|
3767
|
+
showVerificationScreen: false,
|
|
3768
|
+
showGuardianForm: true,
|
|
3769
|
+
verificationError: null,
|
|
3770
|
+
verificationErrorCode: null,
|
|
3771
|
+
canRetryVerification: false,
|
|
3772
|
+
isVerificationComplete: false
|
|
3773
|
+
});
|
|
3774
|
+
}
|
|
3775
|
+
handleChangeGuardian() {
|
|
3776
|
+
this.store.setState({
|
|
3777
|
+
showVerificationScreen: false,
|
|
3778
|
+
showGuardianForm: true,
|
|
3779
|
+
verificationError: null,
|
|
3780
|
+
verificationErrorCode: null,
|
|
3781
|
+
canRetryVerification: false,
|
|
3782
|
+
isVerificationComplete: false,
|
|
3783
|
+
guardianFormData: { guardianName: "", guardianContact: "", guardianRelationship: "" }
|
|
3784
|
+
});
|
|
3785
|
+
}
|
|
3786
|
+
// ─── TTS ───
|
|
3787
|
+
updateTTSButton() {
|
|
3788
|
+
var _a, _b, _c;
|
|
3789
|
+
if (!this.ttsButton) return;
|
|
3790
|
+
if (this.ttsManager.isAvailable) {
|
|
3791
|
+
this.ttsButton.style.display = "flex";
|
|
3792
|
+
clearElement(this.ttsButton);
|
|
3793
|
+
const strokeColor = ((_c = (_b = (_a = this.options.settings) == null ? void 0 : _a.button) == null ? void 0 : _b.language) == null ? void 0 : _c.textColor) || "currentColor";
|
|
3794
|
+
if (this.ttsManager.isPlaying) {
|
|
3795
|
+
const pauseSvg = svg(
|
|
3796
|
+
"svg",
|
|
3797
|
+
{
|
|
3798
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3799
|
+
width: "16",
|
|
3800
|
+
height: "16",
|
|
3801
|
+
viewBox: "0 0 24 24",
|
|
3802
|
+
fill: "none",
|
|
3803
|
+
stroke: strokeColor,
|
|
3804
|
+
"stroke-width": "2",
|
|
3805
|
+
"stroke-linecap": "round",
|
|
3806
|
+
"stroke-linejoin": "round",
|
|
3807
|
+
"aria-hidden": "true"
|
|
3808
|
+
},
|
|
3809
|
+
svg("rect", { x: "6", y: "4", width: "4", height: "16" }),
|
|
3810
|
+
svg("rect", { x: "14", y: "4", width: "4", height: "16" })
|
|
3811
|
+
);
|
|
3812
|
+
this.ttsButton.appendChild(pauseSvg);
|
|
3813
|
+
this.ttsButton.setAttribute("aria-label", "Pause audio playback");
|
|
3814
|
+
this.ttsButton.setAttribute("title", "Pause Talkback");
|
|
3815
|
+
} else {
|
|
3816
|
+
const speakerSvg = svg(
|
|
3817
|
+
"svg",
|
|
3818
|
+
{
|
|
3819
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
3820
|
+
width: "16",
|
|
3821
|
+
height: "16",
|
|
3822
|
+
viewBox: "0 0 24 24",
|
|
3823
|
+
fill: "none",
|
|
3824
|
+
stroke: strokeColor,
|
|
3825
|
+
"stroke-width": "2",
|
|
3826
|
+
"stroke-linecap": "round",
|
|
3827
|
+
"stroke-linejoin": "round",
|
|
3828
|
+
"aria-hidden": "true"
|
|
3829
|
+
},
|
|
3830
|
+
svg("polygon", { points: "11 5 6 9 2 9 2 15 6 15 11 19 11 5" }),
|
|
3831
|
+
svg("path", { d: "M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" })
|
|
3832
|
+
);
|
|
3833
|
+
this.ttsButton.appendChild(speakerSvg);
|
|
3834
|
+
this.ttsButton.setAttribute("aria-label", this.ttsManager.isPaused ? "Resume audio playback" : "Start audio playback");
|
|
3835
|
+
this.ttsButton.setAttribute("title", this.ttsManager.isPaused ? "Resume Talkback" : "Start Talkback");
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
}
|
|
3839
|
+
updateButtonStates(state) {
|
|
3840
|
+
var _a;
|
|
3841
|
+
if (this.acceptBtn) {
|
|
3842
|
+
if (state.isSubmittingAccept) {
|
|
3843
|
+
this.acceptBtn.textContent = "Submitting...";
|
|
3844
|
+
this.acceptBtn.disabled = true;
|
|
3845
|
+
this.acceptBtn.style.opacity = "0.7";
|
|
3846
|
+
this.acceptBtn.style.cursor = "not-allowed";
|
|
3847
|
+
} else {
|
|
3848
|
+
const content = (_a = state.content) == null ? void 0 : _a.detail.active_config;
|
|
3849
|
+
this.acceptBtn.textContent = state.isFullyConsentedReviewMode ? this.options.reviewModeButtonText || "Continue" : this.getTranslatedText("confirm_button_text", (content == null ? void 0 : content.confirm_button_text) || "Accept");
|
|
3850
|
+
this.acceptBtn.disabled = false;
|
|
3851
|
+
this.acceptBtn.style.opacity = "1";
|
|
3852
|
+
this.acceptBtn.style.cursor = "pointer";
|
|
3853
|
+
}
|
|
3854
|
+
}
|
|
3855
|
+
if (this.declineBtn) {
|
|
3856
|
+
this.declineBtn.disabled = state.isSubmitting;
|
|
3857
|
+
this.declineBtn.style.opacity = state.isSubmitting ? "0.7" : "1";
|
|
3858
|
+
this.declineBtn.style.cursor = state.isSubmitting ? "not-allowed" : "pointer";
|
|
3859
|
+
}
|
|
3860
|
+
}
|
|
3861
|
+
};
|
|
3862
|
+
|
|
3863
|
+
// src/styles/inline.ts
|
|
3864
|
+
var inlineStyles = {
|
|
3865
|
+
optionItem: {
|
|
3866
|
+
display: "flex",
|
|
3867
|
+
justifyContent: "space-between",
|
|
3868
|
+
alignItems: "center",
|
|
3869
|
+
margin: 0,
|
|
3870
|
+
padding: 0,
|
|
3871
|
+
boxSizing: "border-box"
|
|
3872
|
+
},
|
|
3873
|
+
optionLeft: {
|
|
3874
|
+
display: "flex",
|
|
3875
|
+
gap: "12px",
|
|
3876
|
+
alignItems: "center",
|
|
3877
|
+
cursor: "pointer",
|
|
3878
|
+
margin: 0,
|
|
3879
|
+
padding: 0,
|
|
3880
|
+
boxSizing: "border-box"
|
|
3881
|
+
},
|
|
3882
|
+
optionTextContainer: {
|
|
3883
|
+
display: "flex",
|
|
3884
|
+
flexDirection: "column",
|
|
3885
|
+
margin: 0,
|
|
3886
|
+
padding: 0,
|
|
3887
|
+
boxSizing: "border-box"
|
|
3888
|
+
},
|
|
3889
|
+
optionTitle: {
|
|
3890
|
+
fontWeight: 500,
|
|
3891
|
+
fontSize: "16px",
|
|
3892
|
+
lineHeight: "150%",
|
|
3893
|
+
letterSpacing: "0.2px",
|
|
3894
|
+
color: "#101828",
|
|
3895
|
+
margin: 0,
|
|
3896
|
+
padding: 0,
|
|
3897
|
+
textAlign: "left"
|
|
3898
|
+
},
|
|
3899
|
+
optionDescription: {
|
|
3900
|
+
fontWeight: 400,
|
|
3901
|
+
fontSize: "12px",
|
|
3902
|
+
lineHeight: "150%",
|
|
3903
|
+
letterSpacing: "0.2px",
|
|
3904
|
+
color: "#475467",
|
|
3905
|
+
verticalAlign: "middle",
|
|
3906
|
+
margin: 0,
|
|
3907
|
+
padding: 0,
|
|
3908
|
+
textAlign: "left"
|
|
3909
|
+
},
|
|
3910
|
+
checkboxLarge: {
|
|
3911
|
+
height: "20px",
|
|
3912
|
+
width: "20px",
|
|
3913
|
+
padding: "4px",
|
|
3914
|
+
borderRadius: "5px",
|
|
3915
|
+
border: "2px solid #d0d5dd",
|
|
3916
|
+
margin: 0,
|
|
3917
|
+
boxSizing: "border-box",
|
|
3918
|
+
appearance: "none",
|
|
3919
|
+
WebkitAppearance: "none",
|
|
3920
|
+
MozAppearance: "none",
|
|
3921
|
+
cursor: "pointer",
|
|
3922
|
+
position: "relative",
|
|
3923
|
+
backgroundColor: "transparent"
|
|
3924
|
+
},
|
|
3925
|
+
dataElementsContainer: {
|
|
3926
|
+
marginLeft: "27px",
|
|
3927
|
+
display: "flex",
|
|
3928
|
+
flexDirection: "column",
|
|
3929
|
+
margin: 0,
|
|
3930
|
+
padding: 0,
|
|
3931
|
+
boxSizing: "border-box",
|
|
3932
|
+
paddingLeft: "22px"
|
|
3933
|
+
},
|
|
3934
|
+
dataElementItem: {
|
|
3935
|
+
display: "flex",
|
|
3936
|
+
alignItems: "center",
|
|
3937
|
+
gap: "10px",
|
|
3938
|
+
justifyContent: "space-between",
|
|
3939
|
+
margin: 0,
|
|
3940
|
+
padding: 0,
|
|
3941
|
+
boxSizing: "border-box",
|
|
3942
|
+
minHeight: "24px"
|
|
3943
|
+
},
|
|
3944
|
+
dataElementText: {
|
|
3945
|
+
fontWeight: 400,
|
|
3946
|
+
fontSize: "14px",
|
|
3947
|
+
lineHeight: "150%",
|
|
3948
|
+
letterSpacing: "0.2px",
|
|
3949
|
+
color: "#344054",
|
|
3950
|
+
margin: 0,
|
|
3951
|
+
padding: 0,
|
|
3952
|
+
textAlign: "left"
|
|
3953
|
+
},
|
|
3954
|
+
checkboxSmall: {
|
|
3955
|
+
height: "16px",
|
|
3956
|
+
width: "16px",
|
|
3957
|
+
padding: "4px",
|
|
3958
|
+
borderRadius: "5px",
|
|
3959
|
+
border: "2px solid #d0d5dd",
|
|
3960
|
+
margin: 0,
|
|
3961
|
+
boxSizing: "border-box",
|
|
3962
|
+
appearance: "none",
|
|
3963
|
+
WebkitAppearance: "none",
|
|
3964
|
+
MozAppearance: "none",
|
|
3965
|
+
cursor: "pointer",
|
|
3966
|
+
position: "relative",
|
|
3967
|
+
backgroundColor: "transparent"
|
|
3968
|
+
},
|
|
3969
|
+
loadingContainer: {
|
|
3970
|
+
display: "flex",
|
|
3971
|
+
flexDirection: "column",
|
|
3972
|
+
alignItems: "center",
|
|
3973
|
+
justifyContent: "center",
|
|
3974
|
+
padding: "2rem",
|
|
3975
|
+
minHeight: "200px",
|
|
3976
|
+
margin: 0,
|
|
3977
|
+
boxSizing: "border-box"
|
|
3978
|
+
},
|
|
3979
|
+
loadingSpinner: {
|
|
3980
|
+
width: "40px",
|
|
3981
|
+
height: "40px",
|
|
3982
|
+
border: "4px solid #f3f3f3",
|
|
3983
|
+
borderTop: "4px solid #3498db",
|
|
3984
|
+
borderRadius: "50%",
|
|
3985
|
+
animation: "spin 1s linear infinite",
|
|
3986
|
+
marginBottom: "1rem",
|
|
3987
|
+
boxSizing: "border-box"
|
|
3988
|
+
}
|
|
3989
|
+
};
|
|
3990
|
+
|
|
3991
|
+
// src/inline/purposes.ts
|
|
3992
|
+
function renderInlinePurposes(purposes, options) {
|
|
3993
|
+
var _a;
|
|
3994
|
+
const {
|
|
3995
|
+
selectedPurposes,
|
|
3996
|
+
collapsedPurposes,
|
|
3997
|
+
selectedDataElements,
|
|
3998
|
+
settings,
|
|
3999
|
+
isMobile,
|
|
4000
|
+
getTranslatedText,
|
|
4001
|
+
onPurposeToggle,
|
|
4002
|
+
onPurposeCollapse,
|
|
4003
|
+
onDataElementToggle
|
|
4004
|
+
} = options;
|
|
4005
|
+
const checkboxes = /* @__PURE__ */ new Map();
|
|
4006
|
+
const collapseButtons = /* @__PURE__ */ new Map();
|
|
4007
|
+
const dataElementContainers = /* @__PURE__ */ new Map();
|
|
4008
|
+
const chevrons = /* @__PURE__ */ new Map();
|
|
4009
|
+
const titleElements = /* @__PURE__ */ new Map();
|
|
4010
|
+
const descriptionElements = /* @__PURE__ */ new Map();
|
|
4011
|
+
const dataElementLabels = /* @__PURE__ */ new Map();
|
|
4012
|
+
const headingColor = (settings == null ? void 0 : settings.headingColor) || "#101828";
|
|
4013
|
+
const textColor = (settings == null ? void 0 : settings.textColor) || "#344054";
|
|
4014
|
+
const container = el("div", {
|
|
4015
|
+
style: { display: "flex", flexDirection: "column", gap: "16px" }
|
|
4016
|
+
});
|
|
4017
|
+
for (const purpose of purposes) {
|
|
4018
|
+
const purposeWrapper = el("div");
|
|
4019
|
+
const chevron = svg(
|
|
4020
|
+
"svg",
|
|
4021
|
+
{
|
|
4022
|
+
width: "7",
|
|
4023
|
+
height: "12",
|
|
4024
|
+
viewBox: "0 0 7 12",
|
|
4025
|
+
fill: "none",
|
|
4026
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
4027
|
+
stroke: (settings == null ? void 0 : settings.headingColor) || "#323B4B",
|
|
4028
|
+
"stroke-width": "2",
|
|
4029
|
+
"stroke-linecap": "round",
|
|
4030
|
+
"stroke-linejoin": "round",
|
|
4031
|
+
style: {
|
|
4032
|
+
transform: !collapsedPurposes[purpose.uuid] ? "rotate(90deg)" : "rotate(0deg)",
|
|
4033
|
+
transition: "transform 0.3s ease"
|
|
4034
|
+
},
|
|
4035
|
+
"aria-hidden": "true"
|
|
4036
|
+
},
|
|
4037
|
+
svg("path", { d: "M1 1L6 6L1 11" })
|
|
4038
|
+
);
|
|
4039
|
+
chevrons.set(purpose.uuid, chevron);
|
|
4040
|
+
const chevronContainer = el(
|
|
4041
|
+
"div",
|
|
4042
|
+
{
|
|
4043
|
+
style: {
|
|
4044
|
+
display: "flex",
|
|
4045
|
+
alignItems: "center",
|
|
4046
|
+
justifyContent: "center",
|
|
4047
|
+
marginLeft: "5px"
|
|
4048
|
+
}
|
|
4049
|
+
},
|
|
4050
|
+
chevron
|
|
4051
|
+
);
|
|
4052
|
+
const titleEl = el("h2", {
|
|
4053
|
+
style: {
|
|
4054
|
+
...inlineStyles.optionTitle,
|
|
4055
|
+
fontSize: isMobile ? "14px" : "16px",
|
|
4056
|
+
color: headingColor
|
|
4057
|
+
}
|
|
4058
|
+
});
|
|
4059
|
+
titleEl.textContent = getTranslatedText("purposes.name", purpose.name, purpose.uuid);
|
|
4060
|
+
titleElements.set(purpose.uuid, titleEl);
|
|
4061
|
+
const descEl = el("h3", {
|
|
4062
|
+
style: {
|
|
4063
|
+
...inlineStyles.optionDescription,
|
|
4064
|
+
fontSize: isMobile ? "11px" : "12px",
|
|
4065
|
+
color: textColor
|
|
4066
|
+
}
|
|
4067
|
+
});
|
|
4068
|
+
descEl.textContent = getTranslatedText("purposes.description", purpose.description, purpose.uuid);
|
|
4069
|
+
descriptionElements.set(purpose.uuid, descEl);
|
|
4070
|
+
const textContainer = el("div", { style: inlineStyles.optionTextContainer }, titleEl, descEl);
|
|
4071
|
+
const collapseBtn = el("div", {
|
|
4072
|
+
style: inlineStyles.optionLeft,
|
|
4073
|
+
role: "button",
|
|
4074
|
+
tabIndex: "0",
|
|
4075
|
+
"aria-expanded": String(!collapsedPurposes[purpose.uuid]),
|
|
4076
|
+
"aria-controls": `purpose-${purpose.uuid}`,
|
|
4077
|
+
"aria-label": `${collapsedPurposes[purpose.uuid] ? "Expand" : "Collapse"} ${getTranslatedText("purposes.name", purpose.name, purpose.uuid)} details`,
|
|
4078
|
+
onClick: () => onPurposeCollapse(purpose.uuid),
|
|
4079
|
+
onKeydown: (e) => {
|
|
4080
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
4081
|
+
e.preventDefault();
|
|
4082
|
+
onPurposeCollapse(purpose.uuid);
|
|
4083
|
+
}
|
|
4084
|
+
}
|
|
4085
|
+
}, chevronContainer, textContainer);
|
|
4086
|
+
collapseButtons.set(purpose.uuid, collapseBtn);
|
|
4087
|
+
const purposeCheckbox = el("input", {
|
|
4088
|
+
type: "checkbox",
|
|
4089
|
+
checked: selectedPurposes[purpose.uuid] || false,
|
|
4090
|
+
"aria-label": `Select all data elements for ${getTranslatedText("purposes.name", purpose.name, purpose.uuid)}`,
|
|
4091
|
+
onChange: () => onPurposeToggle(purpose.uuid)
|
|
4092
|
+
});
|
|
4093
|
+
checkboxes.set(purpose.uuid, purposeCheckbox);
|
|
4094
|
+
const optionRow = el("div", { style: inlineStyles.optionItem }, collapseBtn, purposeCheckbox);
|
|
4095
|
+
const deContainer = el("div", {
|
|
4096
|
+
id: `purpose-${purpose.uuid}`,
|
|
4097
|
+
style: inlineStyles.dataElementsContainer
|
|
4098
|
+
});
|
|
4099
|
+
if (purpose.data_elements && purpose.data_elements.length > 0) {
|
|
4100
|
+
for (const dataElement of purpose.data_elements) {
|
|
4101
|
+
const combinedId = `${purpose.uuid}-${dataElement.uuid}`;
|
|
4102
|
+
const labelSpan = el("span", {
|
|
4103
|
+
style: {
|
|
4104
|
+
...inlineStyles.dataElementText,
|
|
4105
|
+
fontSize: isMobile ? "12px" : "14px",
|
|
4106
|
+
color: textColor
|
|
4107
|
+
}
|
|
4108
|
+
});
|
|
4109
|
+
labelSpan.textContent = getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid);
|
|
4110
|
+
dataElementLabels.set(combinedId, labelSpan);
|
|
4111
|
+
if (dataElement.required) {
|
|
4112
|
+
const asterisk = el("span", {
|
|
4113
|
+
style: { color: "red", marginLeft: "4px" },
|
|
4114
|
+
"aria-label": "required"
|
|
4115
|
+
});
|
|
4116
|
+
asterisk.textContent = "*";
|
|
4117
|
+
labelSpan.appendChild(asterisk);
|
|
4118
|
+
}
|
|
4119
|
+
const deCheckbox = el("input", {
|
|
4120
|
+
type: "checkbox",
|
|
4121
|
+
checked: selectedDataElements[combinedId] || false,
|
|
4122
|
+
"aria-label": `Select ${getTranslatedText("data_elements.name", dataElement.name, dataElement.uuid)}${dataElement.required ? " (required)" : ""}`,
|
|
4123
|
+
onChange: () => onDataElementToggle(dataElement.uuid, purpose.uuid)
|
|
4124
|
+
});
|
|
4125
|
+
checkboxes.set(combinedId, deCheckbox);
|
|
4126
|
+
const deRow = el("div", { style: inlineStyles.dataElementItem }, labelSpan, deCheckbox);
|
|
4127
|
+
deContainer.appendChild(deRow);
|
|
4128
|
+
}
|
|
4129
|
+
}
|
|
4130
|
+
dataElementContainers.set(purpose.uuid, deContainer);
|
|
4131
|
+
if (!collapsedPurposes[purpose.uuid] && ((_a = purpose.data_elements) == null ? void 0 : _a.length) > 0) {
|
|
4132
|
+
deContainer.style.display = "flex";
|
|
4133
|
+
} else {
|
|
4134
|
+
deContainer.style.display = "none";
|
|
4135
|
+
}
|
|
4136
|
+
purposeWrapper.appendChild(optionRow);
|
|
4137
|
+
purposeWrapper.appendChild(deContainer);
|
|
4138
|
+
container.appendChild(purposeWrapper);
|
|
4139
|
+
}
|
|
4140
|
+
return {
|
|
4141
|
+
container,
|
|
4142
|
+
checkboxes,
|
|
4143
|
+
collapseButtons,
|
|
4144
|
+
dataElementContainers,
|
|
4145
|
+
chevrons,
|
|
4146
|
+
titleElements,
|
|
4147
|
+
descriptionElements,
|
|
4148
|
+
dataElementLabels
|
|
4149
|
+
};
|
|
4150
|
+
}
|
|
4151
|
+
|
|
4152
|
+
// src/inline/index.ts
|
|
4153
|
+
var InlineController = class {
|
|
4154
|
+
constructor(options) {
|
|
4155
|
+
this.targetEl = null;
|
|
4156
|
+
this.rootEl = null;
|
|
4157
|
+
this.purposeRefs = null;
|
|
4158
|
+
this.errorEl = null;
|
|
4159
|
+
this.hasSubmitted = false;
|
|
4160
|
+
this.unsubscribe = null;
|
|
4161
|
+
this.options = options;
|
|
4162
|
+
this.store = new Store({
|
|
4163
|
+
content: null,
|
|
4164
|
+
isLoading: true,
|
|
4165
|
+
isSubmitting: false,
|
|
4166
|
+
selectedLanguage: options.language || "en",
|
|
4167
|
+
collapsedPurposes: {},
|
|
4168
|
+
selectedPurposes: {},
|
|
4169
|
+
selectedDataElements: {},
|
|
4170
|
+
hasAlreadyConsented: false,
|
|
4171
|
+
fetchError: null,
|
|
4172
|
+
errorMessage: null
|
|
4173
|
+
});
|
|
4174
|
+
this.mediaQuery = createMediaQuery("(max-width: 768px)");
|
|
4175
|
+
}
|
|
4176
|
+
async init() {
|
|
4177
|
+
injectCheckboxStyles();
|
|
4178
|
+
if (typeof this.options.target === "string") {
|
|
4179
|
+
this.targetEl = document.querySelector(this.options.target);
|
|
4180
|
+
if (!this.targetEl) {
|
|
4181
|
+
throw new Error(`Target element not found: ${this.options.target}`);
|
|
4182
|
+
}
|
|
4183
|
+
} else {
|
|
4184
|
+
this.targetEl = this.options.target;
|
|
4185
|
+
}
|
|
4186
|
+
this.unsubscribe = this.store.subscribe((state, prevState) => {
|
|
4187
|
+
this.handleStateChange(state, prevState);
|
|
4188
|
+
});
|
|
4189
|
+
this.mediaQuery.subscribe(() => this.rebuildUI());
|
|
4190
|
+
this.renderLoading();
|
|
4191
|
+
await this.fetchNotice();
|
|
4192
|
+
}
|
|
4193
|
+
destroy() {
|
|
4194
|
+
var _a;
|
|
4195
|
+
(_a = this.unsubscribe) == null ? void 0 : _a.call(this);
|
|
4196
|
+
this.mediaQuery.destroy();
|
|
4197
|
+
this.store.destroy();
|
|
4198
|
+
if (this.targetEl && this.rootEl) {
|
|
4199
|
+
this.targetEl.removeChild(this.rootEl);
|
|
4200
|
+
}
|
|
4201
|
+
this.rootEl = null;
|
|
4202
|
+
this.purposeRefs = null;
|
|
4203
|
+
}
|
|
4204
|
+
updateToken(accessToken) {
|
|
4205
|
+
this.options = { ...this.options, accessToken };
|
|
4206
|
+
this.tryAutoSubmit();
|
|
4207
|
+
}
|
|
4208
|
+
// ─── Private ───
|
|
4209
|
+
async fetchNotice() {
|
|
4210
|
+
var _a, _b, _c, _d;
|
|
4211
|
+
this.store.setState({ isLoading: true, fetchError: null });
|
|
4212
|
+
try {
|
|
4213
|
+
const data = await fetchConsentContentPublic({
|
|
4214
|
+
org_uuid: this.options.org_uuid,
|
|
4215
|
+
workspace_uuid: this.options.workspace_uuid,
|
|
4216
|
+
notice_uuid: this.options.notice_uuid,
|
|
4217
|
+
baseUrl: this.options.baseUrl,
|
|
4218
|
+
language: this.options.language,
|
|
4219
|
+
specific_uuid: this.options.applicationId
|
|
4220
|
+
});
|
|
4221
|
+
const activeConfig = data.detail.active_config;
|
|
4222
|
+
const initialCollapsed = {};
|
|
4223
|
+
const initialPurposes = {};
|
|
4224
|
+
const initialElements = {};
|
|
4225
|
+
for (const purpose of activeConfig.purposes) {
|
|
4226
|
+
initialCollapsed[purpose.uuid] = true;
|
|
4227
|
+
initialPurposes[purpose.uuid] = false;
|
|
4228
|
+
for (const de of purpose.data_elements) {
|
|
4229
|
+
initialElements[`${purpose.uuid}-${de.uuid}`] = false;
|
|
4230
|
+
}
|
|
4231
|
+
}
|
|
4232
|
+
this.store.setState({
|
|
4233
|
+
content: activeConfig,
|
|
4234
|
+
isLoading: false,
|
|
4235
|
+
selectedLanguage: activeConfig.default_language || this.options.language || "en",
|
|
4236
|
+
collapsedPurposes: initialCollapsed,
|
|
4237
|
+
selectedPurposes: initialPurposes,
|
|
4238
|
+
selectedDataElements: initialElements
|
|
4239
|
+
});
|
|
4240
|
+
} catch (err) {
|
|
4241
|
+
const error = err;
|
|
4242
|
+
if (error.status === 409) {
|
|
4243
|
+
this.store.setState({ hasAlreadyConsented: true, isLoading: false });
|
|
4244
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4245
|
+
} else {
|
|
4246
|
+
this.store.setState({ fetchError: error, isLoading: false });
|
|
4247
|
+
(_d = (_c = this.options).onError) == null ? void 0 : _d.call(_c, error);
|
|
4248
|
+
}
|
|
4249
|
+
}
|
|
4250
|
+
}
|
|
4251
|
+
handleStateChange(state, prevState) {
|
|
4252
|
+
if (prevState.isLoading && !state.isLoading && state.content) {
|
|
4253
|
+
this.rebuildUI();
|
|
4254
|
+
}
|
|
4255
|
+
if (state.hasAlreadyConsented || state.fetchError) {
|
|
4256
|
+
if (this.rootEl && this.targetEl) {
|
|
4257
|
+
this.targetEl.removeChild(this.rootEl);
|
|
4258
|
+
this.rootEl = null;
|
|
4259
|
+
}
|
|
4260
|
+
return;
|
|
4261
|
+
}
|
|
4262
|
+
if (state.selectedPurposes !== prevState.selectedPurposes || state.selectedDataElements !== prevState.selectedDataElements) {
|
|
4263
|
+
this.updateCheckboxes(state);
|
|
4264
|
+
this.notifyValidation(state);
|
|
4265
|
+
this.tryAutoSubmit();
|
|
4266
|
+
}
|
|
4267
|
+
if (state.collapsedPurposes !== prevState.collapsedPurposes) {
|
|
4268
|
+
this.updateCollapseState(state);
|
|
4269
|
+
}
|
|
4270
|
+
if (state.errorMessage !== prevState.errorMessage) {
|
|
4271
|
+
this.updateErrorMessage(state);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
renderLoading() {
|
|
4275
|
+
var _a;
|
|
4276
|
+
if (!this.targetEl) return;
|
|
4277
|
+
const textColor = ((_a = this.options.settings) == null ? void 0 : _a.textColor) || "#344054";
|
|
4278
|
+
this.rootEl = el(
|
|
4279
|
+
"div",
|
|
4280
|
+
{ style: inlineStyles.loadingContainer },
|
|
4281
|
+
el("div", { style: inlineStyles.loadingSpinner }),
|
|
4282
|
+
el("p", { style: { fontSize: "16px", fontWeight: 400, color: textColor } }, "Loading...")
|
|
4283
|
+
);
|
|
4284
|
+
this.targetEl.appendChild(this.rootEl);
|
|
4285
|
+
}
|
|
4286
|
+
rebuildUI() {
|
|
4287
|
+
const state = this.store.getState();
|
|
4288
|
+
if (!state.content || !this.targetEl) return;
|
|
4289
|
+
if (this.rootEl && this.targetEl.contains(this.rootEl)) {
|
|
4290
|
+
this.targetEl.removeChild(this.rootEl);
|
|
4291
|
+
}
|
|
4292
|
+
this.errorEl = el("div", {
|
|
4293
|
+
role: "alert",
|
|
4294
|
+
style: {
|
|
4295
|
+
backgroundColor: "#FEE2E2",
|
|
4296
|
+
color: "#DC2626",
|
|
4297
|
+
padding: "12px",
|
|
4298
|
+
borderRadius: "6px",
|
|
4299
|
+
fontSize: "14px",
|
|
4300
|
+
border: "1px solid #FCA5A5",
|
|
4301
|
+
display: state.errorMessage ? "block" : "none"
|
|
4302
|
+
}
|
|
4303
|
+
});
|
|
4304
|
+
if (state.errorMessage) {
|
|
4305
|
+
this.errorEl.textContent = state.errorMessage;
|
|
4306
|
+
}
|
|
4307
|
+
this.purposeRefs = renderInlinePurposes(state.content.purposes, {
|
|
4308
|
+
selectedPurposes: state.selectedPurposes,
|
|
4309
|
+
collapsedPurposes: state.collapsedPurposes,
|
|
4310
|
+
selectedDataElements: state.selectedDataElements,
|
|
4311
|
+
settings: this.options.settings,
|
|
4312
|
+
isMobile: this.mediaQuery.matches,
|
|
4313
|
+
getTranslatedText: (key, defaultText, itemId) => this.getTranslatedText(key, defaultText, itemId),
|
|
4314
|
+
onPurposeToggle: (uuid) => this.handlePurposeCheckboxChange(uuid),
|
|
4315
|
+
onPurposeCollapse: (uuid) => this.togglePurposeCollapse(uuid),
|
|
4316
|
+
onDataElementToggle: (elementUuid, purposeUuid) => this.handleDataElementCheckboxChange(elementUuid, purposeUuid)
|
|
4317
|
+
});
|
|
4318
|
+
this.rootEl = el(
|
|
4319
|
+
"div",
|
|
4320
|
+
{ style: { display: "flex", flexDirection: "column", gap: "16px" } },
|
|
4321
|
+
this.errorEl,
|
|
4322
|
+
this.purposeRefs.container
|
|
4323
|
+
);
|
|
4324
|
+
this.targetEl.appendChild(this.rootEl);
|
|
4325
|
+
this.notifyValidation(state);
|
|
4326
|
+
}
|
|
4327
|
+
getTranslatedText(key, defaultText, itemId) {
|
|
4328
|
+
var _a, _b;
|
|
4329
|
+
const state = this.store.getState();
|
|
4330
|
+
const content = state.content;
|
|
4331
|
+
if (!content) return defaultText;
|
|
4332
|
+
const lang = state.selectedLanguage;
|
|
4333
|
+
if (lang === "English" || lang === "en" || lang === "EN") {
|
|
4334
|
+
if (key === "privacy_policy_anchor_text" && content.privacy_policy_anchor_text) {
|
|
4335
|
+
return content.privacy_policy_anchor_text;
|
|
4336
|
+
}
|
|
4337
|
+
return defaultText;
|
|
4338
|
+
}
|
|
4339
|
+
const translations = content.supported_languages_and_translations;
|
|
4340
|
+
if (!translations) return defaultText;
|
|
4341
|
+
const translationMap = translations[lang];
|
|
4342
|
+
if (!translationMap) return defaultText;
|
|
4343
|
+
if (itemId) {
|
|
4344
|
+
if (key === "purposes.name" || key === "purposes.description") {
|
|
4345
|
+
const purposeData = (_a = translationMap.purposes) == null ? void 0 : _a[itemId];
|
|
4346
|
+
if (purposeData) {
|
|
4347
|
+
if (typeof purposeData === "string") {
|
|
4348
|
+
return key === "purposes.name" ? purposeData : defaultText;
|
|
4349
|
+
}
|
|
4350
|
+
return key === "purposes.name" ? purposeData.name || defaultText : purposeData.description || defaultText;
|
|
4351
|
+
}
|
|
4352
|
+
return defaultText;
|
|
4353
|
+
}
|
|
4354
|
+
if (key.startsWith("data_elements.")) {
|
|
4355
|
+
return ((_b = translationMap.data_elements) == null ? void 0 : _b[itemId]) || defaultText;
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
const value = translationMap[key];
|
|
4359
|
+
if (typeof value === "string" && value) return value;
|
|
4360
|
+
return defaultText;
|
|
4361
|
+
}
|
|
4362
|
+
togglePurposeCollapse(purposeUuid) {
|
|
4363
|
+
const state = this.store.getState();
|
|
4364
|
+
this.store.setState({
|
|
4365
|
+
collapsedPurposes: {
|
|
4366
|
+
...state.collapsedPurposes,
|
|
4367
|
+
[purposeUuid]: !state.collapsedPurposes[purposeUuid]
|
|
4368
|
+
}
|
|
4369
|
+
});
|
|
4370
|
+
}
|
|
4371
|
+
handlePurposeCheckboxChange(purposeUuid) {
|
|
4372
|
+
const state = this.store.getState();
|
|
4373
|
+
const content = state.content;
|
|
4374
|
+
if (!content) return;
|
|
4375
|
+
const purpose = content.purposes.find((p) => p.uuid === purposeUuid);
|
|
4376
|
+
if (!purpose) return;
|
|
4377
|
+
const newPurposeState = !state.selectedPurposes[purposeUuid];
|
|
4378
|
+
const updatedElements = { ...state.selectedDataElements };
|
|
4379
|
+
for (const de of purpose.data_elements) {
|
|
4380
|
+
updatedElements[`${purposeUuid}-${de.uuid}`] = newPurposeState;
|
|
4381
|
+
}
|
|
4382
|
+
this.store.setState({
|
|
4383
|
+
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: newPurposeState },
|
|
4384
|
+
selectedDataElements: updatedElements
|
|
4385
|
+
});
|
|
4386
|
+
}
|
|
4387
|
+
handleDataElementCheckboxChange(elementUuid, purposeUuid) {
|
|
4388
|
+
const state = this.store.getState();
|
|
4389
|
+
const content = state.content;
|
|
4390
|
+
if (!content) return;
|
|
4391
|
+
const purpose = content.purposes.find((p) => p.uuid === purposeUuid);
|
|
4392
|
+
if (!purpose) return;
|
|
4393
|
+
const combinedId = `${purposeUuid}-${elementUuid}`;
|
|
4394
|
+
const newElementState = !state.selectedDataElements[combinedId];
|
|
4395
|
+
const newDataElements = {
|
|
4396
|
+
...state.selectedDataElements,
|
|
4397
|
+
[combinedId]: newElementState
|
|
4398
|
+
};
|
|
4399
|
+
const requiredElements = purpose.data_elements.filter((el2) => el2.required);
|
|
4400
|
+
let shouldCheckPurpose;
|
|
4401
|
+
if (requiredElements.length > 0) {
|
|
4402
|
+
shouldCheckPurpose = requiredElements.every(
|
|
4403
|
+
(el2) => newDataElements[`${purposeUuid}-${el2.uuid}`]
|
|
4404
|
+
);
|
|
4405
|
+
} else {
|
|
4406
|
+
shouldCheckPurpose = purpose.data_elements.some(
|
|
4407
|
+
(el2) => newDataElements[`${purposeUuid}-${el2.uuid}`]
|
|
4408
|
+
);
|
|
4409
|
+
}
|
|
4410
|
+
this.store.setState({
|
|
4411
|
+
selectedDataElements: newDataElements,
|
|
4412
|
+
selectedPurposes: { ...state.selectedPurposes, [purposeUuid]: shouldCheckPurpose }
|
|
4413
|
+
});
|
|
4414
|
+
}
|
|
4415
|
+
updateCheckboxes(state) {
|
|
4416
|
+
if (!this.purposeRefs) return;
|
|
4417
|
+
for (const [key, checkbox] of this.purposeRefs.checkboxes) {
|
|
4418
|
+
if (key in state.selectedPurposes) {
|
|
4419
|
+
checkbox.checked = state.selectedPurposes[key] || false;
|
|
4420
|
+
} else {
|
|
4421
|
+
checkbox.checked = state.selectedDataElements[key] || false;
|
|
4422
|
+
}
|
|
4423
|
+
}
|
|
4424
|
+
}
|
|
4425
|
+
updateCollapseState(state) {
|
|
4426
|
+
var _a;
|
|
4427
|
+
if (!this.purposeRefs) return;
|
|
4428
|
+
for (const [purposeUuid, container] of this.purposeRefs.dataElementContainers) {
|
|
4429
|
+
const content = this.store.getState().content;
|
|
4430
|
+
const purpose = content == null ? void 0 : content.purposes.find((p) => p.uuid === purposeUuid);
|
|
4431
|
+
const isCollapsed = state.collapsedPurposes[purposeUuid];
|
|
4432
|
+
container.style.display = !isCollapsed && ((_a = purpose == null ? void 0 : purpose.data_elements) == null ? void 0 : _a.length) ? "flex" : "none";
|
|
4433
|
+
const chevron = this.purposeRefs.chevrons.get(purposeUuid);
|
|
4434
|
+
if (chevron) {
|
|
4435
|
+
chevron.style.transform = !isCollapsed ? "rotate(90deg)" : "rotate(0deg)";
|
|
4436
|
+
}
|
|
4437
|
+
const collapseBtn = this.purposeRefs.collapseButtons.get(purposeUuid);
|
|
4438
|
+
if (collapseBtn) {
|
|
4439
|
+
collapseBtn.setAttribute("aria-expanded", String(!isCollapsed));
|
|
4440
|
+
}
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4443
|
+
updateErrorMessage(state) {
|
|
4444
|
+
if (!this.errorEl) return;
|
|
4445
|
+
if (state.errorMessage) {
|
|
4446
|
+
this.errorEl.textContent = state.errorMessage;
|
|
4447
|
+
this.errorEl.style.display = "block";
|
|
4448
|
+
} else {
|
|
4449
|
+
this.errorEl.style.display = "none";
|
|
4450
|
+
}
|
|
4451
|
+
}
|
|
4452
|
+
areAllRequiredElementsChecked() {
|
|
4453
|
+
const state = this.store.getState();
|
|
4454
|
+
const content = state.content;
|
|
4455
|
+
if (!content) return false;
|
|
4456
|
+
return content.purposes.every((purpose) => {
|
|
4457
|
+
const requiredElements = purpose.data_elements.filter((el2) => el2.required);
|
|
4458
|
+
return requiredElements.every(
|
|
4459
|
+
(el2) => state.selectedDataElements[`${purpose.uuid}-${el2.uuid}`]
|
|
4460
|
+
);
|
|
4461
|
+
});
|
|
4462
|
+
}
|
|
4463
|
+
notifyValidation(state) {
|
|
4464
|
+
var _a, _b;
|
|
4465
|
+
const isValid = this.areAllRequiredElementsChecked();
|
|
4466
|
+
(_b = (_a = this.options).onValidationChange) == null ? void 0 : _b.call(_a, isValid);
|
|
4467
|
+
}
|
|
4468
|
+
async tryAutoSubmit() {
|
|
4469
|
+
var _a, _b;
|
|
4470
|
+
const state = this.store.getState();
|
|
4471
|
+
const accessToken = this.options.accessToken;
|
|
4472
|
+
if (accessToken && state.content && this.areAllRequiredElementsChecked() && !state.isSubmitting && !this.hasSubmitted) {
|
|
4473
|
+
this.hasSubmitted = true;
|
|
4474
|
+
try {
|
|
4475
|
+
await this.handleSubmit(accessToken);
|
|
4476
|
+
} catch (err) {
|
|
4477
|
+
this.hasSubmitted = false;
|
|
4478
|
+
(_b = (_a = this.options).onError) == null ? void 0 : _b.call(_a, err);
|
|
4479
|
+
}
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
async handleSubmit(tokenToUse) {
|
|
4483
|
+
var _a, _b;
|
|
4484
|
+
const state = this.store.getState();
|
|
4485
|
+
const content = state.content;
|
|
4486
|
+
if (!tokenToUse || !content) {
|
|
4487
|
+
this.store.setState({ errorMessage: "Unable to submit consent. Please try again." });
|
|
4488
|
+
throw new Error("Access token or content not available");
|
|
4489
|
+
}
|
|
4490
|
+
try {
|
|
4491
|
+
this.store.setState({ isSubmitting: true, errorMessage: null });
|
|
4492
|
+
await submitConsentEventPublic({
|
|
4493
|
+
org_uuid: this.options.org_uuid,
|
|
4494
|
+
workspace_uuid: this.options.workspace_uuid,
|
|
4495
|
+
accessToken: tokenToUse,
|
|
4496
|
+
baseUrl: this.options.baseUrl,
|
|
4497
|
+
noticeUuid: content.notice_uuid,
|
|
4498
|
+
purposes: content.purposes.map((purpose) => {
|
|
4499
|
+
const hasSelectedRequired = purpose.data_elements.some(
|
|
4500
|
+
(el2) => el2.required && (state.selectedDataElements[`${purpose.uuid}-${el2.uuid}`] || false)
|
|
4501
|
+
);
|
|
4502
|
+
const hasSelectedAny = purpose.data_elements.some(
|
|
4503
|
+
(el2) => state.selectedDataElements[`${purpose.uuid}-${el2.uuid}`] || false
|
|
4504
|
+
);
|
|
4505
|
+
const purposeSelected = state.selectedPurposes[purpose.uuid] || hasSelectedRequired || hasSelectedAny;
|
|
4506
|
+
return {
|
|
4507
|
+
...purpose,
|
|
4508
|
+
selected: purposeSelected,
|
|
4509
|
+
data_elements: purpose.data_elements.map((element) => ({
|
|
4510
|
+
...element,
|
|
4511
|
+
selected: state.selectedDataElements[`${purpose.uuid}-${element.uuid}`] || false
|
|
4512
|
+
}))
|
|
4513
|
+
};
|
|
4514
|
+
}),
|
|
4515
|
+
declined: false,
|
|
4516
|
+
meta_data: this.options.applicationId ? { specific_uuid: this.options.applicationId } : void 0
|
|
4517
|
+
});
|
|
4518
|
+
(_b = (_a = this.options).onAccept) == null ? void 0 : _b.call(_a);
|
|
4519
|
+
this.hasSubmitted = false;
|
|
4520
|
+
} catch (error) {
|
|
4521
|
+
const err = error;
|
|
4522
|
+
const message = err.status === 500 ? "An error occurred while submitting your consent. Please try again later." : err.message || "Failed to submit consent. Please try again.";
|
|
4523
|
+
this.store.setState({ errorMessage: message });
|
|
4524
|
+
this.hasSubmitted = false;
|
|
4525
|
+
throw error;
|
|
4526
|
+
} finally {
|
|
4527
|
+
this.store.setState({ isSubmitting: false });
|
|
4528
|
+
}
|
|
4529
|
+
}
|
|
4530
|
+
};
|
|
4531
|
+
|
|
4532
|
+
// src/modal/digilocker-callback.ts
|
|
4533
|
+
function getErrorMessage(errorCode) {
|
|
4534
|
+
switch (errorCode) {
|
|
4535
|
+
case "GUARDIAN_UNDER_18":
|
|
4536
|
+
return "Guardian verification failed: The guardian must be 18 years or older.";
|
|
4537
|
+
case "SESSION_EXPIRED":
|
|
4538
|
+
return "Verification session has expired. Please restart the verification process.";
|
|
4539
|
+
case "TOKEN_FAILED":
|
|
4540
|
+
return "DigiLocker authentication failed. Please try again.";
|
|
4541
|
+
case "DIGILOCKER_AUTH_FAILED":
|
|
4542
|
+
return "DigiLocker authorization was cancelled or failed. Please try again.";
|
|
4543
|
+
case "NO_AUTH_CODE":
|
|
4544
|
+
return "Missing authorization code from DigiLocker. Please try again.";
|
|
4545
|
+
default:
|
|
4546
|
+
return "Verification failed. Please try again.";
|
|
4547
|
+
}
|
|
4548
|
+
}
|
|
4549
|
+
function handleDigilockerCallback(options) {
|
|
4550
|
+
var _a, _b, _c;
|
|
4551
|
+
injectCheckboxStyles();
|
|
4552
|
+
const params = new URLSearchParams(window.location.search);
|
|
4553
|
+
const sessionToken = params.get("session_token");
|
|
4554
|
+
const urlStatus = params.get("status");
|
|
4555
|
+
const verificationReference = params.get("verification_reference");
|
|
4556
|
+
const errorCode = params.get("error_code");
|
|
4557
|
+
const persistedState = getConsentState();
|
|
4558
|
+
const originalUrl = (persistedState == null ? void 0 : persistedState.originalUrl) || "/";
|
|
4559
|
+
const storedBaseUrl = persistedState == null ? void 0 : persistedState.baseUrl;
|
|
4560
|
+
const storedSessionToken = persistedState == null ? void 0 : persistedState.sessionToken;
|
|
4561
|
+
const activeSessionToken = sessionToken || storedSessionToken;
|
|
4562
|
+
const apiBaseUrl = options.baseUrl || storedBaseUrl;
|
|
4563
|
+
let isInIframe = false;
|
|
4564
|
+
try {
|
|
4565
|
+
isInIframe = window.self !== window.top;
|
|
4566
|
+
} catch {
|
|
4567
|
+
isInIframe = true;
|
|
4568
|
+
}
|
|
4569
|
+
const container = el("div", {
|
|
4570
|
+
style: {
|
|
4571
|
+
display: "flex",
|
|
4572
|
+
flexDirection: "column",
|
|
4573
|
+
alignItems: "center",
|
|
4574
|
+
justifyContent: "center",
|
|
4575
|
+
minHeight: "200px",
|
|
4576
|
+
padding: "32px",
|
|
4577
|
+
fontFamily: "system-ui, -apple-system, sans-serif"
|
|
4578
|
+
}
|
|
4579
|
+
});
|
|
4580
|
+
const statusEl = el("p", {
|
|
4581
|
+
style: { fontSize: "16px", fontWeight: 600, color: "#101828", textAlign: "center" }
|
|
4582
|
+
});
|
|
4583
|
+
statusEl.textContent = "Processing verification...";
|
|
4584
|
+
const spinnerEl = el("div", {
|
|
4585
|
+
style: {
|
|
4586
|
+
width: "40px",
|
|
4587
|
+
height: "40px",
|
|
4588
|
+
border: "4px solid #f3f3f3",
|
|
4589
|
+
borderTop: "4px solid #3498db",
|
|
4590
|
+
borderRadius: "50%",
|
|
4591
|
+
animation: "spin 1s linear infinite",
|
|
4592
|
+
marginBottom: "16px"
|
|
4593
|
+
}
|
|
4594
|
+
});
|
|
4595
|
+
container.appendChild(spinnerEl);
|
|
4596
|
+
container.appendChild(statusEl);
|
|
4597
|
+
document.body.appendChild(container);
|
|
4598
|
+
const redirectToOriginal = (result, ref) => {
|
|
4599
|
+
updateConsentState({
|
|
4600
|
+
verificationResult: result,
|
|
4601
|
+
verificationReference: ref,
|
|
4602
|
+
flowState: result === "verified" ? "consent-form" : "error"
|
|
4603
|
+
});
|
|
4604
|
+
statusEl.textContent = result === "verified" ? "Verification successful! Redirecting..." : "Verification failed. Redirecting...";
|
|
4605
|
+
spinnerEl.style.display = "none";
|
|
4606
|
+
setTimeout(() => {
|
|
4607
|
+
window.location.href = originalUrl;
|
|
4608
|
+
}, 1500);
|
|
4609
|
+
};
|
|
4610
|
+
if (urlStatus === "verified" && verificationReference) {
|
|
4611
|
+
if (isInIframe && window.parent) {
|
|
4612
|
+
window.parent.postMessage({
|
|
4613
|
+
type: "DIGILOCKER_VERIFICATION_STATUS",
|
|
4614
|
+
status: "verified",
|
|
4615
|
+
verificationReference
|
|
4616
|
+
}, window.location.origin);
|
|
4617
|
+
statusEl.textContent = "Verification successful!";
|
|
4618
|
+
spinnerEl.style.display = "none";
|
|
4619
|
+
return;
|
|
4620
|
+
}
|
|
4621
|
+
(_a = options.onSuccess) == null ? void 0 : _a.call(options, verificationReference);
|
|
4622
|
+
redirectToOriginal("verified", verificationReference);
|
|
4623
|
+
return;
|
|
4624
|
+
}
|
|
4625
|
+
if (urlStatus === "failed" || urlStatus === "expired") {
|
|
4626
|
+
const errorMsg = getErrorMessage(errorCode);
|
|
4627
|
+
if (isInIframe && window.parent) {
|
|
4628
|
+
window.parent.postMessage({
|
|
4629
|
+
type: "DIGILOCKER_VERIFICATION_STATUS",
|
|
4630
|
+
status: urlStatus,
|
|
4631
|
+
errorCode,
|
|
4632
|
+
error: errorMsg
|
|
4633
|
+
}, window.location.origin);
|
|
4634
|
+
statusEl.textContent = errorMsg;
|
|
4635
|
+
statusEl.style.color = "#DC2626";
|
|
4636
|
+
spinnerEl.style.display = "none";
|
|
4637
|
+
return;
|
|
4638
|
+
}
|
|
4639
|
+
(_b = options.onError) == null ? void 0 : _b.call(options, new Error(errorMsg), errorCode || void 0);
|
|
4640
|
+
redirectToOriginal(urlStatus);
|
|
4641
|
+
return;
|
|
4642
|
+
}
|
|
4643
|
+
if (!activeSessionToken) {
|
|
4644
|
+
statusEl.textContent = "No verification session found.";
|
|
4645
|
+
statusEl.style.color = "#DC2626";
|
|
4646
|
+
spinnerEl.style.display = "none";
|
|
4647
|
+
(_c = options.onError) == null ? void 0 : _c.call(options, new Error("No session token available"));
|
|
4648
|
+
return;
|
|
4649
|
+
}
|
|
4650
|
+
let retryCount = 0;
|
|
4651
|
+
const MAX_RETRIES = 3;
|
|
4652
|
+
const pollStatus = async () => {
|
|
4653
|
+
var _a2, _b2, _c2, _d, _e;
|
|
4654
|
+
try {
|
|
4655
|
+
const response = await verifyGuardianStatus({
|
|
4656
|
+
accessToken: options.accessToken,
|
|
4657
|
+
baseUrl: apiBaseUrl,
|
|
4658
|
+
sessionToken: activeSessionToken
|
|
4659
|
+
});
|
|
4660
|
+
if (response.status === "verified") {
|
|
4661
|
+
const ref = response.verification_reference || ((_a2 = response.guardian_details) == null ? void 0 : _a2.verification_reference);
|
|
4662
|
+
if (isInIframe && window.parent) {
|
|
4663
|
+
window.parent.postMessage({
|
|
4664
|
+
type: "DIGILOCKER_VERIFICATION_STATUS",
|
|
4665
|
+
status: "verified",
|
|
4666
|
+
verificationReference: ref
|
|
4667
|
+
}, window.location.origin);
|
|
4668
|
+
statusEl.textContent = "Verification successful!";
|
|
4669
|
+
spinnerEl.style.display = "none";
|
|
4670
|
+
return;
|
|
4671
|
+
}
|
|
4672
|
+
if (ref) (_b2 = options.onSuccess) == null ? void 0 : _b2.call(options, ref);
|
|
4673
|
+
redirectToOriginal("verified", ref || void 0);
|
|
4674
|
+
return;
|
|
4675
|
+
}
|
|
4676
|
+
if (response.status === "failed" || response.status === "expired") {
|
|
4677
|
+
const errorMsg = getVerificationError(response.error_code, response.error);
|
|
4678
|
+
if (isInIframe && window.parent) {
|
|
4679
|
+
window.parent.postMessage({
|
|
4680
|
+
type: "DIGILOCKER_VERIFICATION_STATUS",
|
|
4681
|
+
status: response.status,
|
|
4682
|
+
errorCode: response.error_code,
|
|
4683
|
+
error: errorMsg
|
|
4684
|
+
}, window.location.origin);
|
|
4685
|
+
statusEl.textContent = errorMsg;
|
|
4686
|
+
statusEl.style.color = "#DC2626";
|
|
4687
|
+
spinnerEl.style.display = "none";
|
|
4688
|
+
return;
|
|
4689
|
+
}
|
|
4690
|
+
(_c2 = options.onError) == null ? void 0 : _c2.call(options, new Error(errorMsg), response.error_code || void 0);
|
|
4691
|
+
redirectToOriginal(response.status);
|
|
4692
|
+
return;
|
|
4693
|
+
}
|
|
4694
|
+
retryCount++;
|
|
4695
|
+
if (retryCount > MAX_RETRIES) {
|
|
4696
|
+
const timeoutMsg = "Verification is taking too long. Please return to the original page.";
|
|
4697
|
+
statusEl.textContent = timeoutMsg;
|
|
4698
|
+
statusEl.style.color = "#DC2626";
|
|
4699
|
+
spinnerEl.style.display = "none";
|
|
4700
|
+
(_d = options.onError) == null ? void 0 : _d.call(options, new Error(timeoutMsg));
|
|
4701
|
+
redirectToOriginal("failed");
|
|
4702
|
+
return;
|
|
4703
|
+
}
|
|
4704
|
+
const delay = 3e3 * Math.pow(2, retryCount - 1);
|
|
4705
|
+
statusEl.textContent = "Still processing... Please wait.";
|
|
4706
|
+
setTimeout(pollStatus, delay);
|
|
4707
|
+
} catch (error) {
|
|
4708
|
+
retryCount++;
|
|
4709
|
+
if (retryCount > MAX_RETRIES) {
|
|
4710
|
+
const errMsg = error.message || "Failed to check verification status.";
|
|
4711
|
+
statusEl.textContent = errMsg;
|
|
4712
|
+
statusEl.style.color = "#DC2626";
|
|
4713
|
+
spinnerEl.style.display = "none";
|
|
4714
|
+
(_e = options.onError) == null ? void 0 : _e.call(options, error);
|
|
4715
|
+
redirectToOriginal("failed");
|
|
4716
|
+
return;
|
|
4717
|
+
}
|
|
4718
|
+
const delay = 3e3 * Math.pow(2, retryCount - 1);
|
|
4719
|
+
setTimeout(pollStatus, delay);
|
|
4720
|
+
}
|
|
4721
|
+
};
|
|
4722
|
+
setTimeout(pollStatus, 3e3);
|
|
4723
|
+
}
|
|
4724
|
+
function getVerificationError(errorCode, fallbackError) {
|
|
4725
|
+
return getErrorMessage(errorCode) || fallbackError || "Verification failed. Please try again.";
|
|
4726
|
+
}
|
|
4727
|
+
|
|
4728
|
+
// src/index.ts
|
|
4729
|
+
var activeModal = null;
|
|
4730
|
+
var activeInline = null;
|
|
4731
|
+
function init(options) {
|
|
4732
|
+
destroy();
|
|
4733
|
+
activeModal = new ModalController(options);
|
|
4734
|
+
activeModal.init().catch((err) => {
|
|
4735
|
+
var _a;
|
|
4736
|
+
(_a = options.onError) == null ? void 0 : _a.call(options, err);
|
|
4737
|
+
});
|
|
4738
|
+
}
|
|
4739
|
+
function inline(options) {
|
|
4740
|
+
if (activeInline) {
|
|
4741
|
+
activeInline.destroy();
|
|
4742
|
+
activeInline = null;
|
|
4743
|
+
}
|
|
4744
|
+
activeInline = new InlineController(options);
|
|
4745
|
+
activeInline.init().catch((err) => {
|
|
4746
|
+
var _a;
|
|
4747
|
+
(_a = options.onError) == null ? void 0 : _a.call(options, err);
|
|
4748
|
+
});
|
|
4749
|
+
}
|
|
4750
|
+
function destroy() {
|
|
4751
|
+
if (activeModal) {
|
|
4752
|
+
activeModal.destroy();
|
|
4753
|
+
activeModal = null;
|
|
4754
|
+
}
|
|
4755
|
+
if (activeInline) {
|
|
4756
|
+
activeInline.destroy();
|
|
4757
|
+
activeInline = null;
|
|
4758
|
+
}
|
|
4759
|
+
}
|
|
4760
|
+
function digilockerCallback(options) {
|
|
4761
|
+
handleDigilockerCallback(options);
|
|
4762
|
+
}
|
|
4763
|
+
var RedactoConsent = {
|
|
4764
|
+
init,
|
|
4765
|
+
inline,
|
|
4766
|
+
destroy,
|
|
4767
|
+
digilockerCallback,
|
|
4768
|
+
version: "0.1.0"
|
|
4769
|
+
};
|
|
4770
|
+
var src_default = RedactoConsent;
|