@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.
@@ -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;